From 648c83ea032831379b61624b6bcd3d743e4f94b9 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 10:18:59 +0000 Subject: [PATCH 0001/1302] Fix ejabberdctl output formatting (#3979) ECMA-48 SGR sequence ESC [21m is actually 'set double underline' but was incorrectly implemented as 'set normal intensity' in Linux prior to 4.17. The correct sequence for 'set normal intensity' is ESC [22m, which fixes output formatting of 'ejabberdctl' and 'ejabberdctl help' on macos. --- src/ejabberd_ctl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 1dca5af7a..314dd7d98 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -498,7 +498,7 @@ is_supported_args(Args) -> %% Commands are Bold -define(B1, "\e[1m"). --define(B2, "\e[21m"). +-define(B2, "\e[22m"). -define(C(S), case ShCode of true -> [?B1, S, ?B2]; false -> S end). %% Arguments are Dim From b6dde410003d04c990a9d731b91b16dc477384a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 19 Jan 2023 11:45:33 +0100 Subject: [PATCH 0002/1302] Improve output in gen_html_doc_for_commands command --- src/ejabberd_commands_doc.erl | 41 +++++++++++++++-------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index ca446e5ad..bab76209e 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -33,9 +33,11 @@ -include("ejabberd_commands.hrl"). -define(RAW(V), if HTMLOutput -> fxml:crypt(iolist_to_binary(V)); true -> iolist_to_binary(V) end). --define(TAG(N), if HTMLOutput -> [<<"<", ??N, "/>">>]; true -> md_tag(N, <<"">>) end). --define(TAG(N, V), if HTMLOutput -> [<<"<", ??N, ">">>, V, <<"">>]; true -> md_tag(N, V) end). --define(TAG(N, C, V), if HTMLOutput -> [<<"<", ??N, " class='", C, "'>">>, V, <<"">>]; true -> md_tag(N, V) end). +-define(TAG_BIN(N), (atom_to_binary(N, latin1))/binary). +-define(TAG_STR(N), atom_to_list(N)). +-define(TAG(N), if HTMLOutput -> [<<"<", ?TAG_BIN(N), "/>">>]; true -> md_tag(N, <<"">>) end). +-define(TAG(N, V), if HTMLOutput -> [<<"<", ?TAG_BIN(N), ">">>, V, <<"">>]; true -> md_tag(N, V) end). +-define(TAG(N, C, V), if HTMLOutput -> [<<"<", ?TAG_BIN(N), " class='", C, "'>">>, V, <<"">>]; true -> md_tag(N, V) end). -define(TAG_R(N, V), ?TAG(N, ?RAW(V))). -define(TAG_R(N, C, V), ?TAG(N, C, ?RAW(V))). -define(SPAN(N, V), ?TAG_R(span, ??N, V)). @@ -414,9 +416,9 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, _ -> ?TAG('div', "note-down", ?RAW(Note)) end, - [NoteEl, - ?TAG(h1, atom_to_list(Name)), + [?TAG(h1, atom_to_list(Name)), ?TAG(p, ?RAW(Desc)), + NoteEl, case LongDesc of "" -> []; _ -> ?TAG(p, ?RAW(LongDesc)) @@ -434,24 +436,17 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, end. find_commands_definitions() -> - case code:lib_dir(ejabberd, ebin) of - {error, _} -> - lists:map(fun({N, _, _}) -> - ejabberd_commands:get_command_definition(N) - end, ejabberd_commands:list_commands()); - Path -> - lists:flatmap(fun(P) -> - Mod = list_to_atom(filename:rootname(P)), - code:ensure_loaded(Mod), - Cs = case erlang:function_exported(Mod, get_commands_spec, 0) of - true -> - apply(Mod, get_commands_spec, []); - _ -> - [] - end, - [C#ejabberd_commands{definer = Mod} || C <- Cs] - end, filelib:wildcard("*.beam", Path)) - end. + lists:flatmap( + fun(Mod) -> + code:ensure_loaded(Mod), + Cs = case erlang:function_exported(Mod, get_commands_spec, 0) of + true -> + apply(Mod, get_commands_spec, []); + _ -> + [] + end, + [C#ejabberd_commands{definer = Mod} || C <- Cs] + end, ejabberd_config:beams(all)). generate_html_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), From 758c87f56489c479fe44413389802ca1da1ed278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 19 Jan 2023 13:24:51 +0100 Subject: [PATCH 0003/1302] Revert notes placement when generating markdown api commands documentation --- src/ejabberd_commands_doc.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index bab76209e..5f2cb0e04 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -415,14 +415,19 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, "" -> []; _ -> ?TAG('div', "note-down", ?RAW(Note)) end, + {NotePre, NotePost} = + if HTMLOutput -> {[], NoteEl}; + true -> {NoteEl, []} + end, - [?TAG(h1, atom_to_list(Name)), + [NotePre, + ?TAG(h1, atom_to_list(Name)), ?TAG(p, ?RAW(Desc)), - NoteEl, case LongDesc of "" -> []; _ -> ?TAG(p, ?RAW(LongDesc)) end, + NotePost, ?TAG(h2, <<"Arguments:">>), ArgsText, ?TAG(h2, <<"Result:">>), ResultText, ?TAG(h2, <<"Tags:">>), ?TAG(p, TagsText)] From ec6f5c17c8fd86b94089735945ca0625a2c6567a Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Mon, 12 Dec 2022 16:04:18 +0000 Subject: [PATCH 0004/1302] Correct README for creating test docker MS SQL DB --- test/docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/docker/README.md b/test/docker/README.md index b19321215..93b1a3504 100644 --- a/test/docker/README.md +++ b/test/docker/README.md @@ -21,7 +21,7 @@ The following commands will create the necessary login, user and database, will ``` docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql -docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /mssql.sql +docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql ``` ## Running tests From 5e94fdcfd54fc44e2332c9cf265901635cf529e3 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Fri, 9 Dec 2022 09:50:52 +0000 Subject: [PATCH 0005/1302] MS SQL schema fixes * Add missing 'mix' tables and indexes * Fix text vs varchar issues Various tests triggered this error: The data types text and varchar are incompatible in the equal to operator. Caused by incompatible 'text' columns in muc_online_room, muc_online_users, pubsub_node_option, and pubsub_node tables. * Fix definition of mqtt_pub table This table incorrectly included 'server_host' column in old schema, and had other inconsistencies. --- sql/mssql.sql | 126 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 32 deletions(-) diff --git a/sql/mssql.sql b/sql/mssql.sql index 928f19fab..355b5316b 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -118,10 +118,10 @@ CREATE INDEX [muc_room_host_created_at] ON [muc_registered] (host, nick) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_online_room] ( - [name] [varchar] (250) NOT NULL, - [host] [varchar] (250) NOT NULL, - [node] [text] NOT NULL, - [pid] [text] NOT NULL + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [pid] [varchar] (100) NOT NULL ); CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host) @@ -133,7 +133,7 @@ CREATE TABLE [dbo].[muc_online_users] ( [resource] [varchar] (250) NOT NULL, [name] [varchar] (250) NOT NULL, [host] [varchar] (250) NOT NULL, - node text NOT NULL + [node] [varchar] (250) NOT NULL ); CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) @@ -226,9 +226,9 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[pubsub_node_option] ( [nodeid] [bigint] NULL, - [name] [text] NOT NULL, - [val] [text] NOT NULL -) TEXTIMAGE_ON [PRIMARY]; + [name] [varchar] (250) NOT NULL, + [val] [varchar] (250) NOT NULL +); CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -272,13 +272,13 @@ CREATE TABLE [dbo].[pubsub_node] ( [host] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, [parent] [varchar] (255) NOT NULL DEFAULT '', - [plugin] [text] NOT NULL, + [plugin] [varchar] (32) NOT NULL, [nodeid] [bigint] IDENTITY(1,1) NOT NULL, CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED ( [nodeid] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) -) TEXTIMAGE_ON [PRIMARY]; +); CREATE INDEX [pubsub_node_parent] ON [pubsub_node] (parent) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -366,13 +366,12 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[sr_group] ( [name] [varchar] (250) NOT NULL, [opts] [text] NOT NULL, - [created_at] [datetime] NOT NULL DEFAULT GETDATE(), - CONSTRAINT [sr_group_PRIMARY] PRIMARY KEY CLUSTERED -( - [name] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; +CREATE UNIQUE CLUSTERED INDEX [sr_group_name] ON [sr_group] ([name]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + CREATE TABLE [dbo].[sr_user] ( [jid] [varchar] (250) NOT NULL, [grp] [varchar] (250) NOT NULL, @@ -548,22 +547,85 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE UNIQUE INDEX [i_push_ut] ON [push_session] (username, timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE TABLE [dbo].[mix_channel] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [hidden] [smallint] NOT NULL, + [hmac_key] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_channel] ON [mix_channel] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_channel_serv] ON [mix_channel] (service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_participant] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [nick] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE INDEX [mix_participant] ON [mix_participant] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_participant_chan_serv] ON [mix_participant] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_subscription] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [mix_subscription] ON [mix_subscription] (channel, service, username, domain, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_ud] ON [mix_subscription] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_node] ON [mix_subscription] (channel, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv] ON [mix_subscription] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_pam] ( + [username] [varchar] (250) NOT NULL, + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_pam] ON [mix_pam] (username, channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + CREATE TABLE [dbo].[mqtt_pub] ( - [username] [varchar](191) NOT NULL, - [server_host] [varchar](191) NOT NULL, - [resource] [varchar](191) NOT NULL, - [topic] [varchar](191) NOT NULL, - [qos] [tinyint] NOT NULL, - [payload] [varbinary](max) NOT NULL, - [payload_format] [tinyint] NOT NULL, - [content_type] [text] NOT NULL, - [response_topic] [text] NOT NULL, - [correlation_data] [varbinary](max) NOT NULL, - [user_properties] [varbinary](max) NOT NULL, - [expiry] [int] NOT NULL, - CONSTRAINT [i_mqtt_topic_server] PRIMARY KEY CLUSTERED -( - [topic] ASC, - [server_host] ASC -)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] + [username] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [topic] [varchar] (250) NOT NULL, + [qos] [tinyint] NOT NULL, + [payload] [varbinary](max) NOT NULL, + [payload_format] [tinyint] NOT NULL, + [content_type] [text] NOT NULL, + [response_topic] [text] NOT NULL, + [correlation_data] [varbinary](max) NOT NULL, + [user_properties] [varbinary](max) NOT NULL, + [expiry] [int] NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mqtt_topic] ON [mqtt_pub] (topic) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); From 19f2f1fa862320afa1e86a153eb312aaaced663e Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Mon, 12 Dec 2022 16:05:46 +0000 Subject: [PATCH 0006/1302] Fix MS SQL error caused by ORDER BY in subquery 'The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.' Omit the ORDER BY clause from subquery if the SELECT is not constrained by TOP. --- src/mod_mam_sql.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 2d60aea4b..695542387 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -441,6 +441,11 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) -> true -> [] end, + SubOrderClause = if LimitClause /= [], TopClause /= [] -> + <<" ORDER BY timestamp DESC ">>; + true -> + [] + end, WithTextClause = if is_binary(WithText), WithText /= <<>> -> [<<" and match (txt) against (">>, ToString(WithText), <<")">>]; @@ -528,7 +533,7 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) -> % XEP-0059: Result Set Management % 2.5 Requesting the Last Page in a Result Set [<<"SELECT">>, UserSel, <<" timestamp, xml, peer, kind, nick FROM (">>, - Query, <<" ORDER BY timestamp DESC ">>, + Query, SubOrderClause, LimitClause, <<") AS t ORDER BY timestamp ASC;">>]; _ -> [Query, <<" ORDER BY timestamp ASC ">>, From 93bf4d5411137cc48a51464487236938f039b113 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 17 Nov 2022 12:03:09 +0000 Subject: [PATCH 0007/1302] New SQL schema migrate fix 'server_host' column on 'route' table already exists in old schema and does not need adding for new schema migration. --- sql/pg.new.sql | 5 ----- src/mod_admin_update_sql.erl | 5 ----- 2 files changed, 10 deletions(-) diff --git a/sql/pg.new.sql b/sql/pg.new.sql index de1c28a2e..4a742756d 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -170,11 +170,6 @@ -- CREATE INDEX i_mix_pam_us ON mix_pam (username, server_host); -- ALTER TABLE mix_pam ALTER COLUMN server_host DROP DEFAULT; --- ALTER TABLE route ADD COLUMN server_host text NOT NULL DEFAULT ''; --- DROP INDEX i_route; --- CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); --- ALTER TABLE i_route ALTER COLUMN server_host DROP DEFAULT; - -- ALTER TABLE mqtt_pub ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_mqtt_topic; -- CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host); diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index adcc5b117..3a6aa0dfb 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -271,11 +271,6 @@ update_tables(State) -> create_index(State, "mix_pam", "i_mix_pam_us", ["username", "server_host"]), drop_sh_default(State, "mix_pam"), - add_sh_column(State, "route"), - drop_index(State, "i_route"), - create_unique_index(State, "route", "i_route", ["domain", "server_host", "node", "pid"]), - drop_sh_default(State, "route"), - add_sh_column(State, "mqtt_pub"), drop_index(State, "i_mqtt_topic"), create_unique_index(State, "mqtt_pub", "i_mqtt_topic_server", ["topic", "server_host"]), From 06ffe995e1db6168c15200028360517c54212ed4 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Tue, 22 Nov 2022 12:15:35 +0000 Subject: [PATCH 0008/1302] Remove unnecessary indexes For columns are already included in a compound index there is no benefit to having a separate index with a subset of the same columns in the same order, it just wastes space. --- sql/lite.new.sql | 12 ------------ sql/lite.sql | 10 ---------- sql/mssql.sql | 17 ----------------- sql/mysql.new.sql | 14 ++------------ sql/mysql.old-to-new.sql | 5 ----- sql/mysql.sql | 10 ---------- sql/pg.new.sql | 31 ++++--------------------------- sql/pg.sql | 10 ---------- src/mod_admin_update_sql.erl | 5 ----- 9 files changed, 6 insertions(+), 108 deletions(-) diff --git a/sql/lite.new.sql b/sql/lite.new.sql index d9ba4dee8..43baa0e1b 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -52,7 +52,6 @@ CREATE TABLE rosterusers ( ); CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers (server_host, username, jid); -CREATE INDEX i_rosteru_sh_username ON rosterusers (server_host, username); CREATE INDEX i_rosteru_sh_jid ON rosterusers (server_host, jid); @@ -84,7 +83,6 @@ CREATE TABLE sr_user ( ); CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user (server_host, jid, grp); -CREATE INDEX i_sr_user_sh_jid ON sr_user (server_host, jid); CREATE INDEX i_sr_user_sh_grp ON sr_user (server_host, grp); CREATE TABLE spool ( @@ -190,7 +188,6 @@ CREATE TABLE privacy_list ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX i_privacy_list_sh_username ON privacy_list (server_host, username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list (server_host, username, name); CREATE TABLE privacy_list_data ( @@ -215,9 +212,6 @@ CREATE TABLE private_storage ( PRIMARY KEY (server_host, username, namespace) ); -CREATE INDEX i_private_storage_sh_username ON private_storage (server_host, username); - - CREATE TABLE roster_version ( username text NOT NULL, server_host text NOT NULL, @@ -319,7 +313,6 @@ CREATE TABLE muc_online_users ( ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); -CREATE INDEX i_muc_online_users_us ON muc_online_users (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, @@ -389,7 +382,6 @@ CREATE TABLE route ( ); CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); -CREATE INDEX i_route_domain ON route(domain); CREATE TABLE bosh ( sid text NOT NULL, @@ -449,7 +441,6 @@ CREATE TABLE mix_participant ( ); CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel, service); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -461,9 +452,7 @@ CREATE TABLE mix_subscription ( ); CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel, service, username, domain); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel, service); CREATE TABLE mix_pam ( username text NOT NULL, @@ -475,7 +464,6 @@ CREATE TABLE mix_pam ( ); CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); -CREATE INDEX i_mix_pam_us ON mix_pam (username, server_host); CREATE TABLE mqtt_pub ( username text NOT NULL, diff --git a/sql/lite.sql b/sql/lite.sql index fbba55c9d..a920a6cd5 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -47,7 +47,6 @@ CREATE TABLE rosterusers ( ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers (username, jid); -CREATE INDEX i_rosteru_username ON rosterusers (username); CREATE INDEX i_rosteru_jid ON rosterusers (jid); @@ -74,7 +73,6 @@ CREATE TABLE sr_user ( ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user (jid, grp); -CREATE INDEX i_sr_user_jid ON sr_user (jid); CREATE INDEX i_sr_user_grp ON sr_user (grp); CREATE TABLE spool ( @@ -169,7 +167,6 @@ CREATE TABLE privacy_list ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX i_privacy_list_username ON privacy_list (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list (username, name); CREATE TABLE privacy_list_data ( @@ -192,7 +189,6 @@ CREATE TABLE private_storage ( created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -CREATE INDEX i_private_storage_username ON private_storage (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage (username, namespace); @@ -291,7 +287,6 @@ CREATE TABLE muc_online_users ( ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host); -CREATE INDEX i_muc_online_users_us ON muc_online_users (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, @@ -358,7 +353,6 @@ CREATE TABLE route ( ); CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid); -CREATE INDEX i_route_domain ON route(domain); CREATE TABLE bosh ( sid text NOT NULL, @@ -417,7 +411,6 @@ CREATE TABLE mix_participant ( ); CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel, service); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -429,9 +422,7 @@ CREATE TABLE mix_subscription ( ); CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel, service, username, domain); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel, service); CREATE TABLE mix_pam ( username text NOT NULL, @@ -442,7 +433,6 @@ CREATE TABLE mix_pam ( ); CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service); -CREATE INDEX i_mix_pam_us ON mix_pam (username); CREATE TABLE mqtt_pub ( username text NOT NULL, diff --git a/sql/mssql.sql b/sql/mssql.sql index 355b5316b..66c523e53 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -138,8 +138,6 @@ CREATE TABLE [dbo].[muc_online_users] ( CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[muc_room_subscribers] ( [room] [varchar] (191) NOT NULL, @@ -174,9 +172,6 @@ CREATE TABLE [dbo].[privacy_list] ( )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ); -CREATE INDEX [privacy_list_username] ON [privacy_list] (username) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE UNIQUE INDEX [privacy_list_username_name] ON [privacy_list] (username, name) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -203,9 +198,6 @@ CREATE TABLE [dbo].[private_storage] ( [created_at] [datetime] NOT NULL DEFAULT GETDATE() ) TEXTIMAGE_ON [PRIMARY]; -CREATE INDEX [private_storage_username] ON [private_storage] (username) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE UNIQUE CLUSTERED INDEX [private_storage_username_namespace] ON [private_storage] (username, namespace) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -320,9 +312,6 @@ CREATE TABLE [dbo].[rosterusers] ( CREATE UNIQUE CLUSTERED INDEX [rosterusers_username_jid] ON [rosterusers] ([username], [jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [rosterusers_username] ON [rosterusers] ([username]) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE INDEX [rosterusers_jid] ON [rosterusers] ([jid]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -381,9 +370,6 @@ CREATE TABLE [dbo].[sr_user] ( CREATE UNIQUE CLUSTERED INDEX [sr_user_jid_group] ON [sr_user] ([jid], [grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [sr_user_jid] ON [sr_user] ([jid]) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE INDEX [sr_user_grp] ON [sr_user] ([grp]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); @@ -520,9 +506,6 @@ CREATE TABLE [dbo].[route] ( CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE INDEX [route_domain] ON [route] (domain) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE TABLE [dbo].[bosh] ( [sid] [varchar] (255) NOT NULL, [node] [varchar] (255) NOT NULL, diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index f60016794..cbd9414aa 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -56,7 +56,6 @@ CREATE TABLE rosterusers ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers(server_host(191), username(75), jid(75)); -CREATE INDEX i_rosteru_sh_username ON rosterusers(server_host(191), username); CREATE INDEX i_rosteru_sh_jid ON rosterusers(server_host(191), jid); CREATE TABLE rostergroups ( @@ -87,7 +86,6 @@ CREATE TABLE sr_user ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_sr_user_sh_jid_group ON sr_user(server_host(191), jid, grp); -CREATE INDEX i_sr_user_sh_jid ON sr_user(server_host(191), jid); CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp); CREATE TABLE spool ( @@ -195,7 +193,6 @@ CREATE TABLE privacy_list ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_privacy_list_sh_username USING BTREE ON privacy_list(server_host(191), username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name USING BTREE ON privacy_list (server_host(191), username(75), name(75)); CREATE TABLE privacy_list_data ( @@ -218,11 +215,10 @@ CREATE TABLE private_storage ( server_host varchar(191) NOT NULL, namespace varchar(191) NOT NULL, data text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (server_host(191), username, namespace) + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_private_storage_sh_username USING BTREE ON private_storage(server_host(191), username); +CREATE UNIQUE INDEX i_private_storage_sh_sername_namespace USING BTREE ON private_storage(server_host(191), username, namespace); -- Not tested in mysql CREATE TABLE roster_version ( @@ -335,7 +331,6 @@ CREATE TABLE muc_online_users ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); -CREATE INDEX i_muc_online_users_us USING BTREE ON muc_online_users(username(75), server(75)); CREATE TABLE muc_room_subscribers ( room varchar(191) NOT NULL, @@ -405,7 +400,6 @@ CREATE TABLE route ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); -CREATE INDEX i_route_domain ON route(domain(75)); CREATE TABLE bosh ( sid text NOT NULL, @@ -465,7 +459,6 @@ CREATE TABLE mix_participant ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), service(191), username(191), domain(191)); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel(191), service(191)); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -477,9 +470,7 @@ CREATE TABLE mix_subscription ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153), service(153), username(153), domain(153), node(153)); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel(191), service(191), username(191), domain(191)); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel(191), service(191), node(191)); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel(191), service(191)); CREATE TABLE mix_pam ( username text NOT NULL, @@ -491,7 +482,6 @@ CREATE TABLE mix_pam ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), server_host(191), channel(191), service(191)); -CREATE INDEX i_mix_pam_us ON mix_pam (username(191), server_host(191)); CREATE TABLE mqtt_pub ( username varchar(191) NOT NULL, diff --git a/sql/mysql.old-to-new.sql b/sql/mysql.old-to-new.sql index 9614d55a8..ae3ab490f 100644 --- a/sql/mysql.old-to-new.sql +++ b/sql/mysql.old-to-new.sql @@ -33,14 +33,12 @@ BEGIN ALTER TABLE `rosterusers` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `rosterusers` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `rosterusers` ADD UNIQUE INDEX `i_rosteru_sh_user_jid` (`server_host`, `username`(75), `jid`(75)); - ALTER TABLE `rosterusers` ADD INDEX `i_rosteru_sh_username` (`server_host`, `username`); ALTER TABLE `rosterusers` ADD INDEX `i_rosteru_sh_jid` (`server_host`, `jid`); ALTER TABLE `private_storage` DROP INDEX `i_private_storage_username_namespace`; ALTER TABLE `private_storage` DROP INDEX `i_private_storage_username`; ALTER TABLE `private_storage` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `private_storage` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `private_storage` ADD PRIMARY KEY (`server_host`, `username`, `namespace`); - ALTER TABLE `private_storage` ADD INDEX `i_private_storage_sh_username` USING BTREE (`server_host`, `username`); ALTER TABLE `mqtt_pub` DROP INDEX `i_mqtt_topic`; ALTER TABLE `mqtt_pub` ADD COLUMN `server_host` VARCHAR (191) NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `mqtt_pub` ALTER COLUMN `server_host` DROP DEFAULT; @@ -94,14 +92,12 @@ BEGIN ALTER TABLE `privacy_list` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `privacy_list` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `privacy_list` ADD UNIQUE INDEX `i_privacy_list_sh_username_name` USING BTREE (`server_host`, `username`(75), `name`(75)); - ALTER TABLE `privacy_list` ADD INDEX `i_privacy_list_sh_username` USING BTREE (`server_host`, `username`); ALTER TABLE `sr_user` DROP INDEX `i_sr_user_jid`; ALTER TABLE `sr_user` DROP INDEX `i_sr_user_grp`; ALTER TABLE `sr_user` DROP INDEX `i_sr_user_jid_group`; ALTER TABLE `sr_user` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `jid`; ALTER TABLE `sr_user` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `sr_user` ADD UNIQUE INDEX `i_sr_user_sh_jid_group` (`server_host`, `jid`, `grp`); - ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_jid` (`server_host`, `jid`); ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_grp` (`server_host`, `grp`); ALTER TABLE `sr_user` ADD PRIMARY KEY (`server_host`, `jid`, `grp`); ALTER TABLE `muc_online_users` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; @@ -119,7 +115,6 @@ BEGIN ALTER TABLE `mix_pam` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `mix_pam` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `mix_pam` ADD UNIQUE INDEX `i_mix_pam` (`username`(191), `server_host`, `channel`(191), `service`(191)); - ALTER TABLE `mix_pam` ADD INDEX `i_mix_pam_us` (`username`(191), `server_host`); ALTER TABLE `route` CHANGE COLUMN `server_host` `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL; ALTER TABLE `users` DROP PRIMARY KEY; ALTER TABLE `users` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; diff --git a/sql/mysql.sql b/sql/mysql.sql index d6fcb000c..af93302dd 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -51,7 +51,6 @@ CREATE TABLE rosterusers ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(75)); -CREATE INDEX i_rosteru_username ON rosterusers(username); CREATE INDEX i_rosteru_jid ON rosterusers(jid); CREATE TABLE rostergroups ( @@ -77,7 +76,6 @@ CREATE TABLE sr_user ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_sr_user_jid_group ON sr_user(jid(75), grp(75)); -CREATE INDEX i_sr_user_jid ON sr_user(jid); CREATE INDEX i_sr_user_grp ON sr_user(grp); CREATE TABLE spool ( @@ -174,7 +172,6 @@ CREATE TABLE privacy_list ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_privacy_list_username USING BTREE ON privacy_list(username); CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_list (username(75), name(75)); CREATE TABLE privacy_list_data ( @@ -199,7 +196,6 @@ CREATE TABLE private_storage ( created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE INDEX i_private_storage_username USING BTREE ON private_storage(username); CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON private_storage(username(75), namespace(75)); -- Not tested in mysql @@ -307,7 +303,6 @@ CREATE TABLE muc_online_users ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75)); -CREATE INDEX i_muc_online_users_us USING BTREE ON muc_online_users(username(75), server(75)); CREATE TABLE muc_room_subscribers ( room varchar(191) NOT NULL, @@ -374,7 +369,6 @@ CREATE TABLE route ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75)); -CREATE INDEX i_route_domain ON route(domain(75)); CREATE TABLE bosh ( sid text NOT NULL, @@ -433,7 +427,6 @@ CREATE TABLE mix_participant ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), service(191), username(191), domain(191)); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel(191), service(191)); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -445,9 +438,7 @@ CREATE TABLE mix_subscription ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153), service(153), username(153), domain(153), node(153)); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel(191), service(191), username(191), domain(191)); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel(191), service(191), node(191)); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel(191), service(191)); CREATE TABLE mix_pam ( username text NOT NULL, @@ -458,7 +449,6 @@ CREATE TABLE mix_pam ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), channel(191), service(191)); -CREATE INDEX i_mix_pam_u ON mix_pam (username(191)); CREATE TABLE mqtt_pub ( username varchar(191) NOT NULL, diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 4a742756d..f3888da07 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -30,10 +30,8 @@ -- ALTER TABLE rosterusers ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_rosteru_user_jid; --- DROP INDEX i_rosteru_username; -- DROP INDEX i_rosteru_jid; -- CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); --- CREATE INDEX i_rosteru_sh_username ON rosterusers USING btree (server_host, username); -- CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); -- ALTER TABLE rosterusers ALTER COLUMN server_host DROP DEFAULT; @@ -48,10 +46,8 @@ -- ALTER TABLE sr_user ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_sr_user_jid_grp; --- DROP INDEX i_sr_user_jid; -- DROP INDEX i_sr_user_grp; -- ALTER TABLE sr_user ADD PRIMARY KEY (server_host, jid, grp); --- CREATE INDEX i_sr_user_sh_jid ON sr_user USING btree (server_host, jid); -- CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); -- ALTER TABLE sr_user ALTER COLUMN server_host DROP DEFAULT; @@ -114,17 +110,13 @@ -- ALTER TABLE privacy_default_list ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE privacy_list ADD COLUMN server_host text NOT NULL DEFAULT ''; --- DROP INDEX i_privacy_list_username; -- DROP INDEX i_privacy_list_username_name; --- CREATE INDEX i_privacy_list_sh_username ON privacy_list USING btree (server_host, username); -- CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); -- ALTER TABLE privacy_list ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE private_storage ADD COLUMN server_host text NOT NULL DEFAULT ''; --- DROP INDEX i_private_storage_username; -- DROP INDEX i_private_storage_username_namespace; -- ALTER TABLE private_storage ADD PRIMARY KEY (server_host, username, namespace); --- CREATE INDEX i_private_storage_sh_username ON private_storage USING btree (server_host, username); -- ALTER TABLE private_storage ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE roster_version ADD COLUMN server_host text NOT NULL DEFAULT ''; @@ -165,9 +157,7 @@ -- ALTER TABLE mix_pam ADD COLUMN server_host text NOT NULL DEFAULT ''; -- DROP INDEX i_mix_pam; --- DROP INDEX i_mix_pam_us; -- CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); --- CREATE INDEX i_mix_pam_us ON mix_pam (username, server_host); -- ALTER TABLE mix_pam ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE mqtt_pub ADD COLUMN server_host text NOT NULL DEFAULT ''; @@ -216,7 +206,6 @@ CREATE TABLE rosterusers ( ); CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (server_host, username, jid); -CREATE INDEX i_rosteru_sh_username ON rosterusers USING btree (server_host, username); CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid); @@ -233,8 +222,7 @@ CREATE TABLE sr_group ( name text NOT NULL, server_host text NOT NULL, opts text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now(), - PRIMARY KEY (server_host, name) + created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group USING btree (server_host, name); @@ -243,12 +231,10 @@ CREATE TABLE sr_user ( jid text NOT NULL, server_host text NOT NULL, grp text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now(), - PRIMARY KEY (server_host, jid, grp) + created_at TIMESTAMP NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user USING btree (server_host, jid, grp); -CREATE INDEX i_sr_user_sh_jid ON sr_user USING btree (server_host, jid); CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp); CREATE TABLE spool ( @@ -354,7 +340,6 @@ CREATE TABLE privacy_list ( created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_privacy_list_sh_username ON privacy_list USING btree (server_host, username); CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USING btree (server_host, username, name); CREATE TABLE privacy_list_data ( @@ -377,12 +362,10 @@ CREATE TABLE private_storage ( server_host text NOT NULL, namespace text NOT NULL, data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now(), - PRIMARY KEY (server_host, username, namespace) + created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_private_storage_sh_username ON private_storage USING btree (server_host, username); - +CREATE UNIQUE INDEX i_private_storage_sh_username_namespace ON private_storage USING btree (server_host, username, namespace); CREATE TABLE roster_version ( username text NOT NULL, @@ -497,7 +480,6 @@ CREATE TABLE muc_online_users ( ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); -CREATE INDEX i_muc_online_users_us ON muc_online_users USING btree (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, @@ -569,7 +551,6 @@ CREATE TABLE route ( ); CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); -CREATE INDEX i_route_domain ON route USING btree (domain); CREATE TABLE bosh ( sid text NOT NULL, @@ -629,7 +610,6 @@ CREATE TABLE mix_participant ( ); CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel, service); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -641,9 +621,7 @@ CREATE TABLE mix_subscription ( ); CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel, service, username, domain); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel, service); CREATE TABLE mix_pam ( username text NOT NULL, @@ -655,7 +633,6 @@ CREATE TABLE mix_pam ( ); CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel, service); -CREATE INDEX i_mix_pam_us ON mix_pam (username, server_host); CREATE TABLE mqtt_pub ( username text NOT NULL, diff --git a/sql/pg.sql b/sql/pg.sql index 03e3d5294..5a0d4b766 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -51,7 +51,6 @@ CREATE TABLE rosterusers ( ); CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers USING btree (username, jid); -CREATE INDEX i_rosteru_username ON rosterusers USING btree (username); CREATE INDEX i_rosteru_jid ON rosterusers USING btree (jid); @@ -78,7 +77,6 @@ CREATE TABLE sr_user ( ); CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user USING btree (jid, grp); -CREATE INDEX i_sr_user_jid ON sr_user USING btree (jid); CREATE INDEX i_sr_user_grp ON sr_user USING btree (grp); CREATE TABLE spool ( @@ -173,7 +171,6 @@ CREATE TABLE privacy_list ( created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_privacy_list_username ON privacy_list USING btree (username); CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list USING btree (username, name); CREATE TABLE privacy_list_data ( @@ -198,7 +195,6 @@ CREATE TABLE private_storage ( created_at TIMESTAMP NOT NULL DEFAULT now() ); -CREATE INDEX i_private_storage_username ON private_storage USING btree (username); CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_storage USING btree (username, namespace); @@ -309,7 +305,6 @@ CREATE TABLE muc_online_users ( ); CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host); -CREATE INDEX i_muc_online_users_us ON muc_online_users USING btree (username, server); CREATE TABLE muc_room_subscribers ( room text NOT NULL, @@ -378,7 +373,6 @@ CREATE TABLE route ( ); CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid); -CREATE INDEX i_route_domain ON route USING btree (domain); CREATE TABLE bosh ( sid text NOT NULL, @@ -437,7 +431,6 @@ CREATE TABLE mix_participant ( ); CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, service, username, domain); -CREATE INDEX i_mix_participant_chan_serv ON mix_participant (channel, service); CREATE TABLE mix_subscription ( channel text NOT NULL, @@ -449,9 +442,7 @@ CREATE TABLE mix_subscription ( ); CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, service, username, domain, node); -CREATE INDEX i_mix_subscription_chan_serv_ud ON mix_subscription (channel, service, username, domain); CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (channel, service, node); -CREATE INDEX i_mix_subscription_chan_serv ON mix_subscription (channel, service); CREATE TABLE mix_pam ( username text NOT NULL, @@ -462,7 +453,6 @@ CREATE TABLE mix_pam ( ); CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service); -CREATE INDEX i_mix_pam_us ON mix_pam (username); CREATE TABLE mqtt_pub ( username text NOT NULL, diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 3a6aa0dfb..e9f248f5c 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -129,7 +129,6 @@ update_tables(State) -> drop_index(State, "i_rosteru_username"), drop_index(State, "i_rosteru_jid"), create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]), - create_index(State, "rosterusers", "i_rosteru_sh_username", ["server_host", "username"]), create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]), drop_sh_default(State, "rosterusers"), @@ -149,7 +148,6 @@ update_tables(State) -> drop_index(State, "i_sr_user_grp"), add_pkey(State, "sr_user", ["server_host", "jid", "grp"]), create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), - create_index(State, "sr_user", "i_sr_user_sh_jid", ["server_host", "jid"]), create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), drop_sh_default(State, "sr_user"), @@ -217,7 +215,6 @@ update_tables(State) -> add_sh_column(State, "privacy_list"), drop_index(State, "i_privacy_list_username"), drop_index(State, "i_privacy_list_username_name"), - create_index(State, "privacy_list", "i_privacy_list_sh_username", ["server_host", "username"]), create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]), drop_sh_default(State, "privacy_list"), @@ -225,7 +222,6 @@ update_tables(State) -> drop_index(State, "i_private_storage_username"), drop_index(State, "i_private_storage_username_namespace"), add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), - create_index(State, "private_storage", "i_private_storage_sh_username", ["server_host", "username"]), drop_sh_default(State, "private_storage"), add_sh_column(State, "roster_version"), @@ -268,7 +264,6 @@ update_tables(State) -> drop_index(State, "i_mix_pam"), drop_index(State, "i_mix_pam_us"), create_unique_index(State, "mix_pam", "i_mix_pam", ["username", "server_host", "channel", "service"]), - create_index(State, "mix_pam", "i_mix_pam_us", ["username", "server_host"]), drop_sh_default(State, "mix_pam"), add_sh_column(State, "mqtt_pub"), From 6fc67d83f4b5e1f2f09527e7e27373be9795cffb Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Sat, 14 Jan 2023 12:03:41 +0000 Subject: [PATCH 0009/1302] Minor MS SQL improvements Support 'sql_ssl' option for MS SQL - set Encryption=required and Encrypt=yes in ODBC connection string to require SSL using default FreeTDS driver and Microsoft ODBC Driver for SQL Server repectively. Allow setting full ODBC connection string in 'sql_server' for MS SQL, allowing custom connection configuration beyond what is possible with just 'sql_odbc_driver' option. --- src/ejabberd_options_doc.erl | 18 +++++++++++------- src/ejabberd_sql.erl | 33 +++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index fb04d9c86..ba4b585b3 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1293,9 +1293,9 @@ doc() -> note => "added in 20.12", desc => ?T("Path to the ODBC driver to use to connect to a Microsoft SQL " - "Server database. This option is only valid if the _`sql_type`_ " - "option is set to 'mssql'. " - "The default value is: 'libtdsodbc.so'")}}, + "Server database. This option only applies if the _`sql_type`_ " + "option is set to 'mssql' and _`sql_server`_ is not an ODBC " + "connection string. The default value is: 'libtdsodbc.so'")}}, {sql_password, #{value => ?T("Password"), desc => @@ -1334,14 +1334,15 @@ doc() -> {sql_server, #{value => ?T("Host"), desc => - ?T("A hostname or an IP address of the SQL server. " + ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " + "'mssql' or 'odbc' this can also be an ODBC connection string. " "The default value is 'localhost'.")}}, {sql_ssl, #{value => "true | false", note => "improved in 20.03", desc => ?T("Whether to use SSL encrypted connections to the " - "SQL server. The option is only available for MySQL and " + "SQL server. The option is only available for MySQL, MS SQL and " "PostgreSQL. The default value is 'false'.")}}, {sql_ssl_cafile, #{value => ?T("Path"), @@ -1350,7 +1351,8 @@ doc() -> "be used to verify SQL connections. Implies _`sql_ssl`_ " "and _`sql_ssl_verify`_ options are set to 'true'. " "There is no default which means " - "certificate verification is disabled.")}}, + "certificate verification is disabled. " + "This option has no effect for MS SQL.")}}, {sql_ssl_certfile, #{value => ?T("Path"), desc => @@ -1358,13 +1360,15 @@ doc() -> "for SSL connections to the SQL server. Implies _`sql_ssl`_ " "option is set to 'true'. There is no default which means " "ejabberd won't provide a client certificate to the SQL " - "server.")}}, + "server. " + "This option has no effect for MS SQL.")}}, {sql_ssl_verify, #{value => "true | false", desc => ?T("Whether to verify SSL connection to the SQL server against " "CA root certificates defined in _`sql_ssl_cafile`_ option. " "Implies _`sql_ssl`_ option is set to 'true'. " + "This option has no effect for MS SQL. " "The default value is 'false'.")}}, {sql_start_interval, #{value => "timeout()", diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index ebe106464..4e92e0574 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -1159,9 +1159,19 @@ db_opts(Host) -> SSLOpts = get_ssl_opts(Transport, Host), case Type of mssql -> - [mssql, <<"DRIVER=ODBC;SERVER=", Server/binary, ";UID=", User/binary, - ";DATABASE=", DB/binary ,";PWD=", Pass/binary, - ";PORT=", (integer_to_binary(Port))/binary ,";CLIENT_CHARSET=UTF-8;">>, Timeout]; + case odbc_server_is_connstring(Server) of + true -> + [mssql, Server, Timeout]; + false -> + Encryption = case Transport of + tcp -> <<"">>; + ssl -> <<";ENCRYPTION=require;ENCRYPT=yes">> + end, + [mssql, <<"DRIVER=ODBC;SERVER=", Server/binary, ";DATABASE=", DB/binary, + ";UID=", User/binary, ";PWD=", Pass/binary, + ";PORT=", (integer_to_binary(Port))/binary, Encryption/binary, + ";CLIENT_CHARSET=UTF-8;">>, Timeout] + end; _ -> [Type, Server, Port, DB, User, Pass, Timeout, Transport, SSLOpts] end @@ -1171,6 +1181,8 @@ warn_if_ssl_unsupported(tcp, _) -> ok; warn_if_ssl_unsupported(ssl, pgsql) -> ok; +warn_if_ssl_unsupported(ssl, mssql) -> + ok; warn_if_ssl_unsupported(ssl, mysql) -> ok; warn_if_ssl_unsupported(ssl, Type) -> @@ -1203,7 +1215,7 @@ get_ssl_opts(ssl, Host) -> get_ssl_opts(tcp, _) -> []. -init_mssql(Host) -> +init_mssql_odbcinst(Host) -> Driver = ejabberd_option:sql_odbc_driver(Host), ODBCINST = io_lib:fwrite("[ODBC]~n" "Driver = ~s~n", [Driver]), @@ -1225,6 +1237,19 @@ init_mssql(Host) -> Err end. +init_mssql(Host) -> + Server = ejabberd_option:sql_server(Host), + case odbc_server_is_connstring(Server) of + true -> ok; + false -> init_mssql_odbcinst(Host) + end. + +odbc_server_is_connstring(Server) -> + case binary:match(Server, <<"=">>) of + nomatch -> false; + _ -> true + end. + write_file_if_new(File, Payload) -> case filelib:is_file(File) of true -> ok; From aeed1679d8b3193ff5508d09754a8c2ac6f2cbe0 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 13:05:30 +0000 Subject: [PATCH 0010/1302] Add 'new' schema for MS SQL --- sql/mssql.new.sql | 646 +++++++++++++++++++++++++++++++++ test/docker/docker-compose.yml | 1 + 2 files changed, 647 insertions(+) create mode 100644 sql/mssql.new.sql diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql new file mode 100644 index 000000000..f72b40bfa --- /dev/null +++ b/sql/mssql.new.sql @@ -0,0 +1,646 @@ +-- +-- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- +-- 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 2 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, write to the Free Software Foundation, Inc., +-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +-- + +SET ANSI_PADDING OFF; +SET ANSI_NULLS ON; +SET QUOTED_IDENTIFIER ON; +SET ANSI_PADDING ON; + +CREATE TABLE [dbo].[archive] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [timestamp] [bigint] NOT NULL, + [peer] [varchar] (250) NOT NULL, + [bare_peer] [varchar] (250) NOT NULL, + [xml] [ntext] NOT NULL, + [txt] [ntext] NULL, + [id] [bigint] IDENTITY(1,1) NOT NULL, + [kind] [varchar] (10) NULL, + [nick] [varchar] (250) NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [archive_sh_username_timestamp] ON [archive] (server_host, username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_username_peer] ON [archive] (server_host, username, peer) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_username_bare_peer] ON [archive] (server_host, username, bare_peer) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [archive_sh_timestamp] ON [archive] (server_host, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[archive_prefs] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [def] [text] NOT NULL, + [always] [text] NOT NULL, + [never] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [archive_prefs_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[caps_features] ( + [node] [varchar] (250) NOT NULL, + [subnode] [varchar] (250) NOT NULL, + [feature] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[last] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [seconds] [text] NOT NULL, + [state] [text] NOT NULL, + CONSTRAINT [last_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[motd] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [xml] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [motd_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[muc_registered] ( + [jid] [varchar] (255) NOT NULL, + [host] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [nick] [varchar] (255) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE INDEX [muc_registered_nick] ON [muc_registered] (nick) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE CLUSTERED INDEX [muc_registered_jid_host] ON [muc_registered] (jid, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_room] ( + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [opts] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [muc_room_name_host] ON [muc_room] (name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE INDEX [muc_room_host_created_at] ON [muc_registered] (host, nick) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_room] ( + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [pid] [varchar] (100) NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_online_users] ( + [username] [varchar] (250) NOT NULL, + [server] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + [host] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[muc_room_subscribers] ( + [room] [varchar] (191) NOT NULL, + [host] [varchar] (191) NOT NULL, + [jid] [varchar] (191) NOT NULL, + [nick] [text] NOT NULL, + [nodes] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE UNIQUE CLUSTERED INDEX [muc_room_subscribers_host_room_jid] ON [muc_room_subscribers] (host, room, jid); +CREATE INDEX [muc_room_subscribers_host_jid] ON [muc_room_subscribers] (host, jid); +CREATE INDEX [muc_room_subscribers_jid] ON [muc_room_subscribers] (jid); + +CREATE TABLE [dbo].[privacy_default_list] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + CONSTRAINT [privacy_default_list_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE TABLE [dbo].[privacy_list] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [name] [varchar] (250) NOT NULL, + [id] [bigint] IDENTITY(1,1) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [privacy_list_PK] PRIMARY KEY CLUSTERED +( + [id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE UNIQUE INDEX [privacy_list_sh_username_name] ON [privacy_list] (server_host, username, name) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[privacy_list_data] ( + [id] [bigint] NULL, + [t] [char] (1) NOT NULL, + [value] [text] NOT NULL, + [action] [char] (1) NOT NULL, + [ord] [smallint] NOT NULL, + [match_all] [smallint] NOT NULL, + [match_iq] [smallint] NOT NULL, + [match_message] [smallint] NOT NULL, + [match_presence_in] [smallint] NOT NULL, + [match_presence_out] [smallint] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [privacy_list_data_id] ON [privacy_list_data] (id) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[private_storage] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [namespace] [varchar] (250) NOT NULL, + [data] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [private_storage_sh_username_namespace] ON [private_storage] (server_host, username, namespace) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_item] ( + [nodeid] [bigint] NULL, + [itemid] [varchar] (255) NOT NULL, + [publisher] [varchar] (250) NOT NULL, + [creation] [varchar] (32) NOT NULL, + [modification] [varchar] (32) NOT NULL, + [payload] [text] NOT NULL DEFAULT '' +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [pubsub_item_itemid] ON [pubsub_item] (itemid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE CLUSTERED INDEX [pubsub_item_nodeid_itemid] ON [pubsub_item] (nodeid, itemid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node_option] ( + [nodeid] [bigint] NULL, + [name] [varchar] (250) NOT NULL, + [val] [varchar] (250) NOT NULL +); + +CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node_owner] ( + [nodeid] [bigint] NULL, + [owner] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [pubsub_node_owner_nodeid] ON [pubsub_node_owner] (nodeid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_state] ( + [nodeid] [bigint] NULL, + [jid] [varchar] (255) NOT NULL, + [affiliation] [char] (1) NOT NULL, + [subscriptions] [text] NOT NULL DEFAULT '', + [stateid] [bigint] IDENTITY(1,1) NOT NULL, + CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED +( + [stateid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [pubsub_state_jid] ON [pubsub_state] (jid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE INDEX [pubsub_state_nodeid_jid] ON [pubsub_state] (nodeid, jid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_subscription_opt] ( + [subid] [varchar] (255) NOT NULL, + [opt_name] [varchar] (32) NOT NULL, + [opt_value] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [pubsub_subscription_opt_subid_opt_name] ON [pubsub_subscription_opt] (subid, opt_name) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[pubsub_node] ( + [host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [parent] [varchar] (255) NOT NULL DEFAULT '', + [plugin] [varchar] (32) NOT NULL, + [nodeid] [bigint] IDENTITY(1,1) NOT NULL, + CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED +( + [nodeid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE INDEX [pubsub_node_parent] ON [pubsub_node] (parent) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE UNIQUE INDEX [pubsub_node_host_node] ON [pubsub_node] (host, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[roster_version] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [version] [text] NOT NULL, + CONSTRAINT [roster_version_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[rostergroups] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [grp] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE CLUSTERED INDEX [rostergroups_sh_username_jid] ON [rostergroups] ([server_host], [username], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[rosterusers] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [nick] [text] NOT NULL, + [subscription] [char] (1) NOT NULL, + [ask] [char] (1) NOT NULL, + [askmessage] [text] NOT NULL, + [server] [char] (1) NOT NULL, + [subscribe] [text] NOT NULL, + [type] [text] NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [rosterusers_sh_username_jid] ON [rosterusers] ([server_host], [username], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [rosterusers_sh_jid] ON [rosterusers] ([server_host], [jid]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[sm] ( + [usec] [bigint] NOT NULL, + [pid] [varchar] (100) NOT NULL, + [node] [varchar] (255) NOT NULL, + [username] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [resource] [varchar] (255) NOT NULL, + [priority] [text] NOT NULL, + [info] [text] NOT NULL +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [sm_sid] ON [sm] (usec, pid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sm_node] ON [sm] (node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sm_sh_username] ON [sm] (server_host, username) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[spool] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [xml] [text] NOT NULL, + [seq] [bigint] IDENTITY(1,1) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [spool_PK] PRIMARY KEY CLUSTERED +( + [seq] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [spool_sh_username] ON [spool] (server_host, username) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [spool_created_at] ON [spool] (created_at) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +; + +CREATE TABLE [dbo].[sr_group] ( + [name] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [opts] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [sr_group_sh_name] ON [sr_group] ([server_host], [name]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[sr_user] ( + [jid] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [grp] [varchar] (250) NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +); + +CREATE UNIQUE CLUSTERED INDEX [sr_user_sh_jid_group] ON [sr_user] ([server_host], [jid], [grp]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [sr_user_sh_grp] ON [sr_user] ([server_host], [grp]) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[users] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [password] [text] NOT NULL, + [serverkey] [text] NOT NULL DEFAULT '', + [salt] [text] NOT NULL DEFAULT '', + [iterationcount] [smallint] NOT NULL DEFAULT 0, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[vcard] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [vcard] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE(), + CONSTRAINT [vcard_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [username] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[vcard_search] ( + [username] [varchar] (250) NOT NULL, + [lusername] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [fn] [text] NOT NULL, + [lfn] [varchar] (250) NOT NULL, + [family] [text] NOT NULL, + [lfamily] [varchar] (250) NOT NULL, + [given] [text] NOT NULL, + [lgiven] [varchar] (250) NOT NULL, + [middle] [text] NOT NULL, + [lmiddle] [varchar] (250) NOT NULL, + [nickname] [text] NOT NULL, + [lnickname] [varchar] (250) NOT NULL, + [bday] [text] NOT NULL, + [lbday] [varchar] (250) NOT NULL, + [ctry] [text] NOT NULL, + [lctry] [varchar] (250) NOT NULL, + [locality] [text] NOT NULL, + [llocality] [varchar] (250) NOT NULL, + [email] [text] NOT NULL, + [lemail] [varchar] (250) NOT NULL, + [orgname] [text] NOT NULL, + [lorgname] [varchar] (250) NOT NULL, + [orgunit] [text] NOT NULL, + [lorgunit] [varchar] (250) NOT NULL, + CONSTRAINT [vcard_search_PRIMARY] PRIMARY KEY CLUSTERED +( + [server_host] ASC, + [lusername] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE INDEX [vcard_search_sh_lfn] ON [vcard_search] (server_host, lfn) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lfamily] ON [vcard_search] (server_host, lfamily) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lgiven] ON [vcard_search] (server_host, lgiven) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lmiddle] ON [vcard_search] (server_host, lmiddle) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lnickname] ON [vcard_search] (server_host, lnickname) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lbday] ON [vcard_search] (server_host, lbday) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lctry] ON [vcard_search] (server_host, lctry) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_llocality] ON [vcard_search] (server_host, llocality) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lemail] ON [vcard_search] (server_host, lemail) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lorgname] ON [vcard_search] (server_host, lorgname) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [vcard_search_sh_lorgunit] ON [vcard_search] (server_host, lorgunit) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +ALTER TABLE [dbo].[pubsub_item] WITH CHECK ADD CONSTRAINT [pubsub_item_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_item] CHECK CONSTRAINT [pubsub_item_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_node_option] WITH CHECK ADD CONSTRAINT [pubsub_node_option_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_node_option] CHECK CONSTRAINT [pubsub_node_option_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_node_owner] WITH CHECK ADD CONSTRAINT [pubsub_node_owner_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_node_owner] CHECK CONSTRAINT [pubsub_node_owner_ibfk_1]; + +ALTER TABLE [dbo].[pubsub_state] WITH CHECK ADD CONSTRAINT [pubsub_state_ibfk_1] FOREIGN KEY([nodeid]) +REFERENCES [dbo].[pubsub_node] ([nodeid]) +ON DELETE CASCADE; + +ALTER TABLE [dbo].[pubsub_state] CHECK CONSTRAINT [pubsub_state_ibfk_1]; + +CREATE TABLE [dbo].[oauth_token] ( + [token] [varchar] (250) NOT NULL, + [jid] [text] NOT NULL, + [scope] [text] NOT NULL, + [expire] [bigint] NOT NULL, + CONSTRAINT [oauth_token_PRIMARY] PRIMARY KEY CLUSTERED +( + [token] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +) TEXTIMAGE_ON [PRIMARY]; + +CREATE TABLE [dbo].[route] ( + [domain] [varchar] (255) NOT NULL, + [server_host] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL, + [local_hint] [text] NOT NULL +); + +CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[bosh] ( + [sid] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [pid] [varchar](100) NOT NULL + CONSTRAINT [bosh_PRIMARY] PRIMARY KEY CLUSTERED +( + [sid] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) +); + +CREATE TABLE [dbo].[push_session] ( + [username] [varchar] (255) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [timestamp] [bigint] NOT NULL, + [service] [varchar] (255) NOT NULL, + [node] [varchar] (255) NOT NULL, + [xml] [varchar] (255) NOT NULL +); + +CREATE UNIQUE NONCLUSTERED INDEX [push_session_susn] ON [push_session] (server_host, username, service, node)) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [push_session_sh_username_timestamp] ON [push_session] (server_host, username, timestamp) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_channel] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [hidden] [smallint] NOT NULL, + [hmac_key] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mix_channel] ON [mix_channel] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_channel_serv] ON [mix_channel] (service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_participant] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [nick] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE INDEX [mix_participant] ON [mix_participant] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_participant_chan_serv] ON [mix_participant] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_subscription] ( + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [username] [varchar] (250) NOT NULL, + [domain] [varchar] (250) NOT NULL, + [node] [varchar] (250) NOT NULL, + [jid] [varchar] (250) NOT NULL +); + +CREATE UNIQUE INDEX [mix_subscription] ON [mix_subscription] (channel, service, username, domain, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_ud] ON [mix_subscription] (channel, service, username, domain) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv_node] ON [mix_subscription] (channel, service, node) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE INDEX [mix_subscription_chan_serv] ON [mix_subscription] (channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mix_pam] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [channel] [varchar] (250) NOT NULL, + [service] [varchar] (250) NOT NULL, + [id] [text] NOT NULL, + [created_at] [datetime] NOT NULL DEFAULT GETDATE() +) TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE NONCLUSTERED INDEX [mix_pam] ON [mix_pam] (username, server_host, channel, service) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + +CREATE TABLE [dbo].[mqtt_pub] ( + [username] [varchar] (250) NOT NULL, + [server_host] [varchar] (250) NOT NULL, + [resource] [varchar] (250) NOT NULL, + [topic] [varchar] (250) NOT NULL, + [qos] [tinyint] NOT NULL, + [payload] [varbinary](max) NOT NULL, + [payload_format] [tinyint] NOT NULL, + [content_type] [text] NOT NULL, + [response_topic] [text] NOT NULL, + [correlation_data] [varbinary](max) NOT NULL, + [user_properties] [varbinary](max) NOT NULL, + [expiry] [int] NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; + +CREATE UNIQUE CLUSTERED INDEX [mqtt_topic_server] ON [mqtt_pub] (topic, server_host) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); diff --git a/test/docker/docker-compose.yml b/test/docker/docker-compose.yml index 7ce610eab..bf99ae04e 100644 --- a/test/docker/docker-compose.yml +++ b/test/docker/docker-compose.yml @@ -24,6 +24,7 @@ services: - mssqldata:/var/opt/mssql - ./db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro - ../../sql/mssql.sql:/mssql.sql:ro + - ../../sql/mssql.new.sql:/mssql.new.sql:ro restart: always ports: - 1433:1433 From d4ab4d16e88148a97ba926b80a0a901308d2c7ed Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 12:57:33 +0000 Subject: [PATCH 0011/1302] Use python3 to run extauth.py for tests --- test/ejabberd_SUITE_data/ejabberd.cfg | 2 +- test/ejabberd_SUITE_data/ejabberd.extauth.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ejabberd_SUITE_data/ejabberd.cfg b/test/ejabberd_SUITE_data/ejabberd.cfg index f071ffe8b..251f23118 100644 --- a/test/ejabberd_SUITE_data/ejabberd.cfg +++ b/test/ejabberd_SUITE_data/ejabberd.cfg @@ -64,7 +64,7 @@ {host_config, "localhost", [{auth_method, internal}]}. {host_config, "extauth.localhost", [{auth_method, external}, - {extauth_program, "python extauth.py"}]}. + {extauth_program, "python3 extauth.py"}]}. {host_config, "mnesia.localhost", [{auth_method, internal}, {{add, modules}, [{mod_announce, [{db_type, internal}]}, diff --git a/test/ejabberd_SUITE_data/ejabberd.extauth.yml b/test/ejabberd_SUITE_data/ejabberd.extauth.yml index 660ddccd6..11a67d2cc 100644 --- a/test/ejabberd_SUITE_data/ejabberd.extauth.yml +++ b/test/ejabberd_SUITE_data/ejabberd.extauth.yml @@ -1,5 +1,5 @@ define_macro: EXTAUTH_CONFIG: queue_type: ram - extauth_program: "python extauth.py" + extauth_program: "python3 extauth.py" auth_method: external From f7f0d3b1fba30eedf7033bb1339f4a97acb6248d Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 15:44:44 +0000 Subject: [PATCH 0012/1302] Enable MySQL support for new schema migration --- sql/mysql.new.sql | 2 +- src/mod_admin_update_sql.erl | 112 +++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index cbd9414aa..959fbff84 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -85,7 +85,7 @@ CREATE TABLE sr_user ( PRIMARY KEY (server_host(191), jid, grp) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -CREATE UNIQUE INDEX i_sr_user_sh_jid_group ON sr_user(server_host(191), jid, grp); +CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user(server_host(191), jid, grp); CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp); CREATE TABLE spool ( diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index e9f248f5c..ec86cece7 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -63,7 +63,7 @@ depends(_Host, _Opts) -> get_commands_spec() -> [#ejabberd_commands{name = update_sql, tags = [sql], - desc = "Convert PostgreSQL DB to the new format", + desc = "Convert MySQL or PostgreSQL DB to the new format", module = ?MODULE, function = update_sql, args = [], args_example = [], @@ -93,6 +93,7 @@ update_sql(Host) -> DBType = ejabberd_option:sql_type(LHost), IsSupported = case DBType of + mysql -> true; pgsql -> true; _ -> false end, @@ -125,15 +126,15 @@ update_tables(State) -> drop_sh_default(State, "last"), add_sh_column(State, "rosterusers"), - drop_index(State, "i_rosteru_user_jid"), - drop_index(State, "i_rosteru_username"), - drop_index(State, "i_rosteru_jid"), + drop_index(State, "rosterusers", "i_rosteru_user_jid"), + drop_index(State, "rosterusers", "i_rosteru_username"), + drop_index(State, "rosterusers", "i_rosteru_jid"), create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]), create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]), drop_sh_default(State, "rosterusers"), add_sh_column(State, "rostergroups"), - drop_index(State, "pk_rosterg_user_jid"), + drop_index(State, "rostergroups", "pk_rosterg_user_jid"), create_index(State, "rostergroups", "i_rosterg_sh_user_jid", ["server_host", "username", "jid"]), drop_sh_default(State, "rostergroups"), @@ -143,27 +144,27 @@ update_tables(State) -> drop_sh_default(State, "sr_group"), add_sh_column(State, "sr_user"), - drop_index(State, "i_sr_user_jid_grp"), - drop_index(State, "i_sr_user_jid"), - drop_index(State, "i_sr_user_grp"), + drop_index(State, "sr_user", "i_sr_user_jid_grp"), + drop_index(State, "sr_user", "i_sr_user_jid"), + drop_index(State, "sr_user", "i_sr_user_grp"), add_pkey(State, "sr_user", ["server_host", "jid", "grp"]), create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), drop_sh_default(State, "sr_user"), add_sh_column(State, "spool"), - drop_index(State, "i_despool"), + drop_index(State, "spool", "i_despool"), create_index(State, "spool", "i_spool_sh_username", ["server_host", "username"]), drop_sh_default(State, "spool"), add_sh_column(State, "archive"), - drop_index(State, "i_username"), - drop_index(State, "i_username_timestamp"), - drop_index(State, "i_timestamp"), - drop_index(State, "i_peer"), - drop_index(State, "i_bare_peer"), - drop_index(State, "i_username_peer"), - drop_index(State, "i_username_bare_peer"), + drop_index(State, "archive", "i_username"), + drop_index(State, "archive", "i_username_timestamp"), + drop_index(State, "archive", "i_timestamp"), + drop_index(State, "archive", "i_peer"), + drop_index(State, "archive", "i_bare_peer"), + drop_index(State, "archive", "i_username_peer"), + drop_index(State, "archive", "i_username_bare_peer"), create_index(State, "archive", "i_archive_sh_username_timestamp", ["server_host", "username", "timestamp"]), create_index(State, "archive", "i_archive_sh_timestamp", ["server_host", "timestamp"]), create_index(State, "archive", "i_archive_sh_username_peer", ["server_host", "username", "peer"]), @@ -182,17 +183,17 @@ update_tables(State) -> add_sh_column(State, "vcard_search"), drop_pkey(State, "vcard_search"), - drop_index(State, "i_vcard_search_lfn"), - drop_index(State, "i_vcard_search_lfamily"), - drop_index(State, "i_vcard_search_lgiven"), - drop_index(State, "i_vcard_search_lmiddle"), - drop_index(State, "i_vcard_search_lnickname"), - drop_index(State, "i_vcard_search_lbday"), - drop_index(State, "i_vcard_search_lctry"), - drop_index(State, "i_vcard_search_llocality"), - drop_index(State, "i_vcard_search_lemail"), - drop_index(State, "i_vcard_search_lorgname"), - drop_index(State, "i_vcard_search_lorgunit"), + drop_index(State, "vcard_search", "i_vcard_search_lfn"), + drop_index(State, "vcard_search", "i_vcard_search_lfamily"), + drop_index(State, "vcard_search", "i_vcard_search_lgiven"), + drop_index(State, "vcard_search", "i_vcard_search_lmiddle"), + drop_index(State, "vcard_search", "i_vcard_search_lnickname"), + drop_index(State, "vcard_search", "i_vcard_search_lbday"), + drop_index(State, "vcard_search", "i_vcard_search_lctry"), + drop_index(State, "vcard_search", "i_vcard_search_llocality"), + drop_index(State, "vcard_search", "i_vcard_search_lemail"), + drop_index(State, "vcard_search", "i_vcard_search_lorgname"), + drop_index(State, "vcard_search", "i_vcard_search_lorgunit"), add_pkey(State, "vcard_search", ["server_host", "username"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]), @@ -213,14 +214,14 @@ update_tables(State) -> drop_sh_default(State, "privacy_default_list"), add_sh_column(State, "privacy_list"), - drop_index(State, "i_privacy_list_username"), - drop_index(State, "i_privacy_list_username_name"), + drop_index(State, "privacy_list", "i_privacy_list_username"), + drop_index(State, "privacy_list", "i_privacy_list_username_name"), create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]), drop_sh_default(State, "privacy_list"), add_sh_column(State, "private_storage"), - drop_index(State, "i_private_storage_username"), - drop_index(State, "i_private_storage_username_namespace"), + drop_index(State, "private_storage", "i_private_storage_username"), + drop_index(State, "private_storage", "i_private_storage_username_namespace"), add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), drop_sh_default(State, "private_storage"), @@ -247,27 +248,28 @@ update_tables(State) -> drop_sh_default(State, "motd"), add_sh_column(State, "sm"), - drop_index(State, "i_sm_sid"), - drop_index(State, "i_sm_username"), + drop_index(State, "sm", "i_sm_sid"), + drop_index(State, "sm", "i_sm_username"), add_pkey(State, "sm", ["usec", "pid"]), create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]), drop_sh_default(State, "sm"), add_sh_column(State, "push_session"), - drop_index(State, "i_push_usn"), - drop_index(State, "i_push_ut"), + drop_index(State, "push_session", "i_push_usn"), + drop_index(State, "push_session", "i_push_ut"), add_pkey(State, "push_session", ["server_host", "username", "timestamp"]), create_unique_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]), drop_sh_default(State, "push_session"), add_sh_column(State, "mix_pam"), - drop_index(State, "i_mix_pam"), - drop_index(State, "i_mix_pam_us"), + drop_index(State, "mix_pam", "i_mix_pam"), + drop_index(State, "mix_pam", "i_mix_pam_u"), + drop_index(State, "mix_pam", "i_mix_pam_us"), create_unique_index(State, "mix_pam", "i_mix_pam", ["username", "server_host", "channel", "service"]), drop_sh_default(State, "mix_pam"), add_sh_column(State, "mqtt_pub"), - drop_index(State, "i_mqtt_topic"), + drop_index(State, "mqtt_pub", "i_mqtt_topic"), create_unique_index(State, "mqtt_pub", "i_mqtt_topic_server", ["topic", "server_host"]), drop_sh_default(State, "mqtt_pub"), @@ -282,7 +284,7 @@ add_sh_column(#state{dbtype = pgsql} = State, Table) -> add_sh_column(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, - ["ALTER TABLE ", Table, " ADD COLUMN server_host text NOT NULL DEFAULT '", + ["ALTER TABLE ", Table, " ADD COLUMN server_host varchar(191) NOT NULL DEFAULT '", (State#state.escape)(State#state.host), "';"]). @@ -301,7 +303,8 @@ add_pkey(#state{dbtype = pgsql} = State, Table, Cols) -> State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]); add_pkey(#state{dbtype = mysql} = State, Table, Cols) -> - SCols = string:join(Cols, ", "), + Cols2 = [C ++ mysql_keylen(Table, C) || C <- Cols], + SCols = string:join(Cols2, ", "), sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]). @@ -315,14 +318,14 @@ drop_sh_default(#state{dbtype = mysql} = State, Table) -> State#state.host, ["ALTER TABLE ", Table, " ALTER COLUMN server_host DROP DEFAULT;"]). -drop_index(#state{dbtype = pgsql} = State, Index) -> +drop_index(#state{dbtype = pgsql} = State, _Table, Index) -> sql_query( State#state.host, ["DROP INDEX ", Index, ";"]); -drop_index(#state{dbtype = mysql} = State, Index) -> +drop_index(#state{dbtype = mysql} = State, Table, Index) -> sql_query( State#state.host, - ["DROP INDEX ", Index, ";"]). + ["ALTER TABLE ", Table, " DROP INDEX ", Index, ";"]). create_unique_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> SCols = string:join(Cols, ", "), @@ -331,7 +334,7 @@ create_unique_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> ["CREATE UNIQUE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); create_unique_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> - Cols2 = [C ++ "(75)" || C <- Cols], + Cols2 = [C ++ mysql_keylen(Index, C) || C <- Cols], SCols = string:join(Cols2, ", "), sql_query( State#state.host, @@ -345,13 +348,30 @@ create_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> ["CREATE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); create_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> - Cols2 = [C ++ "(75)" || C <- Cols], + Cols2 = [C ++ mysql_keylen(Index, C) || C <- Cols], SCols = string:join(Cols2, ", "), sql_query( State#state.host, ["CREATE INDEX ", Index, " ON ", Table, "(", SCols, ");"]). +mysql_keylen(_, "bare_peer") -> "(191)"; +mysql_keylen(_, "channel") -> "(191)"; +mysql_keylen(_, "domain") -> "(75)"; +mysql_keylen(_, "jid") -> "(75)"; +mysql_keylen(_, "name") -> "(75)"; +mysql_keylen(_, "node") -> "(75)"; +mysql_keylen(_, "peer") -> "(191)"; +mysql_keylen(_, "pid") -> "(75)"; +mysql_keylen(_, "server_host") -> "(191)"; +mysql_keylen(_, "service") -> "(191)"; +mysql_keylen(_, "topic") -> "(191)"; +mysql_keylen("i_privacy_list_sh_username_name", "username") -> "(75)"; +mysql_keylen("i_rosterg_sh_user_jid", "username") -> "(75)"; +mysql_keylen("i_rosteru_sh_user_jid", "username") -> "(75)"; +mysql_keylen(_, "username") -> "(191)"; +mysql_keylen(_, _) -> "". + sql_query(Host, Query) -> io:format("executing \"~ts\" on ~ts~n", [Query, Host]), case ejabberd_sql:sql_query(Host, Query) of @@ -369,5 +389,5 @@ mod_doc() -> ?T("This module can be used to update existing SQL database " "from the default to the new schema. Check the section " "http://../database/#default-and-new-schemas[Default and New Schemas] for details. " - "Please note that only PostgreSQL is supported. " + "Please note that only MySQL and PostgreSQL are supported. " "When the module is loaded use _`update_sql`_ API.")}. From c7c982b67b9df9aad2808411b4509a4da8cf123e Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 19:26:13 +0000 Subject: [PATCH 0013/1302] Add MS SQL support for new schema migration --- src/mod_admin_update_sql.erl | 77 +++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index ec86cece7..f3a458a96 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -63,7 +63,7 @@ depends(_Host, _Opts) -> get_commands_spec() -> [#ejabberd_commands{name = update_sql, tags = [sql], - desc = "Convert MySQL or PostgreSQL DB to the new format", + desc = "Convert MS SQL, MySQL or PostgreSQL DB to the new format", module = ?MODULE, function = update_sql, args = [], args_example = [], @@ -93,6 +93,7 @@ update_sql(Host) -> DBType = ejabberd_option:sql_type(LHost), IsSupported = case DBType of + mssql -> true; mysql -> true; pgsql -> true; _ -> false @@ -139,7 +140,7 @@ update_tables(State) -> drop_sh_default(State, "rostergroups"), add_sh_column(State, "sr_group"), - add_pkey(State, "sr_group", ["server_host", "name"]), + drop_index(State, "sr_group", "i_sr_group_name"), create_unique_index(State, "sr_group", "i_sr_group_sh_name", ["server_host", "name"]), drop_sh_default(State, "sr_group"), @@ -147,7 +148,6 @@ update_tables(State) -> drop_index(State, "sr_user", "i_sr_user_jid_grp"), drop_index(State, "sr_user", "i_sr_user_jid"), drop_index(State, "sr_user", "i_sr_user_grp"), - add_pkey(State, "sr_user", ["server_host", "jid", "grp"]), create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), drop_sh_default(State, "sr_user"), @@ -257,8 +257,8 @@ update_tables(State) -> add_sh_column(State, "push_session"), drop_index(State, "push_session", "i_push_usn"), drop_index(State, "push_session", "i_push_ut"), - add_pkey(State, "push_session", ["server_host", "username", "timestamp"]), create_unique_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]), + create_index(State, "push_session", "i_push_session_sh_username_timestamp", ["server_host", "username", "timestamp"]), drop_sh_default(State, "push_session"), add_sh_column(State, "mix_pam"), @@ -281,6 +281,12 @@ add_sh_column(#state{dbtype = pgsql} = State, Table) -> ["ALTER TABLE ", Table, " ADD COLUMN server_host text NOT NULL DEFAULT '", (State#state.escape)(State#state.host), "';"]); +add_sh_column(#state{dbtype = mssql} = State, Table) -> + sql_query( + State#state.host, + ["ALTER TABLE [", Table, "] ADD [server_host] varchar (250) NOT NULL CONSTRAINT [server_host_default] DEFAULT '", + (State#state.escape)(State#state.host), + "';"]); add_sh_column(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, @@ -292,6 +298,10 @@ drop_pkey(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " DROP CONSTRAINT ", Table, "_pkey;"]); +drop_pkey(#state{dbtype = mssql} = State, Table) -> + sql_query( + State#state.host, + ["ALTER TABLE [", Table, "] DROP CONSTRAINT [", Table, "_PRIMARY];"]); drop_pkey(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, @@ -302,6 +312,13 @@ add_pkey(#state{dbtype = pgsql} = State, Table, Cols) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]); +add_pkey(#state{dbtype = mssql} = State, Table, Cols) -> + SCols = string:join(Cols, "], ["), + sql_query( + State#state.host, + ["ALTER TABLE [", Table, "] ADD CONSTRAINT [", Table, "_PRIMARY] PRIMARY KEY CLUSTERED ([", SCols, "]) ", + "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ", + "ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY];"]); add_pkey(#state{dbtype = mysql} = State, Table, Cols) -> Cols2 = [C ++ mysql_keylen(Table, C) || C <- Cols], SCols = string:join(Cols2, ", "), @@ -313,6 +330,10 @@ drop_sh_default(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ALTER COLUMN server_host DROP DEFAULT;"]); +drop_sh_default(#state{dbtype = mssql} = State, Table) -> + sql_query( + State#state.host, + ["ALTER TABLE [", Table, "] DROP CONSTRAINT [server_host_default];"]); drop_sh_default(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, @@ -322,6 +343,10 @@ drop_index(#state{dbtype = pgsql} = State, _Table, Index) -> sql_query( State#state.host, ["DROP INDEX ", Index, ";"]); +drop_index(#state{dbtype = mssql} = State, Table, Index) -> + sql_query( + State#state.host, + ["DROP INDEX [", mssql_old_index_name(Index), "] ON [", Table, "];"]); drop_index(#state{dbtype = mysql} = State, Table, Index) -> sql_query( State#state.host, @@ -333,6 +358,15 @@ create_unique_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> State#state.host, ["CREATE UNIQUE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); +create_unique_index(#state{dbtype = mssql} = State, Table, "i_privacy_list_sh_username_name" = Index, Cols) -> + create_index(State, Table, Index, Cols); +create_unique_index(#state{dbtype = mssql} = State, Table, Index, Cols) -> + SCols = string:join(Cols, ", "), + sql_query( + State#state.host, + ["CREATE UNIQUE ", mssql_clustered(Index), "INDEX [", mssql_new_index_name(Index), "] ", + "ON [", Table, "] (", SCols, ") ", + "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);"]); create_unique_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> Cols2 = [C ++ mysql_keylen(Index, C) || C <- Cols], SCols = string:join(Cols2, ", "), @@ -347,6 +381,12 @@ create_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> State#state.host, ["CREATE INDEX ", Index, " ON ", Table, " USING btree (", SCols, ");"]); +create_index(#state{dbtype = mssql} = State, Table, Index, Cols) -> + SCols = string:join(Cols, ", "), + sql_query( + State#state.host, + ["CREATE INDEX [", mssql_new_index_name(Index), "] ON [", Table, "] (", SCols, ") ", + "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);"]); create_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> Cols2 = [C ++ mysql_keylen(Index, C) || C <- Cols], SCols = string:join(Cols2, ", "), @@ -355,6 +395,33 @@ create_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> ["CREATE INDEX ", Index, " ON ", Table, "(", SCols, ");"]). +mssql_old_index_name("i_bare_peer") -> "archive_bare_peer"; +mssql_old_index_name("i_peer") -> "archive_peer"; +mssql_old_index_name("i_timestamp") -> "archive_timestamp"; +mssql_old_index_name("i_username") -> "archive_username"; +mssql_old_index_name("i_username_bare_peer") -> "archive_username_bare_peer"; +mssql_old_index_name("i_username_peer") -> "archive_username_peer"; +mssql_old_index_name("i_username_timestamp") -> "archive_username_timestamp"; +mssql_old_index_name("i_push_usn") -> "i_push_usn"; +mssql_old_index_name("i_push_ut") -> "i_push_ut"; +mssql_old_index_name("pk_rosterg_user_jid") -> "rostergroups_username_jid"; +mssql_old_index_name("i_rosteru_jid") -> "rosterusers_jid"; +mssql_old_index_name("i_rosteru_username") -> "rosterusers_username"; +mssql_old_index_name("i_rosteru_user_jid") -> "rosterusers_username_jid"; +mssql_old_index_name("i_despool") -> "spool_username"; +mssql_old_index_name("i_sr_user_jid_grp") -> "sr_user_jid_group"; +mssql_old_index_name(Index) -> string:substr(Index, 3). + +mssql_new_index_name("i_rosterg_sh_user_jid") -> "rostergroups_sh_username_jid"; +mssql_new_index_name("i_rosteru_sh_jid") -> "rosterusers_sh_jid"; +mssql_new_index_name("i_rosteru_sh_user_jid") -> "rosterusers_sh_username_jid"; +mssql_new_index_name("i_sr_user_sh_jid_grp") -> "sr_user_sh_jid_group"; +mssql_new_index_name(Index) -> string:substr(Index, 3). + +mssql_clustered("i_mix_pam") -> ""; +mssql_clustered("i_push_session_susn") -> ""; +mssql_clustered(_) -> "CLUSTERED ". + mysql_keylen(_, "bare_peer") -> "(191)"; mysql_keylen(_, "channel") -> "(191)"; mysql_keylen(_, "domain") -> "(75)"; @@ -389,5 +456,5 @@ mod_doc() -> ?T("This module can be used to update existing SQL database " "from the default to the new schema. Check the section " "http://../database/#default-and-new-schemas[Default and New Schemas] for details. " - "Please note that only MySQL and PostgreSQL are supported. " + "Please note that only MS SQL, MySQL, and PostgreSQL are supported. " "When the module is loaded use _`update_sql`_ API.")}. From d5bf051e79672691b14012c15f16245668a835c2 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 19:17:24 +0000 Subject: [PATCH 0014/1302] Fix minor SQL schema inconsistencies --- sql/lite.new.sql | 1 + sql/mssql.sql | 2 +- sql/mysql.new.sql | 1 + sql/mysql.old-to-new.sql | 4 ++-- sql/pg.new.sql | 4 ++++ sql/pg.sql | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sql/lite.new.sql b/sql/lite.new.sql index 43baa0e1b..f48a393a0 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -414,6 +414,7 @@ CREATE TABLE push_session ( ); CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, username, service, node); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username, timestamp); CREATE TABLE mix_channel ( channel text NOT NULL, diff --git a/sql/mssql.sql b/sql/mssql.sql index 66c523e53..cf2f8b040 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -527,7 +527,7 @@ CREATE TABLE [dbo].[push_session] ( CREATE UNIQUE CLUSTERED INDEX [i_push_usn] ON [push_session] (username, service, node) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE UNIQUE INDEX [i_push_ut] ON [push_session] (username, timestamp) +CREATE INDEX [i_push_ut] ON [push_session] (username, timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE TABLE [dbo].[mix_channel] ( diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 959fbff84..6254282d5 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -432,6 +432,7 @@ CREATE TABLE push_session ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host(191), username(191), service(191), node(191)); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session (server_host, username(191), timestamp); CREATE TABLE mix_channel ( channel text NOT NULL, diff --git a/sql/mysql.old-to-new.sql b/sql/mysql.old-to-new.sql index ae3ab490f..a58a90a46 100644 --- a/sql/mysql.old-to-new.sql +++ b/sql/mysql.old-to-new.sql @@ -17,6 +17,7 @@ BEGIN ALTER TABLE `push_session` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `push_session` ADD PRIMARY KEY (`server_host`, `username`(191), `timestamp`); ALTER TABLE `push_session` ADD UNIQUE INDEX `i_push_session_susn` (`server_host`, `username`(191), `service`(191), `node`(191)); + ALTER TABLE `push_session` ADD INDEX `i_push_session_sh_username_timestamp` (`server_host`, `username`(191), `timestamp`); ALTER TABLE `roster_version` DROP PRIMARY KEY; ALTER TABLE `roster_version` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `roster_version` ALTER COLUMN `server_host` DROP DEFAULT; @@ -73,10 +74,10 @@ BEGIN ALTER TABLE `last` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `username`; ALTER TABLE `last` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `last` ADD PRIMARY KEY (`server_host`, `username`); + ALTER TABLE `sr_group` DROP INDEX `i_sr_group_name`; ALTER TABLE `sr_group` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `name`; ALTER TABLE `sr_group` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `sr_group` ADD UNIQUE INDEX `i_sr_group_sh_name` (`server_host`, `name`); - ALTER TABLE `sr_group` ADD PRIMARY KEY (`server_host`, `name`); ALTER TABLE `muc_registered` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; ALTER TABLE `muc_registered` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `sm` DROP INDEX `i_node`; @@ -99,7 +100,6 @@ BEGIN ALTER TABLE `sr_user` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `sr_user` ADD UNIQUE INDEX `i_sr_user_sh_jid_group` (`server_host`, `jid`, `grp`); ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_grp` (`server_host`, `grp`); - ALTER TABLE `sr_user` ADD PRIMARY KEY (`server_host`, `jid`, `grp`); ALTER TABLE `muc_online_users` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; ALTER TABLE `muc_online_users` ALTER COLUMN `server_host` DROP DEFAULT; ALTER TABLE `vcard` DROP PRIMARY KEY; diff --git a/sql/pg.new.sql b/sql/pg.new.sql index f3888da07..32246d3f6 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -41,7 +41,9 @@ -- ALTER TABLE rostergroups ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE sr_group ADD COLUMN server_host text NOT NULL DEFAULT ''; +-- DROP INDEX i_sr_group_name; -- ALTER TABLE sr_group ADD PRIMARY KEY (server_host, name); +-- CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group USING btree (server_host, name); -- ALTER TABLE sr_group ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE sr_user ADD COLUMN server_host text NOT NULL DEFAULT ''; @@ -153,6 +155,7 @@ -- DROP INDEX i_push_ut; -- ALTER TABLE push_session ADD PRIMARY KEY (server_host, username, timestamp); -- CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (server_host, username, service, node); +-- CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING btree (server_host, username, timestamp); -- ALTER TABLE push_session ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE mix_pam ADD COLUMN server_host text NOT NULL DEFAULT ''; @@ -583,6 +586,7 @@ CREATE TABLE push_session ( ); CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (server_host, username, service, node); +CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING btree (server_host, username, timestamp); CREATE TABLE mix_channel ( channel text NOT NULL, diff --git a/sql/pg.sql b/sql/pg.sql index 5a0d4b766..fe244c757 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -403,7 +403,7 @@ CREATE TABLE push_session ( ); CREATE UNIQUE INDEX i_push_usn ON push_session USING btree (username, service, node); -CREATE UNIQUE INDEX i_push_ut ON push_session USING btree (username, timestamp); +CREATE INDEX i_push_ut ON push_session USING btree (username, timestamp); CREATE TABLE mix_channel ( channel text NOT NULL, From 4f0e426a12aadc6a7249e6a8d6fe62cdb0579cd7 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Thu, 19 Jan 2023 23:33:46 +0000 Subject: [PATCH 0015/1302] Change PostgreSQL SERIAL to BIGSERIAL columns This is consistent with other schemas, internally consistent with foreign keys, and allows for > 2B records in these tables. --- sql/pg.new.sql | 10 +++++----- sql/pg.sql | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 32246d3f6..ba0dc31dd 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -244,7 +244,7 @@ CREATE TABLE spool ( username text NOT NULL, server_host text NOT NULL, xml text NOT NULL, - seq SERIAL, + seq BIGSERIAL, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -258,7 +258,7 @@ CREATE TABLE archive ( bare_peer text NOT NULL, xml text NOT NULL, txt text, - id SERIAL, + id BIGSERIAL, kind text, nick text, created_at TIMESTAMP NOT NULL DEFAULT now() @@ -339,7 +339,7 @@ CREATE TABLE privacy_list ( username text NOT NULL, server_host text NOT NULL, name text NOT NULL, - id SERIAL UNIQUE, + id BIGSERIAL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -394,7 +394,7 @@ CREATE TABLE pubsub_node ( node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, - nodeid SERIAL UNIQUE + nodeid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); @@ -417,7 +417,7 @@ CREATE TABLE pubsub_state ( jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', - stateid SERIAL UNIQUE + stateid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); diff --git a/sql/pg.sql b/sql/pg.sql index fe244c757..813e690dd 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -82,7 +82,7 @@ CREATE INDEX i_sr_user_grp ON sr_user USING btree (grp); CREATE TABLE spool ( username text NOT NULL, xml text NOT NULL, - seq SERIAL, + seq BIGSERIAL, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -95,7 +95,7 @@ CREATE TABLE archive ( bare_peer text NOT NULL, xml text NOT NULL, txt text, - id SERIAL, + id BIGSERIAL, kind text, nick text, created_at TIMESTAMP NOT NULL DEFAULT now() @@ -167,7 +167,7 @@ CREATE TABLE privacy_default_list ( CREATE TABLE privacy_list ( username text NOT NULL, name text NOT NULL, - id SERIAL UNIQUE, + id BIGSERIAL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -220,7 +220,7 @@ CREATE TABLE pubsub_node ( node text NOT NULL, parent text NOT NULL DEFAULT '', plugin text NOT NULL, - nodeid SERIAL UNIQUE + nodeid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent); CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node); @@ -243,7 +243,7 @@ CREATE TABLE pubsub_state ( jid text NOT NULL, affiliation character(1), subscriptions text NOT NULL DEFAULT '', - stateid SERIAL UNIQUE + stateid BIGSERIAL UNIQUE ); CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid); CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, jid); From 6cf1e059936d245c4a4bf9249ea07ea2baf02962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 20 Jan 2023 16:27:03 +0100 Subject: [PATCH 0016/1302] Try to populate room history from mam when unhibernating --- src/mod_mam.erl | 3 ++- src/mod_muc_room.erl | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index f1c2787dc..25a70c264 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1316,8 +1316,9 @@ msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick, end, Pkt3 = maybe_update_from_to( Pkt2, JidRequestor, JidArchive, Peer, MsgType, Nick), + Pkt4 = xmpp:put_meta(Pkt3, archive_nick, Nick), Delay = #delay{stamp = TS, from = jid:make(LServer)}, - {ok, #forwarded{sub_els = [Pkt3], delay = Delay}} + {ok, #forwarded{sub_els = [Pkt4], delay = Delay}} catch _:{xmpp_codec, Why} -> ?ERROR_MSG("Failed to decode raw element ~p from message " "archive of user ~ts: ~ts", diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index e352114d1..f17bf1f2b 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -316,20 +316,41 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) process_flag(trap_exit, true), Shaper = ejabberd_shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), + Jid = jid:make(Room, Host), State = set_opts(Opts, #state{host = Host, server_host = ServerHost, access = Access, room = Room, history = lqueue_new(HistorySize, QueueType), - jid = jid:make(Room, Host), + jid = Jid, room_queue = RoomQueue, room_shaper = Shaper}), add_to_log(room_existence, started, State), ejabberd_hooks:run(start_room, ServerHost, [ServerHost, Room, Host]), State1 = cleanup_affiliations(State), + State2 = + case {lists:keyfind(hibernation_time, 1, Opts), + (State1#state.config)#config.mam, + (State1#state.history)#lqueue.max} of + {{_, V}, true, L} when is_integer(V), L > 0 -> + {Msgs, _, _} = mod_mam:select(ServerHost, Jid, Jid, [], + #rsm_set{max = L, before = <<"9999999999999999">>}, + groupchat, only_messages), + Hist2 = + lists:foldl( + fun({_, TS, #forwarded{sub_els = [#message{meta = #{archive_nick := Nick}} = Msg]}}, Hist) -> + Pkt = xmpp:set_from_to(Msg, jid:replace_resource(Jid, Nick), Jid), + Size = element_size(Pkt), + lqueue_in({Nick, Pkt, false, misc:usec_to_now(TS), Size}, Hist) + end, State1#state.history, Msgs), + State1#state{history = Hist2}; + OTher -> + ?WARNING_MSG("OTHER ~p ~p", [Jid, OTher]), + State1 + end, erlang:send_after(?CLEAN_ROOM_TIMEOUT, self(), close_room_if_temporary_and_empty), - {ok, normal_state, reset_hibernate_timer(State1)}. + {ok, normal_state, reset_hibernate_timer(State2)}. normal_state({route, <<"">>, #message{from = From, type = Type, lang = Lang} = Packet}, From 3b345380383a9f31add418f35f27ef6f50db6529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 20 Jan 2023 17:12:50 +0100 Subject: [PATCH 0017/1302] Remove debug line from last commit --- src/mod_muc_room.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f17bf1f2b..11f397085 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -344,8 +344,7 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) lqueue_in({Nick, Pkt, false, misc:usec_to_now(TS), Size}, Hist) end, State1#state.history, Msgs), State1#state{history = Hist2}; - OTher -> - ?WARNING_MSG("OTHER ~p ~p", [Jid, OTher]), + _ -> State1 end, erlang:send_after(?CLEAN_ROOM_TIMEOUT, self(), From 6a8899677dd202009a84cae2b26010950de026b0 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Sat, 21 Jan 2023 12:59:33 +0000 Subject: [PATCH 0018/1302] Un-deprecate ejabberd_config:set_option/2 There does not appear to be an alternative way to set individual config options, and this is already used by test/ejabberd_SUITE.erl --- src/ejabberd_config.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index b503a4e8b..2745f5545 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -37,13 +37,13 @@ -export([beams/1, validators/1, globals/0, may_hide_data/1]). -export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]). -export([callback_modules/1]). +-export([set_option/2]). %% Deprecated functions --export([get_option/2, set_option/2]). +-export([get_option/2]). -export([get_version/0, get_myhosts/0]). -export([get_mylang/0, get_lang/1]). -deprecated([{get_option, 2}, - {set_option, 2}, {get_version, 0}, {get_myhosts, 0}, {get_mylang, 0}, From 9398052b658052c9fb53045c228d46290db7893c Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Sat, 21 Jan 2023 12:14:55 +0000 Subject: [PATCH 0019/1302] New schema migration 'update_sql' improvements - check that server_host column does not already exist before addding it and making other changes to table (update_sql becomes idempotent, yay!) - check that indexes exist before dropping them (some are historical and are not created in more recent deployments), elminating spurious errors from logs - update new_sql_schema config after migration, to allow near zero-downtime migrations (and help with automated testing) --- src/mod_admin_update_sql.erl | 515 +++++++++++++++++++++++------------ 1 file changed, 345 insertions(+), 170 deletions(-) diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index f3a458a96..5b65ec3c6 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -112,182 +112,313 @@ update_sql(Host) -> State = #state{host = LHost, dbtype = DBType, escape = Escape}, - update_tables(State) + update_tables(State), + check_config() + end. + +check_config() -> + case ejabberd_sql:use_new_schema() of + true -> ok; + false -> + ejabberd_config:set_option(new_sql_schema, true), + io:format('~nNOTE: you must add "new_sql_schema: true" to ejabberd.yml before next restart~n~n', []) end. update_tables(State) -> - add_sh_column(State, "users"), - drop_pkey(State, "users"), - add_pkey(State, "users", ["server_host", "username"]), - drop_sh_default(State, "users"), + case add_sh_column(State, "users") of + true -> + drop_pkey(State, "users"), + add_pkey(State, "users", ["server_host", "username"]), + drop_sh_default(State, "users"); + false -> + ok + end, - add_sh_column(State, "last"), - drop_pkey(State, "last"), - add_pkey(State, "last", ["server_host", "username"]), - drop_sh_default(State, "last"), + case add_sh_column(State, "last") of + true -> + drop_pkey(State, "last"), + add_pkey(State, "last", ["server_host", "username"]), + drop_sh_default(State, "last"); + false -> + ok + end, - add_sh_column(State, "rosterusers"), - drop_index(State, "rosterusers", "i_rosteru_user_jid"), - drop_index(State, "rosterusers", "i_rosteru_username"), - drop_index(State, "rosterusers", "i_rosteru_jid"), - create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]), - create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]), - drop_sh_default(State, "rosterusers"), + case add_sh_column(State, "rosterusers") of + true -> + drop_index(State, "rosterusers", "i_rosteru_user_jid"), + drop_index(State, "rosterusers", "i_rosteru_username"), + drop_index(State, "rosterusers", "i_rosteru_jid"), + create_unique_index(State, "rosterusers", "i_rosteru_sh_user_jid", ["server_host", "username", "jid"]), + create_index(State, "rosterusers", "i_rosteru_sh_jid", ["server_host", "jid"]), + drop_sh_default(State, "rosterusers"); + false -> + ok + end, - add_sh_column(State, "rostergroups"), - drop_index(State, "rostergroups", "pk_rosterg_user_jid"), - create_index(State, "rostergroups", "i_rosterg_sh_user_jid", ["server_host", "username", "jid"]), - drop_sh_default(State, "rostergroups"), + case add_sh_column(State, "rostergroups") of + true -> + drop_index(State, "rostergroups", "pk_rosterg_user_jid"), + create_index(State, "rostergroups", "i_rosterg_sh_user_jid", ["server_host", "username", "jid"]), + drop_sh_default(State, "rostergroups"); + false -> + ok + end, - add_sh_column(State, "sr_group"), - drop_index(State, "sr_group", "i_sr_group_name"), - create_unique_index(State, "sr_group", "i_sr_group_sh_name", ["server_host", "name"]), - drop_sh_default(State, "sr_group"), + case add_sh_column(State, "sr_group") of + true -> + drop_index(State, "sr_group", "i_sr_group_name"), + create_unique_index(State, "sr_group", "i_sr_group_sh_name", ["server_host", "name"]), + drop_sh_default(State, "sr_group"); + false -> + ok + end, - add_sh_column(State, "sr_user"), - drop_index(State, "sr_user", "i_sr_user_jid_grp"), - drop_index(State, "sr_user", "i_sr_user_jid"), - drop_index(State, "sr_user", "i_sr_user_grp"), - create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), - create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), - drop_sh_default(State, "sr_user"), + case add_sh_column(State, "sr_user") of + true -> + drop_index(State, "sr_user", "i_sr_user_jid_grp"), + drop_index(State, "sr_user", "i_sr_user_jid"), + drop_index(State, "sr_user", "i_sr_user_grp"), + create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), + create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), + drop_sh_default(State, "sr_user"); + false -> + ok + end, - add_sh_column(State, "spool"), - drop_index(State, "spool", "i_despool"), - create_index(State, "spool", "i_spool_sh_username", ["server_host", "username"]), - drop_sh_default(State, "spool"), + case add_sh_column(State, "spool") of + true -> + drop_index(State, "spool", "i_despool"), + create_index(State, "spool", "i_spool_sh_username", ["server_host", "username"]), + drop_sh_default(State, "spool"); + false -> + ok + end, - add_sh_column(State, "archive"), - drop_index(State, "archive", "i_username"), - drop_index(State, "archive", "i_username_timestamp"), - drop_index(State, "archive", "i_timestamp"), - drop_index(State, "archive", "i_peer"), - drop_index(State, "archive", "i_bare_peer"), - drop_index(State, "archive", "i_username_peer"), - drop_index(State, "archive", "i_username_bare_peer"), - create_index(State, "archive", "i_archive_sh_username_timestamp", ["server_host", "username", "timestamp"]), - create_index(State, "archive", "i_archive_sh_timestamp", ["server_host", "timestamp"]), - create_index(State, "archive", "i_archive_sh_username_peer", ["server_host", "username", "peer"]), - create_index(State, "archive", "i_archive_sh_username_bare_peer", ["server_host", "username", "bare_peer"]), - drop_sh_default(State, "archive"), + case add_sh_column(State, "archive") of + true -> + drop_index(State, "archive", "i_username"), + drop_index(State, "archive", "i_username_timestamp"), + drop_index(State, "archive", "i_timestamp"), + drop_index(State, "archive", "i_peer"), + drop_index(State, "archive", "i_bare_peer"), + drop_index(State, "archive", "i_username_peer"), + drop_index(State, "archive", "i_username_bare_peer"), + create_index(State, "archive", "i_archive_sh_username_timestamp", ["server_host", "username", "timestamp"]), + create_index(State, "archive", "i_archive_sh_timestamp", ["server_host", "timestamp"]), + create_index(State, "archive", "i_archive_sh_username_peer", ["server_host", "username", "peer"]), + create_index(State, "archive", "i_archive_sh_username_bare_peer", ["server_host", "username", "bare_peer"]), + drop_sh_default(State, "archive"); + false -> + ok + end, - add_sh_column(State, "archive_prefs"), - drop_pkey(State, "archive_prefs"), - add_pkey(State, "archive_prefs", ["server_host", "username"]), - drop_sh_default(State, "archive_prefs"), + case add_sh_column(State, "archive_prefs") of + true -> + drop_pkey(State, "archive_prefs"), + add_pkey(State, "archive_prefs", ["server_host", "username"]), + drop_sh_default(State, "archive_prefs"); + false -> + ok + end, - add_sh_column(State, "vcard"), - drop_pkey(State, "vcard"), - add_pkey(State, "vcard", ["server_host", "username"]), - drop_sh_default(State, "vcard"), + case add_sh_column(State, "vcard") of + true -> + drop_pkey(State, "vcard"), + add_pkey(State, "vcard", ["server_host", "username"]), + drop_sh_default(State, "vcard"); + false -> + ok + end, - add_sh_column(State, "vcard_search"), - drop_pkey(State, "vcard_search"), - drop_index(State, "vcard_search", "i_vcard_search_lfn"), - drop_index(State, "vcard_search", "i_vcard_search_lfamily"), - drop_index(State, "vcard_search", "i_vcard_search_lgiven"), - drop_index(State, "vcard_search", "i_vcard_search_lmiddle"), - drop_index(State, "vcard_search", "i_vcard_search_lnickname"), - drop_index(State, "vcard_search", "i_vcard_search_lbday"), - drop_index(State, "vcard_search", "i_vcard_search_lctry"), - drop_index(State, "vcard_search", "i_vcard_search_llocality"), - drop_index(State, "vcard_search", "i_vcard_search_lemail"), - drop_index(State, "vcard_search", "i_vcard_search_lorgname"), - drop_index(State, "vcard_search", "i_vcard_search_lorgunit"), - add_pkey(State, "vcard_search", ["server_host", "username"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lgiven", ["server_host", "lgiven"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lmiddle", ["server_host", "lmiddle"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lnickname", ["server_host", "lnickname"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lbday", ["server_host", "lbday"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lctry", ["server_host", "lctry"]), - create_index(State, "vcard_search", "i_vcard_search_sh_llocality", ["server_host", "llocality"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lemail", ["server_host", "lemail"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lorgname", ["server_host", "lorgname"]), - create_index(State, "vcard_search", "i_vcard_search_sh_lorgunit", ["server_host", "lorgunit"]), - drop_sh_default(State, "vcard_search"), + case add_sh_column(State, "vcard_search") of + true -> + drop_pkey(State, "vcard_search"), + drop_index(State, "vcard_search", "i_vcard_search_lfn"), + drop_index(State, "vcard_search", "i_vcard_search_lfamily"), + drop_index(State, "vcard_search", "i_vcard_search_lgiven"), + drop_index(State, "vcard_search", "i_vcard_search_lmiddle"), + drop_index(State, "vcard_search", "i_vcard_search_lnickname"), + drop_index(State, "vcard_search", "i_vcard_search_lbday"), + drop_index(State, "vcard_search", "i_vcard_search_lctry"), + drop_index(State, "vcard_search", "i_vcard_search_llocality"), + drop_index(State, "vcard_search", "i_vcard_search_lemail"), + drop_index(State, "vcard_search", "i_vcard_search_lorgname"), + drop_index(State, "vcard_search", "i_vcard_search_lorgunit"), + add_pkey(State, "vcard_search", ["server_host", "username"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lgiven", ["server_host", "lgiven"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lmiddle", ["server_host", "lmiddle"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lnickname", ["server_host", "lnickname"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lbday", ["server_host", "lbday"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lctry", ["server_host", "lctry"]), + create_index(State, "vcard_search", "i_vcard_search_sh_llocality", ["server_host", "llocality"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lemail", ["server_host", "lemail"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lorgname", ["server_host", "lorgname"]), + create_index(State, "vcard_search", "i_vcard_search_sh_lorgunit", ["server_host", "lorgunit"]), + drop_sh_default(State, "vcard_search"); + false -> + ok + end, - add_sh_column(State, "privacy_default_list"), - drop_pkey(State, "privacy_default_list"), - add_pkey(State, "privacy_default_list", ["server_host", "username"]), - drop_sh_default(State, "privacy_default_list"), + case add_sh_column(State, "privacy_default_list") of + true -> + drop_pkey(State, "privacy_default_list"), + add_pkey(State, "privacy_default_list", ["server_host", "username"]), + drop_sh_default(State, "privacy_default_list"); + false -> + ok + end, - add_sh_column(State, "privacy_list"), - drop_index(State, "privacy_list", "i_privacy_list_username"), - drop_index(State, "privacy_list", "i_privacy_list_username_name"), - create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]), - drop_sh_default(State, "privacy_list"), + case add_sh_column(State, "privacy_list") of + true -> + drop_index(State, "privacy_list", "i_privacy_list_username"), + drop_index(State, "privacy_list", "i_privacy_list_username_name"), + create_unique_index(State, "privacy_list", "i_privacy_list_sh_username_name", ["server_host", "username", "name"]), + drop_sh_default(State, "privacy_list"); + false -> + ok + end, - add_sh_column(State, "private_storage"), - drop_index(State, "private_storage", "i_private_storage_username"), - drop_index(State, "private_storage", "i_private_storage_username_namespace"), - add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), - drop_sh_default(State, "private_storage"), + case add_sh_column(State, "private_storage") of + true -> + drop_index(State, "private_storage", "i_private_storage_username"), + drop_index(State, "private_storage", "i_private_storage_username_namespace"), + add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), + drop_sh_default(State, "private_storage"); + false -> + ok + end, - add_sh_column(State, "roster_version"), - drop_pkey(State, "roster_version"), - add_pkey(State, "roster_version", ["server_host", "username"]), - drop_sh_default(State, "roster_version"), + case add_sh_column(State, "roster_version") of + true -> + drop_pkey(State, "roster_version"), + add_pkey(State, "roster_version", ["server_host", "username"]), + drop_sh_default(State, "roster_version"); + false -> + ok + end, - add_sh_column(State, "muc_room"), - drop_sh_default(State, "muc_room"), + case add_sh_column(State, "muc_room") of + true -> + drop_sh_default(State, "muc_room"); + false -> + ok + end, - add_sh_column(State, "muc_registered"), - drop_sh_default(State, "muc_registered"), + case add_sh_column(State, "muc_registered") of + true -> + drop_sh_default(State, "muc_registered"); + false -> + ok + end, - add_sh_column(State, "muc_online_room"), - drop_sh_default(State, "muc_online_room"), + case add_sh_column(State, "muc_online_room") of + true -> + drop_sh_default(State, "muc_online_room"); + false -> + ok + end, - add_sh_column(State, "muc_online_users"), - drop_sh_default(State, "muc_online_users"), + case add_sh_column(State, "muc_online_users") of + true -> + drop_sh_default(State, "muc_online_users"); + false -> + ok + end, - add_sh_column(State, "motd"), - drop_pkey(State, "motd"), - add_pkey(State, "motd", ["server_host", "username"]), - drop_sh_default(State, "motd"), + case add_sh_column(State, "motd") of + true -> + drop_pkey(State, "motd"), + add_pkey(State, "motd", ["server_host", "username"]), + drop_sh_default(State, "motd"); + false -> + ok + end, - add_sh_column(State, "sm"), - drop_index(State, "sm", "i_sm_sid"), - drop_index(State, "sm", "i_sm_username"), - add_pkey(State, "sm", ["usec", "pid"]), - create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]), - drop_sh_default(State, "sm"), + case add_sh_column(State, "sm") of + true -> + drop_index(State, "sm", "i_sm_sid"), + drop_index(State, "sm", "i_sm_username"), + add_pkey(State, "sm", ["usec", "pid"]), + create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]), + drop_sh_default(State, "sm"); + false -> + ok + end, - add_sh_column(State, "push_session"), - drop_index(State, "push_session", "i_push_usn"), - drop_index(State, "push_session", "i_push_ut"), - create_unique_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]), - create_index(State, "push_session", "i_push_session_sh_username_timestamp", ["server_host", "username", "timestamp"]), - drop_sh_default(State, "push_session"), + case add_sh_column(State, "push_session") of + true -> + drop_index(State, "push_session", "i_push_usn"), + drop_index(State, "push_session", "i_push_ut"), + create_unique_index(State, "push_session", "i_push_session_susn", ["server_host", "username", "service", "node"]), + create_index(State, "push_session", "i_push_session_sh_username_timestamp", ["server_host", "username", "timestamp"]), + drop_sh_default(State, "push_session"); + false -> + ok + end, - add_sh_column(State, "mix_pam"), - drop_index(State, "mix_pam", "i_mix_pam"), - drop_index(State, "mix_pam", "i_mix_pam_u"), - drop_index(State, "mix_pam", "i_mix_pam_us"), - create_unique_index(State, "mix_pam", "i_mix_pam", ["username", "server_host", "channel", "service"]), - drop_sh_default(State, "mix_pam"), + case add_sh_column(State, "mix_pam") of + true -> + drop_index(State, "mix_pam", "i_mix_pam"), + drop_index(State, "mix_pam", "i_mix_pam_u"), + drop_index(State, "mix_pam", "i_mix_pam_us"), + create_unique_index(State, "mix_pam", "i_mix_pam", ["username", "server_host", "channel", "service"]), + drop_sh_default(State, "mix_pam"); + false -> + ok + end, - add_sh_column(State, "mqtt_pub"), - drop_index(State, "mqtt_pub", "i_mqtt_topic"), - create_unique_index(State, "mqtt_pub", "i_mqtt_topic_server", ["topic", "server_host"]), - drop_sh_default(State, "mqtt_pub"), + case add_sh_column(State, "mqtt_pub") of + true -> + drop_index(State, "mqtt_pub", "i_mqtt_topic"), + create_unique_index(State, "mqtt_pub", "i_mqtt_topic_server", ["topic", "server_host"]), + drop_sh_default(State, "mqtt_pub"); + false -> + ok + end, ok. -add_sh_column(#state{dbtype = pgsql} = State, Table) -> +check_sh_column(#state{dbtype = mysql} = State, Table) -> + DB = ejabberd_option:sql_database(State#state.host), + sql_query( + State#state.host, + ["SELECT 1 FROM information_schema.columns ", + "WHERE table_name = '", Table, "' AND column_name = 'server_host' ", + "AND table_schema = '", (State#state.escape)(DB), "' ", + "GROUP BY table_name, column_name;"], false); +check_sh_column(State, Table) -> + DB = ejabberd_option:sql_database(State#state.host), + sql_query( + State#state.host, + ["SELECT 1 FROM information_schema.columns ", + "WHERE table_name = '", Table, "' AND column_name = 'server_host' ", + "AND table_catalog = '", (State#state.escape)(DB), "' ", + "GROUP BY table_name, column_name;"], false). + +add_sh_column(State, Table) -> + case check_sh_column(State, Table) of + true -> false; + false -> + do_add_sh_column(State, Table), + true + end. + +do_add_sh_column(#state{dbtype = pgsql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD COLUMN server_host text NOT NULL DEFAULT '", (State#state.escape)(State#state.host), "';"]); -add_sh_column(#state{dbtype = mssql} = State, Table) -> +do_add_sh_column(#state{dbtype = mssql} = State, Table) -> sql_query( State#state.host, - ["ALTER TABLE [", Table, "] ADD [server_host] varchar (250) NOT NULL CONSTRAINT [server_host_default] DEFAULT '", + ["ALTER TABLE [", Table, "] ADD [server_host] varchar (250) NOT NULL ", + "CONSTRAINT [server_host_default] DEFAULT '", (State#state.escape)(State#state.host), "';"]); -add_sh_column(#state{dbtype = mysql} = State, Table) -> +do_add_sh_column(#state{dbtype = mysql} = State, Table) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD COLUMN server_host varchar(191) NOT NULL DEFAULT '", @@ -339,15 +470,44 @@ drop_sh_default(#state{dbtype = mysql} = State, Table) -> State#state.host, ["ALTER TABLE ", Table, " ALTER COLUMN server_host DROP DEFAULT;"]). -drop_index(#state{dbtype = pgsql} = State, _Table, Index) -> +check_index(#state{dbtype = pgsql} = State, Table, Index) -> + sql_query( + State#state.host, + ["SELECT 1 FROM pg_indexes WHERE tablename = '", Table, + "' AND indexname = '", Index, "';"], false); +check_index(#state{dbtype = mssql} = State, Table, Index) -> + sql_query( + State#state.host, + ["SELECT 1 FROM sys.tables t ", + "INNER JOIN sys.indexes i ON i.object_id = t.object_id ", + "WHERE i.index_id > 0 ", + "AND i.name = '", Index, "' ", + "AND t.name = '", Table, "';"], false); +check_index(#state{dbtype = mysql} = State, Table, Index) -> + DB = ejabberd_option:sql_database(State#state.host), + sql_query( + State#state.host, + ["SELECT 1 FROM information_schema.statistics ", + "WHERE table_name = '", Table, "' AND index_name = '", Index, "' ", + "AND table_schema = '", (State#state.escape)(DB), "' ", + "GROUP BY table_name, index_name;"], false). + +drop_index(State, Table, Index) -> + OldIndex = old_index_name(State#state.dbtype, Index), + case check_index(State, Table, OldIndex) of + true -> do_drop_index(State, Table, OldIndex); + false -> ok + end. + +do_drop_index(#state{dbtype = pgsql} = State, _Table, Index) -> sql_query( State#state.host, ["DROP INDEX ", Index, ";"]); -drop_index(#state{dbtype = mssql} = State, Table, Index) -> +do_drop_index(#state{dbtype = mssql} = State, Table, Index) -> sql_query( State#state.host, - ["DROP INDEX [", mssql_old_index_name(Index), "] ON [", Table, "];"]); -drop_index(#state{dbtype = mysql} = State, Table, Index) -> + ["DROP INDEX [", Index, "] ON [", Table, "];"]); +do_drop_index(#state{dbtype = mysql} = State, Table, Index) -> sql_query( State#state.host, ["ALTER TABLE ", Table, " DROP INDEX ", Index, ";"]). @@ -364,7 +524,7 @@ create_unique_index(#state{dbtype = mssql} = State, Table, Index, Cols) -> SCols = string:join(Cols, ", "), sql_query( State#state.host, - ["CREATE UNIQUE ", mssql_clustered(Index), "INDEX [", mssql_new_index_name(Index), "] ", + ["CREATE UNIQUE ", mssql_clustered(Index), "INDEX [", new_index_name(State#state.dbtype, Index), "] ", "ON [", Table, "] (", SCols, ") ", "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);"]); create_unique_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> @@ -376,47 +536,52 @@ create_unique_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> SCols, ");"]). create_index(#state{dbtype = pgsql} = State, Table, Index, Cols) -> + NewIndex = new_index_name(State#state.dbtype, Index), SCols = string:join(Cols, ", "), sql_query( State#state.host, - ["CREATE INDEX ", Index, " ON ", Table, " USING btree (", + ["CREATE INDEX ", NewIndex, " ON ", Table, " USING btree (", SCols, ");"]); create_index(#state{dbtype = mssql} = State, Table, Index, Cols) -> + NewIndex = new_index_name(State#state.dbtype, Index), SCols = string:join(Cols, ", "), sql_query( State#state.host, - ["CREATE INDEX [", mssql_new_index_name(Index), "] ON [", Table, "] (", SCols, ") ", + ["CREATE INDEX [", NewIndex, "] ON [", Table, "] (", SCols, ") ", "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);"]); create_index(#state{dbtype = mysql} = State, Table, Index, Cols) -> - Cols2 = [C ++ mysql_keylen(Index, C) || C <- Cols], + NewIndex = new_index_name(State#state.dbtype, Index), + Cols2 = [C ++ mysql_keylen(NewIndex, C) || C <- Cols], SCols = string:join(Cols2, ", "), sql_query( State#state.host, - ["CREATE INDEX ", Index, " ON ", Table, "(", + ["CREATE INDEX ", NewIndex, " ON ", Table, "(", SCols, ");"]). -mssql_old_index_name("i_bare_peer") -> "archive_bare_peer"; -mssql_old_index_name("i_peer") -> "archive_peer"; -mssql_old_index_name("i_timestamp") -> "archive_timestamp"; -mssql_old_index_name("i_username") -> "archive_username"; -mssql_old_index_name("i_username_bare_peer") -> "archive_username_bare_peer"; -mssql_old_index_name("i_username_peer") -> "archive_username_peer"; -mssql_old_index_name("i_username_timestamp") -> "archive_username_timestamp"; -mssql_old_index_name("i_push_usn") -> "i_push_usn"; -mssql_old_index_name("i_push_ut") -> "i_push_ut"; -mssql_old_index_name("pk_rosterg_user_jid") -> "rostergroups_username_jid"; -mssql_old_index_name("i_rosteru_jid") -> "rosterusers_jid"; -mssql_old_index_name("i_rosteru_username") -> "rosterusers_username"; -mssql_old_index_name("i_rosteru_user_jid") -> "rosterusers_username_jid"; -mssql_old_index_name("i_despool") -> "spool_username"; -mssql_old_index_name("i_sr_user_jid_grp") -> "sr_user_jid_group"; -mssql_old_index_name(Index) -> string:substr(Index, 3). +old_index_name(mssql, "i_bare_peer") -> "archive_bare_peer"; +old_index_name(mssql, "i_peer") -> "archive_peer"; +old_index_name(mssql, "i_timestamp") -> "archive_timestamp"; +old_index_name(mssql, "i_username") -> "archive_username"; +old_index_name(mssql, "i_username_bare_peer") -> "archive_username_bare_peer"; +old_index_name(mssql, "i_username_peer") -> "archive_username_peer"; +old_index_name(mssql, "i_username_timestamp") -> "archive_username_timestamp"; +old_index_name(mssql, "i_push_usn") -> "i_push_usn"; +old_index_name(mssql, "i_push_ut") -> "i_push_ut"; +old_index_name(mssql, "pk_rosterg_user_jid") -> "rostergroups_username_jid"; +old_index_name(mssql, "i_rosteru_jid") -> "rosterusers_jid"; +old_index_name(mssql, "i_rosteru_username") -> "rosterusers_username"; +old_index_name(mssql, "i_rosteru_user_jid") -> "rosterusers_username_jid"; +old_index_name(mssql, "i_despool") -> "spool_username"; +old_index_name(mssql, "i_sr_user_jid_grp") -> "sr_user_jid_group"; +old_index_name(mssql, Index) -> string:substr(Index, 3); +old_index_name(_Type, Index) -> Index. -mssql_new_index_name("i_rosterg_sh_user_jid") -> "rostergroups_sh_username_jid"; -mssql_new_index_name("i_rosteru_sh_jid") -> "rosterusers_sh_jid"; -mssql_new_index_name("i_rosteru_sh_user_jid") -> "rosterusers_sh_username_jid"; -mssql_new_index_name("i_sr_user_sh_jid_grp") -> "sr_user_sh_jid_group"; -mssql_new_index_name(Index) -> string:substr(Index, 3). +new_index_name(mssql, "i_rosterg_sh_user_jid") -> "rostergroups_sh_username_jid"; +new_index_name(mssql, "i_rosteru_sh_jid") -> "rosterusers_sh_jid"; +new_index_name(mssql, "i_rosteru_sh_user_jid") -> "rosterusers_sh_username_jid"; +new_index_name(mssql, "i_sr_user_sh_jid_grp") -> "sr_user_sh_jid_group"; +new_index_name(mssql, Index) -> string:substr(Index, 3); +new_index_name(_Type, Index) -> Index. mssql_clustered("i_mix_pam") -> ""; mssql_clustered("i_push_session_susn") -> ""; @@ -440,11 +605,21 @@ mysql_keylen(_, "username") -> "(191)"; mysql_keylen(_, _) -> "". sql_query(Host, Query) -> - io:format("executing \"~ts\" on ~ts~n", [Query, Host]), + sql_query(Host, Query, true). + +sql_query(Host, Query, Log) -> + case Log of + true -> io:format("executing \"~ts\" on ~ts~n", [Query, Host]); + false -> ok + end, case ejabberd_sql:sql_query(Host, Query) of + {selected, _Cols, []} -> + false; + {selected, _Cols, [_Rows]} -> + true; {error, Error} -> io:format("error: ~p~n", [Error]), - ok; + false; _ -> ok end. From 56e974ab8039bf3a41ac7bf33bdc216da5f40ff9 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Fri, 20 Jan 2023 17:41:09 +0000 Subject: [PATCH 0020/1302] Add ability to run tests on upgraded DB To test update_sql operation and functionality of resulting DB: 1. Load original schema to DB 2. Set {update_sql, true} in suite.erl 3. Run tests --- .github/workflows/ci.yml | 51 +++++++++++++++++++---- src/mod_admin_update_sql.erl | 2 + test/ejabberd_SUITE.erl | 59 ++++++++++++++------------- test/ejabberd_SUITE_data/ejabberd.yml | 1 + test/suite.erl | 1 + 5 files changed, 78 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba236f9ec..37ddd6c17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,19 +62,19 @@ jobs: run: | sudo systemctl start mysql.service sudo systemctl start postgresql.service + mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test';" - mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" - mysql -u root -proot ejabberd_test < sql/mysql.sql + mysql -u ejabberd_test -pejabberd_test ejabberd_test < sql/mysql.sql pg_isready + sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" sudo -u postgres psql -c "CREATE USER ejabberd_test WITH PASSWORD 'ejabberd_test';" - sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" - sudo -u postgres psql ejabberd_test -f sql/pg.sql sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" + PGPASSWORD="ejabberd_test" psql -h localhost -U ejabberd_test ejabberd_test -f sql/pg.sql sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" @@ -221,6 +221,42 @@ jobs: CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'` echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/" + - name: Check for changes to trigger schema upgrade test + uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + sql: + - 'sql/**' + - 'src/mod_admin_update_sql.erl' + + - name: Prepare for schema upgrade test + id: prepupgradetest + if: ${{ steps.filter.outputs.sql == 'true' }} + run: | + [[ -d logs ]] && rm -rf logs + [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true + sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl + - name: Run tests on upgraded schema (mysql, pgsql) + run: CT_BACKENDS=mysql,pgsql make test + if: always() && steps.prepupgradetest.outcome != 'skipped' + id: ctupgradedschema + - name: Check results + if: always() && steps.ctupgradedschema.outcome != 'skipped' + run: | + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true + ln `find logs/ -name suite.log` logs/suite.log + grep 'TEST COMPLETE' logs/suite.log + grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log + test $(find logs/ -empty -name error.log) + - name: View logs failures + if: failure() && steps.ctupgradedschema.outcome != 'skipped' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' + - name: Prepare new schema run: | [[ -d logs ]] && rm -rf logs @@ -230,11 +266,11 @@ jobs: mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" - mysql -u root -proot ejabberd_test < sql/mysql.new.sql + mysql -u ejabberd_test -pejabberd_test ejabberd_test < sql/mysql.new.sql sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" - sudo -u postgres psql ejabberd_test -f sql/pg.new.sql sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" + PGPASSWORD="ejabberd_test" psql -h localhost -U ejabberd_test ejabberd_test -f sql/pg.new.sql sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" @@ -242,7 +278,8 @@ jobs: SEQUENCES IN SCHEMA public TO ejabberd_test;" sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl - - run: CT_BACKENDS=mysql,pgsql make test + - name: Run tests on new schema (mysql, pgsql) + run: CT_BACKENDS=mysql,pgsql make test id: ctnewschema - name: Check results if: always() && steps.ctnewschema.outcome != 'skipped' diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 5b65ec3c6..4bc9561c7 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -34,6 +34,8 @@ % Commands API -export([update_sql/0]). +% For testing +-export([update_sql/1]). -include("logger.hrl"). -include("ejabberd_commands.hrl"). diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 696d4cf17..5bd1e43b7 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -99,7 +99,8 @@ do_init_per_group(mysql, Config) -> case catch ejabberd_sql:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?MYSQL_VHOST), - clear_sql_tables(mysql, ?config(base_dir, Config)), + clear_sql_tables(mysql, Config), + update_sql(?MYSQL_VHOST, Config), set_opt(server, ?MYSQL_VHOST, Config); Err -> {skip, {mysql_not_available, Err}} @@ -108,7 +109,8 @@ do_init_per_group(mssql, Config) -> case catch ejabberd_sql:sql_query(?MSSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?MSSQL_VHOST), - clear_sql_tables(mssql, ?config(base_dir, Config)), + clear_sql_tables(mssql, Config), + update_sql(?MSSQL_VHOST, Config), set_opt(server, ?MSSQL_VHOST, Config); Err -> {skip, {mssql_not_available, Err}} @@ -117,7 +119,8 @@ do_init_per_group(pgsql, Config) -> case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?PGSQL_VHOST), - clear_sql_tables(pgsql, ?config(base_dir, Config)), + clear_sql_tables(pgsql, Config), + update_sql(?PGSQL_VHOST, Config), set_opt(server, ?PGSQL_VHOST, Config); Err -> {skip, {pgsql_not_available, Err}} @@ -1011,37 +1014,35 @@ bookmark_conference() -> '$handle_undefined_function'(_, _) -> erlang:error(undef). + %%%=================================================================== %%% SQL stuff %%%=================================================================== -clear_sql_tables(sqlite, _BaseDir) -> +update_sql(Host, Config) -> + case ?config(update_sql, Config) of + true -> + mod_admin_update_sql:update_sql(Host); + false -> ok + end. + +schema_suffix(Config) -> + case ejabberd_sql:use_new_schema() of + true -> + case ?config(update_sql, Config) of + true -> ".sql"; + _ -> ".new.sql" + end; + _ -> ".sql" + end. + +clear_sql_tables(sqlite, _Config) -> ok; -clear_sql_tables(Type, BaseDir) -> +clear_sql_tables(Type, Config) -> + BaseDir = ?config(base_dir, Config), {VHost, File} = case Type of - mysql -> - Path = case ejabberd_sql:use_new_schema() of - true -> - "mysql.new.sql"; - false -> - "mysql.sql" - end, - {?MYSQL_VHOST, Path}; - mssql -> - Path = case ejabberd_sql:use_new_schema() of - true -> - "mssql.new.sql"; - false -> - "mssql.sql" - end, - {?MSSQL_VHOST, Path}; - pgsql -> - Path = case ejabberd_sql:use_new_schema() of - true -> - "pg.new.sql"; - false -> - "pg.sql" - end, - {?PGSQL_VHOST, Path} + mysql -> {?MYSQL_VHOST, "mysql" ++ schema_suffix(Config)}; + mssql -> {?MSSQL_VHOST, "mssql" ++ schema_suffix(Config)}; + pgsql -> {?PGSQL_VHOST, "pg" ++ schema_suffix(Config)} end, SQLFile = filename:join([BaseDir, "sql", File]), CreationQueries = read_sql_queries(SQLFile), diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 195917b68..ebbcfa596 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -108,6 +108,7 @@ max_fsm_queue: 1000 queue_type: file modules: mod_adhoc: [] + mod_admin_update_sql: [] mod_announce: [] mod_configure: [] mod_disco: [] diff --git a/test/suite.erl b/test/suite.erl index 468c6c93b..95b991d71 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -131,6 +131,7 @@ init_config(Config) -> {resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, + {update_sql, false}, {password, Password}, {backends, Backends} |Config]. From cbbf85c55536a323f0394790051335a0d16473f4 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Fri, 20 Jan 2023 20:51:42 +0000 Subject: [PATCH 0021/1302] Add support for running tests on MS SQL --- .github/workflows/ci.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37ddd6c17..61bd07ab2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,8 +58,19 @@ jobs: wget https://github.com/processone/ejabberd/raw/21.12/rebar3 chmod +x rebar3 + - name: Install MS SQL Server + run: | + docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \ + -v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \ + -v $(pwd)/sql/mssql.sql:/mssql.sql:ro \ + -v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \ + -p 1433:1433 --name ejabberd-mssql "mcr.microsoft.com/mssql/server:2019-latest" + sleep 10 + - name: Prepare databases run: | + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql sudo systemctl start mysql.service sudo systemctl start postgresql.service mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" @@ -237,8 +248,8 @@ jobs: [[ -d logs ]] && rm -rf logs [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true sed -i 's|update_sql, false|update_sql, true|g' test/suite.erl - - name: Run tests on upgraded schema (mysql, pgsql) - run: CT_BACKENDS=mysql,pgsql make test + - name: Run DB tests on upgraded schema (mssql, mysql, pgsql) + run: CT_BACKENDS=mssql,mysql,pgsql make test if: always() && steps.prepupgradetest.outcome != 'skipped' id: ctupgradedschema - name: Check results @@ -261,8 +272,12 @@ jobs: run: | [[ -d logs ]] && rm -rf logs [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" mysql -u root -proot -e "DROP DATABASE ejabberd_test;" sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" @@ -278,8 +293,8 @@ jobs: SEQUENCES IN SCHEMA public TO ejabberd_test;" sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl - - name: Run tests on new schema (mysql, pgsql) - run: CT_BACKENDS=mysql,pgsql make test + - name: Run DB tests on new schema (mssql, mysql, pgsql) + run: CT_BACKENDS=mssql,mysql,pgsql make test id: ctnewschema - name: Check results if: always() && steps.ctnewschema.outcome != 'skipped' From 0c1cf43519d1e3cf9555c6c62e222a39b3febaec Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Sat, 21 Jan 2023 14:22:52 +0000 Subject: [PATCH 0022/1302] Fix a long standing bug in new schema migration ... and make the test that uncovered it explicitly fail (there was already a TODO) instead of passing but with errors logged --- sql/pg.new.sql | 2 +- src/mod_admin_update_sql.erl | 2 +- test/vcard_tests.erl | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/pg.new.sql b/sql/pg.new.sql index ba0dc31dd..6a13ad6f3 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -92,7 +92,7 @@ -- DROP INDEX i_vcard_search_lemail; -- DROP INDEX i_vcard_search_lorgname; -- DROP INDEX i_vcard_search_lorgunit; --- ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, lusername); -- CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); -- CREATE INDEX i_vcard_search_sh_lfamily ON vcard_search(server_host, lfamily); -- CREATE INDEX i_vcard_search_sh_lgiven ON vcard_search(server_host, lgiven); diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 4bc9561c7..db7730e95 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -246,7 +246,7 @@ update_tables(State) -> drop_index(State, "vcard_search", "i_vcard_search_lemail"), drop_index(State, "vcard_search", "i_vcard_search_lorgname"), drop_index(State, "vcard_search", "i_vcard_search_lorgunit"), - add_pkey(State, "vcard_search", ["server_host", "username"]), + add_pkey(State, "vcard_search", ["server_host", "lusername"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfn", ["server_host", "lfn"]), create_index(State, "vcard_search", "i_vcard_search_sh_lfamily", ["server_host", "lfamily"]), create_index(State, "vcard_search", "i_vcard_search_sh_lgiven", ["server_host", "lgiven"]), diff --git a/test/vcard_tests.erl b/test/vcard_tests.erl index ba1c676d1..e3170f78d 100644 --- a/test/vcard_tests.erl +++ b/test/vcard_tests.erl @@ -31,6 +31,7 @@ recv_presence/1, recv/1]). -include("suite.hrl"). +-include_lib("stdlib/include/assert.hrl"). %%%=================================================================== %%% API @@ -83,9 +84,9 @@ get_set(Config) -> "personal website: http://www.saint-andre.com/">>}, #iq{type = result, sub_els = []} = send_recv(Config, #iq{type = set, sub_els = [VCard]}), - %% TODO: check if VCard == VCard1. - #iq{type = result, sub_els = [_VCard1]} = + #iq{type = result, sub_els = [VCard1]} = send_recv(Config, #iq{type = get, sub_els = [#vcard_temp{}]}), + ?assertEqual(VCard, VCard1), disconnect(Config). service_vcard(Config) -> From c5c7e7fc4db97022986d1595b73781571a3d59f2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 25 Jan 2023 13:25:56 +0100 Subject: [PATCH 0023/1302] ext_mod: Improve support for loading *.so files from ext_mod dependencies Copying files from deps/*/priv/*.so to the ejabberd priv/ is not possible when running ejabberd as OTP release or in a container. Instead, let's copy the deps/*/priv/*.so maintaining the file structure, and then using code:add_pathz for those dirs. This partially reverts 5c1b72853f8cfcd25d1ed6231a9f1c78ea19b95e --- src/ext_mod.erl | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index e3176d29e..32f1f6fc8 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -66,7 +66,7 @@ init([]) -> {ok, #state{}}. add_paths() -> - [code:add_patha(module_ebin_dir(Module)) + [code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]) || {Module, _} <- installed()]. handle_call(Request, From, State) -> @@ -229,7 +229,7 @@ install(Package) when is_binary(Package) -> Module = misc:binary_to_atom(Package), case compile_and_install(Module, Attrs) of ok -> - code:add_patha(module_ebin_dir(Module)), + code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]), ejabberd_config:reload(), copy_commit_json(Package, Attrs), case erlang:function_exported(Module, post_install, 0) of @@ -256,7 +256,7 @@ uninstall(Package) when is_binary(Package) -> || Host <- ejabberd_option:hosts()], code:purge(Module), code:delete(Module), - code:del_path(module_ebin_dir(Module)), + [code:del_path(PathDelete) || PathDelete <- [module_ebin_dir(Module)|module_deps_dirs(Module)]], delete_path(module_lib_dir(Module)), ejabberd_config:reload(); false -> @@ -654,11 +654,12 @@ install(Module, Spec, SrcDir, LibDir) -> Files1 = [{File, copy(File, filename:join(LibDir, File))} || File <- filelib:wildcard("{ebin,priv,conf,include}/**")], Files2 = [{File, copy(File, filename:join(LibDir, filename:join(lists:nthtail(2,filename:split(File)))))} - || File <- filelib:wildcard("deps/*/{ebin,priv}/**")], + || File <- filelib:wildcard("deps/*/ebin/**")], + Files3 = [{File, copy(File, filename:join(LibDir, File))} + || File <- filelib:wildcard("deps/*/priv/**")], Errors = lists:dropwhile(fun({_, ok}) -> true; (_) -> false - end, Files1++Files2), - copy_priv_files(LibDir), + end, Files1++Files2++Files3), inform_module_configuration(Module, LibDir, Files1), Result = case Errors of [{F, {error, E}}|_] -> @@ -671,12 +672,6 @@ install(Module, Spec, SrcDir, LibDir) -> file:set_cwd(CurDir), Result. -copy_priv_files(LibDir) -> - file:set_cwd(LibDir), - EjabberdLibDir = code:lib_dir(ejabberd), - [{File, copy(File, filename:join(EjabberdLibDir, File))} - || File <- filelib:wildcard("{priv}/**")]. - inform_module_configuration(Module, LibDir, Files1) -> Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true; (_) -> false @@ -736,6 +731,14 @@ rebar_dep({App, _, {git, Url, Ref}}) -> "; (cd "++filename:basename(App)++ "; git checkout -q "++Ref++")"}. +module_deps_dirs(Module) -> + SrcDir = module_src_dir(Module), + LibDir = module_lib_dir(Module), + DepsDir = filename:join(LibDir, "deps"), + Deps = rebar_deps(filename:join(SrcDir, "rebar.config")) + ++ rebar_deps(filename:join(SrcDir, "rebar.config.script")), + [filename:join(DepsDir, App) || {App, _Cmd} <- Deps]. + %% -- YAML spec parser consult(File) -> From 43cae922f3cce02456186bf754774bdc8e70bc3e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 25 Jan 2023 17:20:23 +0100 Subject: [PATCH 0024/1302] Update Alpine to 3.17 to get Elixir 1.14 required by recent libraries --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 254bbe917..d143bacc2 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 AS build +FROM alpine:3.17 AS build ARG VERSION=master RUN apk upgrade --update musl \ @@ -63,7 +63,7 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \ \ncertfiles: \ \n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml" -FROM alpine:3.16 +FROM alpine:3.17 ENV HOME=/opt/ejabberd ARG VERSION=master From 03cbc9b004991017e2e0b5813a1923e495ef6e38 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jan 2023 14:49:10 +0100 Subject: [PATCH 0025/1302] Revert previous commit, stick with Alpine 3.16 and Erlang/OTP 24 (#3983) Alpine 3.17 includes Erlang/OTP 25, and it segfaults when used in QEMU for arm64. Revert "Update Alpine to 3.17 to get Elixir 1.14 required by recent libraries" This reverts commit 43cae922f3cce02456186bf754774bdc8e70bc3e. --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index d143bacc2..254bbe917 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17 AS build +FROM alpine:3.16 AS build ARG VERSION=master RUN apk upgrade --update musl \ @@ -63,7 +63,7 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \ \ncertfiles: \ \n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml" -FROM alpine:3.17 +FROM alpine:3.16 ENV HOME=/opt/ejabberd ARG VERSION=master From f650b1e83c7d6795cb9107b50eec90d593e97571 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 26 Jan 2023 02:47:34 +0300 Subject: [PATCH 0026/1302] Log HTTP handler exceptions --- src/ejabberd_http.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index a33232422..42693514b 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -370,7 +370,15 @@ process(Handlers, Request) -> HandlerModule:socket_handoff( LocalPath, Request, HandlerOpts); false -> - HandlerModule:process(LocalPath, Request) + try + HandlerModule:process(LocalPath, Request) + catch + Class:Reason:Stack -> + ?ERROR_MSG( + "HTTP handler crashed: ~s", + [misc:format_exception(2, Class, Reason, Stack)]), + erlang:raise(Class, Reason, Stack) + end end, ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]), R; From f6b5a521040d524d359758404b565e92eaa26823 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 27 Jan 2023 03:53:28 +0300 Subject: [PATCH 0027/1302] Add s2s_out_bounce_packet hook --- src/ejabberd_s2s_out.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 778a53469..b9ae1abb3 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -360,10 +360,17 @@ bounce_message_queue(FromTo, State) -> -spec bounce_packet(xmpp_element(), state()) -> state(). bounce_packet(Pkt, State) when ?is_stanza(Pkt) -> - Lang = xmpp:get_lang(Pkt), - Err = mk_bounce_error(Lang, State), - ejabberd_router:route_error(Pkt, Err), - State; + #{server_host := Host} = State, + case ejabberd_hooks:run_fold( + s2s_out_bounce_packet, Host, State, [Pkt]) of + ignore -> + State; + State2 -> + Lang = xmpp:get_lang(Pkt), + Err = mk_bounce_error(Lang, State2), + ejabberd_router:route_error(Pkt, Err), + State2 + end; bounce_packet(_, State) -> State. From 9842b035e30fe8c247fe356a5b791fd268db858c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 15:27:40 +0100 Subject: [PATCH 0028/1302] Fix compilation problem with Erlang/OTP older than 21 introduced recently Those macros were first introduced in c88a2d0 --- src/ejabberd_http.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 42693514b..03f567582 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -39,6 +39,7 @@ -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). +-include("ejabberd_stacktrace.hrl"). -include_lib("kernel/include/file.hrl"). -record(state, {sockmod, @@ -373,10 +374,10 @@ process(Handlers, Request) -> try HandlerModule:process(LocalPath, Request) catch - Class:Reason:Stack -> + ?EX_RULE(Class, Reason, Stack) -> ?ERROR_MSG( "HTTP handler crashed: ~s", - [misc:format_exception(2, Class, Reason, Stack)]), + [misc:format_exception(2, Class, Reason, ?EX_STACK(Stack))]), erlang:raise(Class, Reason, Stack) end end, From fb16727180f60da5455ecf032b50080a34e812ef Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 15:40:34 +0100 Subject: [PATCH 0029/1302] Ammend previous commit with another fix --- src/ejabberd_http.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 03f567582..f29f36063 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -378,7 +378,7 @@ process(Handlers, Request) -> ?ERROR_MSG( "HTTP handler crashed: ~s", [misc:format_exception(2, Class, Reason, ?EX_STACK(Stack))]), - erlang:raise(Class, Reason, Stack) + erlang:raise(Class, Reason, ?EX_STACK(Stack)) end end, ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]), From 1a6baf223c6b0b17a8d0492b7c22cf39fd1fcccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 1 Feb 2023 16:58:25 +0100 Subject: [PATCH 0030/1302] Re-allow anonymous connection for connection without client certificates This fixes issue #3985. Initial issue was introduced in 5506b838c803c33c6fd5b1af00d62482c4a75c60 adding tls client cert authentication. --- src/mod_mqtt_session.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl index f3dfe2272..cf61efea6 100644 --- a/src/mod_mqtt_session.erl +++ b/src/mod_mqtt_session.erl @@ -1214,7 +1214,13 @@ authenticate(#connect{password = Pass, properties = Props} = Pkt, State) -> true -> {ok, JID, pkix}; false -> - {error, 'not-authorized'} + {error, 'not-authorized'}; + no_cert -> + case ejabberd_auth:check_password_with_authmodule( + LUser, <<>>, LServer, Pass) of + {true, AuthModule} -> {ok, JID, AuthModule}; + false -> {error, 'not-authorized'} + end end; _ -> case ejabberd_auth:check_password_with_authmodule( @@ -1230,7 +1236,7 @@ authenticate(#connect{password = Pass, properties = Props} = Pkt, State) -> -spec tls_auth(jid:jid(), state()) -> boolean(). tls_auth(_JID, #state{tls_verify = false}) -> - false; + no_cert; tls_auth(JID, State) -> case State#state.socket of {fast_tls, Sock} -> @@ -1251,10 +1257,10 @@ tls_auth(JID, State) -> false end; error -> - false + no_cert end; _ -> - false + no_cert end. get_cert_jid(Cert) -> From 5c3b43cd6340b640adac848717c6f8f768fdec83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 1 Feb 2023 17:32:22 +0100 Subject: [PATCH 0031/1302] Update spec --- src/mod_mqtt_session.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl index cf61efea6..37ea8ce02 100644 --- a/src/mod_mqtt_session.erl +++ b/src/mod_mqtt_session.erl @@ -1234,7 +1234,7 @@ authenticate(#connect{password = Pass, properties = Props} = Pkt, State) -> Err end. --spec tls_auth(jid:jid(), state()) -> boolean(). +-spec tls_auth(jid:jid(), state()) -> boolean() | no_cert. tls_auth(_JID, #state{tls_verify = false}) -> no_cert; tls_auth(JID, State) -> From 5ca59807d9a16e858b23a4d96bb7225ef109919a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 3 Feb 2023 11:37:54 +0100 Subject: [PATCH 0032/1302] Fix problem with results of mam queries using rsm with max and before Plus add test case for it. --- src/mod_mam_sql.erl | 2 +- test/mam_tests.erl | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 695542387..a0dd6b6d1 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -441,7 +441,7 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) -> true -> [] end, - SubOrderClause = if LimitClause /= [], TopClause /= [] -> + SubOrderClause = if LimitClause /= []; TopClause /= [] -> <<" ORDER BY timestamp DESC ">>; true -> [] diff --git a/test/mam_tests.erl b/test/mam_tests.erl index 30b0179a6..ed2fecae9 100644 --- a/test/mam_tests.erl +++ b/test/mam_tests.erl @@ -642,7 +642,8 @@ query_rsm_after(Config, From, To, NS) -> query_rsm_before(Config, From, To) -> lists:foreach( fun(NS) -> - query_rsm_before(Config, From, To, NS) + query_rsm_before(Config, From, To, NS), + query_last_message(Config, From, To, NS) end, ?VERSIONS). query_rsm_before(Config, From, To, NS) -> @@ -661,6 +662,16 @@ query_rsm_before(Config, From, To, NS) -> Last end, <<"">>, lists:reverse([lists:seq(1, N) || N <- lists:seq(0, 5)])). +query_last_message(Config, From, To, NS) -> + ct:comment("Retrieving last message", []), + QID = p1_rand:get_string(), + Query = #mam_query{xmlns = NS, id = QID, + rsm = #rsm_set{before = <<>>, max = 1}}, + ID = send_query(Config, Query), + recv_archived_messages(Config, From, To, QID, [5]), + RSM = ?match(#rsm_set{} = RSM, recv_fin(Config, ID, QID, NS, false), RSM), + match_rsm_count(RSM, 5). + match_rsm_count(#rsm_set{count = undefined}, _) -> %% The backend doesn't support counting ok; From 3de803be2fc9c84110bd86bd54c59a2f250f76bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 8 Feb 2023 11:04:26 +0100 Subject: [PATCH 0033/1302] Add get_room_history command in mod_muc_admin --- src/mod_muc_admin.erl | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 4c20c4d9d..7bdd51f42 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -46,7 +46,7 @@ web_page_host/3, mod_opt_type/1, mod_options/1, get_commands_spec/0, find_hosts/1, room_diagnostics/2, - get_room_pid/2]). + get_room_pid/2, get_room_history/2]). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -405,7 +405,19 @@ get_commands_spec() -> result_desc = "Affiliation of the user", result_example = member, args = [{name, binary}, {service, binary}, {jid, binary}], - result = {affiliation, atom}} + result = {affiliation, atom}}, + #ejabberd_commands{name = get_room_history, tags = [muc_room], + desc = "Get history of messages stored inside MUC room state", + module = ?MODULE, function = get_room_history, + args_desc = ["Room name", "MUC service"], + args_example = ["room1", "muc.example.com"], + result_desc = "Affiliation of the user", + result_example = member, + args = [{name, binary}, {service, binary}], + result = {history, {list, + {entry, {tuple, + [{timestamp, string}, + {message, string}]}}}}} ]. @@ -1290,6 +1302,23 @@ get_room_affiliations(Name, Service) -> throw({error, "The room does not exist."}) end. +get_room_history(Name, Service) -> + case get_room_pid(Name, Service) of + Pid when is_pid(Pid) -> + case mod_muc_room:get_state(Pid) of + {ok, StateData} -> + History = p1_queue:to_list((StateData#state.history)#lqueue.queue), + lists:map( + fun({_Nick, Packet, _HaveSubject, TimeStamp, _Size}) -> + {xmpp_util:encode_timestamp(TimeStamp), fxml:element_to_binary(xmpp:encode(Packet))} + end, History); + _ -> + throw({error, "Unable to fetch room state."}) + end; + _ -> + throw({error, "The room does not exist."}) + end. + %%---------------------------- %% Get Room Affiliation %%---------------------------- From 54cf37e917a44792a5bbbec913060b9ef9eb797b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 8 Feb 2023 19:06:44 +0100 Subject: [PATCH 0034/1302] Invalidate vcard_xupdate cache on all nodes when vcard is updated --- src/mod_vcard_xupdate.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index bd04517b4..d169b78f4 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -107,7 +107,7 @@ user_send_packet(Acc) -> -spec vcard_set(iq()) -> iq(). vcard_set(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) -> - ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}), + ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}, ejabberd_cluster:get_nodes()), ejabberd_sm:force_update_presence({LUser, LServer}), IQ; vcard_set(Acc) -> @@ -117,7 +117,7 @@ vcard_set(Acc) -> remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}). + ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}, ejabberd_cluster:get_nodes()). %%==================================================================== %% Storage From d91812730b5744749bd9de4f0d2db5a9469d1d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 8 Feb 2023 19:53:19 +0100 Subject: [PATCH 0035/1302] Stop ejabberd_system_monitor before stopping node Sometimes monitor module is performing checks when node is stopping and this causes crash in monitoring process. --- src/ejabberd_app.erl | 1 + src/ejabberd_system_monitor.erl | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index d5dba4c1c..24e0b74c0 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -103,6 +103,7 @@ prep_stop(State) -> ejabberd_sm:stop(), ejabberd_service:stop(), ejabberd_s2s:stop(), + ejabberd_system_monitor:stop(), gen_mod:stop(), State. diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl index 931a0f18d..eec7556a1 100644 --- a/src/ejabberd_system_monitor.erl +++ b/src/ejabberd_system_monitor.erl @@ -30,7 +30,7 @@ -author('ekhramtsov@process-one.net'). %% API --export([start/0, config_reloaded/0]). +-export([start/0, config_reloaded/0, stop/0]). %% gen_event callbacks -export([init/1, handle_event/2, handle_call/2, @@ -68,6 +68,10 @@ start() -> ejabberd:start_app(os_mon), set_oom_watermark(). +-spec stop() -> term(). +stop() -> + gen_event:delete_handler(alarm_handler, ?MODULE, []). + excluded_apps() -> [os_mon, mnesia, sasl, stdlib, kernel]. @@ -115,7 +119,8 @@ handle_info(Info, State) -> ?WARNING_MSG("unexpected info: ~p~n", [Info]), {ok, State}. -terminate(_Reason, _State) -> +terminate(_Reason, State) -> + misc:cancel_timer(State#state.tref), ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50). code_change(_OldVsn, State, _Extra) -> From 4e7aa41e3f8d12bc7bc62405f5fc5256b07900e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Feb 2023 12:55:31 +0100 Subject: [PATCH 0036/1302] Make subscribers members by default --- src/mod_muc_room.erl | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 11f397085..2b9a30e13 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1022,12 +1022,8 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData not Moderated, IsSubscriber} of {true, _} -> true; {_, true} -> - case get_default_role(get_affiliation(From, StateData), - StateData) of - moderator -> true; - participant -> true; - _ -> false - end; + % We assume all subscribers are at least members + true; _ -> false end, From f10f6d176ffcebdc0baf51c7849800a7747e0138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Feb 2023 12:56:41 +0100 Subject: [PATCH 0037/1302] Store state in db in mod_muc:create_room() --- src/mod_muc.erl | 11 +++++++++++ src/mod_muc_admin.erl | 11 +---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index e7f8ca951..e1da09a7e 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -317,6 +317,15 @@ create_room(Host, Name, Opts) -> store_room(ServerHost, Host, Name, Opts) -> store_room(ServerHost, Host, Name, Opts, undefined). +maybe_store_new_room(ServerHost, Host, Name, Opts) -> + case {proplists:get_bool(persistent, Opts), proplists:get_value(subscribers, Opts, [])} of + {false, []} -> + {atomic, ok}; + {_, Subs} -> + Changes = [{add_subscription, JID, Nick, Nodes} || {JID, Nick, Nodes} <- Subs], + store_room(ServerHost, Host, Name, Opts, Changes) + end. + store_room(ServerHost, Host, Name, Opts, ChangesHints) -> LServer = jid:nameprep(ServerHost), Mod = gen_mod:db_mod(LServer, ?MODULE), @@ -417,6 +426,7 @@ handle_call({create, Room, Host, Opts}, _From, RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), case start_room(RMod, Host, ServerHost, Room, NewOpts) of {ok, _} -> + maybe_store_new_room(ServerHost, Host, Room, NewOpts), ejabberd_hooks:run(create_room, ServerHost, [ServerHost, Room, Host]), {reply, ok, State}; Err -> @@ -432,6 +442,7 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From, RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), case start_room(RMod, Host, ServerHost, Room, NewOpts, From, Nick) of {ok, _} -> + maybe_store_new_room(ServerHost, Host, Room, NewOpts), ejabberd_hooks:run(create_room, ServerHost, [ServerHost, Room, Host]), {reply, ok, State}; Err -> diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 7bdd51f42..c13dd92ca 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -733,7 +733,7 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> lists:keysort(1, DefRoomOpts)), case mod_muc:create_room(Host, Name, RoomOpts) of ok -> - maybe_store_room(ServerHost, Host, Name, RoomOpts); + ok; {error, _} -> throw({error, "Unable to start room"}) end; @@ -744,15 +744,6 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> end end. -maybe_store_room(ServerHost, Host, Name, RoomOpts) -> - case proplists:get_bool(persistent, RoomOpts) of - true -> - {atomic, _} = mod_muc:store_room(ServerHost, Host, Name, RoomOpts), - ok; - false -> - ok - end. - %% Create the room only in the database. %% It is required to restart the MUC service for the room to appear. muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> From dfe4884d16e640e7065c2ada293aaa5b6a2b9e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Feb 2023 13:03:46 +0100 Subject: [PATCH 0038/1302] Allow passing affiliations and subscribers to create_room_with_opts command --- src/mod_muc_admin.erl | 63 ++++++++++++++++++++++++++++++++++++++++++- src/mod_muc_room.erl | 20 +++++++------- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index c13dd92ca..dde9fd6c5 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -162,9 +162,14 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = create_room_with_opts, tags = [muc_room], desc = "Create a MUC room name@service in host with given options", + longdesc = "To set affilitions string value must have format 'Type:JID,Type:JID' " + "for example 'owner:bob@example.com,member:peter@example.com'. Subscribers can be " + "define with string 'JID:Nick:Node1:Node2,JID:Nick:Node3' for example " + "'bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages'.", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], - args_example = ["room1", "muc.example.com", "localhost", [{"members_only","true"}]], + args_example = ["room1", "muc.example.com", "localhost", + [{"members_only","true"}, {"subscribers", "bob@example.com:Bob:messages"}]], args = [{name, binary}, {service, binary}, {host, binary}, {options, {list, @@ -1166,10 +1171,66 @@ format_room_option(OptionString, ValueString) -> ValueString; lang -> ValueString; pubsub -> ValueString; + affiliations -> + [parse_affiliation_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; + subscribers -> + [parse_subscription_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; _ -> misc:binary_to_atom(ValueString) end, {Option, Value}. +parse_affiliation_string(String) -> + {Type, JidS} = case String of + <<"owner:", Jid/binary>> -> {owner, Jid}; + <<"admin:", Jid/binary>> -> {admin, Jid}; + <<"member:", Jid/binary>> -> {member, Jid}; + <<"outcast:", Jid/binary>> -> {outcast, Jid}; + _ -> throw({error, "Invalid 'affiliation'"}) + end, + try jid:decode(JidS) of + #jid{luser = U, lserver = S, lresource = R} -> + {{U, S, R}, {Type, <<>>}} + catch _:{bad_jid, _} -> + throw({error, "Malformed JID in affiliation"}) + end. + +parse_subscription_string(String) -> + case str:tokens(String, <<":">>) of + [_] -> + throw({error, "Invalid 'subscribers' - missing nick"}); + [_, _] -> + throw({error, "Invalid 'subscribers' - missing nodes"}); + [JidS, Nick | Nodes] -> + Nodes2 = parse_nodes(Nodes, []), + try jid:decode(JidS) of + Jid -> + {Jid, Nick, Nodes2} + catch _:{bad_jid, _} -> + throw({error, "Malformed JID in 'subscribers'"}) + end + end. + +parse_nodes([], Acc) -> + Acc; +parse_nodes([<<"presence">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_PRESENCE | Acc]); +parse_nodes([<<"messages">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_MESSAGES | Acc]); +parse_nodes([<<"participants">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_PARTICIPANTS | Acc]); +parse_nodes([<<"affiliations">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_AFFILIATIONS | Acc]); +parse_nodes([<<"subject">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_SUBJECT | Acc]); +parse_nodes([<<"config">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_CONFIG | Acc]); +parse_nodes([<<"system">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_SYSTEM | Acc]); +parse_nodes([<<"subscribers">> | Rest], Acc) -> + parse_nodes(Rest, [?NS_MUCSUB_NODES_SUBSCRIBERS | Acc]); +parse_nodes(_, _) -> + throw({error, "Invalid 'subscribers' - unknown node name used"}). + %% @doc Get the Pid of an existing MUC room, or 'room_not_found'. -spec get_room_pid(binary(), binary()) -> pid() | room_not_found | invalid_service. get_room_pid(Name, Service) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 2b9a30e13..237246ef4 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -294,15 +294,15 @@ init([Host, ServerHost, Access, Room, HistorySize, process_flag(trap_exit, true), Shaper = ejabberd_shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), - State = set_affiliation(Creator, owner, - #state{host = Host, server_host = ServerHost, - access = Access, room = Room, - history = lqueue_new(HistorySize, QueueType), - jid = jid:make(Room, Host), - just_created = true, - room_queue = RoomQueue, - room_shaper = Shaper}), - State1 = set_opts(DefRoomOpts, State), + State = set_opts(DefRoomOpts, + #state{host = Host, server_host = ServerHost, + access = Access, room = Room, + history = lqueue_new(HistorySize, QueueType), + jid = jid:make(Room, Host), + just_created = true, + room_queue = RoomQueue, + room_shaper = Shaper}), + State1 = set_affiliation(Creator, owner, State), store_room(State1), ?INFO_MSG("Created MUC room ~ts@~ts by ~ts", [Room, Host, jid:encode(Creator)]), @@ -4038,7 +4038,7 @@ set_opts([{Opt, Val} | Opts], StateData) -> end, muc_subscribers_new(), Val), StateData#state{muc_subscribers = MUCSubscribers}; affiliations -> - StateData#state{affiliations = maps:from_list(Val)}; + set_affiliations(maps:from_list(Val), StateData); roles -> StateData#state{roles = maps:from_list(Val)}; subject -> From 9503beca6ca0221d20667c38bd52552ea92d7c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Feb 2023 13:42:52 +0100 Subject: [PATCH 0039/1302] Make mod_muc_room:set_opts process persistent flag first As processing some other options depends on this setting flag in room state. --- src/mod_muc_room.erl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 237246ef4..a80559f5b 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3882,14 +3882,23 @@ remove_nonmembers(StateData) -> end, StateData, get_users_and_subscribers(StateData)). -spec set_opts([{atom(), any()}], state()) -> state(). -set_opts([], StateData) -> +set_opts(Opts, StateData) -> + case lists:keytake(persistent, 1, Opts) of + false -> + set_opts2(Opts, StateData); + {value, Tuple, Rest} -> + set_opts2([Tuple | Rest], StateData) + end. + +-spec set_opts2([{atom(), any()}], state()) -> state(). +set_opts2([], StateData) -> set_vcard_xupdate(StateData); -set_opts([{vcard, Val} | Opts], StateData) +set_opts2([{vcard, Val} | Opts], StateData) when is_record(Val, vcard_temp) -> %% default_room_options is setting a default room vcard ValRaw = fxml:element_to_binary(xmpp:encode(Val)), - set_opts([{vcard, ValRaw} | Opts], StateData); -set_opts([{Opt, Val} | Opts], StateData) -> + set_opts2([{vcard, ValRaw} | Opts], StateData); +set_opts2([{Opt, Val} | Opts], StateData) -> NSD = case Opt of title -> StateData#state{config = @@ -4058,7 +4067,7 @@ set_opts([{Opt, Val} | Opts], StateData) -> ?INFO_MSG("Unknown MUC room option, will be discarded: ~p", [Other]), StateData end, - set_opts(Opts, NSD). + set_opts2(Opts, NSD). -spec set_vcard_xupdate(state()) -> state(). set_vcard_xupdate(#state{config = From d504ed8a9b387ae69677e5dec63838f825601e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 06:16:08 +0000 Subject: [PATCH 0040/1302] Container: Bump docker/build-push-action from 3 to 4 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3...v4) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 201c65d8b..dbae22f2d 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -61,7 +61,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Build and push Docker image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: build-args: | VERSION=${{ steps.gitdescribe.outputs.ver }} From dea452bdfd11d18bc0bf4ffe28a9f6286ea5ac5b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Feb 2023 15:33:02 +0100 Subject: [PATCH 0041/1302] Fix Prospector and Pylint warnings in test extauth.py --- test/ejabberd_SUITE_data/extauth.py | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/test/ejabberd_SUITE_data/extauth.py b/test/ejabberd_SUITE_data/extauth.py index 394c2126a..e34208ed7 100755 --- a/test/ejabberd_SUITE_data/extauth.py +++ b/test/ejabberd_SUITE_data/extauth.py @@ -1,51 +1,56 @@ +"""extauth dummy script for ejabberd testing.""" + import sys import struct -def read_from_stdin(bytes): - if hasattr(sys.stdin, 'buffer'): - return sys.stdin.buffer.read(bytes) - else: - return sys.stdin.read(bytes) +def read_from_stdin(read_bytes): + """Read buffer from standard input.""" + if hasattr(sys.stdin, 'buffer'): + return sys.stdin.buffer.read(read_bytes) + return sys.stdin.read(read_bytes) def read(): + """Read input and process the command.""" (pkt_size,) = struct.unpack('>H', read_from_stdin(2)) pkt = sys.stdin.read(pkt_size) cmd = pkt.split(':')[0] if cmd == 'auth': - u, s, p = pkt.split(':', 3)[1:] - if u == "wrong": + user, _, _ = pkt.split(':', 3)[1:] + if user == "wrong": write(False) else: write(True) elif cmd == 'isuser': - u, s = pkt.split(':', 2)[1:] - if u == "wrong": + user, _ = pkt.split(':', 2)[1:] + if user == "wrong": write(False) else: write(True) elif cmd == 'setpass': - u, s, p = pkt.split(':', 3)[1:] + user, _, _ = pkt.split(':', 3)[1:] write(True) elif cmd == 'tryregister': - u, s, p = pkt.split(':', 3)[1:] + user, _, _ = pkt.split(':', 3)[1:] write(True) elif cmd == 'removeuser': - u, s = pkt.split(':', 2)[1:] + user, _ = pkt.split(':', 2)[1:] write(True) elif cmd == 'removeuser3': - u, s, p = pkt.split(':', 3)[1:] + user, _, _ = pkt.split(':', 3)[1:] write(True) else: write(False) read() def write(result): + """write result to standard output.""" if result: sys.stdout.write('\x00\x02\x00\x01') else: sys.stdout.write('\x00\x02\x00\x00') sys.stdout.flush() + if __name__ == "__main__": try: read() From 0def333550a140ddb5a43e1a861c2a79d0d4b48e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Feb 2023 16:12:18 +0100 Subject: [PATCH 0042/1302] Fix Remark-lint warnings --- .github/ISSUE_TEMPLATE/bug_report.md | 3 +-- .github/ISSUE_TEMPLATE/feature_request.md | 9 ++++++--- CHANGELOG.md | 6 +++--- CODE_OF_CONDUCT.md | 2 +- COMPILE.md | 22 +++++++++++----------- CONTRIBUTING.md | 5 ++--- README.md | 5 ++--- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9b0b529e5..d984ed09d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,8 +6,7 @@ assignees: '' --- -Before creating a ticket, please consider if this should fit the discussion forum better: -https://github.com/processone/ejabberd/discussions +Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better. ## Environment diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 2bca321a2..0ac588c37 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,17 +7,20 @@ assignees: '' --- -Before creating a ticket, please consider if this should fit the discussion forum better: -https://github.com/processone/ejabberd/discussions +Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better. **Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +A clear and concise description of what the problem is. Ex. I'm always frustrated when... **Describe the solution you'd like** + A clear and concise description of what you want to happen. **Describe alternatives you've considered** + A clear and concise description of any alternative solutions or features you've considered. **Additional context** + Add any other context or screenshots about the feature request here. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bad89f4b..6e1aabd65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -486,7 +486,7 @@ Translations: changed to logging disabled - Increase default shaper limits (this should help with delays for clients that are using jingle) -- Fix couple compatibility problems which prevented working on +- Fix couple compatibility problems which prevented working on erlang R19 - Fix sending presence unavailable when session terminates for clients that only send directed presences (helps with sometimes @@ -501,7 +501,7 @@ Translations: - Add support of ssl connection when connection to mysql database (configured with `sql_ssl: true` option) - Experimental support for cockroachdb when configured - with postgres connector + with postgres connector - Add cache and optimize queries issued by `mod_shared_roster`, this should greatly improve performance of this module when used with `sql` backend @@ -798,6 +798,6 @@ Translations: # Version 18.12 * MAM data store compression -* Proxy protocol support (http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) +* Proxy protocol support * MUC Self-Ping optimization (XEP-0410) * Bookmarks conversion (XEP-0411) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d3b197910..a9145ba2f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at conduct@process-one.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at the email address: conduct AT process-one.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff --git a/COMPILE.md b/COMPILE.md index 5bcfaa581..34709ea2f 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -15,20 +15,20 @@ Requirements To compile ejabberd you need: - - GNU Make - - GCC - - Libexpat ≥ 1.95 - - Libyaml ≥ 0.1.4 - - Erlang/OTP ≥ 19.3 - - OpenSSL ≥ 1.0.0 +- GNU Make +- GCC +- Libexpat ≥ 1.95 +- Libyaml ≥ 0.1.4 +- Erlang/OTP ≥ 19.3 +- OpenSSL ≥ 1.0.0 Other optional libraries are: - - Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138) - - PAM library, for Pluggable Authentication Modules (PAM) - - ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA - challenges - - Elixir ≥ 1.10.3, to support Elixir, and alternative to rebar/rebar3 +- Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138) +- PAM library, for Pluggable Authentication Modules (PAM) +- ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA + challenges +- Elixir ≥ 1.10.3, to support Elixir, and alternative to rebar/rebar3 If your system splits packages in libraries and development headers, install the development packages too. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 136ef3077..a0048c4b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,9 +34,9 @@ To save your and our time, we will systematically close all issues that are requ support and redirect people to the section you are reading right now. Other channels for support are: -- [ejabberd Mailing List][list] -- [ejabberd XMPP room][muc]: ejabberd@conference.process-one.net +- ejabberd XMPP room: [ejabberd@conference.process-one.net][muc] - [ejabberd XMPP room logs][logs] +- [ejabberd Mailing List][list] ### Found an Issue or Bug? @@ -147,4 +147,3 @@ gives us the option to relicense the code with a more permissive license in the [doc-repo]: https://github.com/processone/docs.ejabberd.im [developer-setup]: https://docs.ejabberd.im/developer/ [cla]: https://www.process-one.net/resources/ejabberd-cla.pdf -[license]: https://github.com/processone/ejabberd/blob/master/COPYING diff --git a/README.md b/README.md index 026959eb2..8faa5d78b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ or in your local machine as explained in [Localization][localization]. Documentation for developers is available in [ejabberd docs: Developers][docs-dev]. Security reports or concerns should preferably be reported privately, -please send an email to the address: contact [at] process-one [dot] net +please send an email to the address: contact at process-one dot net or some other method from [ProcessOne Contact][p1contact]. For commercial offering and support, including [ejabberd Business Edition][p1home] @@ -85,7 +85,7 @@ Community There are several places to get in touch with other ejabberd developers and administrators: -- [ejabberd XMPP chatroom][muc]: ejabberd@conference.process-one.net +- ejabberd XMPP chatroom: [ejabberd@conference.process-one.net][muc] - [Mailing list][list] - [GitHub Discussions][discussions] - [Stack Overflow][stackoverflow] @@ -105,7 +105,6 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [erlang]: https://www.erlang.org/ [features]: https://docs.ejabberd.im/admin/introduction/ [fluux]: https://fluux.io/ -[github]: https://github.com/processone/ejabberd [homebrew]: https://docs.ejabberd.im/admin/installation/#homebrew [hubecs]: https://hub.docker.com/r/ejabberd/ecs/ [im]: https://ejabberd.im/ From 66d58504d0f398a5fb360f498ac0811cf2b26081 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Feb 2023 17:31:26 +0100 Subject: [PATCH 0043/1302] Fix TSQLlint warning about typo in mssql.new.sql --- sql/mssql.new.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql index f72b40bfa..c78e28ea2 100644 --- a/sql/mssql.new.sql +++ b/sql/mssql.new.sql @@ -554,7 +554,7 @@ CREATE TABLE [dbo].[push_session] ( [xml] [varchar] (255) NOT NULL ); -CREATE UNIQUE NONCLUSTERED INDEX [push_session_susn] ON [push_session] (server_host, username, service, node)) +CREATE UNIQUE NONCLUSTERED INDEX [push_session_susn] ON [push_session] (server_host, username, service, node) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); CREATE INDEX [push_session_sh_username_timestamp] ON [push_session] (server_host, username, timestamp) From c9143dd3d81bbbec0cc091cb82abb5135698c58b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Feb 2023 20:01:45 +0100 Subject: [PATCH 0044/1302] Fix TSQLlint warnings in MSSQL test script --- test/docker/db/mssql/initdb/initdb_mssql.sql | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/docker/db/mssql/initdb/initdb_mssql.sql b/test/docker/db/mssql/initdb/initdb_mssql.sql index a9ec5a5a8..b8208ad70 100644 --- a/test/docker/db/mssql/initdb/initdb_mssql.sql +++ b/test/docker/db/mssql/initdb/initdb_mssql.sql @@ -1,8 +1,16 @@ -USE [master] +SET ANSI_NULLS ON; +SET NOCOUNT ON; +SET QUOTED_IDENTIFIER ON; +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; + +USE [master]; GO +-- prevent creation when already exists IF DB_ID('ejabberd_test') IS NOT NULL - set noexec on -- prevent creation when already exists +BEGIN +SET NOEXEC ON; +END CREATE DATABASE ejabberd_test; GO From 2137a4f663705771f6f9e8030e85daf46998b29f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Feb 2023 21:47:28 +0100 Subject: [PATCH 0045/1302] Fix Shellcheck warnings in shell scripts --- rel/setup-dev.sh | 11 +++++------ rel/setup-relive.sh | 10 +++++----- tools/generate-doap.sh | 28 +++++++++++++++------------- tools/prepare-tr.sh | 40 +++++++++++++++++++++------------------- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/rel/setup-dev.sh b/rel/setup-dev.sh index 79171ffe0..d9d94a2d0 100755 --- a/rel/setup-dev.sh +++ b/rel/setup-dev.sh @@ -1,25 +1,24 @@ -echo -n "===> Preparing dev configuration files: " +printf "===> Preparing dev configuration files: " -PWD_DIR=`pwd` +PWD_DIR=$(pwd) REL_DIR=$PWD_DIR/_build/dev/rel/ejabberd/ CON_DIR=$REL_DIR/conf/ [ -z "$REL_DIR_TEMP" ] && REL_DIR_TEMP=$REL_DIR CON_DIR_TEMP=$REL_DIR_TEMP/conf/ -BIN_DIR_TEMP=$REL_DIR_TEMP/bin/ -cd $CON_DIR_TEMP +cd $CON_DIR_TEMP || exit sed -i "s|# certfiles:|certfiles:\n - $CON_DIR/cert.pem|g" ejabberd.yml.example sed -i "s|certfiles:|ca_file: $CON_DIR/ca.pem\ncertfiles:|g" ejabberd.yml.example sed -i 's|^acl:$|acl:\n admin: [user: admin]|g' ejabberd.yml.example [ ! -f "$CON_DIR/ejabberd.yml" ] \ - && echo -n "ejabberd.yml " \ + && printf "ejabberd.yml " \ && mv ejabberd.yml.example ejabberd.yml sed -i "s|#' POLL|EJABBERD_BYPASS_WARNINGS=true\n\n#' POLL|g" ejabberdctl.cfg.example [ ! -f "$CON_DIR/ejabberdctl.cfg" ] \ - && echo -n "ejabberdctl.cfg " \ + && printf "ejabberdctl.cfg " \ && mv ejabberdctl.cfg.example ejabberdctl.cfg echo "" diff --git a/rel/setup-relive.sh b/rel/setup-relive.sh index 24c252287..a4c88f6c5 100755 --- a/rel/setup-relive.sh +++ b/rel/setup-relive.sh @@ -1,4 +1,4 @@ -PWD_DIR=`pwd` +PWD_DIR=$(pwd) REL_DIR=$PWD_DIR/_build/relive/ CON_DIR=$REL_DIR/conf/ @@ -15,17 +15,17 @@ cp ejabberd.yml.example $CON_DIR/ejabberd.yml.example cp test/ejabberd_SUITE_data/ca.pem $CON_DIR cp test/ejabberd_SUITE_data/cert.pem $CON_DIR -cd $CON_DIR_TEMP +cd $CON_DIR_TEMP || exit sed -i "s|# certfiles:|certfiles:\n - $CON_DIR/cert.pem|g" ejabberd.yml.example sed -i "s|certfiles:|ca_file: $CON_DIR/ca.pem\ncertfiles:|g" ejabberd.yml.example sed -i 's|^acl:$|acl:\n admin: [user: admin]|g' ejabberd.yml.example [ ! -f "$CON_DIR/ejabberd.yml" ] \ - && echo -n "ejabberd.yml " \ + && printf "ejabberd.yml " \ && mv ejabberd.yml.example ejabberd.yml sed -i "s|#' POLL|EJABBERD_BYPASS_WARNINGS=true\n\n#' POLL|g" ejabberdctl.cfg.example [ ! -f "$CON_DIR/ejabberdctl.cfg" ] \ - && echo -n "ejabberdctl.cfg " \ + && printf "ejabberdctl.cfg " \ && mv ejabberdctl.cfg.example ejabberdctl.cfg \ - || echo -n + || printf diff --git a/tools/generate-doap.sh b/tools/generate-doap.sh index d6706396a..fb146599f 100755 --- a/tools/generate-doap.sh +++ b/tools/generate-doap.sh @@ -76,7 +76,7 @@ write_rfcs() int=$(echo $1 | sed 's/^0*//') imp=$(grep "\-protocol({rfc, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1 \2/") - [ "$imp" == "" ] && imp="NA 0.0" + [ "$imp" = "" ] && imp="NA 0.0" echo " " >>$out } @@ -88,7 +88,7 @@ write_xeps() comments2="" int=$(echo $1 | sed 's/^0*//') imp=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1 \2/") - [ "$imp" == "" ] && imp="NA 0.0" + [ "$imp" = "" ] && imp="NA 0.0" sourcefiles=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1/" | tr '\012' ',' | sed 's|,$||' | sed 's|,|, |g' | sed 's|^ejabberd$||') @@ -103,15 +103,17 @@ write_xeps() [ -n "$comments" ] && comments2=", $comments" note="$sourcefiles$comments2" - echo " " >>$out - echo " " >>$out - echo " " >>$out - echo " $versions" >>$out - echo " $since" >>$out - echo " $status" >>$out - echo " $note" >>$out - echo " " >>$out - echo " " >>$out + { + echo " " + echo " " + echo " " + echo " $versions" + echo " $since" + echo " $status" + echo " $note" + echo " " + echo " " + } >>$out } [ $# -eq 1 ] && BASE="$1" || BASE="$PWD" @@ -121,13 +123,13 @@ final=ejabberd.doap write_doap_head $final -for x_num in $(grep "\-protocol({rfc" $BASE/src/* | sed "s/,//" | awk '{printf("%04d\n", $2)}' | sort -u) +grep "\-protocol({rfc" $BASE/src/* | sed "s/,//" | awk '{printf("%04d\n", $2)}' | sort -u | while IFS= read -r x_num do write_rfcs $x_num $temp done echo "" >>$temp -for x_num in $(grep "\-protocol({xep" $BASE/src/* | sed "s/,//" | awk '{printf("%04d\n", $2)}' | sort -u) +grep "\-protocol({xep" $BASE/src/* | sed "s/,//" | awk '{printf("%04d\n", $2)}' | sort -u | while IFS= read -r x_num do write_xeps $x_num $temp done diff --git a/tools/prepare-tr.sh b/tools/prepare-tr.sh index 1eeaaa64c..3c5596189 100755 --- a/tools/prepare-tr.sh +++ b/tools/prepare-tr.sh @@ -32,7 +32,7 @@ extract_lang_po2msg () MSGSTR_PATH=$PO_PATH.msgstr MSGS_PATH=$LANG_CODE.msg - cd $PO_DIR + cd $PO_DIR || exit # Check PO has correct ~ # Let's convert to C format so we can use msgfmt @@ -48,11 +48,13 @@ extract_lang_po2msg () msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH - echo "%% Generated automatically" >$MSGS_PATH - echo "%% DO NOT EDIT: run \`make translations\` instead" >>$MSGS_PATH - echo "%% To improve translations please read:" >>$MSGS_PATH - echo "%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/" >>$MSGS_PATH - echo "" >>$MSGS_PATH + { + echo "%% Generated automatically" + echo "%% DO NOT EDIT: run \`make translations\` instead" + echo "%% To improve translations please read:" + echo "%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/" + echo "" + } >>$MSGS_PATH paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH rm $MS_PATH @@ -68,29 +70,29 @@ extract_lang_updateall () echo "Generating POT..." extract_lang_src2pot - cd $MSGS_DIR + cd $MSGS_DIR || exit echo "" - echo -e "File Missing (fuzzy) Language Last translator" - echo -e "---- ------- ------- -------- ---------------" - for i in $( ls *.msg ) ; do + echo "File Missing (fuzzy) Language Last translator" + echo "---- ------- ------- -------- ---------------" + for i in *.msg ; do LANG_CODE=${i%.msg} - echo -n $LANG_CODE | awk '{printf "%-6s", $1 }' + printf "%s" "$LANG_CODE" | awk '{printf "%-6s", $1 }' PO=$PO_DIR/$LANG_CODE.po extract_lang_popot2po $LANG_CODE extract_lang_po2msg $LANG_CODE - MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4+$7 }'` - echo -n " $MISSING" + MISSING=$(msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4+$7 }') + printf " %s" "$MISSING" - FUZZY=`msgfmt --statistics $PO 2>&1 | awk '{printf "%7s", $4 }'` - echo -n " $FUZZY" + FUZZY=$(msgfmt --statistics $PO 2>&1 | awk '{printf "%7s", $4 }') + printf " %s" "$FUZZY" - LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'` - echo -n " $LANGUAGE" + LANGUAGE=$(grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\n\"//g' | awk '{printf "%-12s", $1}') + printf " %s" "$LANGUAGE" - LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'` + LASTAUTH=$(grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\n\"//g') echo " $LASTAUTH" done echo "" @@ -101,7 +103,7 @@ extract_lang_updateall () cd .. } -EJA_DIR=`pwd` +EJA_DIR=$(pwd) PROJECT=ejabberd DEPS_DIR=$1 MSGS_DIR=$EJA_DIR/priv/msgs From 9bb86132c602e81332c719244a47679172507261 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Feb 2023 11:29:51 +0100 Subject: [PATCH 0046/1302] CI: Use default verbosity to let log files contain XMPP stanzas This partially reverts 2a7780507 --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61bd07ab2..ad4b8fd9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,8 +111,7 @@ jobs: eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config - echo '{ct_extra_params, "-verbosity 20"}.' >>rebar.config - echo "{ct_opts, [{verbosity, 20}, {keep_logs, 20}]}." >>rebar.config + echo "{ct_opts, [{keep_logs, 20}]}." >>rebar.config - name: Remove syntax_tools from release run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script From 9f0a5c5ef02a5d2830f621283034748954d014dc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Feb 2023 12:52:55 +0100 Subject: [PATCH 0047/1302] CI: Produce less verbose logs for tests This gets a line first introduced in 5b5548b8c that was lost later --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad4b8fd9e..286c647ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,6 +176,7 @@ jobs: id: ct run: | (cd priv && ln -sf ../sql) + sed -i -e 's/ct:pal/ct:log/' test/suite.erl COMMIT=`echo $GITHUB_SHA | cut -c 1-7` DATE=`date +%s` REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"` From 1c82daacb2bb3787bf7bbcd377d128abde2cf66b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Feb 2023 16:45:38 +0100 Subject: [PATCH 0048/1302] CI: Upload Common Test logs as artifact in case of failure --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 286c647ab..98246e369 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -311,3 +311,17 @@ jobs: 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' find logs/ -name error.log -exec cat '{}' ';' find logs/ -name exunit.log -exec cat '{}' ';' + + - name: Upload CT logs + if: failure() + uses: actions/upload-artifact@v3 + with: + name: ejabberd-ct-logs-${{matrix.otp}} + # + # Appending the wildcard character ("*") is a trick to make + # "ejabberd-packages" the root directory of the uploaded ZIP file: + # + # https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions + # + path: _build/test/logs + retention-days: 14 From 37676757e3252b36102f5d1798aca3700752280d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 Feb 2023 09:39:28 +0100 Subject: [PATCH 0049/1302] CI: Add OTP 26.0-rc1 to the test matrix But disable dialyzer in 26, as it complains verbosely without reason --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98246e369..0ab0152ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '21.3', '24.3', '25'] + otp: ['20.0', '21.3', '24.3', '25', '26.0-rc1'] runs-on: ubuntu-20.04 services: redis: @@ -149,6 +149,7 @@ jobs: - run: make options - run: make xref - run: make dialyzer + if: matrix.otp != '26.0-rc1' - name: Check Production Release run: | From 5cd6c524ea158264716367421df55f57168cfd74 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Feb 2023 15:20:22 +0100 Subject: [PATCH 0050/1302] Allow XML to be visible in web browser in Common Test page --- test/suite.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/suite.erl b/test/suite.erl index 95b991d71..2afc6165c 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -547,10 +547,11 @@ decode_stream_element(NS, El) -> decode(El, NS, []). format_element(El) -> - case erlang:function_exported(ct, log, 5) of + Bin = case erlang:function_exported(ct, log, 5) of true -> ejabberd_web_admin:pretty_print_xml(El); false -> io_lib:format("~p~n", [El]) - end. + end, + binary:replace(Bin, <<"<">>, <<"<">>, [global]). decode(El, NS, Opts) -> try From 04b431f1919e171e03c416bf29ce65c74c7d4794 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:09:18 +0000 Subject: [PATCH 0051/1302] Bump ex_doc from 0.29.1 to 0.29.2 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.29.1 to 0.29.2. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.29.1...v0.29.2) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index fcfbd5357..80194a735 100644 --- a/mix.lock +++ b/mix.lock @@ -1,12 +1,12 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"}, "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, + "ex_doc": {:hex, :ex_doc, "0.29.2", "dfa97532ba66910b2a3016a4bbd796f41a86fc71dd5227e96f4c8581fdf0fdf0", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "6b5d7139eda18a753e3250e27e4a929f8d2c880dd0d460cb9986305dea3e03af"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, From f953dc3f5ee1d220add70f4e55a53463ec25e3f3 Mon Sep 17 00:00:00 2001 From: Blake Miller <96107547+based-a-tron@users.noreply.github.com> Date: Sun, 19 Mar 2023 01:40:19 -0400 Subject: [PATCH 0052/1302] Persist `none` role for outcasts `none` roles *should* be persisted for banned users. I totally forgot about this, my bad. I'm shocked nobody else noticed it. --- src/mod_muc_room.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index a80559f5b..24b81db50 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1751,10 +1751,12 @@ set_role(JID, Role, StateData) -> end, StateData#state.users, LJIDs), StateData#state.nicks} end, + Affiliation = get_affiliation(JID, StateData), Roles = case Role of %% Don't persist 'none' role: if someone is kicked, they will - %% maintain the same role they had *before* they were kicked - none -> + %% maintain the same role they had *before* they were kicked, + %% unless they were banned + none when Affiliation /= outcast -> StateData#state.roles; NewRole -> maps:put(jid:remove_resource(LJID), @@ -3070,7 +3072,7 @@ process_item_change(Item, SD, UJID) -> process_iq_mucsub(JID, #iq{type = set, sub_els = [#muc_unsubscribe{}]}, SD), - set_affiliation(JID, outcast, set_role(JID, none, SD2), Reason); + set_role(JID, none, set_affiliation(JID, outcast, SD2, Reason)); {JID, affiliation, A, Reason} when (A == admin) or (A == owner) -> SD1 = set_affiliation(JID, A, SD, Reason), SD2 = set_role(JID, moderator, SD1), From 3c97775573d672f185a3d5839ec82e5fb3debbc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 21 Mar 2023 15:30:36 +0100 Subject: [PATCH 0053/1302] Pubsub xdata fields max_item/item_expira/children_max use 'max' not 'infinity' Codec in xmpp crashes when we use infinity (see issue #4011), so lets convert those values before passing them to xmpp:encode(0 --- src/mod_pubsub.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index f9a909d44..b8f4c4905 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3456,6 +3456,10 @@ get_configure_xfields(_Type, Options, Lang, Groups) -> {true, {roster_groups_allowed, Value, Groups}}; ({sql, _}) -> false; ({rsm, _}) -> false; + ({Item, infinity}) when Item == max_items; + Item == item_expire; + Item == children_max -> + {true, {Item, max}}; (_) -> true end, Options), Lang). From af29adb558914f66e12b652eff853f403403f8ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 07:04:12 +0000 Subject: [PATCH 0054/1302] Bump ex_doc from 0.29.2 to 0.29.3 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.29.2 to 0.29.3. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/v0.29.3/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.29.2...v0.29.3) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 80194a735..e55592656 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.29.2", "dfa97532ba66910b2a3016a4bbd796f41a86fc71dd5227e96f4c8581fdf0fdf0", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "6b5d7139eda18a753e3250e27e4a929f8d2c880dd0d460cb9986305dea3e03af"}, + "ex_doc": {:hex, :ex_doc, "0.29.3", "f07444bcafb302db86e4f02d8bbcd82f2e881a0dcf4f3e4740e4b8128b9353f7", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3dc6787d7b08801ec3b51e9bd26be5e8826fbf1a17e92d1ebc252e1a1c75bfe1"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, From 6c620f6f430d4c7e1e6cb0287755856f12bbaac2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Mar 2023 13:18:19 +0100 Subject: [PATCH 0055/1302] Remove wrong get_room_history command fields specification --- src/mod_muc_admin.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index dde9fd6c5..ed7bc3b5d 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -416,8 +416,6 @@ get_commands_spec() -> module = ?MODULE, function = get_room_history, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], - result_desc = "Affiliation of the user", - result_example = member, args = [{name, binary}, {service, binary}], result = {history, {list, {entry, {tuple, From c4a2f8d64fe94fdc0f6ae2d854b3fe46b222e79f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 1 Feb 2023 11:52:47 +0100 Subject: [PATCH 0056/1302] captcha_url option now accepts 'auto' value, and it's the default --- src/ejabberd_captcha.erl | 55 +++++++++++++++++++++++++++++++++++- src/ejabberd_option.erl | 2 +- src/ejabberd_options.erl | 6 ++-- src/ejabberd_options_doc.erl | 14 ++++++--- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 69ad752b9..12ab5abb8 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -441,18 +441,37 @@ do_create_image(Key, FileName) when is_binary(FileName) -> get_prog_name() -> case ejabberd_option:captcha_cmd() of undefined -> - ?DEBUG("The option captcha_cmd is not configured, " + ?WARNING_MSG("The option captcha_cmd is not configured, " "but some module wants to use the CAPTCHA " "feature.", []), false; FileName -> + maybe_warning_norequesthandler(), FileName end. +maybe_warning_norequesthandler() -> + Host = hd(ejabberd_option:hosts()), + URL = get_auto_url(any, ?MODULE, Host), + case URL of + undefined -> + ?WARNING_MSG("The option captcha_cmd is configured, " + "but there is NO request_handler in listen option " + "configured with ejabberd_captcha. Please check " + "https://docs.ejabberd.im/admin/configuration/basic/#captcha", + []); + _ -> + ok + end. + -spec get_url(binary()) -> binary(). get_url(Str) -> case ejabberd_option:captcha_url() of + auto -> + Host = ejabberd_config:get_myname(), + URL = get_auto_url(any, ?MODULE, Host), + <>; undefined -> URL = parse_captcha_host(), <>; @@ -477,6 +496,40 @@ parse_captcha_host() -> <<"http://", (ejabberd_config:get_myname())/binary>> end. +get_auto_url(Tls, Module, Host) -> + case find_handler_port_path(Tls, Module) of + [] -> undefined; + TPPs -> + {ThisTls, Port, Path} = case lists:keyfind(true, 1, TPPs) of + false -> + lists:keyfind(false, 1, TPPs); + TPP -> + TPP + end, + Protocol = case ThisTls of + false -> <<"http">>; + true -> <<"https">> + end, + <>))/binary>> + end. + +find_handler_port_path(Tls, Module) -> + lists:filtermap( + fun({{Port, _, _}, + ejabberd_http, + #{tls := ThisTls, request_handlers := Handlers}}) + when (Tls == any) or (Tls == ThisTls) -> + case lists:keyfind(Module, 2, Handlers) of + false -> false; + {Path, Module} -> {true, {ThisTls, Port, Path}} + end; + (_) -> false + end, ets:tab2list(ejabberd_listener)). + get_transfer_protocol(PortString) -> PortNumber = binary_to_integer(PortString), PortListeners = get_port_listeners(PortNumber), diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 4ac2e88a4..b71f088c6 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -329,7 +329,7 @@ captcha_host() -> captcha_limit() -> ejabberd_config:get_option({captcha_limit, global}). --spec captcha_url() -> 'undefined' | binary(). +-spec captcha_url() -> 'auto' | 'undefined' | binary(). captcha_url() -> ejabberd_config:get_option({captcha_url, global}). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index ed3720291..c2af225cf 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -123,7 +123,9 @@ opt_type(captcha_host) -> opt_type(captcha_limit) -> econf:pos_int(infinity); opt_type(captcha_url) -> - econf:url(); + econf:either( + econf:url(), + econf:enum([auto, undefined])); opt_type(certfiles) -> econf:list(econf:binary()); opt_type(cluster_backend) -> @@ -546,7 +548,7 @@ options() -> {captcha_cmd, undefined}, {captcha_host, <<"">>}, {captcha_limit, infinity}, - {captcha_url, undefined}, + {captcha_url, auto}, {certfiles, undefined}, {cluster_backend, mnesia}, {cluster_nodes, []}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index ba4b585b3..cbf98c88d 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -458,8 +458,8 @@ doc() -> note => "improved in 23.01", desc => ?T("Full path to a script that generates http://../basic/#captcha[CAPTCHA] images. " - "@VERSION@ is replaced with ejabberd version number in XX.YY format. " - "@SEMVER@ is replaced with ejabberd version number in semver format " + "'@VERSION@' is replaced with ejabberd version number in 'XX.YY' format. " + "'@SEMVER@' is replaced with ejabberd version number in semver format " "when compiled with Elixir's mix, or XX.YY format otherwise. " "Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support. " "There is no default value: when this option is not " @@ -477,11 +477,17 @@ doc() -> #{value => "String", desc => ?T("Deprecated. Use _`captcha_url`_ instead.")}}, {captcha_url, - #{value => ?T("URL"), + #{value => ?T("URL | auto | undefined"), + note => "improved in 23.xx", desc => ?T("An URL where http://../basic/#captcha[CAPTCHA] requests should be sent. NOTE: you need " "to configure 'request_handlers' for 'ejabberd_http' listener " - "as well. There is no default value.")}}, + "as well. " + "If set to 'auto', it builds the URL using a 'request_handler' " + "already enabled, with encryption if available. " + "If set to 'undefined', it builds the URL using " + "the deprecated _`captcha_host`_ + /captcha. " + "The default value is 'auto'.")}}, {certfiles, #{value => "[Path, ...]", desc => From e2496562f9e46cfa5a614e1b3660a36618e7832c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 Jun 2022 18:37:40 +0200 Subject: [PATCH 0057/1302] Preliminary support to store extra elements in subscription request (#840) --- src/mod_roster.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 227594e6a..d537a0933 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -567,24 +567,25 @@ transaction(LUser, LServer, LJIDs, F) -> -spec in_subscription(boolean(), presence()) -> boolean(). in_subscription(_, #presence{from = JID, to = To, + sub_els = SubEls, type = Type, status = Status}) -> #jid{user = User, server = Server} = To, Reason = if Type == subscribe -> xmpp:get_text(Status); true -> <<"">> end, process_subscription(in, User, Server, JID, Type, - Reason). + Reason, SubEls). -spec out_subscription(presence()) -> boolean(). out_subscription(#presence{from = From, to = JID, type = Type}) -> #jid{user = User, server = Server} = From, - process_subscription(out, User, Server, JID, Type, <<"">>). + process_subscription(out, User, Server, JID, Type, <<"">>, []). -spec process_subscription(in | out, binary(), binary(), jid(), subscribe | subscribed | unsubscribe | unsubscribed, - binary()) -> boolean(). + binary(), [fxml:xmlel()]) -> boolean(). process_subscription(Direction, User, Server, JID1, - Type, Reason) -> + Type, Reason, SubEls) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LJID = jid:tolower(jid:remove_resource(JID1)), @@ -618,6 +619,7 @@ process_subscription(Direction, User, Server, JID1, {Subscription, Pending} -> NewItem = Item#roster{subscription = Subscription, ask = Pending, + xs = SubEls, askmessage = AskMessage}, roster_subscribe_t(LUser, LServer, LJID, NewItem), case mod_roster_opt:store_current_id(LServer) of @@ -983,6 +985,7 @@ resend_pending_subscriptions(#{jid := JID} = State) -> Sub = #presence{from = jid:make(R#roster.jid), to = BareJID, type = subscribe, + sub_els = R#roster.xs, status = xmpp:mk_text(Status)}, ejabberd_c2s:send(AccState, Sub); (_, AccState) -> From 1d62dc462183efb0f423e3eddfdc87f5283ea693 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 7 Jun 2022 12:07:23 +0200 Subject: [PATCH 0058/1302] Set roster name from XEP-0172, or the stored one (#1611) --- src/mod_roster.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mod_roster.erl b/src/mod_roster.erl index d537a0933..7ea3dac81 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -619,6 +619,7 @@ process_subscription(Direction, User, Server, JID1, {Subscription, Pending} -> NewItem = Item#roster{subscription = Subscription, ask = Pending, + name = get_nick_subels(SubEls, Item#roster.name), xs = SubEls, askmessage = AskMessage}, roster_subscribe_t(LUser, LServer, LJID, NewItem), @@ -657,6 +658,12 @@ process_subscription(Direction, User, Server, JID1, false end. +get_nick_subels(SubEls, Default) -> + case xmpp:get_subtag(#presence{sub_els = SubEls}, #nick{}) of + {nick, N} -> N; + _ -> Default + end. + %% in_state_change(Subscription, Pending, Type) -> NewState %% NewState = none | {NewSubscription, NewPending} -ifdef(ROSTER_GATEWAY_WORKAROUND). From cc5c3f7b2c760f2b861258f901085be95677fa3e Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 12:09:39 +0100 Subject: [PATCH 0059/1302] ejabberdctl: Don't use .../releases/COOKIE, it's no longer included And slightly clean the .erlang.cookie line This partially reverts 9c23a7dc3fc84281b1fda058db26ae1a309f7b17 --- .github/container/ejabberdctl.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index de3826a46..1f4d902ec 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -17,8 +17,8 @@ ERTS_VSN="{{erts_vsn}}" ERL="{{erl}}" IEX="{{bindir}}/iex" EPMD="{{epmd}}" -[ -z "$ERLANG_COOKIE" ] && ERL_OPTIONS="-setcookie $(cat "${SCRIPT_DIR%/*}/releases/COOKIE")" -[ -n "$ERLANG_COOKIE" ] && [ ! -f "$HOME"/.erlang.cookie ] && echo "$ERLANG_COOKIE" > "$HOME"/.erlang.cookie && chmod 400 "$HOME"/.erlang.cookie +COOKIE_FILE="$HOME"/.erlang.cookie +[ -n "$ERLANG_COOKIE" ] && [ ! -f "$COOKIE_FILE" ] && echo "$ERLANG_COOKIE" > "$COOKIE_FILE" && chmod 400 "$COOKIE_FILE" # check the proper system user is used case $(id -un) in From 7e6d25314d5a92e62ff0e82e671246149e983e86 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 13:27:27 +0100 Subject: [PATCH 0060/1302] make-packages: Fix for installers workflow, which didn't find lynx... --- tools/make-packages | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-packages b/tools/make-packages index 3b3388ae8..8e3585f48 100755 --- a/tools/make-packages +++ b/tools/make-packages @@ -203,7 +203,7 @@ make_package() for arch in $architectures do - tar_name="$rel_name-$rel_vsn-linux-$arch.tar.gz" + tar_name="$rel_name-$rel_vsn-linux-gnu-$arch.tar.gz" arch_dir="$tmp_dir/$arch" opt_dir="$arch_dir/opt" etc_dir="$arch_dir/etc" From 976c6c5e4170ef6f9c161975dbd786e155d72c65 Mon Sep 17 00:00:00 2001 From: Saarko Date: Mon, 13 Mar 2023 17:45:51 +0100 Subject: [PATCH 0061/1302] make-binaries: bump versions, e.g. erlang/otp to 25.3 --- tools/make-binaries | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 751c03c1b..f808a0cdc 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,19 +67,19 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.25.0' termcap_vsn='1.3.1' -expat_vsn='2.4.9' +expat_vsn='2.5.0' zlib_vsn='1.2.13' yaml_vsn='0.2.5' -ssl_vsn='1.1.1q' -otp_vsn='24.3.4.5' -elixir_vsn='1.14.0' +ssl_vsn='1.1.1t' +otp_vsn='25.3' +elixir_vsn='1.14.3' pam_vsn='1.5.2' -png_vsn='1.6.38' +png_vsn='1.6.39' jpeg_vsn='9e' -webp_vsn='1.2.4' +webp_vsn='1.3.0' gd_vsn='2.3.3' odbc_vsn='2.3.11' -sqlite_vsn='3390300' +sqlite_vsn='3410100' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From 440ede313bf39f4eaae590c8b4cb528832e69a74 Mon Sep 17 00:00:00 2001 From: Saarko Date: Mon, 13 Mar 2023 17:57:55 +0100 Subject: [PATCH 0062/1302] make-binaries: fix building with erlang/otp v25.x Signed-off-by: sando38 --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index f808a0cdc..c124db01d 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -258,7 +258,7 @@ create_common_config() CT_ARCH_64=y CT_KERNEL_LINUX=y CT_LINUX_V_3_16=y - CT_GLIBC_V_2_17=y + CT_GLIBC_V_2_19=y CT_GLIBC_KERNEL_VERSION_NONE=y CT_LOG_PROGRESS_BAR=n EOF @@ -715,7 +715,7 @@ build_rel() export CFLAGS="-g0 -O2 -pipe -fomit-frame-pointer -static-libgcc $CPPFLAGS" export CXXFLAGS="$CFLAGS -static-libstdc++" export LDFLAGS="-L$prefix/lib -static-libgcc -static-libstdc++" - export ERL_COMPILER_OPTIONS='[deterministic, no_debug_info]' + export ERL_COMPILER_OPTIONS='[no_debug_info]' # Building 25.x fails with 'deterministic'. if [ "$mode" = 'cross' ] then configure="./configure --host=$target --build=$platform" From de477f7b6c7cb5e477f4633456a6508f0fc6a7cf Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 12:12:39 +0100 Subject: [PATCH 0063/1302] Container: Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 254bbe917..d143bacc2 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 AS build +FROM alpine:3.17 AS build ARG VERSION=master RUN apk upgrade --update musl \ @@ -63,7 +63,7 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \ \ncertfiles: \ \n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml" -FROM alpine:3.16 +FROM alpine:3.17 ENV HOME=/opt/ejabberd ARG VERSION=master From b0f0dd3227d95105011dbeb9165bf86fdcb2c61b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jan 2023 15:05:45 +0100 Subject: [PATCH 0064/1302] Container: Expose only HOME volume, it contains all the required subdirs --- .github/container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index d143bacc2..221cfbb7c 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -112,7 +112,7 @@ HEALTHCHECK \ WORKDIR $HOME USER ejabberd -VOLUME ["$HOME/conf", "$HOME/database", "$HOME/logs", "$HOME/upload"] +VOLUME ["$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 ENTRYPOINT ["/usr/local/bin/ejabberdctl"] From 2c1ee698ccf6a10a5c00c34d9a61670111105dd7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jan 2023 15:53:27 +0100 Subject: [PATCH 0065/1302] Container: Copy captcha scripts to /opt/ejabberd-*/lib like the installers Instead of a path like /opt/ejabberd-master/lib/ejabberd-23.1.0/priv/bin they are now in /opt/ejabberd-master/lib --- .github/container/Dockerfile | 4 +--- CONTAINER.md | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 221cfbb7c..9c5b2bfa0 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -42,9 +42,7 @@ RUN cp -r _build/prod/rel/ejabberd/ /opt/ejabberd-$VERSION \ && mkdir -p /opt/ejabberd \ && mv /opt/ejabberd-$VERSION/conf /opt/ejabberd/conf -RUN BINPATH=$(dirname $(find /opt -name msgs))/bin/ \ - && mkdir -p $BINPATH \ - && cp tools/captcha*.sh $BINPATH +RUN cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib" RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/ diff --git a/CONTAINER.md b/CONTAINER.md index d5643a7ce..697084442 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -156,7 +156,7 @@ docker exec -it ejabberd vi conf/ejabberd.yml and add the required options: ``` -captcha_cmd: /opt/ejabberd-22.04/lib/ejabberd-22.04/priv/bin/captcha.sh +captcha_cmd: /opt/ejabberd-22.04/lib/captcha.sh captcha_url: https://localhost:5443/captcha ``` From 874b9616801848e30b7ca4317eade493c3036655 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jan 2023 16:02:50 +0100 Subject: [PATCH 0066/1302] Container: Remove unused Mix stuff: ejabberd script and static COOKIE Instead of including this file in the container with static content: /opt/ejabberd-master/releases/COOKIE the cookie file will be generated by erlang in /opt/ejabberd/.erlang.cookie or by ejabberdctl if ERLANG_COOKIE environment variable was provided. --- .github/container/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 9c5b2bfa0..5034616b8 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -46,6 +46,9 @@ RUN cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib" RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/ +RUN find "/opt/ejabberd-$VERSION/bin" -name 'ejabberd' -delete \ + && find "/opt/ejabberd-$VERSION/releases" -name 'COOKIE' -delete + RUN export PEM=/opt/ejabberd/conf/server.pem \ && curl -o "/opt/ejabberd/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ && openssl req -x509 \ From 7c634f3615256d8b57861d8c5ab754618a0b8856 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 18:59:57 +0100 Subject: [PATCH 0067/1302] Container: No need of openssl package at runtime --- .github/container/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 5034616b8..49850e629 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -80,7 +80,6 @@ RUN apk upgrade --update musl \ libwebp \ linux-pam \ ncurses-libs \ - openssl \ sqlite \ sqlite-libs \ unixodbc \ From c71887db43461b02ae441131973dc3438b6085b5 Mon Sep 17 00:00:00 2001 From: Saarko Date: Mon, 30 Jan 2023 13:02:55 +0100 Subject: [PATCH 0068/1302] Container: Add tini as runtime init --- .github/container/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 49850e629..cf3f595b3 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -82,6 +82,7 @@ RUN apk upgrade --update musl \ ncurses-libs \ sqlite \ sqlite-libs \ + tini \ unixodbc \ yaml \ zlib \ @@ -115,5 +116,5 @@ USER ejabberd VOLUME ["$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 -ENTRYPOINT ["/usr/local/bin/ejabberdctl"] +ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"] CMD ["foreground"] From d15cf994a26d744dc2f9b31980f946fa7203180b Mon Sep 17 00:00:00 2001 From: Saarko Date: Fri, 18 Nov 2022 21:55:17 +0100 Subject: [PATCH 0069/1302] Container: Add METHOD to build container using packages (#3983) make-*: include musl build in make-binaries Ctr actions: use github runners to provide bootstrap erlang - adjust make-binaries script to use github runners' installed erlang for bootstrapping - this reduces the need to build an unnecessary toolchain for glibc based binaries --- .github/container/Dockerfile | 233 ++++++++++++++++++++------------ .github/workflows/container.yml | 42 +++++- CONTAINER.md | 12 ++ tools/make-binaries | 97 +++++++++---- tools/make-installers | 4 +- 5 files changed, 274 insertions(+), 114 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index cf3f595b3..54dd540e3 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,56 +1,103 @@ -FROM alpine:3.17 AS build -ARG VERSION=master +#' Define default build variables +ARG ALPINE_VSN='3.17' +ARG UID='9000' +ARG USER='ejabberd' +ARG HOME="opt/$USER" +ARG METHOD='direct' +ARG BUILD_DIR="/$USER" +ARG VERSION='master' -RUN apk upgrade --update musl \ - && apk add \ - autoconf \ - automake \ - bash \ - build-base \ - curl \ - elixir \ - erlang-odbc \ - erlang-reltool \ - expat-dev \ - file \ - gd-dev \ - git \ - jpeg-dev \ - libpng-dev \ - libwebp-dev \ - linux-pam-dev \ - openssl \ - openssl-dev \ - sqlite-dev \ - yaml-dev \ - zlib-dev +################################################################################ +#' METHOD='direct' - build and install ejabberd directly from source +FROM alpine:${ALPINE_VSN} AS direct + +RUN apk -U add --no-cache \ + autoconf \ + automake \ + bash \ + build-base \ + curl \ + elixir \ + erlang-odbc \ + erlang-reltool \ + expat-dev \ + file \ + gd-dev \ + git \ + jpeg-dev \ + libpng-dev \ + libwebp-dev \ + linux-pam-dev \ + openssl-dev \ + sqlite-dev \ + yaml-dev \ + zlib-dev RUN mix local.hex --force \ && mix local.rebar --force -COPY . ./ejabberd - -WORKDIR ejabberd +ARG BUILD_DIR +COPY / $BUILD_DIR/ +WORKDIR $BUILD_DIR RUN mv .github/container/ejabberdctl.template . \ && ./autogen.sh \ && ./configure --with-rebar=mix --enable-all \ && make deps \ && make rel -RUN cp -r _build/prod/rel/ejabberd/ /opt/ejabberd-$VERSION \ - && mkdir -p /opt/ejabberd \ - && mv /opt/ejabberd-$VERSION/conf /opt/ejabberd/conf +WORKDIR /rootfs +ARG VERSION +ARG HOME +RUN mkdir -p $HOME $HOME-$VERSION \ + && cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \ + && mv $HOME-$VERSION/conf $HOME/conf -RUN cp -p 'tools/captcha'*'.sh' "/opt/ejabberd-$VERSION/lib" +RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib -RUN [ ! -d .ejabberd-modules ] || cp -r .ejabberd-modules /opt/ejabberd/ +RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ + && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete -RUN find "/opt/ejabberd-$VERSION/bin" -name 'ejabberd' -delete \ - && find "/opt/ejabberd-$VERSION/releases" -name 'COOKIE' -delete +RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ + && sed -i '/^loglevel:/a \ \ + \nca_file: /opt/ejabberd/conf/cacert.pem \ + \ncertfiles: \ + \n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml" -RUN export PEM=/opt/ejabberd/conf/server.pem \ - && curl -o "/opt/ejabberd/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ +################################################################################ +#' METHOD='package' - install ejabberd from binary tarball package +FROM alpine:${ALPINE_VSN} AS package +COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/ +WORKDIR /rootfs +ARG HOME +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && mkdir -p $home_root_dir \ + && ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \ + && tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir + +################################################################################ +#' Prepare ejabberd for runtime +FROM ${METHOD} AS ejabberd +RUN apk -U add --no-cache \ + git \ + libcap-utils \ + openssl + +WORKDIR /rootfs +ARG HOME +RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload + +ARG BUILD_DIR +RUN if [ ! -d $HOME/.ejabberd-modules ]; \ + then \ + if [ -d $BUILD_DIR/.ejabberd-modules ]; \ + then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \ + else git clone https://github.com/processone/ejabberd-contrib --depth 1 \ + $HOME/.ejabberd-modules/sources/ejabberd-contrib; \ + fi \ + fi + +RUN export PEM=$HOME/conf/server.pem \ && openssl req -x509 \ -batch \ -nodes \ @@ -58,63 +105,81 @@ RUN export PEM=/opt/ejabberd/conf/server.pem \ -keyout $PEM \ -out $PEM \ -days 3650 \ - -subj "/CN=localhost" \ - && sed -i '/^loglevel:/a \ \ - \nca_file: /opt/ejabberd/conf/cacert.pem \ - \ncertfiles: \ - \n - /opt/ejabberd/conf/server.pem' "/opt/ejabberd/conf/ejabberd.yml" + -subj "/CN=localhost" -FROM alpine:3.17 -ENV HOME=/opt/ejabberd -ARG VERSION=master +RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ + && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \ + && echo -e \ + "#!/bin/sh \ + \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ + \nexport CONFIG_DIR=/$HOME/conf \ + \nexport LOGS_DIR=/$HOME/logs \ + \nexport SPOOL_DIR=/$HOME/database \ + \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \ + > usr/local/bin/ejabberdctl \ + && chmod +x usr/local/bin/* -RUN apk upgrade --update musl \ - && apk add \ - expat \ - freetds \ - gd \ - jpeg \ - libgd \ - libpng \ - libstdc++ \ - libwebp \ - linux-pam \ - ncurses-libs \ - sqlite \ - sqlite-libs \ - tini \ - unixodbc \ - yaml \ - zlib \ - && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so \ - && rm -rf /var/cache/apk/* +ARG UID +RUN chown -R $UID:$UID $HOME -COPY --from=build /opt /opt -RUN echo -e \ - "#!/bin/sh \ - \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ - \nexport CONFIG_DIR=/opt/ejabberd/conf \ - \nexport LOGS_DIR=/opt/ejabberd/logs \ - \nexport SPOOL_DIR=/opt/ejabberd/database \ - \nexec /opt/ejabberd-$VERSION/bin/ejabberdctl \"\$@\"" > /usr/local/bin/ejabberdctl \ - && chmod +x /usr/local/bin/ejabberdctl +################################################################################ +#' METHOD='package' - install runtime dependencies +FROM alpine:${ALPINE_VSN} AS runtime-package +RUN apk -U upgrade --available --no-cache \ + && apk add --no-cache \ + libcap2 \ + tini -RUN addgroup ejabberd -g 9000 \ - && adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \ - && mkdir -p $HOME/conf $HOME/database $HOME/logs $HOME/upload \ - && chown -R ejabberd:ejabberd $HOME +################################################################################ +#' METHOD='direct' - install runtime dependencies +FROM runtime-package AS runtime-direct +RUN apk add --no-cache \ + expat \ + freetds \ + gd \ + jpeg \ + libgd \ + libpng \ + libstdc++ \ + libwebp \ + linux-pam \ + ncurses-libs \ + sqlite \ + sqlite-libs \ + unixodbc \ + yaml \ + zlib \ + && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so + +################################################################################ +#' Finalize runtime environment +FROM runtime-${METHOD} AS runtime +ARG USER +ARG UID +ARG HOME +RUN addgroup $USER -g $UID \ + && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER + +################################################################################ +#' Build together production image +FROM scratch AS prod +ARG USER +ARG HOME + +COPY --from=runtime / / +COPY --from=ejabberd /rootfs / HEALTHCHECK \ --interval=1m \ --timeout=5s \ --start-period=5s \ --retries=10 \ - CMD /usr/local/bin/ejabberdctl status + CMD ejabberdctl status -WORKDIR $HOME -USER ejabberd -VOLUME ["$HOME"] +WORKDIR /$HOME +USER $USER +VOLUME ["/$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 -ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"] -CMD ["foreground"] +ENTRYPOINT ["/sbin/tini","--"] +CMD ["ejabberdctl", "foreground"] diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index dbae22f2d..3663d74a8 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,6 +1,8 @@ name: Container on: + schedule: + - cron: '22 2 */6 * *' # every 6 days to avoid gha cache being evicted push: paths-ignore: - '.devcontainer/**' @@ -21,12 +23,49 @@ jobs: permissions: packages: write steps: - - name: Check out repository code uses: actions/checkout@v3 with: fetch-depth: 0 + - name: Cache build directory + uses: actions/cache@v3 + with: + path: ~/build/ + key: ${{runner.os}}-ctr-ct-ng-1.25.0 + + - name: Get erlang/OTP version for bootstrapping + run: | + echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV + echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV + + - name: Install prerequisites + run: | + sudo apt-get -qq update + sudo apt-get -qq install makeself + # https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile + sudo apt-get -qq install build-essential autoconf bison flex gawk + sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin + sudo apt-get -qq install python3-dev texinfo unzip + + - name: Install erlang/OTP + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VSN }} + elixir-version: ${{ env.ELIXIR_VSN }} + version-type: strict + + - name: Build musl-libc based binary archives + run: | + sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries + mv .github/container/ejabberdctl.template . + CHECK_DEPS=false tools/make-binaries + + - name: Collect packages + run: | + mkdir tarballs + mv ejabberd-*.tar.gz tarballs + - name: Checkout ejabberd-contrib uses: actions/checkout@v3 with: @@ -64,6 +103,7 @@ jobs: uses: docker/build-push-action@v4 with: build-args: | + METHOD=package VERSION=${{ steps.gitdescribe.outputs.ver }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/CONTAINER.md b/CONTAINER.md index 697084442..869d1a2cd 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -262,6 +262,18 @@ Example using environment variables (see full example [docker-compose.yml](https Generating a Container Image ============================ +> By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, +> it is a fast and direct method. +> +> However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25 +> for the `arm64` architecture. +> +> Providing `build-args: METHOD=package` is an alternate method to build the container +> used by the Github Actions workflow that provides `amd64` and `arm64` container images. +> It first builds an ejabberd binary package, and later installs it in the image. +> That method avoids using QEMU, so it can build `arm64` container images, but is extremely +> slow the first time it's used, and consequently not recommended for general use. + This container image includes ejabberd as a standalone OTP release built using Elixir. That OTP release is configured with: diff --git a/tools/make-binaries b/tools/make-binaries index c124db01d..273ad117f 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -117,7 +117,8 @@ odbc_tar="$odbc_dir.tar.gz" rel_tar="$rel_name-$mix_vsn.tar.gz" ct_jobs=$(nproc) src_dir="$root_dir/src" -platform='x86_64-pc-linux-gnu' +platform=$(gcc -dumpmachine) +platform_libc=$(echo $platform | sed "s/\-/\ /g" | awk '{print $NF}') targets='x86_64-linux-gnu aarch64-linux-gnu' build_start=$(date '+%F %T') have_current_deps='false' @@ -257,10 +258,37 @@ create_common_config() CT_CC_LANG_CXX=y CT_ARCH_64=y CT_KERNEL_LINUX=y + CT_LOG_PROGRESS_BAR=n + EOF +} +#. + +#' Create Crosstool-NG configuration file for glibc. +create_gnu_config() +{ + local file="$1" + + create_common_config "$file" + + cat >>"$file" <<-'EOF' CT_LINUX_V_3_16=y CT_GLIBC_V_2_19=y CT_GLIBC_KERNEL_VERSION_NONE=y - CT_LOG_PROGRESS_BAR=n + EOF +} +#. + +#' Create Crosstool-NG configuration file for musl. +create_musl_config() +{ + local file="$1" + + create_common_config "$file" + + cat >>"$file" <<-'EOF' + CT_EXPERIMENTAL=y + CT_LIBC_MUSL=y + CT_MUSL_V_1_2_2=y EOF } #. @@ -269,8 +297,10 @@ create_common_config() create_x64_config() { local file="$1" + local libc="$2" create_common_config "$file" + create_${libc}_config "$file" cat >>"$file" <<-'EOF' CT_ARCH_X86=y @@ -282,8 +312,10 @@ create_x64_config() create_arm64_config() { local file="$1" + local libc="$2" create_common_config "$file" + create_${libc}_config "$file" cat >>"$file" <<-'EOF' CT_ARCH_ARM=y @@ -319,6 +351,13 @@ add_otp_path() if [ "$mode" = 'native' ] then native_otp_bin="$prefix/bin" + # for github runners to build for non-native systems + # https://github.com/marketplace/actions/setup-erlang-otp-with-optional-elixir-and-mix-and-or-rebar3 + elif [ ! -z "${INSTALL_DIR_FOR_OTP-}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR-}" ] + then + native_otp_bin="$INSTALL_DIR_FOR_OTP/bin" + native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin" + export PATH="$native_elixir_bin:$PATH" fi export PATH="$native_otp_bin:$PATH" } @@ -437,8 +476,9 @@ build_toolchain() { local target="$1" local prefix="$2" + local libc="$3" local arch=$(arch_name "$target") - + if [ -d "$prefix" ] then info "Using existing toolchain in $prefix ..." @@ -458,9 +498,9 @@ build_toolchain() cd "$OLDPWD" fi - info "Building toolchain for $arch ..." + info "Building toolchain for $arch-$libc ..." cd "$root_dir" - create_${arch}_config 'defconfig' + create_${arch}_config 'defconfig' "$libc" ct-ng defconfig sed -i -e "s|^CT_ZLIB_VERSION=.*|CT_ZLIB_VERSION=\"$zlib_vsn\"|" .config sed -i -e 's|^CT_ZLIB_MIRRORS=.*|CT_ZLIB_MIRRORS="https://github.com/madler/zlib/releases/download/v${CT_ZLIB_VERSION} https://www.zlib.net/ https://www.zlib.net/fossils/"|' .config @@ -477,6 +517,7 @@ build_deps() local mode="$1" local target="$2" local prefix="$3" + local libc="$4" local arch="$(arch_name "$target")" local target_src_dir="$prefix/src" local saved_path="$PATH" @@ -507,7 +548,7 @@ build_deps() tar -xJf "$src_dir/$pam_tar" cd "$OLDPWD" - info "Building Termcap $termcap_vsn for $arch ..." + info "Building Termcap $termcap_vsn for $arch-$libc ..." cd "$target_src_dir/$termcap_dir" $configure --prefix="$prefix" cat >'config.h' <<-'EOF' @@ -535,24 +576,24 @@ build_deps() make install cd "$OLDPWD" - info "Building zlib $zlib_vsn for $arch ..." + info "Building zlib $zlib_vsn for $arch-$libc ..." cd "$target_src_dir/$zlib_dir" CFLAGS="$CFLAGS -O3 -fPIC" ./configure --prefix="$prefix" --static make make install cd "$OLDPWD" - info "Building OpenSSL $ssl_vsn for $arch ..." + info "Building OpenSSL $ssl_vsn for $arch-$libc ..." cd "$target_src_dir/$ssl_dir" CFLAGS="$CFLAGS -O3 -fPIC" ./Configure no-shared no-ui-console \ --prefix="$prefix" \ --openssldir="$prefix" \ - "linux-${target%-linux-gnu}" + "linux-${target%-linux-*}" make build_libs make install_dev cd "$OLDPWD" - info "Building Expat $expat_vsn for $arch ..." + info "Building Expat $expat_vsn for $arch-$libc ..." cd "$target_src_dir/$expat_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ --without-docbook \ @@ -561,7 +602,7 @@ build_deps() make install cd "$OLDPWD" - info "Building LibYAML $yaml_vsn for $arch ..." + info "Building LibYAML $yaml_vsn for $arch-$libc ..." cd "$target_src_dir/$yaml_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -569,7 +610,7 @@ build_deps() make install cd "$OLDPWD" - info "Building SQLite $sqlite_vsn for $arch ..." + info "Building SQLite $sqlite_vsn for $arch-$libc ..." cd "$target_src_dir/$sqlite_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -577,7 +618,7 @@ build_deps() make install cd "$OLDPWD" - info "Building ODBC $odbc_vsn for $arch ..." + info "Building ODBC $odbc_vsn for $arch-$libc ..." cd "$target_src_dir/$odbc_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -594,7 +635,7 @@ build_deps() make install cd "$OLDPWD" - info "Building libpng $png_vsn for $arch ..." + info "Building libpng $png_vsn for $arch-$libc ..." cd "$target_src_dir/$png_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -602,7 +643,7 @@ build_deps() make install cd "$OLDPWD" - info "Building JPEG $jpeg_vsn for $arch ..." + info "Building JPEG $jpeg_vsn for $arch-$libc ..." cd "$target_src_dir/$jpeg_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -610,7 +651,7 @@ build_deps() make install cd "$OLDPWD" - info "Building WebP $webp_vsn for $arch ..." + info "Building WebP $webp_vsn for $arch-$libc ..." cd "$target_src_dir/$webp_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ CFLAGS="$CFLAGS -O3 -fPIC" @@ -618,7 +659,7 @@ build_deps() make install cd "$OLDPWD" - info "Building LibGD $gd_vsn for $arch ..." + info "Building LibGD $gd_vsn for $arch-$libc ..." cd "$target_src_dir/$gd_dir" $configure --prefix="$prefix" --enable-static --disable-shared \ --with-zlib="$prefix" \ @@ -640,7 +681,7 @@ build_deps() make install cd "$OLDPWD" - info "Building Erlang/OTP $otp_vsn for $arch ..." + info "Building Erlang/OTP $otp_vsn for $arch-$libc ..." if [ "$mode" = 'cross' ] then add_otp_path "$mode" "$prefix" @@ -669,7 +710,7 @@ build_deps() fi cd "$OLDPWD" - info "Building Elixir $elixir_vsn for $arch ..." + info "Building Elixir $elixir_vsn for $arch-$libc ..." cd "$target_src_dir/$elixir_dir" make install PREFIX="$prefix" cd "$OLDPWD" @@ -684,11 +725,12 @@ build_rel() local mode="$1" local target="$2" local prefix="$3" + local libc="$4" local arch="$(arch_name "$target")" local rel_dir="$PWD/_build/prod" local target_data_dir="$prefix/$rel_name" local target_dst_dir="$prefix/$rel_name-$rel_vsn" - local target_dst_tar="$rel_name-$rel_vsn-linux-$arch.tar.gz" + local target_dst_tar="$rel_name-$rel_vsn-linux-$libc-$arch.tar.gz" local saved_path="$PATH" # @@ -723,7 +765,7 @@ build_rel() fi if [ $have_current_deps = false ] - then build_deps "$mode" "$target" "$prefix" + then build_deps "$mode" "$target" "$prefix" "$libc" fi add_otp_path "$mode" "$prefix" @@ -738,7 +780,7 @@ build_rel() info "Removing old $rel_name builds" rm -rf '_build' 'deps' - info "Building $rel_name $rel_vsn for $arch ..." + info "Building $rel_name $rel_vsn for $arch-$libc ..." ./autogen.sh eimp_cflags='-fcommon' eimp_libs='-lwebp -ljpeg -lpng -lz -lm' @@ -782,7 +824,7 @@ build_rel() unset host_alias build_alias fi - info "Putting together $rel_name $rel_vsn archive for $arch ..." + info "Putting together $rel_name $rel_vsn archive for $arch-$libc ..." mkdir "$target_dst_dir" tar -C "$target_dst_dir" -xzf "$rel_dir/$rel_tar" create_data_dir "$target_dst_dir" "$target_data_dir" @@ -870,15 +912,16 @@ export LC_ALL='C.UTF-8' # Elixir insists on a UTF-8 environment. for target in $targets do - prefix="$build_dir/$(arch_name "$target")" + libc="$(echo $target | sed "s/\-/\ /g" | awk '{print $NF}')" + prefix="$build_dir/$(arch_name "$target")-$libc" toolchain_dir="$ct_prefix_dir/$target" - if [ "$(uname -m)-linux-gnu" = "$target" ] + if [ "$(uname -m)-linux-$platform_libc" = "$target" ] then mode='native' else mode='cross' fi - build_toolchain "$target" "$toolchain_dir" - build_rel "$mode" "$target" "$prefix" + build_toolchain "$target" "$toolchain_dir" "$libc" + build_rel "$mode" "$target" "$prefix" "$libc" done save_built_dep_vsns diff --git a/tools/make-installers b/tools/make-installers index d54a90bd8..ee0ee1ed1 100755 --- a/tools/make-installers +++ b/tools/make-installers @@ -80,7 +80,7 @@ create_help_file() local file="$1" cat >"$file" <<-EOF - This is the $rel_name $rel_vsn-$iteration installer for linux-$arch + This is the $rel_name $rel_vsn-$iteration installer for linux-gnu-$arch Visit: $home_url @@ -354,7 +354,7 @@ create_setup_script() for arch in $architectures do - tar_name="$rel_name-$rel_vsn-linux-$arch.tar.gz" + tar_name="$rel_name-$rel_vsn-linux-gnu-$arch.tar.gz" installer_name="$rel_name-$rel_vsn-$iteration-linux-$arch.run" test -e "$tar_name" || tools/make-binaries From fbfcebf4172e257b18a4db93c1a9a8a8b890a672 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jan 2023 15:34:42 +0100 Subject: [PATCH 0070/1302] Container: Remove Elixir Matchers to prevent useless warnings in github actions page --- .github/workflows/container.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 3663d74a8..e471356fd 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -55,6 +55,14 @@ jobs: elixir-version: ${{ env.ELIXIR_VSN }} version-type: strict + - name: Remove Elixir Matchers + run: | + echo "::remove-matcher owner=elixir-mixCompileWarning::" + echo "::remove-matcher owner=elixir-credoOutputDefault::" + echo "::remove-matcher owner=elixir-mixCompileError::" + echo "::remove-matcher owner=elixir-mixTestFailure::" + echo "::remove-matcher owner=elixir-dialyzerOutputDefault::" + - name: Build musl-libc based binary archives run: | sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries From 64e1cfcbbad9ed0370846341f591a530a433bbbc Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Mar 2023 11:50:46 +0200 Subject: [PATCH 0071/1302] Test only with oldest OTP supported, newest stable and bleeding edge --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/runtime.yml | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ab0152ce..2bdec91d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '21.3', '24.3', '25', '26.0-rc1'] + otp: ['20.0', '25.3', '26.0-rc2'] runs-on: ubuntu-20.04 services: redis: @@ -38,7 +38,7 @@ jobs: - uses: actions/checkout@v3 - name: Test shell scripts - if: matrix.otp == 25 + if: matrix.otp == '25.3' run: | shellcheck test/ejabberd_SUITE_data/gencerts.sh shellcheck tools/captcha.sh @@ -46,7 +46,7 @@ jobs: shellcheck -x ejabberdctl.template - name: Get specific Erlang/OTP - if: matrix.otp != 25 + if: matrix.otp != '25.3' uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} @@ -124,7 +124,7 @@ jobs: key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - name: Download test logs - if: matrix.otp == 25 && github.repository == 'processone/ejabberd' + if: matrix.otp == '25.3' && github.repository == 'processone/ejabberd' continue-on-error: true run: | mkdir -p _build/test @@ -149,7 +149,7 @@ jobs: - run: make options - run: make xref - run: make dialyzer - if: matrix.otp != '26.0-rc1' + if: matrix.otp != '26.0-rc2' - name: Check Production Release run: | @@ -205,7 +205,7 @@ jobs: find logs/ -name exunit.log -exec cat '{}' ';' - name: Send to coveralls - if: matrix.otp == 25 + if: matrix.otp == '25.3' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 4c059c97b..492048572 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,8 +31,11 @@ jobs: strategy: fail-fast: false matrix: - otp: ['19.3', '20.3', '24.3', '25'] + otp: ['20.3', '25.3', '26'] rebar: ['rebar', 'rebar3'] + exclude: + - otp: '26' + rebar: 'rebar' runs-on: ubuntu-latest container: image: erlang:${{ matrix.otp }} From 6da1bb5b22f631839bd22da3d5536a46adaf12e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 30 Mar 2023 14:37:13 +0200 Subject: [PATCH 0072/1302] Add support for "xep-0424 Message Moderation" This fixes issue #3730 --- rebar.config | 2 +- src/mod_mam.erl | 16 +++++++- src/mod_mam_mnesia.erl | 18 +++++++- src/mod_mam_sql.erl | 8 +++- src/mod_muc_room.erl | 93 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 131 insertions(+), 6 deletions(-) diff --git a/rebar.config b/rebar.config index 746f62fb7..388d450ab 100644 --- a/rebar.config +++ b/rebar.config @@ -73,7 +73,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.6.1"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "1107d05640ccd352613b2867ab24c5649603a470"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 25a70c264..d88ac430e 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -44,7 +44,8 @@ mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2, is_empty_for_user/2, is_empty_for_room/3, check_create_room/4, process_iq/3, store_mam_message/7, make_id/0, wrap_as_mucsub/2, select/7, - delete_old_messages_batch/5, delete_old_messages_status/1, delete_old_messages_abort/1]). + delete_old_messages_batch/5, delete_old_messages_status/1, delete_old_messages_abort/1, + remove_message_from_archive/3]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -352,6 +353,19 @@ remove_mam_for_user_with_peer(User, Server, Peer) -> {error, <<"Invalid peer JID">>} end. +-spec remove_message_from_archive(User :: binary(), Server :: binary(), StanzaId :: integer()) -> + ok | {error, binary()}. +remove_message_from_archive(User, Server, StanzaId) -> + Mod = gen_mod:db_mod(Server, ?MODULE), + case Mod:remove_from_archive(User, Server, StanzaId) of + ok -> + ok; + {error, Bin} when is_binary(Bin) -> + {error, Bin}; + {error, _} -> + {error, <<"Db returned error">>} + end. + get_module_host(LServer) -> try gen_mod:db_mod(LServer, ?MODULE) catch error:{module_not_loaded, ?MODULE, LServer} -> diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 6fb60dcb9..f9e366860 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -81,7 +81,7 @@ remove_from_archive(LUser, LServer, none) -> {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} end; -remove_from_archive(LUser, LServer, WithJid) -> +remove_from_archive(LUser, LServer, #jid{} = WithJid) -> US = {LUser, LServer}, Peer = jid:remove_resource(jid:split(WithJid)), F = fun () -> @@ -96,6 +96,22 @@ remove_from_archive(LUser, LServer, WithJid) -> case mnesia:transaction(F) of {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} + end; +remove_from_archive(LUser, LServer, StanzaId) -> + Timestamp = misc:usec_to_now(StanzaId), + US = {LUser, LServer}, + F = fun () -> + Msgs = mnesia:select( + archive_msg, + ets:fun2ms( + fun(#archive_msg{us = US1, timestamp = Timestamp1} = Msg) + when US1 == US, Timestamp1 == Timestamp -> Msg + end)), + lists:foreach(fun mnesia:delete_object/1, Msgs) + end, + case mnesia:transaction(F) of + {atomic, _} -> ok; + {aborted, Reason} -> {error, Reason} end. delete_old_messages(global, TimeStamp, Type) -> diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index a0dd6b6d1..b21e84a7b 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -64,12 +64,18 @@ remove_from_archive(LUser, LServer, none) -> {error, Reason} -> {error, Reason}; _ -> ok end; -remove_from_archive(LUser, LServer, WithJid) -> +remove_from_archive(LUser, LServer, #jid{} = WithJid) -> Peer = jid:encode(jid:remove_resource(WithJid)), case ejabberd_sql:sql_query(LServer, ?SQL("delete from archive where username=%(LUser)s and %(LServer)H and bare_peer=%(Peer)s")) of {error, Reason} -> {error, Reason}; _ -> ok + end; +remove_from_archive(LUser, LServer, StanzaId) -> + case ejabberd_sql:sql_query(LServer, + ?SQL("delete from archive where username=%(LUser)s and %(LServer)H and timestamp=%(StanzaId)d")) of + {error, Reason} -> {error, Reason}; + _ -> ok end. count_messages_to_delete(ServerHost, TimeStamp, Type) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 24b81db50..71f7418db 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -499,6 +499,15 @@ normal_state({route, <<"">>, process_iq_captcha(From, IQ, StateData); #adhoc_command{} -> process_iq_adhoc(From, IQ, StateData); + #fasten_apply_to{} = ApplyTo -> + case xmpp:get_subtag(ApplyTo, #message_moderate{}) of + #message_moderate{} = Moderate -> + process_iq_moderate(From, IQ, ApplyTo, Moderate, StateData); + _ -> + Txt = ?T("The feature requested is not " + "supported by the conference"), + {error, xmpp:err_service_unavailable(Txt, Lang)} + end; _ -> Txt = ?T("The feature requested is not " "supported by the conference"), @@ -1059,7 +1068,8 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData drop -> {next_state, normal_state, StateData}; NewPacket1 -> - NewPacket = xmpp:put_meta(xmpp:remove_subtag(NewPacket1, #nick{}), + NewPacket = xmpp:put_meta(xmpp:remove_subtag( + add_stanza_id(NewPacket1, StateData), #nick{}), muc_sender_real_jid, From), Node = if Subject == [] -> ?NS_MUCSUB_NODES_MESSAGES; true -> ?NS_MUCSUB_NODES_SUBJECT @@ -1108,6 +1118,30 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData {next_state, normal_state, StateData} end. +-spec add_stanza_id(Packet :: message(), State :: state()) -> message(). +add_stanza_id(Packet, #state{jid = JID}) -> + {AddId, NewPacket} = + case xmpp:get_meta(Packet, stanza_id, false) of + false -> + GenID = erlang:system_time(microsecond), + {true, xmpp:put_meta(Packet, stanza_id, GenID)}; + _ -> + StanzaIds = xmpp:get_subtags(Packet, #stanza_id{}), + HasOurStanzaId = lists:any( + fun(#stanza_id{by = JID2}) when JID == JID2 -> true; + (_) -> false + end, StanzaIds), + {not HasOurStanzaId, Packet} + end, + if + AddId -> + ID = xmpp:get_meta(NewPacket, stanza_id), + IDs = integer_to_binary(ID), + xmpp:append_subtags(NewPacket, [#stanza_id{by = JID, id = IDs}]); + true -> + Packet + end. + -spec process_normal_message(jid(), message(), state()) -> state(). process_normal_message(From, #message{lang = Lang} = Pkt, StateData) -> Action = lists:foldl( @@ -2853,6 +2887,18 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) -> StateData#state{just_created = erlang:system_time(microsecond)} end. +remove_from_history(StanzaId, #state{history = #lqueue{queue = Queue} = LQueue} = StateData) -> + NewQ = p1_queue:foldl( + fun({_, Pkt, _, _, _} = Entry, Acc) -> + case xmpp:get_meta(Pkt, stanza_id, 0) of + V when V == StanzaId -> + Acc; + _ -> + p1_queue:in(Entry, Acc) + end + end, p1_queue:new(), Queue), + StateData#state{history = LQueue#lqueue{queue = NewQ}}. + -spec send_history(jid(), [lqueue_elem()], state()) -> ok. send_history(JID, History, StateData) -> lists:foreach( @@ -4237,7 +4283,7 @@ maybe_forget_room(StateData) -> make_disco_info(_From, StateData) -> Config = StateData#state.config, Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, - ?NS_COMMANDS, + ?NS_COMMANDS, ?NS_MESSAGE_MODERATE, ?CONFIG_OPT_TO_FEATURE((Config#config.public), <<"muc_public">>, <<"muc_hidden">>), ?CONFIG_OPT_TO_FEATURE((Config#config.persistent), @@ -4991,6 +5037,49 @@ add_presence_hats(JID, Pres, StateData) -> Pres end. +-spec process_iq_moderate(jid(), iq(), fasten_apply_to(), message_moderate(), state()) -> + {result, undefined, state()} | + {error, stanza_error()}. +process_iq_moderate(_From, #iq{type = get}, _ApplyTo, _Moderate, _StateData) -> + {error, xmpp:err_bad_request()}; +process_iq_moderate(From, #iq{type = set, lang = Lang}, + #fasten_apply_to{id = Id}, + #message_moderate{reason = Reason}, + #state{config = Config, jid = JID, server_host = Server} = StateData) -> + FAffiliation = get_affiliation(From, StateData), + FRole = get_role(From, StateData), + IsModerator = FRole == moderator orelse FAffiliation == owner orelse + FAffiliation == admin, + case IsModerator of + false -> + {error, xmpp:err_forbidden( + ?T("Only moderators are allowed to retract messages"), Lang)}; + _ -> + try binary_to_integer(Id) of + StanzaId -> + case Config#config.mam of + true -> + JIDs = jid:encode(JID), + mod_mam:remove_message_from_archive(JIDs, Server, StanzaId); + _ -> + ok + end, + Packet = #message{type = groupchat, + sub_els = [ + #fasten_apply_to{id = Id, sub_els = [ + #message_moderated{reason = Reason, + retract = #message_retract{}} + ]}]}, + send_wrapped_multiple(JID, + get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_MESSAGES, StateData), + Packet, ?NS_MUCSUB_NODES_MESSAGES, StateData), + {result, undefined, remove_from_history(StanzaId, StateData)} + catch _:_ -> + {error, xmpp:err_bad_request( + ?T("Stanza id is not valid"), Lang)} + end + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support From f6385fae508d0f90faa8f0d2a78091c9455194e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 30 Mar 2023 14:53:51 +0200 Subject: [PATCH 0073/1302] Fix dialyzer warning --- src/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 71f7418db..bcc167f53 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1126,7 +1126,7 @@ add_stanza_id(Packet, #state{jid = JID}) -> GenID = erlang:system_time(microsecond), {true, xmpp:put_meta(Packet, stanza_id, GenID)}; _ -> - StanzaIds = xmpp:get_subtags(Packet, #stanza_id{}), + StanzaIds = xmpp:get_subtags(Packet, #stanza_id{by = #jid{}}), HasOurStanzaId = lists:any( fun(#stanza_id{by = JID2}) when JID == JID2 -> true; (_) -> false From d43ce53f9a7a7bc5df8992c0800d3781cf1f4256 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Mar 2023 16:54:35 +0200 Subject: [PATCH 0074/1302] Update xmpp version in mix following commit 6da1bb5b2 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index daaaea132..21d8ce596 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.6.0"}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "1107d05640ccd352613b2867ab24c5649603a470", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index e55592656..4621841cd 100644 --- a/mix.lock +++ b/mix.lock @@ -30,6 +30,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.7", "d6bdcf0aa72c927fbe8b779fc4ef1f3916c5450b2ff136c800a7a0361fb1ddff", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3fb1f07aaa630b2276e83d267557d1ceb3d2ce52d1145de71864160210655852"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.6.1", "4075de3c1bb63c7838dc6c8d06167ffba2e39fd285e2a295287f26c1d8f64707", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "87fee84f805a151f4d85c1c11c16a738716dfaae353cb88898c0254f05df81c0"}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "1107d05640ccd352613b2867ab24c5649603a470", [ref: "1107d05640ccd352613b2867ab24c5649603a470"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } From 99e51a2123fb92facbcd11009f23a80189dd5356 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Mar 2023 17:01:44 +0200 Subject: [PATCH 0075/1302] Mention in mod_mam.erl its support for XEP-0425: Message Moderation Then run "make doap" to regenerate ejabberd.doap --- ejabberd.doap | 9 +++++++++ src/mod_mam.erl | 1 + 2 files changed, 10 insertions(+) diff --git a/ejabberd.doap b/ejabberd.doap index 0a85515d0..662dba347 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -665,6 +665,15 @@ mod_private + + + + 0.2.1 + 23.04 + + mod_mam + + diff --git a/src/mod_mam.erl b/src/mod_mam.erl index d88ac430e..7c40b17fc 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -28,6 +28,7 @@ -protocol({xep, 313, '0.6.1', '15.06', "", ""}). -protocol({xep, 334, '0.2'}). -protocol({xep, 359, '0.5.0'}). +-protocol({xep, 425, '0.2.1', '23.04', "", ""}). -protocol({xep, 441, '0.2.0'}). -behaviour(gen_mod). From 401bdedae8d5a93ba278e599267ecc45d82551d3 Mon Sep 17 00:00:00 2001 From: sando38 Date: Fri, 7 Apr 2023 20:42:04 +0200 Subject: [PATCH 0076/1302] Dockerfile: Detect runtime dependencies automatically Only libcap2 and tini can't be auto-detected. libcap2 has been renamed in Alpine version 3.17, hence the Dockerfile is not compatible with Alpine versions <3.17 --- .github/container/Dockerfile | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 54dd540e3..7672e8f2e 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -117,7 +117,10 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ \nexport SPOOL_DIR=/$HOME/database \ \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \ > usr/local/bin/ejabberdctl \ - && chmod +x usr/local/bin/* + && chmod +x usr/local/bin/* \ + && scanelf --needed --nobanner --format '%n#p' --recursive $home_root_dir \ + | tr ',' '\n' | sort -u | awk 'system("[ -e $home_root_dir" $1 " ]") == 0 { next } \ + { print "so:" $1 }' > /tmp/runDeps ARG UID RUN chown -R $UID:$UID $HOME @@ -133,23 +136,9 @@ RUN apk -U upgrade --available --no-cache \ ################################################################################ #' METHOD='direct' - install runtime dependencies FROM runtime-package AS runtime-direct +COPY --from=ejabberd /tmp/runDeps /tmp/runDeps RUN apk add --no-cache \ - expat \ - freetds \ - gd \ - jpeg \ - libgd \ - libpng \ - libstdc++ \ - libwebp \ - linux-pam \ - ncurses-libs \ - sqlite \ - sqlite-libs \ - unixodbc \ - yaml \ - zlib \ - && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so + $(cat /tmp/runDeps) ################################################################################ #' Finalize runtime environment From dee0ec50b9a21a23d307ba3f2e5f3b12c481bae5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 3 Apr 2023 17:24:24 +0200 Subject: [PATCH 0077/1302] Remove ci-19.3, as Github Actions no longer supports ubuntu-18.04 --- .github/workflows/ci-19.3.yml | 229 ---------------------------------- 1 file changed, 229 deletions(-) delete mode 100644 .github/workflows/ci-19.3.yml diff --git a/.github/workflows/ci-19.3.yml b/.github/workflows/ci-19.3.yml deleted file mode 100644 index c65a26050..000000000 --- a/.github/workflows/ci-19.3.yml +++ /dev/null @@ -1,229 +0,0 @@ -name: CI (19.3) - -on: - push: - paths-ignore: - - '.devcontainer/**' - - 'examples/**' - - 'lib/**' - - 'man/**' - - 'priv/**' - - '**.md' - pull_request: - paths-ignore: - - '.devcontainer/**' - - 'examples/**' - - 'lib/**' - - 'man/**' - - 'priv/**' - - '**.md' - -jobs: - - tests: - name: Tests - strategy: - fail-fast: false - matrix: - otp: ['19.3'] - runs-on: ubuntu-18.04 - services: - redis: - image: redis - ports: - - 6379:6379 - - steps: - - - uses: actions/checkout@v3 - - - name: Get specific Erlang/OTP - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ matrix.otp }} - - - name: Get a compatible Rebar3 - run: | - rm rebar3 - wget https://github.com/processone/ejabberd/raw/21.12/rebar3 - chmod +x rebar3 - - - name: Prepare databases - run: | - sudo systemctl start mysql.service - sudo systemctl start postgresql.service - mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost' - IDENTIFIED BY 'ejabberd_test';" - mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" - mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* - TO 'ejabberd_test'@'localhost';" - mysql -u root -proot ejabberd_test < sql/mysql.sql - pg_isready - sudo -u postgres psql -c "CREATE USER ejabberd_test - WITH PASSWORD 'ejabberd_test';" - sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" - sudo -u postgres psql ejabberd_test -f sql/pg.sql - sudo -u postgres psql -c "GRANT ALL PRIVILEGES - ON DATABASE ejabberd_test TO ejabberd_test;" - sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL - TABLES IN SCHEMA public - TO ejabberd_test;" - sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL - SEQUENCES IN SCHEMA public - TO ejabberd_test;" - - - name: Prepare libraries - run: | - sudo apt-get -qq update - sudo apt-get -y purge libgd3 nginx - sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ - libsqlite3-dev libwebp-dev libyaml-dev - - - name: Prepare rebar - run: | - echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2} - ]}.' >>rebar.config - echo '{xref_checks, [deprecated_function_calls, deprecated_functions, - locals_not_used, undefined_function_calls, undefined_functions]}. - % Disabled: exports_not_used,' >>rebar.config - echo '{dialyzer, [{get_warnings, true}, {plt_extra_apps, [cache_tab, - eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, - mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config - echo '{ct_extra_params, "-verbosity 20"}.' >>rebar.config - echo "{ct_opts, [{verbosity, 20}, {keep_logs, 20}]}." >>rebar.config - - - name: Remove syntax_tools from release - run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script - - - name: Cache rebar - uses: actions/cache@v3 - with: - path: | - ~/.cache/rebar3/ - key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - - - name: Compile - run: | - ./autogen.sh - ./configure --with-rebar=./rebar3 \ - --prefix=/tmp/ejabberd \ - --enable-all \ - --disable-elixir \ - --disable-mssql \ - --disable-odbc - make update - make - - - run: make install -s - - run: make hooks - - run: make options - - run: make xref - - run: make dialyzer - - - name: Check Production Release - run: | - make rel - RE=_build/prod/rel/ejabberd - $RE/bin/ejabberdctl start - $RE/bin/ejabberdctl started - $RE/bin/ejabberdctl stop - $RE/bin/ejabberdctl stopped - cat $RE/logs/ejabberd.log - grep -q "is stopped in" $RE/logs/ejabberd.log - - - name: Check Development Release - run: | - make dev - RE=_build/dev/rel/ejabberd - $RE/bin/ejabberdctl start - $RE/bin/ejabberdctl started - $RE/bin/ejabberdctl stop - $RE/bin/ejabberdctl stopped - cat $RE/logs/ejabberd.log - grep -q "is stopped in" $RE/logs/ejabberd.log - - - name: Run tests - id: ct - run: | - (cd priv && ln -sf ../sql) - COMMIT=`echo $GITHUB_SHA | cut -c 1-7` - DATE=`date +%s` - REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"` - NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT - LABEL=`git show -s --format=%s | cut -c 1-30` - ./rebar3 ct --name $NODENAME --label "$LABEL" - ./rebar3 cover - - - name: Check results - if: always() && (steps.ct.outcome != 'skipped' || steps.ct2.outcome != 'skipped') - id: ctresults - run: | - [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true - ln `find logs/ -name suite.log` logs/suite.log - grep 'TEST COMPLETE' logs/suite.log - grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log - test $(find logs/ -empty -name error.log) - - - name: View logs failures - if: failure() && steps.ctresults.outcome == 'failure' - run: | - cat logs/suite.log | awk \ - 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' - find logs/ -name error.log -exec cat '{}' ';' - find logs/ -name exunit.log -exec cat '{}' ';' - - - name: Upload test logs - if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' - uses: peaceiris/actions-gh-pages@v3 - with: - publish_dir: _build/test - exclude_assets: '.github,lib,plugins' - external_repository: processone/ecil - deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} - keep_files: true - - - name: View ECIL address - if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' - run: | - CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'` - echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/" - - - name: Prepare new schema - run: | - [[ -d logs ]] && rm -rf logs - [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true - mysql -u root -proot -e "DROP DATABASE ejabberd_test;" - sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" - mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" - mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* - TO 'ejabberd_test'@'localhost';" - mysql -u root -proot ejabberd_test < sql/mysql.new.sql - sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" - sudo -u postgres psql ejabberd_test -f sql/pg.new.sql - sudo -u postgres psql -c "GRANT ALL PRIVILEGES - ON DATABASE ejabberd_test TO ejabberd_test;" - sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL - TABLES IN SCHEMA public - TO ejabberd_test;" - sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL - SEQUENCES IN SCHEMA public - TO ejabberd_test;" - sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl - - run: CT_BACKENDS=mysql,pgsql make test - id: ctnewschema - - name: Check results - if: always() && steps.ctnewschema.outcome != 'skipped' - run: | - [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true - ln `find logs/ -name suite.log` logs/suite.log - grep 'TEST COMPLETE' logs/suite.log - grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log - test $(find logs/ -empty -name error.log) - - name: View logs failures - if: failure() && steps.ctnewschema.outcome != 'skipped' - run: | - cat logs/suite.log | awk \ - 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' - find logs/ -name error.log -exec cat '{}' ';' - find logs/ -name exunit.log -exec cat '{}' ';' From 10635bccc9584b52443e1875995b23ffeee30000 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 3 Apr 2023 17:06:49 +0200 Subject: [PATCH 0078/1302] Container: Reword sentences about docker.io and ghcr.io --- CONTAINER.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 869d1a2cd..ae8a679ff 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -17,17 +17,16 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. [mqtt]: https://mqtt.org/ [sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol -This document explains how to use the -[ejabberd container images](https://github.com/processone/ejabberd/pkgs/container/ejabberd) -available in the GitHub Container Registry, +This document explains how to use the `ejabberd` container image available in +[ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), built using the files in `.github/container/`. -Alternatively, there are also -[ejabberd-ecs Docker images](https://hub.docker.com/r/ejabberd/ecs/) -available in Docker Hub, +Alternatively, there is also the `ecs` container image available in +[docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), built using the [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) repository. +Check the [differences between `ejabberd` and `ecs` images](https://github.com/processone/docker-ejabberd/blob/master/ecs/HUB-README.md#alternative-image-in-github). If you are using a Windows operating system, check the tutorials mentioned in [ejabberd Docs > Docker Image](https://docs.ejabberd.im/admin/installation/#docker-image). @@ -206,7 +205,7 @@ explains how to install an additional module using docker-compose. ## Commands on start The ejabberdctl script reads the `CTL_ON_CREATE` environment variable -the first time the docker container is started, +the first time the container is started, and reads `CTL_ON_START` every time the container is started. Those variables can contain one ejabberdctl command, or several commands separated with the blankspace and `;` characters. From 6705679cf344a0c5c0302503e4adc4a241061a5c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Apr 2023 11:11:16 +0200 Subject: [PATCH 0079/1302] Container: Update instructions to build image following d15cf994a (#3983) --- CONTAINER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTAINER.md b/CONTAINER.md index ae8a679ff..462866384 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -285,7 +285,7 @@ That OTP release is configured with: Build ejabberd Community Server base image from ejabberd master on GitHub: ```bash -docker build \ +docker buildx build \ -t personal/ejabberd \ -f .github/container/Dockerfile \ . From 38eb50bf5c78b2f4e589138cd55baf3c8b4bf9b5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Apr 2023 12:09:37 +0200 Subject: [PATCH 0080/1302] Container: Reorganize how to build container image --- CONTAINER.md | 58 +++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 462866384..99d0b9475 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -258,23 +258,10 @@ Example using environment variables (see full example [docker-compose.yml](https ``` -Generating a Container Image -============================ - -> By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, -> it is a fast and direct method. -> -> However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25 -> for the `arm64` architecture. -> -> Providing `build-args: METHOD=package` is an alternate method to build the container -> used by the Github Actions workflow that provides `amd64` and `arm64` container images. -> It first builds an ejabberd binary package, and later installs it in the image. -> That method avoids using QEMU, so it can build `arm64` container images, but is extremely -> slow the first time it's used, and consequently not recommended for general use. +Build a Container Image +======================= This container image includes ejabberd as a standalone OTP release built using Elixir. - That OTP release is configured with: - `mix.exs`: Customize ejabberd release @@ -282,7 +269,9 @@ That OTP release is configured with: - `config/runtime.exs`: Customize ejabberd paths - `ejabberd.yml.template`: ejabberd default config file -Build ejabberd Community Server base image from ejabberd master on GitHub: +## Direct build + +Build ejabberd Community Server container image from ejabberd master git repository: ```bash docker buildx build \ @@ -291,18 +280,7 @@ docker buildx build \ . ``` -Build ejabberd Community Server base image for a given ejabberd version, -both for amd64 and arm64 architectures: - -```bash -VERSION=22.05 -git checkout $VERSION -docker buildx build \ - --platform=linux/amd64,linux/arm64 - -t personal/ejabberd:$VERSION \ - -f .github/container/Dockerfile \ - . -``` +## Podman build It's also possible to use podman instead of docker, just notice: - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile @@ -323,6 +301,30 @@ podman exec -it eja1 sh podman stop eja1 ``` +## Package build for `arm64` + +By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, +it is a fast and direct method. +However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25 +for the `arm64` architecture. + +Providing `--build-arg METHOD=package` is an alternate method to build the container +used by the Github Actions workflow that provides `amd64` and `arm64` container images. +It first builds an ejabberd binary package, and later installs it in the image. +That method avoids using QEMU, so it can build `arm64` container images, but is extremely +slow the first time it's used, and consequently not recommended for general use. + +In this case, to build the ejabberd container image for arm64 architecture: + +```bash +docker buildx build \ + --build-arg METHOD=package \ + --platform linux/arm64 \ + -t personal/ejabberd:$VERSION \ + -f .github/container/Dockerfile \ + . +``` + Composer Examples ================= From 34420444dbd0ee909a4beacbeaaef629ee44a972 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Apr 2023 15:12:33 +0200 Subject: [PATCH 0081/1302] Container: Revert change in entrypoint that was added in d15cf994a --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 7672e8f2e..ef467329b 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -170,5 +170,5 @@ USER $USER VOLUME ["/$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 -ENTRYPOINT ["/sbin/tini","--"] -CMD ["ejabberdctl", "foreground"] +ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"] +CMD ["foreground"] From d12e5a44b82451b68d33bd3e36eced233428af37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 14 Apr 2023 12:05:49 +0200 Subject: [PATCH 0082/1302] Add by attribute to generated muc moderation messages --- mix.exs | 2 +- rebar.config | 2 +- src/mod_muc_room.erl | 37 +++++++++++++++++++++---------------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/mix.exs b/mix.exs index 21d8ce596..38cba9790 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "1107d05640ccd352613b2867ab24c5649603a470", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "9ba5f2b3a66f929c5a9e455e9124b8752ada4b43", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 388d450ab..f1fdd07df 100644 --- a/rebar.config +++ b/rebar.config @@ -73,7 +73,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "1107d05640ccd352613b2867ab24c5649603a470"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "9ba5f2b3a66f929c5a9e455e9124b8752ada4b43"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index bcc167f53..2be5b4a78 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -2153,11 +2153,22 @@ get_priority_from_presence(#presence{priority = Prio}) -> _ -> Prio end. --spec find_nick_by_jid(jid(), state()) -> binary(). +-spec find_nick_by_jid(jid() | undefined, state()) -> binary(). +find_nick_by_jid(undefined, _StateData) -> + <<>>; find_nick_by_jid(JID, StateData) -> LJID = jid:tolower(JID), - #user{nick = Nick} = maps:get(LJID, StateData#state.users), - Nick. + case maps:find(LJID, StateData#state.users) of + {ok, #user{nick = Nick}} -> + Nick; + _ -> + case maps:find(LJID, (StateData#state.muc_subscribers)#muc_subscribers.subscribers) of + {ok, #subscriber{nick = Nick}} -> + Nick; + _ -> + <<>> + end + end. -spec is_nick_change(jid(), binary(), state()) -> boolean(). is_nick_change(JID, Nick, StateData) -> @@ -2890,7 +2901,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) -> remove_from_history(StanzaId, #state{history = #lqueue{queue = Queue} = LQueue} = StateData) -> NewQ = p1_queue:foldl( fun({_, Pkt, _, _, _} = Entry, Acc) -> - case xmpp:get_meta(Pkt, stanza_id, 0) of + case xmpp:get_meta(Pkt, stanza_id, missing) of V when V == StanzaId -> Acc; _ -> @@ -3448,7 +3459,7 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation, send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation, StateData) -> #user{jid = RealJID, nick = Nick} = maps:get(jid:tolower(UJID), StateData#state.users), - ActorNick = get_actor_nick(MJID, StateData), + ActorNick = find_nick_by_jid(MJID, StateData), %% TODO: optimize further UserMap = maps:merge( @@ -3491,15 +3502,6 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation, end end, ok, UserMap). --spec get_actor_nick(undefined | jid(), state()) -> binary(). -get_actor_nick(undefined, _StateData) -> - <<"">>; -get_actor_nick(MJID, StateData) -> - try maps:get(jid:tolower(MJID), StateData#state.users) of - #user{nick = ActorNick} -> ActorNick - catch _:{badkey, _} -> <<"">> - end. - -spec convert_legacy_fields([xdata_field()]) -> [xdata_field()]. convert_legacy_fields(Fs) -> lists:map( @@ -5064,16 +5066,19 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, _ -> ok end, + By = jid:replace_resource(JID, find_nick_by_jid(From, StateData)), Packet = #message{type = groupchat, sub_els = [ #fasten_apply_to{id = Id, sub_els = [ - #message_moderated{reason = Reason, + #message_moderated{by = By, reason = Reason, retract = #message_retract{}} ]}]}, send_wrapped_multiple(JID, get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_MESSAGES, StateData), Packet, ?NS_MUCSUB_NODES_MESSAGES, StateData), - {result, undefined, remove_from_history(StanzaId, StateData)} + NSD = add_message_to_history(<<"">>, + StateData#state.jid, Packet, StateData), + {result, undefined, remove_from_history(StanzaId, NSD)} catch _:_ -> {error, xmpp:err_bad_request( ?T("Stanza id is not valid"), Lang)} From 1114a35e0abb960e6a6020e834928961f3627ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 14 Apr 2023 12:19:38 +0200 Subject: [PATCH 0083/1302] Recognize message retractions in mod_muc --- mix.exs | 2 +- rebar.config | 2 +- src/mod_muc_room.erl | 60 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/mix.exs b/mix.exs index 38cba9790..7e63787c7 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "9ba5f2b3a66f929c5a9e455e9124b8752ada4b43", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "46b6c192d353e60e13a5cf8ed467c5696729b624", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index f1fdd07df..cb3200ac7 100644 --- a/rebar.config +++ b/rebar.config @@ -73,7 +73,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "9ba5f2b3a66f929c5a9e455e9124b8752ada4b43"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "46b6c192d353e60e13a5cf8ed467c5696729b624"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 2be5b4a78..3093de248 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1074,19 +1074,20 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData Node = if Subject == [] -> ?NS_MUCSUB_NODES_MESSAGES; true -> ?NS_MUCSUB_NODES_SUBJECT end, + NewStateData2 = check_message_for_retractions(NewPacket1, NewStateData1), send_wrapped_multiple( jid:replace_resource(StateData#state.jid, FromNick), get_users_and_subscribers_with_node(Node, StateData), - NewPacket, Node, NewStateData1), - NewStateData2 = case has_body_or_subject(NewPacket) of + NewPacket, Node, NewStateData2), + NewStateData3 = case has_body_or_subject(NewPacket) of true -> add_message_to_history(FromNick, From, NewPacket, - NewStateData1); + NewStateData2); false -> - NewStateData1 + NewStateData2 end, - {next_state, normal_state, NewStateData2} + {next_state, normal_state, NewStateData3} end; _ -> Err = case (StateData#state.config)#config.allow_change_subj of @@ -1118,6 +1119,34 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData {next_state, normal_state, StateData} end. +-spec check_message_for_retractions(Packet :: message(), State :: state()) -> state(). +check_message_for_retractions(Packet, + #state{config = Config, jid = JID, server_host = Server} = State) -> + case xmpp:get_subtag(Packet, #fasten_apply_to{}) of + #fasten_apply_to{id = ID} = F -> + case xmpp:get_subtag(F, #message_retract{}) of + #message_retract{} -> + #jid{luser = U, lserver = S} = xmpp:get_from(Packet), + case remove_from_history({U, S}, ID, State) of + {NewState, StanzaId} when is_integer(StanzaId) -> + case Config#config.mam of + true -> + JIDs = jid:encode(JID), + mod_mam:remove_message_from_archive(JIDs, Server, StanzaId), + NewState; + _ -> + NewState + end; + {NewState, _} -> + NewState + end; + _ -> + State + end; + _ -> + State + end. + -spec add_stanza_id(Packet :: message(), State :: state()) -> message(). add_stanza_id(Packet, #state{jid = JID}) -> {AddId, NewPacket} = @@ -2910,6 +2939,25 @@ remove_from_history(StanzaId, #state{history = #lqueue{queue = Queue} = LQueue} end, p1_queue:new(), Queue), StateData#state{history = LQueue#lqueue{queue = NewQ}}. +remove_from_history({U1, S1}, OriginId, #state{history = #lqueue{queue = Queue} = LQueue} = StateData) -> + {NewQ, StanzaId} = p1_queue:foldl( + fun({_, Pkt, _, _, _} = Entry, {Q, none}) -> + case jid:tolower(xmpp:get_from(Pkt)) of + {U2, S2, _} when U1 == U2, S1 == S2 -> + case xmpp:get_subtag(Pkt, #origin_id{}) of + #origin_id{id = V} when V == OriginId -> + {Q, xmpp:get_meta(Pkt, stanza_id, missing)}; + _ -> + {p1_queue:in(Entry, Q), none} + end; + _ -> + {p1_queue:in(Entry, Q), none} + end; + (Entry, {Q, S}) -> + {p1_queue:in(Entry, Q), S} + end, {p1_queue:new(), none}, Queue), + {StateData#state{history = LQueue#lqueue{queue = NewQ}}, StanzaId}. + -spec send_history(jid(), [lqueue_elem()], state()) -> ok. send_history(JID, History, StateData) -> lists:foreach( @@ -4285,7 +4333,7 @@ maybe_forget_room(StateData) -> make_disco_info(_From, StateData) -> Config = StateData#state.config, Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, - ?NS_COMMANDS, ?NS_MESSAGE_MODERATE, + ?NS_COMMANDS, ?NS_MESSAGE_MODERATE, ?NS_MESSAGE_RETRACT, ?CONFIG_OPT_TO_FEATURE((Config#config.public), <<"muc_public">>, <<"muc_hidden">>), ?CONFIG_OPT_TO_FEATURE((Config#config.persistent), From 70cbdd11176aa6dade390e40b90bd5aea862e3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 17 Apr 2023 16:57:54 +0200 Subject: [PATCH 0084/1302] Allow to update state from muc_process_iq hook --- src/mod_muc_room.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 3093de248..f66e32036 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -474,6 +474,8 @@ normal_state({route, <<"">>, [StateData]) of ignore -> {next_state, normal_state, StateData}; + {ignore, StateData2} -> + {next_state, normal_state, StateData2}; #iq{type = T} = IQRes when T == error; T == result -> ejabberd_router:route(IQRes), {next_state, normal_state, StateData}; From c942c31e38df5682a379e1cb5d916f8aeca367cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 17 Apr 2023 16:59:42 +0200 Subject: [PATCH 0085/1302] Add mod_muc_rtbl This implements Real-time blocklists for XMPP (xmppbl.org). Closes #4017 --- src/mod_muc_rtbl.erl | 227 +++++++++++++++++++++++++++++++++++++++ src/mod_muc_rtbl_opt.erl | 20 ++++ 2 files changed, 247 insertions(+) create mode 100644 src/mod_muc_rtbl.erl create mode 100644 src/mod_muc_rtbl_opt.erl diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl new file mode 100644 index 000000000..82fad1fb1 --- /dev/null +++ b/src/mod_muc_rtbl.erl @@ -0,0 +1,227 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_muc_rtbl.erl +%%% Author : Paweł Chmielowski +%%% Purpose : +%%% Created : 17 kwi 2023 by Paweł Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(mod_muc_rtbl). +-author("pawel@process-one.net"). + +-behaviour(gen_mod). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). +-include("mod_muc_room.hrl"). + +%% API +-export([start/2, stop/1, mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). +-export([pubsub_event_handler/1, muc_presence_filter/3, muc_process_iq/2]). + +-record(muc_rtbl, {host_id, blank = blank}). + +start(Host, _Opts) -> + ejabberd_mnesia:create(?MODULE, muc_rtbl, + [{ram_copies, [node()]}, + {local_content, true}, + {attributes, record_info(fields, muc_rtbl)}, + {type, set}]), + ejabberd_hooks:add(local_send_to_resource_hook, Host, + ?MODULE, pubsub_event_handler, 50), + ejabberd_hooks:add(muc_filter_presence, Host, + ?MODULE, muc_presence_filter, 50), + ejabberd_hooks:add(muc_process_iq, Host, + ?MODULE, muc_process_iq, 50), + request_initial_items(Host). + +stop(Host) -> + Jid = service_jid(Host), + IQ = #iq{type = set, from = Jid, to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), + sub_els = [ + #pubsub{unsubscribe = + #ps_unsubscribe{jid = Jid, node = mod_muc_rtbl_opt:rtbl_node(Host)}}]}, + ejabberd_router:route_iq(IQ, fun parse_subscribe_result/1). + +request_initial_items(Host) -> + IQ = #iq{type = get, from = service_jid(Host), + to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), + sub_els = [ + #pubsub{items = #ps_items{node = mod_muc_rtbl_opt:rtbl_node(Host)}}]}, + ejabberd_router:route_iq(IQ, fun parse_initial_items/1). + +parse_initial_items(#iq{type = error} = IQ) -> + ?WARNING_MSG("Fetching initial list failed: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]); +parse_initial_items(#iq{from = From, to = #jid{lserver = Host} = To, type = result} = IQ) -> + case xmpp:get_subtag(IQ, #pubsub{}) of + #pubsub{items = #ps_items{node = Node, items = Items}} -> + Added = lists:foldl( + fun(#ps_item{id = ID}, Acc) -> + mnesia:dirty_write(#muc_rtbl{host_id = {Host, ID}}), + maps:put(ID, true, Acc) + end, #{}, Items), + SubIQ = #iq{type = set, from = To, to = From, + sub_els = [ + #pubsub{subscribe = #ps_subscribe{jid = To, node = Node}}]}, + ejabberd_router:route_iq(SubIQ, fun parse_subscribe_result/1), + notify_rooms(Host, Added); + _ -> + ?WARNING_MSG("Fetching initial list failed: invalid result payload", []) + end. + +parse_subscribe_result(#iq{type = error} = IQ) -> + ?WARNING_MSG("Subscription error: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]); +parse_subscribe_result(_) -> + ok. + +pubsub_event_handler(#message{from = #jid{luser = <<>>, lserver = SServer}, + to = #jid{luser = <<>>, lserver = Server, + lresource = <<"rtbl-", _/binary>>}} = Msg) -> + + SServer2 = mod_muc_rtbl_opt:rtbl_server(Server), + SNode = mod_muc_rtbl_opt:rtbl_node(Server), + if SServer == SServer2 -> + case xmpp:get_subtag(Msg, #ps_event{}) of + #ps_event{items = #ps_items{node = Node, retract = Retract}} when Node == SNode, + is_binary(Retract) -> + mnesia:dirty_delete(muc_rtbl, {Server, Retract}); + #ps_event{items = #ps_items{node = Node, items = Items}} when Node == SNode -> + Added = lists:foldl( + fun(#ps_item{id = ID}, Acc) -> + mnesia:dirty_write(#muc_rtbl{host_id = {Server, ID}}), + maps:put(ID, true, Acc) + end, #{}, Items), + case maps:size(Added) of + 0 -> ok; + _ -> notify_rooms(Server, Added) + end; + _ -> + ok + end; + true -> + ok + end, + stop; +pubsub_event_handler(_) -> + ok. + +muc_presence_filter(#presence{from = #jid{lserver = Server} = From, lang = Lang} = Packet, _State, _Nick) -> + Blocked = + case mnesia:dirty_read(muc_rtbl, {Server, sha256(Server)}) of + [] -> + JIDs = sha256(jid:encode(jid:tolower(jid:remove_resource(From)))), + case mnesia:dirty_read(muc_rtbl, {Server, JIDs}) of + [] -> false; + _ -> true + end; + _ -> true + end, + case Blocked of + false -> Packet; + _ -> + ErrText = ?T("You have been banned from this room"), + Err = xmpp:err_forbidden(ErrText, Lang), + ejabberd_router:route_error(Packet, Err), + drop + end. + +muc_process_iq(#iq{type = set, sub_els = [{rtbl_update, Items}]}, #state{users = Users} = State0) -> + {NewState, _} = + maps:fold( + fun(_, #user{role = moderator}, {State, HostHashes}) -> + {State, HostHashes}; + ({_, S, _} = LJid, #user{jid = JID}, {State, HostHashes}) -> + {Ban, HH2} = + case maps:find(S, HostHashes) of + {ok, Sha} -> + {maps:is_key(Sha, Items), HostHashes}; + _ -> + Sha = sha256(S), + {maps:is_key(Sha, Items), maps:put(S, Sha, HostHashes)} + end, + Ban2 = + case Ban of + false -> + Sha2 = sha256(jid:encode(jid:remove_resource(LJid))), + maps:is_key(Sha2, Items); + _ -> + true + end, + case Ban2 of + true -> + {_, _, State2} = mod_muc_room:handle_event({process_item_change, + {JID, role, none, <<"Banned by RTBL">>}, + undefined}, + normal_state, State), + {State2, HH2}; + _ -> + {State, HH2} + end + end, {State0, #{}}, Users), + {stop, {ignore, NewState}}; +muc_process_iq(IQ, _State) -> + IQ. + +sha256(Data) -> + Bin = crypto:hash(sha256, Data), + str:to_hexlist(Bin). + +notify_rooms(Host, Items) -> + IQ = #iq{type = set, to = jid:make(Host), sub_els = [{rtbl_update, Items}]}, + lists:foreach( + fun(CHost) -> + lists:foreach( + fun({_, _, Pid}) -> + mod_muc_room:route(Pid, IQ) + end, mod_muc:get_online_rooms(CHost)) + end, mod_muc_admin:find_hosts(Host)). + + +service_jid(Host) -> + jid:make(<<>>, Host, <<"rtbl-", (ejabberd_cluster:node_id())/binary>>). + +mod_opt_type(rtbl_server) -> + econf:domain(); +mod_opt_type(rtbl_node) -> + econf:non_empty(econf:binary()). + +mod_options(_Host) -> + [{rtbl_server, <<"xmppbl.org">>}, + {rtbl_node, <<"muc_bans_sha256">>}]. + +mod_doc() -> + #{desc => + [?T("This module implement Real-time blocklists for MUC rooms."), "", + ?T("It works by observing remote pubsub node conforming with " + "specification described in https://xmppbl.org/.")], + opts => + [{rtbl_server, + #{value => ?T("Domain"), + desc => + ?T("Domain of xmpp server that serves block list. " + "The default value is 'xmppbl.org'")}}, + {rtbl_node, + #{value => "PubsubNodeName", + desc => + ?T("Name of pubsub node that should be used to track blocked users. " + "The default value is 'muc_bans_sha256'.")}}]}. + +depends(_, _) -> + [{mod_muc, hard}, {mod_pubsub, soft}]. diff --git a/src/mod_muc_rtbl_opt.erl b/src/mod_muc_rtbl_opt.erl new file mode 100644 index 000000000..b9394bd39 --- /dev/null +++ b/src/mod_muc_rtbl_opt.erl @@ -0,0 +1,20 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_muc_rtbl_opt). + +-export([rtbl_node/1]). +-export([rtbl_server/1]). + +-spec rtbl_node(gen_mod:opts() | global | binary()) -> binary(). +rtbl_node(Opts) when is_map(Opts) -> + gen_mod:get_opt(rtbl_node, Opts); +rtbl_node(Host) -> + gen_mod:get_module_opt(Host, mod_muc_rtbl, rtbl_node). + +-spec rtbl_server(gen_mod:opts() | global | binary()) -> binary(). +rtbl_server(Opts) when is_map(Opts) -> + gen_mod:get_opt(rtbl_server, Opts); +rtbl_server(Host) -> + gen_mod:get_module_opt(Host, mod_muc_rtbl, rtbl_server). + From 98d348893b6e9cfae2b2f8dc33bf334f61647dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 17 Apr 2023 17:07:59 +0200 Subject: [PATCH 0086/1302] Make mod_muc_rtbl notify only local node rooms --- src/mod_muc_rtbl.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 82fad1fb1..f3bdd8386 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -188,8 +188,10 @@ notify_rooms(Host, Items) -> lists:foreach( fun(CHost) -> lists:foreach( - fun({_, _, Pid}) -> - mod_muc_room:route(Pid, IQ) + fun({_, _, Pid}) when node(Pid) == node() -> + mod_muc_room:route(Pid, IQ); + (_) -> + ok end, mod_muc:get_online_rooms(CHost)) end, mod_muc_admin:find_hosts(Host)). From f5b6909cca792510acb3c3b69e4a5f4fec45a04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 17 Apr 2023 18:03:21 +0200 Subject: [PATCH 0087/1302] Unregister hooks on stop in mod_muc_rbtl --- src/mod_muc_rtbl.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index f3bdd8386..7454387a1 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -53,6 +53,12 @@ start(Host, _Opts) -> request_initial_items(Host). stop(Host) -> + ejabberd_hooks:delete(local_send_to_resource_hook, Host, + ?MODULE, pubsub_event_handler, 50), + ejabberd_hooks:delete(muc_filter_presence, Host, + ?MODULE, muc_presence_filter, 50), + ejabberd_hooks:delete(muc_process_iq, Host, + ?MODULE, muc_process_iq, 50), Jid = service_jid(Host), IQ = #iq{type = set, from = Jid, to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), sub_els = [ From 5b695766ae2895f615a3fbea61158807021df585 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 18:43:11 +0200 Subject: [PATCH 0088/1302] Mention what ejabberd version first supports rtbl --- src/mod_muc_rtbl.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 7454387a1..d5fe3bbfd 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -218,7 +218,8 @@ mod_doc() -> #{desc => [?T("This module implement Real-time blocklists for MUC rooms."), "", ?T("It works by observing remote pubsub node conforming with " - "specification described in https://xmppbl.org/.")], + "specification described in https://xmppbl.org/."), "", + ?T("This module is available since ejabberd 23.04.")], opts => [{rtbl_server, #{value => ?T("Domain"), From ec7ff88ddaaa78efd1253a750770e3135cf296a7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 11:10:26 +0200 Subject: [PATCH 0089/1302] =?UTF-8?q?Update=20Portuguese=20translation=20(?= =?UTF-8?q?thanks=20to=20Silv=C3=A9rio=20Santos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/ca.msg | 2 -- priv/msgs/de.msg | 2 -- priv/msgs/el.msg | 2 -- priv/msgs/es.msg | 2 -- priv/msgs/he.msg | 1 - priv/msgs/id.msg | 1 - priv/msgs/pt-br.msg | 2 -- priv/msgs/pt.msg | 23 ++++++++++++++++++++--- priv/msgs/sq.msg | 1 - priv/msgs/zh.msg | 2 -- 10 files changed, 20 insertions(+), 18 deletions(-) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 4e6c8d864..821da7920 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -398,7 +398,6 @@ {"Password:","Contrasenya:"}. {"Path to Dir","Ruta al directori"}. {"Path to File","Ruta al fitxer"}. -{"Payload type","Tipus de payload"}. {"Pending","Pendent"}. {"Period: ","Període: "}. {"Persist items to storage","Persistir elements al guardar"}. @@ -564,7 +563,6 @@ {"The sender of the last received message","Qui ha enviat l'ultim missatge rebut"}. {"The stanza MUST contain only one element, one element, or one element","El paquet DEU contindre només un element , un element , o un element "}. {"The subscription identifier associated with the subscription request","L'identificador de subscripció associat amb la petició de subscripció"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","El tipus de dades al node, usualment especificat pel namespace del payload (si n'hi ha)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","La URL de uns transformació XSL que pot ser aplicada als payloads per a generar un element apropiat de contingut de missatge."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","La URL de una transformació XSL que pot ser aplicada al format de payload per a generar un resultat valid de Data Forms, que el client puga mostrar usant un métode generic de Data Forms"}. {"There was an error changing the password: ","Hi ha hagut un error canviant la contrasenya: "}. diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index b08b43e7d..56dfce48c 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -396,7 +396,6 @@ {"Password:","Passwort:"}. {"Path to Dir","Pfad zum Verzeichnis"}. {"Path to File","Pfad zur Datei"}. -{"Payload type","Nutzdatentyp"}. {"Pending","Ausstehend"}. {"Period: ","Zeitraum: "}. {"Persist items to storage","Items dauerhaft speichern"}. @@ -562,7 +561,6 @@ {"The sender of the last received message","Der Absender der letzten erhaltenen Nachricht"}. {"The stanza MUST contain only one element, one element, or one element","Das Stanza darf nur ein -Element, ein -Element oder ein -Element enthalten"}. {"The subscription identifier associated with the subscription request","Die mit der Abonnement-Anforderung verknüpfte Abonnement-Bezeichnung"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","Die Art der Knotendaten, üblicherweise vom Namensraum der Nutzdaten angegeben (gegebenenfalls)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","Die URL einer XSL-Transformation welche auf Nutzdaten angewendet werden kann, um ein geeignetes Nachrichtenkörper-Element zu generieren."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","Die URL einer XSL-Transformation welche auf das Nutzdaten-Format angewendet werden kann, um ein gültiges Data Forms-Ergebnis zu generieren das der Client mit Hilfe einer generischen Data Forms-Rendering-Engine anzeigen könnte"}. {"There was an error changing the password: ","Es trat ein Fehler beim Ändern des Passwortes auf: "}. diff --git a/priv/msgs/el.msg b/priv/msgs/el.msg index ec07d58c1..1b6f2d1df 100644 --- a/priv/msgs/el.msg +++ b/priv/msgs/el.msg @@ -377,7 +377,6 @@ {"Password:","Κωδικός πρόσβασης:"}. {"Path to Dir","Τοποθεσία κατάλογου αρχείων"}. {"Path to File","Τοποθεσία Αρχείου"}. -{"Payload type","Τύπος φόρτου εργασιών"}. {"Pending","Εκκρεμεί"}. {"Period: ","Περίοδος: "}. {"Persist items to storage","Μόνιμη αποθήκευση στοιχείων"}. @@ -538,7 +537,6 @@ {"The sender of the last received message","Ο αποστολέας του τελευταίου εισερχομένου μηνύματος"}. {"The stanza MUST contain only one element, one element, or one element","Η stanza ΠΡΕΠΕΙ να περιέχει μόνο ένα στοιχείο , ένα στοιχείο ή ένα στοιχείο "}. {"The subscription identifier associated with the subscription request","Το αναγνωριστικό συνδρομής συσχετίστηκε με το αίτημα συνδρομής"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","Ο τύπος των δεδομένων του κόμβου συνήθως προσδιορίζεται από το namespace του φόρτου εργασιών (αν υπάρχουν)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","Το URL ενός μετασχηματισμού XSL το οποίο μπορεί να εφαρμοστεί σε φόρτους εργασίας για να παραχθεί το κατάλληλο στοιχείο του σώματος του μηνύματος."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","Το URL ενός μετασχηματισμού XSL, το οποίο μπορεί να εφαρμοστεί στους τύπους φόρτου εργασίας για να παραχθεί έγκυρο αποτέλεσμα Data Forms, τέτοιο που ο πελάτης μπορεί να εμφανίσει, χρησιμοποιώντας μια ευρείας χρήσης μηχανή επεξεργασίας Data Forms"}. {"There was an error changing the password: ","Παρουσιάστηκε σφάλμα κατά την αλλαγή του κωδικού πρόσβασης: "}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index f3cebdb44..abbaa9bcb 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -398,7 +398,6 @@ {"Password:","Contraseña:"}. {"Path to Dir","Ruta al directorio"}. {"Path to File","Ruta al fichero"}. -{"Payload type","Tipo de payload"}. {"Pending","Pendiente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Persistir elementos al almacenar"}. @@ -564,7 +563,6 @@ {"The sender of the last received message","El emisor del último mensaje recibido"}. {"The stanza MUST contain only one element, one element, or one element","El paquete DEBE contener solo un elemento , un elemento , o un elemento "}. {"The subscription identifier associated with the subscription request","El identificador de suscripción asociado con la petición de suscripción"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","El tipo de datos del nodo, usualmente especificado por el namespace del payload (en caso de haberlo)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","La URL de una transformación XSL que puede aplicarse a payloads para generar un elemento de contenido del mensaje apropiado."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","La URL de una transformación XSL que puede aplicarse al formato de payload para generar un resultado de Formulario de Datos válido, que el cliente pueda mostrar usando un mecanismo de dibujado genérico de Formulario de Datos"}. {"There was an error changing the password: ","Hubo uno error al cambiar la contaseña: "}. diff --git a/priv/msgs/he.msg b/priv/msgs/he.msg index 5a4926a88..60c863a2f 100644 --- a/priv/msgs/he.msg +++ b/priv/msgs/he.msg @@ -337,7 +337,6 @@ {"The collections with which a node is affiliated","האוספים עמם צומת מסונף"}. {"The password is too weak","הסיסמה חלשה מדי"}. {"the password is","הסיסמה היא"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","סוג מידע ממסר, לרוב מצוין לפי מרחב־שמות של מטען הייעוד (אם בכלל)"}. {"There was an error creating the account: ","אירעה שגיאה ביצירת החשבון: "}. {"There was an error deleting the account: ","אירעה שגיאה במחיקת החשבון: "}. {"This room is not anonymous","חדר זה אינו אנונימי"}. diff --git a/priv/msgs/id.msg b/priv/msgs/id.msg index 08a3df2b4..1e6339279 100644 --- a/priv/msgs/id.msg +++ b/priv/msgs/id.msg @@ -289,7 +289,6 @@ {"Password","Sandi"}. {"Path to Dir","Jalur ke Dir"}. {"Path to File","Jalur ke File"}. -{"Payload type","Tipe payload"}. {"Pending","Tertunda"}. {"Period: ","Periode: "}. {"Persist items to storage","Pertahankan item ke penyimpanan"}. diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index bd295de2e..179688b78 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -398,7 +398,6 @@ {"Password:","Senha:"}. {"Path to Dir","Caminho para o diretório"}. {"Path to File","Caminho do arquivo"}. -{"Payload type","Tipo da carga útil"}. {"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. @@ -564,7 +563,6 @@ {"The sender of the last received message","O remetente da última mensagem que foi recebida"}. {"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. {"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","O tipo dos dados do nó, normalmente definido pelo espaço dos nomes da carga útil (caso haja)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","O URL da transformação XSL que pode ser aplicada nas cargas úteis para gerar um elemento apropriado no corpo da mensagem."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","A URL de uma transformação XSL que pode ser aplicada ao formato de carga útil para gerar um Formulário de Dados válido onde o cliente possa exibir usando um mecanismo genérico de renderização do Formulários de Dados"}. {"There was an error changing the password: ","Houve um erro ao alterar a senha: "}. diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index 99a8de3a3..b55d78a0b 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -3,7 +3,7 @@ %% To improve translations please read: %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ -{" (Add * to the end of field to match substring)"," (Adicione * no final do campo para combinar com a substring)"}. +{" (Add * to the end of field to match substring)"," (Adicione * no final do campo para combinar com a sub-cadeia)"}. {" has set the subject to: "," colocou o tópico: "}. {"# participants","# participantes"}. {"A description of the node","Uma descrição do nó"}. @@ -79,6 +79,7 @@ {"Changing role/affiliation is not allowed","Não é permitida a alteração da função/afiliação"}. {"Channel already exists","O canal já existe"}. {"Channel does not exist","O canal não existe"}. +{"Channel JID","Canal JID"}. {"Channels","Canais"}. {"Characters not allowed:","Caracteres não aceitos:"}. {"Chatroom configuration modified","Configuração da sala de bate-papo modificada"}. @@ -98,6 +99,7 @@ {"Configuration","Configuração"}. {"Connected Resources:","Recursos conectados:"}. {"Contact Addresses (normally, room owner or owners)","Endereços de contato (normalmente, o proprietário ou os proprietários da sala)"}. +{"Contrib Modules","Módulos contrib"}. {"Country","País"}. {"CPU Time:","Tempo da CPU:"}. {"Current Discussion Topic","Assunto em discussão"}. @@ -172,6 +174,8 @@ {"Full List of Room Admins","Lista completa dos administradores das salas"}. {"Full List of Room Owners","Lista completa dos proprietários das salas"}. {"Full Name","Nome completo"}. +{"Get List of Online Users","Obter a lista de utilizadores online"}. +{"Get List of Registered Users","Obter a lista de utilizadores registados"}. {"Get Number of Online Users","Obter quantidade de utilizadores online"}. {"Get Number of Registered Users","Obter quantidade de utilizadores registados"}. {"Get Pending","Obter os pendentes"}. @@ -213,6 +217,8 @@ {"Incorrect value of 'action' attribute","Valor incorreto do atributo 'action'"}. {"Incorrect value of 'action' in data form","Valor incorreto de 'action' no formulário de dados"}. {"Incorrect value of 'path' in data form","Valor incorreto de 'path' no formulário de dados"}. +{"Installed Modules:","Módulos instalados:"}. +{"Install","Instalar"}. {"Insufficient privilege","Privilégio insuficiente"}. {"Internal server error","Erro interno do servidor"}. {"Invalid 'from' attribute in forwarded message","Atributo 'from' inválido na mensagem reenviada"}. @@ -229,6 +235,8 @@ {"January","Janeiro"}. {"JID normalization denied by service policy","Normalização JID negada por causa da política de serviços"}. {"JID normalization failed","A normalização JID falhou"}. +{"Joined MIX channels of ~ts","Entrou no canais MIX do ~ts"}. +{"Joined MIX channels:","Uniu-se aos canais MIX:"}. {"joins the room","Entrar na sala"}. {"July","Julho"}. {"June","Junho"}. @@ -349,6 +357,7 @@ {"Number of registered users","Quantidade de utilizadores registados"}. {"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Quantidade de segundos após limpar automaticamente os itens ou `max` para nenhum limite específico que não seja um servidor imposto máximo"}. {"Occupants are allowed to invite others","As pessoas estão autorizadas a convidar outras pessoas"}. +{"Occupants are allowed to query others","Os ocupantes estão autorizados a consultar os outros"}. {"Occupants May Change the Subject","As pessoas talvez possam alterar o assunto"}. {"October","Outubro"}. {"Offline Messages","Mensagens offline"}. @@ -375,11 +384,13 @@ {"Only those on a whitelist may subscribe and retrieve items","Apenas aqueles presentes numa lista branca podem se inscrever e recuperar os itens"}. {"Organization Name","Nome da organização"}. {"Organization Unit","Unidade da organização"}. +{"Other Modules Available:","Outros módulos disponíveis:"}. {"Outgoing s2s Connections","Conexões s2s de Saída"}. {"Outgoing s2s Connections:","Saída das conexões s2s:"}. {"Owner privileges required","São necessários privilégios de dono"}. {"Packet relay is denied by service policy","A retransmissão de pacote é negada por causa da política de serviço"}. {"Packet","Pacote"}. +{"Participant ID","ID do participante"}. {"Participant","Participante"}. {"Password Verification:","Verificação da Palavra-passe:"}. {"Password Verification","Verificação de Palavra-passe"}. @@ -387,7 +398,6 @@ {"Password:","Palavra-chave:"}. {"Path to Dir","Caminho para o directório"}. {"Path to File","Caminho do ficheiro"}. -{"Payload type","Tipo da carga útil"}. {"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. @@ -482,6 +492,7 @@ {"Shut Down Service","Parar Serviço"}. {"SOCKS5 Bytestreams","Bytestreams SOCKS5"}. {"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alguns clientes XMPP podem armazenar a sua palavra-passe no seu computador, só faça isso no seu computador particular por questões de segurança."}. +{"Sources Specs:","Especificações das fontes:"}. {"Specify the access model","Especificar os modelos de acesso"}. {"Specify the event message type","Especificar o tipo de mensagem para o evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. @@ -527,6 +538,8 @@ {"The JIDs of those to contact with questions","Os JIDs daqueles para entrar em contato com perguntas"}. {"The JIDs of those with an affiliation of owner","Os JIDs daqueles com uma afiliação de proprietário"}. {"The JIDs of those with an affiliation of publisher","Os JIDs daqueles com uma afiliação de editor"}. +{"The list of all online users","A lista de todos os utilizadores online"}. +{"The list of all users","A lista de todos os utilizadores"}. {"The list of JIDs that may associate leaf nodes with a collection","A lista dos JIDs que podem associar as páginas dos nós numa coleção"}. {"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","A quantidade máxima de nós relacionados que podem ser associados a uma coleção ou `máximo` para nenhum limite específico que não seja um servidor imposto no máximo"}. {"The minimum number of milliseconds between sending any two notification digests","A quantidade mínima de milissegundos entre o envio do resumo das duas notificações"}. @@ -550,7 +563,6 @@ {"The sender of the last received message","O remetente da última mensagem que foi recebida"}. {"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. {"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","O tipo dos dados do nó, normalmente definido pelo espaço dos nomes da carga útil (caso haja)"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","O URL da transformação XSL que pode ser aplicada nas cargas úteis para gerar um elemento apropriado no corpo da mensagem."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","A URL de uma transformação XSL que pode ser aplicada ao formato de carga útil para gerar um Formulário de Dados válido onde o cliente possa exibir usando um mecanismo genérico de renderização do Formulários de Dados"}. {"There was an error changing the password: ","Houve um erro ao alterar a palavra-passe: "}. @@ -591,6 +603,7 @@ {"Unauthorized","Não Autorizado"}. {"Unexpected action","Ação inesperada"}. {"Unexpected error condition: ~p","Condição de erro inesperada: ~p"}. +{"Uninstall","Desinstalar"}. {"Unregister an XMPP account","Excluir uma conta XMPP"}. {"Unregister","Deletar registo"}. {"Unselect All","Desmarcar todos"}. @@ -601,7 +614,10 @@ {"Update ~p","Atualizar ~p"}. {"Update plan","Plano de atualização"}. {"Update script","Script de atualização"}. +{"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. +{"Update Specs","Atualizar as especificações"}. {"Update","Actualizar"}. +{"Upgrade","Atualização"}. {"Uptime:","Tempo de atividade:"}. {"URL for Archived Discussion Logs","A URL para o arquivamento dos registos da discussão"}. {"User already exists","Utilizador já existe"}. @@ -624,6 +640,7 @@ {"Value of '~s' should be integer","Valor de '~s' deveria ser um inteiro"}. {"Value 'set' of 'type' attribute is not allowed","Valor 'set' não permitido para atributo 'type'"}. {"vCard User Search","Busca de Utilizador vCard"}. +{"View joined MIX channels","Exibir os canais MIX aderidos"}. {"View Queue","Exibir a fila"}. {"View Roster","Ver a lista"}. {"Virtual Hosts","Hosts virtuais"}. diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index e6305d935..c5efe2023 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -253,7 +253,6 @@ {"Password:","Fjalëkalim:"}. {"Path to Dir","Shteg për te Drejtori"}. {"Path to File","Shteg për te Kartelë"}. -{"Payload type","Lloj ngarkese"}. {"Pending","Pezull"}. {"Period: ","Periudhë: "}. {"Ping","Ping"}. diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 8eb4f244b..0320af5ac 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -398,7 +398,6 @@ {"Password:","密码:"}. {"Path to Dir","目录的路径"}. {"Path to File","文件路径"}. -{"Payload type","有效载荷类型"}. {"Pending","挂起"}. {"Period: ","持续时间: "}. {"Persist items to storage","持久化内容条目"}. @@ -564,7 +563,6 @@ {"The sender of the last received message","最后收到的消息的发送者"}. {"The stanza MUST contain only one element, one element, or one element","本节必须只含一个 元素, 元素,或 元素"}. {"The subscription identifier associated with the subscription request","与订阅请求关联的订阅标识符"}. -{"The type of node data, usually specified by the namespace of the payload (if any)","节点数据的类型, 如果有, 通常由有效负载的名称空间指定"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL转换的URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL转换的URL, 可以将其应用于有效负载格式, 以生成有效的数据表单结果, 客户端可以使用通用数据表单呈现引擎来显示该结果"}. {"There was an error changing the password: ","修改密码出错: "}. From e1a8980d6cd16d7db60c946c73d28964ecc5b7eb Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 12:19:37 +0200 Subject: [PATCH 0090/1302] Container: no need to specify captcha_url, auto may be enough in most cases --- CONTAINER.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 99d0b9475..9b66c4a3e 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -153,10 +153,9 @@ Now update your ejabberd configuration file, for example: docker exec -it ejabberd vi conf/ejabberd.yml ``` -and add the required options: -``` +and add this option: +```yaml captcha_cmd: /opt/ejabberd-22.04/lib/captcha.sh -captcha_url: https://localhost:5443/captcha ``` Finally, reload the configuration file or restart the container: @@ -164,6 +163,18 @@ Finally, reload the configuration file or restart the container: docker exec ejabberd ejabberdctl reload_config ``` +If the CAPTCHA image is not visible, there may be a problem generating it +(the ejabberd log file may show some error message); +or the image URL may not be correctly detected by ejabberd, +in that case you can set the correct URL manually, for example: +```yaml +captcha_url: https://localhost:5443/captcha +``` + +For more details about CAPTCHA options, please check the +[CAPTCHA](https://docs.ejabberd.im/admin/configuration/basic/#captcha) +documentation section. + Advanced Container Configuration ================================ From c271d73dbd0ad771784b0117b82bd783a1ef9923 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 12:20:40 +0200 Subject: [PATCH 0091/1302] CI: Update Erlang/OTP to 26.0-rc3 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2bdec91d7..575070886 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25.3', '26.0-rc2'] + otp: ['20.0', '25.3', '26.0-rc3'] runs-on: ubuntu-20.04 services: redis: @@ -149,7 +149,7 @@ jobs: - run: make options - run: make xref - run: make dialyzer - if: matrix.otp != '26.0-rc2' + if: matrix.otp != '26.0-rc3' - name: Check Production Release run: | From d717ffd1a02928b718e9ec4a82709cffa6048d6f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 12:34:30 +0200 Subject: [PATCH 0092/1302] Update version notes of options and commands --- src/ejabberd_options_doc.erl | 2 +- src/mod_admin_update_sql.erl | 1 + src/mod_muc_admin.erl | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index cbf98c88d..0e125e950 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -478,7 +478,7 @@ doc() -> desc => ?T("Deprecated. Use _`captcha_url`_ instead.")}}, {captcha_url, #{value => ?T("URL | auto | undefined"), - note => "improved in 23.xx", + note => "improved in 23.04", desc => ?T("An URL where http://../basic/#captcha[CAPTCHA] requests should be sent. NOTE: you need " "to configure 'request_handlers' for 'ejabberd_http' listener " diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index db7730e95..74d30b3e3 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -66,6 +66,7 @@ depends(_Host, _Opts) -> get_commands_spec() -> [#ejabberd_commands{name = update_sql, tags = [sql], desc = "Convert MS SQL, MySQL or PostgreSQL DB to the new format", + note = "improved in 23.04", module = ?MODULE, function = update_sql, args = [], args_example = [], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index ed7bc3b5d..234f065d3 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -413,6 +413,7 @@ get_commands_spec() -> result = {affiliation, atom}}, #ejabberd_commands{name = get_room_history, tags = [muc_room], desc = "Get history of messages stored inside MUC room state", + note = "added in 23.04", module = ?MODULE, function = get_room_history, args_desc = ["Room name", "MUC service"], args_example = ["room1", "muc.example.com"], From bf5de81b246f5f44d82fcd93bc012856d83d7013 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:14:13 +0000 Subject: [PATCH 0093/1302] Bump ex_doc from 0.29.3 to 0.29.4 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.29.3 to 0.29.4. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.29.3...v0.29.4) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 4621841cd..894e10c8a 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.29.3", "f07444bcafb302db86e4f02d8bbcd82f2e881a0dcf4f3e4740e4b8128b9353f7", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3dc6787d7b08801ec3b51e9bd26be5e8826fbf1a17e92d1ebc252e1a1c75bfe1"}, + "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, From 1ca126381b0657beadb43df2d0ff1f8e89a114a3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Apr 2023 12:38:57 +0200 Subject: [PATCH 0094/1302] Update man --- man/ejabberd.yml.5 | 89 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 5b29ef2d6..017d9e960 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 01/16/2023 +.\" Date: 04/17/2023 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "01/16/2023" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "04/17/2023" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,7 +82,7 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/23\&.01/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/23\&.04/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" @@ -566,7 +566,13 @@ A maximum number of items (not memory!) in cache\&. The rule of thumb, for all t .RS 4 Full path to a script that generates CAPTCHA -images\&. @VERSION@ is replaced with ejabberd version number in XX\&.YY format\&. @SEMVER@ is replaced with ejabberd version number in semver format when compiled with Elixir\(cqs mix, or XX\&.YY format otherwise\&. Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. +images\&. +\fI@VERSION@\fR +is replaced with ejabberd version number in +\fIXX\&.YY\fR +format\&. +\fI@SEMVER@\fR +is replaced with ejabberd version number in semver format when compiled with Elixir\(cqs mix, or XX\&.YY format otherwise\&. Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. .sp When using the ejabberd installers or container image, the example captcha scripts can be used like this: .sp @@ -595,8 +601,10 @@ CAPTCHA generated images per minute for any given JID\&. The option is intended to protect the server from CAPTCHA DoS\&. The default value is \fIinfinity\fR\&. .RE +.sp +\fINote\fR about the next option: improved in 23\&.04: .PP -\fBcaptcha_url\fR: \fIURL\fR +\fBcaptcha_url\fR: \fIURL | auto | undefined\fR .RS 4 An URL where CAPTCHA @@ -604,7 +612,14 @@ requests should be sent\&. NOTE: you need to configure \fIrequest_handlers\fR for \fIejabberd_http\fR -listener as well\&. There is no default value\&. +listener as well\&. If set to +\fIauto\fR, it builds the URL using a +\fIrequest_handler\fR +already enabled, with encryption if available\&. If set to +\fIundefined\fR, it builds the URL using the deprecated +\fIcaptcha_host\fR ++ /captcha\&. The default value is +\fIauto\fR\&. .RE .PP \fBcertfiles\fR: \fI[Path, \&.\&.\&.]\fR @@ -1100,7 +1115,7 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/23\&.01/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/23\&.04/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The \fInew\fR schema allows to handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1216,7 +1231,7 @@ percents\&. .RS 4 Specify which address families to try, in what order\&. The default is \fI[ipv6, ipv4]\fR -which means it first tries connecting with IPv6, if that fails it tries using IPv4\&.This option is obsolete and irrelevant when using ejabberd 23\&.01 and Erlang/OTP 22, or newer versions of them\&. +which means it first tries connecting with IPv6, if that fails it tries using IPv4\&. This option is obsolete and irrelevant when using ejabberd 23\&.01 and Erlang/OTP 22, or newer versions of them\&. .RE .sp \fINote\fR about the next option: added in 20\&.12: @@ -1660,10 +1675,13 @@ An interval to make a dummy SQL request to keep alive the connections to the dat .PP \fBsql_odbc_driver\fR: \fIPath\fR .RS 4 -Path to the ODBC driver to use to connect to a Microsoft SQL Server database\&. This option is only valid if the +Path to the ODBC driver to use to connect to a Microsoft SQL Server database\&. This option only applies if the \fIsql_type\fR option is set to -\fImssql\fR\&. The default value is: +\fImssql\fR +and +\fIsql_server\fR +is not an ODBC connection string\&. The default value is: \fIlibtdsodbc\&.so\fR .RE .PP @@ -1719,7 +1737,12 @@ if the latter is not set\&. .PP \fBsql_server\fR: \fIHost\fR .RS 4 -A hostname or an IP address of the SQL server\&. The default value is +The hostname or IP address of the SQL server\&. For +\fIsql_type\fR +\fImssql\fR +or +\fIodbc\fR +this can also be an ODBC connection string\&. The default value is \fIlocalhost\fR\&. .RE .sp @@ -1727,7 +1750,7 @@ A hostname or an IP address of the SQL server\&. The default value is .PP \fBsql_ssl\fR: \fItrue | false\fR .RS 4 -Whether to use SSL encrypted connections to the SQL server\&. The option is only available for MySQL and PostgreSQL\&. The default value is +Whether to use SSL encrypted connections to the SQL server\&. The option is only available for MySQL, MS SQL and PostgreSQL\&. The default value is \fIfalse\fR\&. .RE .PP @@ -1738,7 +1761,7 @@ A path to a file with CA root certificates that will be used to verify SQL conne and \fIsql_ssl_verify\fR options are set to -\fItrue\fR\&. There is no default which means certificate verification is disabled\&. +\fItrue\fR\&. There is no default which means certificate verification is disabled\&. This option has no effect for MS SQL\&. .RE .PP \fBsql_ssl_certfile\fR: \fIPath\fR @@ -1746,7 +1769,7 @@ options are set to A path to a certificate file that will be used for SSL connections to the SQL server\&. Implies \fIsql_ssl\fR option is set to -\fItrue\fR\&. There is no default which means ejabberd won\(cqt provide a client certificate to the SQL server\&. +\fItrue\fR\&. There is no default which means ejabberd won\(cqt provide a client certificate to the SQL server\&. This option has no effect for MS SQL\&. .RE .PP \fBsql_ssl_verify\fR: \fItrue | false\fR @@ -1756,7 +1779,7 @@ Whether to verify SSL connection to the SQL server against CA root certificates option\&. Implies \fIsql_ssl\fR option is set to -\fItrue\fR\&. The default value is +\fItrue\fR\&. This option has no effect for MS SQL\&. The default value is \fIfalse\fR\&. .RE .PP @@ -1971,7 +1994,7 @@ ejabberdctl srg\-create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g .RE .SS "mod_admin_update_sql" .sp -This module can be used to update existing SQL database from the default to the new schema\&. Check the section Default and New Schemas for details\&. Please note that only PostgreSQL is supported\&. When the module is loaded use \fIupdate_sql\fR API\&. +This module can be used to update existing SQL database from the default to the new schema\&. Check the section Default and New Schemas for details\&. Please note that only MS SQL, MySQL, and PostgreSQL are supported\&. When the module is loaded use \fIupdate_sql\fR API\&. .sp The module has no options\&. .SS "mod_announce" @@ -3866,7 +3889,7 @@ or \fIsubscribe\fR or both, and \fIauthentication\fR -section with username/password field or certfile pointing to client certifcate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certifcate authentication can be only used with mqtts, mqtt5s, wss, wss5\&. +section with username/password field or certfile pointing to client certificate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certifcate authentication can be only used with mqtts, mqtt5s, wss, wss5\&. .RE .RE .sp @@ -4652,6 +4675,34 @@ or a conference JID is appended to the otherwise\&. There is no default value\&. .RE .RE +.SS "mod_muc_rtbl" +.sp +This module implement Real\-time blocklists for MUC rooms\&. +.sp +It works by observing remote pubsub node conforming with specification described in https://xmppbl\&.org/\&. +.sp +This module is available since ejabberd 23\&.04\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBrtbl_node\fR: \fIPubsubNodeName\fR +.RS 4 +Name of pubsub node that should be used to track blocked users\&. The default value is +\fImuc_bans_sha256\fR\&. +.RE +.PP +\fBrtbl_server\fR: \fIDomain\fR +.RS 4 +Domain of xmpp server that serves block list\&. The default value is +\fIxmppbl\&.org\fR +.RE +.RE .SS "mod_multicast" .sp This module implements a service for XEP\-0033: Extended Stanza Addressing\&. @@ -7697,13 +7748,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 23\&.01\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 23\&.04\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/23\&.01/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/23\&.04/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 06669b12e87a0789174759d6c75a7e500f5180cd Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Apr 2023 11:11:08 +0200 Subject: [PATCH 0095/1302] Update changelog --- CHANGELOG.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1aabd65..12ce1b52b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,87 @@ +# Version 23.04 + +General: +- New `s2s_out_bounce_packet` hook +- Re-allow anonymous connection for connection without client certificates ([#3985](https://github.com/processone/ejabberd/issues/3985)) +- Stop `ejabberd_system_monitor` before stopping node +- `captcha_url` option now accepts `auto` value, and it's the default +- `mod_mam`: Add support for XEP-0425: Message Moderation +- `mod_mam_sql`: Fix problem with results of mam queries using rsm with max and before +- `mod_muc_rtbl`: New module for Real-Time Block List for MUC rooms ([#4017](https://github.com/processone/ejabberd/issues/4017)) +- `mod_roster`: Set roster name from XEP-0172, or the stored one ([#1611](https://github.com/processone/ejabberd/issues/1611)) +- `mod_roster`: Preliminary support to store extra elements in subscription request ([#840](https://github.com/processone/ejabberd/issues/840)) +- `mod_pubsub`: Pubsub xdata fields `max_item/item_expira/children_max` use `max` not `infinity` +- `mod_vcard_xupdate`: Invalidate `vcard_xupdate` cache on all nodes when vcard is updated + +Admin: +- `ext_mod`: Improve support for loading `*.so` files from `ext_mod` dependencies +- Improve output in `gen_html_doc_for_commands` command +- Fix ejabberdctl output formatting ([#3979](https://github.com/processone/ejabberd/issues/3979)) +- Log HTTP handler exceptions + +MUC: +- New command `get_room_history` +- Persist `none` role for outcasts +- Try to populate room history from mam when unhibernating +- Make `mod_muc_room:set_opts` process persistent flag first +- Allow passing affiliations and subscribers to `create_room_with_opts` command +- Store state in db in `mod_muc:create_room()` +- Make subscribers members by default + +SQL schemas: +- Fix a long standing bug in new schema migration +- `update_sql` command: Many improvements in new schema migration +- `update_sql` command: Add support to migrate MySQL too +- Change PostgreSQL SERIAL to BIGSERIAL columns +- Fix minor SQL schema inconsistencies +- Remove unnecessary indexes +- New SQL schema migrate fix + +MS SQL: +- MS SQL schema fixes +- Add `new` schema for MS SQL +- Add MS SQL support for new schema migration +- Minor MS SQL improvements +- Fix MS SQL error caused by `ORDER BY` in subquery + +SQL Tests: +- Add support for running tests on MS SQL +- Add ability to run tests on upgraded DB +- Un-deprecate `ejabberd_config:set_option/2` +- Use python3 to run `extauth.py` for tests +- Correct README for creating test docker MS SQL DB +- Fix TSQLlint warnings in MSSQL test script + +Testing: +- Fix Shellcheck warnings in shell scripts +- Fix Remark-lint warnings +- Fix Prospector and Pylint warnings in test `extauth.py` +- Stop testing ejabberd with Erlang/OTP 19.3, as Github Actions no longer supports ubuntu-18.04 +- Test only with oldest OTP supported (20.0), newest stable (25.3) and bleeding edge (26.0-rc2) +- Upload Common Test logs as artifact in case of failure + +`ecs` container image: +- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 +- Add `tini` as runtime init +- Set `ERLANG_NODE` fixed to `ejabberd@localhost` +- Upload images as artifacts to Github Actions +- Publish tag images automatically to ghcr.io + +`ejabberd` container image: +- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 +- Add `METHOD` to build container using packages ([#3983](https://github.com/processone/ejabberd/issues/3983)) +- Add `tini` as runtime init +- Detect runtime dependencies automatically +- Remove unused Mix stuff: ejabberd script and static COOKIE +- Copy captcha scripts to `/opt/ejabberd-*/lib` like the installers +- Expose only `HOME` volume, it contains all the required subdirs +- ejabberdctl: Don't use `.../releases/COOKIE`, it's no longer included + +Installers: +- make-binaries: Bump versions, e.g. erlang/otp to 25.3 +- make-binaries: Fix building with erlang/otp v25.x +- make-packages: Fix for installers workflow, which didn't find lynx + # Version 23.01 General: From 46f33e5051be058a73e6de745879221ef77522aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 18 Apr 2023 11:22:33 +0200 Subject: [PATCH 0096/1302] Update dependencies --- mix.exs | 2 +- mix.lock | 4 ++-- rebar.config | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.exs b/mix.exs index 7e63787c7..490ec4a49 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "46b6c192d353e60e13a5cf8ed467c5696729b624", override: true}, + {:xmpp, ">= 1.6.2"}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 894e10c8a..bb660f38f 100644 --- a/mix.lock +++ b/mix.lock @@ -23,13 +23,13 @@ "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, "p1_mysql": {:hex, :p1_mysql, "1.0.21", "5972add935e7b1b03d981fa88a0d01e96de357443eaf96ca2fb62e465a717f47", [:rebar3], [], "hexpm", "16f197adb99dab034139c429b256d65948a4057d3e4d553adbe5ce3236c4aabf"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.20", "0231de1a427a561afbea89212a61d2409f1a42696eeca16d0085305aee07717a", [:rebar3], [{:xmpp, "1.6.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "240b40fb3fd7e330fdf1d0beff2db0cb090ae5a6eef08a0a157ebb7251ff361c"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.22", "f2ca59b87e8c5dbbbe84d08e307c21f078384232e8fb78a783dbeffa6d37da28", [:rebar3], [{:xmpp, "1.6.2", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "bbc38d3878c7b58ab86c257a2a2ce1bacbd68a5034ebea2735db6a70c1aa12bc"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.7", "d6bdcf0aa72c927fbe8b779fc4ef1f3916c5450b2ff136c800a7a0361fb1ddff", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3fb1f07aaa630b2276e83d267557d1ceb3d2ce52d1145de71864160210655852"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "1107d05640ccd352613b2867ab24c5649603a470", [ref: "1107d05640ccd352613b2867ab24c5649603a470"]}, + "xmpp": {:hex, :xmpp, "1.6.2", "8045dfea83e8996415b9a5161f685cb97dc3c40c0b9c46763a5eca2408017221", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "db2ee6115961fe159bc2629093797ac4535083176817cdbe2ae186a0ff540fde"}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index cb3200ac7..3eb2914e2 100644 --- a/rebar.config +++ b/rebar.config @@ -62,7 +62,7 @@ {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.21"}}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.20"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.22"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, {if_not_rebar3, %% Needed because modules are not fully migrated to new structure and mix @@ -73,7 +73,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "46b6c192d353e60e13a5cf8ed467c5696729b624"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.6.2"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From bb28265261a0d98ece60e9f8579e28c033419a43 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Apr 2023 11:39:33 +0200 Subject: [PATCH 0097/1302] Set version to 23.04 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 56ca5a64d..b95f22439 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.01` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="8.3 (Erlang/OTP 19.3)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From 5b8ebed81b3513ed52769aa47e07b3213cf07577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 20 Apr 2023 10:55:21 +0200 Subject: [PATCH 0098/1302] Optimize mod_privacy_sql:set_list Previously we always did delete everything and set all entries back, now we check if we need to delete anything and if not insert only missing data. --- src/mod_privacy_sql.erl | 96 ++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index 10ec92e0b..fc5ae6e0f 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -107,16 +107,21 @@ set_lists(#privacy{us = {LUser, LServer}, set_list(LUser, LServer, Name, List) -> RItems = lists:map(fun item_to_raw/1, List), - F = fun () -> - ID = case get_privacy_list_id_t(LUser, LServer, Name) of - {selected, []} -> - add_privacy_list(LUser, LServer, Name), - {selected, [{I}]} = - get_privacy_list_id_t(LUser, LServer, Name), - I; - {selected, [{I}]} -> I - end, - set_privacy_list(ID, RItems) + F = fun() -> + {ID, New} = case get_privacy_list_id_t(LUser, LServer, Name) of + {selected, []} -> + add_privacy_list(LUser, LServer, Name), + {selected, [{I}]} = + get_privacy_list_id_t(LUser, LServer, Name), + {I, true}; + {selected, [{I}]} -> {I, false} + end, + case New of + false -> + set_privacy_list(ID, RItems); + _ -> + set_privacy_list_new(ID, RItems) + end end, transaction(LServer, F). @@ -402,22 +407,63 @@ add_privacy_list(LUser, LServer, Name) -> "server_host=%(LServer)s", "name=%(Name)s"])). -set_privacy_list(ID, RItems) -> - ejabberd_sql:sql_query_t( - ?SQL("delete from privacy_list_data where id=%(ID)d")), +set_privacy_list_new(ID, RItems) -> lists:foreach( - fun({SType, SValue, SAction, Order, MatchAll, MatchIQ, - MatchMessage, MatchPresenceIn, MatchPresenceOut}) -> - ejabberd_sql:sql_query_t( - ?SQL("insert into privacy_list_data(id, t, " - "value, action, ord, match_all, match_iq, " - "match_message, match_presence_in, match_presence_out) " - "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s," - " %(Order)d, %(MatchAll)b, %(MatchIQ)b," - " %(MatchMessage)b, %(MatchPresenceIn)b," - " %(MatchPresenceOut)b)")) - end, - RItems). + fun({SType, SValue, SAction, Order, MatchAll, MatchIQ, + MatchMessage, MatchPresenceIn, MatchPresenceOut}) -> + ejabberd_sql:sql_query_t( + ?SQL("insert into privacy_list_data(id, t, " + "value, action, ord, match_all, match_iq, " + "match_message, match_presence_in, match_presence_out) " + "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s," + " %(Order)d, %(MatchAll)b, %(MatchIQ)b," + " %(MatchMessage)b, %(MatchPresenceIn)b," + " %(MatchPresenceOut)b)")) + end, + RItems). + +set_privacy_list(ID, RItems) -> + case ejabberd_sql:sql_query_t( + ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, " + "@(match_iq)b, @(match_message)b, @(match_presence_in)b, " + "@(match_presence_out)b from privacy_list_data " + "where id=%(ID)d")) of + {selected, ExistingItems} -> + {ToAdd2, ToDelete2} = + lists:foldr( + fun(Value, {ToAdd, ToDelete}) -> + case lists:splitwith(fun(E) -> E /= Value end, ToAdd) of + {_S, []} -> + {ToAdd, [Value | ToDelete]}; + {Pfx, [_ | Rest]} -> + {Pfx ++ Rest, ToDelete} + end + end, {RItems, []}, ExistingItems), + ToAdd3 = + if + ToDelete2 /= [] -> + ejabberd_sql:sql_query_t( + ?SQL("delete from privacy_list_data where id=%(ID)d")), + RItems; + true -> + ToAdd2 + end, + lists:foreach( + fun({SType, SValue, SAction, Order, MatchAll, MatchIQ, + MatchMessage, MatchPresenceIn, MatchPresenceOut}) -> + ejabberd_sql:sql_query_t( + ?SQL("insert into privacy_list_data(id, t, " + "value, action, ord, match_all, match_iq, " + "match_message, match_presence_in, match_presence_out) " + "values (%(ID)d, %(SType)s, %(SValue)s, %(SAction)s," + " %(Order)d, %(MatchAll)b, %(MatchIQ)b," + " %(MatchMessage)b, %(MatchPresenceIn)b," + " %(MatchPresenceOut)b)")) + end, + ToAdd3); + Err -> + Err + end. del_privacy_lists(LUser, LServer) -> case ejabberd_sql:sql_query( From d299b97261ef78c9238317c3e057471f881751a9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Apr 2023 12:04:13 +0200 Subject: [PATCH 0099/1302] Raise Erlang/OTP requirement to 20.0 --- COMPILE.md | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/COMPILE.md b/COMPILE.md index 34709ea2f..c6b10800a 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -19,7 +19,7 @@ To compile ejabberd you need: - GCC - Libexpat ≥ 1.95 - Libyaml ≥ 0.1.4 -- Erlang/OTP ≥ 19.3 +- Erlang/OTP ≥ 20.0 - OpenSSL ≥ 1.0.0 Other optional libraries are: diff --git a/configure.ac b/configure.ac index b95f22439..85761366a 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) -REQUIRE_ERLANG_MIN="8.3 (Erlang/OTP 19.3)" +REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) From 0d3f8c7b9fcaa0f627fbec1227b8b2a21a598208 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Apr 2023 13:34:47 +0200 Subject: [PATCH 0100/1302] Make mod_register_web redirect to page that end with / (#3177) Code copied from ejabberd_web_admin.erl, commit 5ec21438 --- src/mod_register_web.erl | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 03e35cbf4..2370bed44 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -62,12 +62,26 @@ depends(_Host, _Opts) -> %%% HTTP handlers %%%---------------------------------------------------------------------- -process([], #request{method = 'GET', lang = Lang}) -> +process(Path, #request{raw_path = RawPath} = Request) -> + Continue = case Path of + [E] -> + binary:match(E, <<".">>) /= nomatch; + _ -> + false + end, + case Continue orelse binary:at(RawPath, size(RawPath) - 1) == $/ of + true -> + process2(Path, Request); + _ -> + {301, [{<<"Location">>, <>}], <<>>} + end. + +process2([], #request{method = 'GET', lang = Lang}) -> index_page(Lang); -process([<<"register.css">>], +process2([<<"register.css">>], #request{method = 'GET'}) -> serve_css(); -process([Section], +process2([Section], #request{method = 'GET', lang = Lang, host = Host, ip = {Addr, _Port}}) -> Host2 = case ejabberd_router:is_my_host(Host) of @@ -82,7 +96,7 @@ process([Section], <<"change_password">> -> form_changepass_get(Host2, Lang); _ -> {404, [], "Not Found"} end; -process([<<"new">>], +process2([<<"new">>], #request{method = 'POST', q = Q, ip = {Ip, _Port}, lang = Lang, host = _HTTPHost}) -> case form_new_post(Q, Ip) of @@ -97,7 +111,7 @@ process([<<"new">>], translate:translate(Lang, get_error_text(Error))]), {404, [], ErrorText} end; -process([<<"delete">>], +process2([<<"delete">>], #request{method = 'POST', q = Q, lang = Lang, host = _HTTPHost}) -> case form_del_post(Q) of @@ -112,7 +126,7 @@ process([<<"delete">>], end; %% TODO: Currently only the first vhost is usable. The web request record %% should include the host where the POST was sent. -process([<<"change_password">>], +process2([<<"change_password">>], #request{method = 'POST', q = Q, lang = Lang, host = _HTTPHost}) -> case form_changepass_post(Q) of @@ -126,7 +140,7 @@ process([<<"change_password">>], {404, [], ErrorText} end; -process(_Path, _Request) -> +process2(_Path, _Request) -> {404, [], "Not Found"}. %%%---------------------------------------------------------------------- From 2a4a6bec18a1ae76f91a26fd4a3361a766383902 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Apr 2023 13:36:37 +0200 Subject: [PATCH 0101/1302] Add trailing backslash to URLs shown in mod_muc_log disco#info --- src/mod_muc_log.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 9e724de32..ad8d5c7b0 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -97,9 +97,9 @@ get_url(#state{room = Room, host = Host, server_host = ServerHost}) -> URL -> case mod_muc_log_opt:dirname(ServerHost) of room_jid -> - {ok, <>}; + {ok, <>}; room_name -> - {ok, <>} + {ok, <>, $/} end catch error:{module_not_loaded, _, _} -> From c6b295b5a02b86b47e379415dd973bc5a48654e9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Apr 2023 09:40:22 +0200 Subject: [PATCH 0102/1302] Fix typo in the previous commit --- src/mod_muc_log.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index ad8d5c7b0..057e4edcd 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -99,7 +99,7 @@ get_url(#state{room = Room, host = Host, server_host = ServerHost}) -> room_jid -> {ok, <>}; room_name -> - {ok, <>, $/} + {ok, <>} end catch error:{module_not_loaded, _, _} -> From d95a1bac3e79f3318124a874c66811e138caf738 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Apr 2023 11:27:13 +0200 Subject: [PATCH 0103/1302] Use container names to differentiate them; don't force logo height --- CONTAINER.md | 7 +++---- README.md | 16 ++++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 9b66c4a3e..f97749a85 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1,11 +1,10 @@ [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&label=&color=49c0c4)](https://github.com/processone/ejabberd/tags) -[![GitHub Container](https://img.shields.io/github/v/tag/processone/ejabberd?label=container&sort=semver)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) -[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/ejabberd/ecs?label=docker)](https://hub.docker.com/r/ejabberd/ecs/) +[![GitHub Container](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=docker)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) -ejabberd Container -================== +`ejabberd` Container Image +========================== [ejabberd][home] is an open-source, robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], diff --git a/README.md b/README.md index 8faa5d78b..9972df564 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@

- +

- - - - + + + +
@@ -39,8 +38,8 @@ There are several ways to install ejabberd: - Source code: compile yourself, see [COMPILE](COMPILE.md) - Installers from [ProcessOne Download][p1download] or [ejabberd GitHub Releases][releases] (run/deb/rpm for x64 and arm64) -- Container image from [ejabberd Docker Hub][hubecs], see [ecs README][docker-ecs-readme] (for x64) -- Container image from [ejabberd Github Packages][packages], see [CONTAINER](CONTAINER.md) (for x64 and arm64) +- `ecs` container image available in [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for x64) +- `ejabberd` container image available in [Github Packages][packages], see [CONTAINER](CONTAINER.md) (for x64 and arm64) - Using your [Operating System package][osp] - Using the [Homebrew][homebrew] package manager @@ -118,6 +117,7 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [p1download]: https://www.process-one.net/en/ejabberd/downloads/ [p1home]: https://www.process-one.net/en/ejabberd/ [packages]: https://github.com/processone/ejabberd/pkgs/container/ejabberd +[packagesecs]: https://github.com/processone/docker-ejabberd/pkgs/container/ecs [releases]: https://github.com/processone/ejabberd/releases [sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol [stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest From 040c72f1c8fd34ce78e5991a6fb418ed6f5335c8 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Tue, 25 Apr 2023 20:11:04 +0100 Subject: [PATCH 0104/1302] ejabberd_listener.erl: Increase default listen queue backlog value to 128, which is the default value on both Linux and FreeBSD. --- src/ejabberd_listener.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 8b02e3847..fcfe44ab3 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -708,6 +708,6 @@ listen_options() -> {ip, {0,0,0,0}}, {accept_interval, 0}, {send_timeout, 15000}, - {backlog, 5}, + {backlog, 128}, {use_proxy_protocol, false}, {supervisor, true}]. From 8e64992f47103f51785a95aee65f3907aff92064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 2 May 2023 11:43:18 +0200 Subject: [PATCH 0105/1302] Use more efficient way to calculate changes in set_privacy_list --- src/mod_privacy_sql.erl | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index fc5ae6e0f..33d70a71c 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -422,6 +422,12 @@ set_privacy_list_new(ID, RItems) -> end, RItems). +calculate_difference(List1, List2) -> + Set1 = gb_sets:from_list(List1), + Set2 = gb_sets:from_list(List2), + {gb_sets:subtract(Set1, Set2), + gb_sets:subtract(Set2, Set1)}. + set_privacy_list(ID, RItems) -> case ejabberd_sql:sql_query_t( ?SQL("select @(t)s, @(value)s, @(action)s, @(ord)d, @(match_all)b, " @@ -429,25 +435,15 @@ set_privacy_list(ID, RItems) -> "@(match_presence_out)b from privacy_list_data " "where id=%(ID)d")) of {selected, ExistingItems} -> - {ToAdd2, ToDelete2} = - lists:foldr( - fun(Value, {ToAdd, ToDelete}) -> - case lists:splitwith(fun(E) -> E /= Value end, ToAdd) of - {_S, []} -> - {ToAdd, [Value | ToDelete]}; - {Pfx, [_ | Rest]} -> - {Pfx ++ Rest, ToDelete} - end - end, {RItems, []}, ExistingItems), - ToAdd3 = - if - ToDelete2 /= [] -> - ejabberd_sql:sql_query_t( - ?SQL("delete from privacy_list_data where id=%(ID)d")), - RItems; - true -> - ToAdd2 - end, + {ToAdd2, ToDelete2} = calculate_difference(RItems, ExistingItems), + ToAdd3 = if + ToDelete2 /= [] -> + ejabberd_sql:sql_query_t( + ?SQL("delete from privacy_list_data where id=%(ID)d")), + RItems; + true -> + ToAdd2 + end, lists:foreach( fun({SType, SValue, SAction, Order, MatchAll, MatchIQ, MatchMessage, MatchPresenceIn, MatchPresenceOut}) -> From dd2efc360b7f6371f870c56a1cf8c4a6c50b87df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 2 May 2023 15:59:40 +0200 Subject: [PATCH 0106/1302] Fix return values from calculate_diff inside mod_privacy_sql --- src/mod_privacy_sql.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index 33d70a71c..ade3bf1ad 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -425,8 +425,8 @@ set_privacy_list_new(ID, RItems) -> calculate_difference(List1, List2) -> Set1 = gb_sets:from_list(List1), Set2 = gb_sets:from_list(List2), - {gb_sets:subtract(Set1, Set2), - gb_sets:subtract(Set2, Set1)}. + {gb_sets:to_list(gb_sets:subtract(Set1, Set2)), + gb_sets:to_list(gb_sets:subtract(Set2, Set1))}. set_privacy_list(ID, RItems) -> case ejabberd_sql:sql_query_t( From 1818a29c29290ae51382f123a3d592e8710ac640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 22 May 2023 12:48:29 +0200 Subject: [PATCH 0107/1302] Don't crash in mod_shared_roster_ldap:get_member_jid on empty output This based on crash from issue #3614 --- src/mod_shared_roster_ldap.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 7c97ce011..67dcdf4e4 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -395,14 +395,14 @@ get_member_jid(#state{user_jid_attr = UserJIDAttr, user_uid = UIDAttr} = State, [{<<"%u">>, UID}])], [UserJIDAttr]), case Entries of - [] -> - {error, error}; [#eldap_entry{attributes = [{UserJIDAttr, [MemberJID | _]}]} | _] -> try jid:decode(MemberJID) of #jid{luser = U, lserver = S} -> {U, S} catch error:{bad_jid, _} -> {error, Host} - end + end; + _ -> + {error, error} end. extract_members(State, Extractor, AuthChecker, #eldap_entry{attributes = Attrs}, {DescAcc, JIDsAcc}) -> From 4a53d4cb56ce677a655d88ea7b818e9666fe40c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 24 May 2023 11:26:48 +0200 Subject: [PATCH 0108/1302] Expand rule "mucsub subscribers are members in members only rooms" to more places --- src/mod_muc_room.erl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f66e32036..b26e02f76 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1695,13 +1695,23 @@ set_affiliations_fallback(Affiliations, StateData) -> -spec get_affiliation(ljid() | jid(), state()) -> affiliation(). get_affiliation(#jid{} = JID, StateData) -> case get_service_affiliation(JID, StateData) of - owner -> - owner; - none -> - case do_get_affiliation(JID, StateData) of - {Affiliation, _Reason} -> Affiliation; - Affiliation -> Affiliation - end + owner -> + owner; + none -> + Aff = case do_get_affiliation(JID, StateData) of + {Affiliation, _Reason} -> Affiliation; + Affiliation -> Affiliation + end, + case {Aff, (StateData#state.config)#config.members_only} of + % Subscribers should be have members affiliation in this case + {none, true} -> + case is_subscriber(JID, StateData) of + true -> member; + _ -> none + end; + _ -> + Aff + end end; get_affiliation(LJID, StateData) -> get_affiliation(jid:make(LJID), StateData). From 3eecf4ae8a5cfc68164dde905ed073482167dd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 24 May 2023 12:15:20 +0200 Subject: [PATCH 0109/1302] Remove existing role information for users that are kicked from room This should fix issue reported in #4035 --- src/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index b26e02f76..f6c5c4bb3 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1832,7 +1832,7 @@ set_role(JID, Role, StateData) -> %% maintain the same role they had *before* they were kicked, %% unless they were banned none when Affiliation /= outcast -> - StateData#state.roles; + maps:remove(jid:remove_resource(LJID), StateData#state.roles); NewRole -> maps:put(jid:remove_resource(LJID), NewRole, From bb8e8923232ce2097fecd1b2a7d422a1debf2041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 7 Jun 2023 15:54:19 +0200 Subject: [PATCH 0110/1302] Add alternate version of mysql upsert This one works by issuing select and then insert or update or skip depending on what select returns. We use this on mysql 5.7.26 and 8.0.20 where previous implementation using 'replace' or 'on conflict update' can cause excessive deadlocks. --- src/ejabberd_sql.erl | 31 ++++++++++++++++++- src/ejabberd_sql_pt.erl | 66 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 4e92e0574..35b77d3d9 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -73,7 +73,7 @@ -record(state, {db_ref :: undefined | pid(), db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql, - db_version :: undefined | non_neg_integer(), + db_version :: undefined | non_neg_integer() | {non_neg_integer(), atom(), non_neg_integer()}, reconnect_count = 0 :: non_neg_integer(), host :: binary(), pending_requests :: p1_queue:queue(), @@ -1123,9 +1123,38 @@ get_db_version(#state{db_type = pgsql} = State) -> ?WARNING_MSG("Error getting pgsql version: ~p", [Res]), State end; +get_db_version(#state{db_type = mysql} = State) -> + case mysql_to_odbc(p1_mysql_conn:squery(State#state.db_ref, + [<<"select version();">>], self(), + [{timeout, 5000}, + {result_type, binary}])) of + {selected, _, [SVersion]} -> + case re:run(SVersion, <<"(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-([^-]*))?">>, + [{capture, all_but_first, binary}]) of + {match, [V1, V2, V3, Type]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + TypeA = binary_to_atom(Type, utf8), + Flags = case TypeA of + 'MariaDB' -> 0; + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> 0 + end, + State#state{db_version = {V, TypeA, Flags}}; + _ -> + ?WARNING_MSG("Error parsing mysql version: ~p", [SVersion]), + State + end; + Res -> + ?WARNING_MSG("Error getting mysql version: ~p", [Res]), + State + end; get_db_version(State) -> State. +bin_to_int(<<>>) -> 0; +bin_to_int(V) -> binary_to_integer(V). + log(Level, Format, Args) -> case Level of debug -> ?DEBUG(Format, Args); diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 303ac1503..5d72fde24 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -567,7 +567,6 @@ parse_upsert_field1([$= | S], Acc, ParamPos, Loc) -> parse_upsert_field1([C | S], Acc, ParamPos, Loc) -> parse_upsert_field1(S, [C | Acc], ParamPos, Loc). - make_sql_upsert(Table, ParseRes, Pos) -> check_upsert(ParseRes, Pos), erl_syntax:fun_expr( @@ -587,6 +586,11 @@ make_sql_upsert(Table, ParseRes, Pos) -> erl_syntax:integer(90100))], [make_sql_upsert_pgsql901(Table, ParseRes), erl_syntax:atom(ok)]), + erl_syntax:clause( + [erl_syntax:atom(mysql), erl_syntax:tuple([erl_syntax:underscore(), erl_syntax:underscore(), erl_syntax:integer(1)])], + [], + [make_sql_upsert_mysql_select(Table, ParseRes), + erl_syntax:atom(ok)]), erl_syntax:clause( [erl_syntax:atom(mysql), erl_syntax:underscore()], [], @@ -682,6 +686,66 @@ make_sql_upsert_insert(Table, ParseRes) -> ]), State. +make_sql_upsert_select(Table, ParseRes) -> + {Fields0, Where0} = + lists:foldl( + fun({Field, key, ST}, {Fie, Whe}) -> + {Fie, [ST#state{ + 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'}] ++ Whe}; + ({Field, {true}, ST}, {Fie, Whe}) -> + {[ST#state{ + 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query'}] ++ Fie, Whe}; + (_, Acc) -> + Acc + end, {[], []}, ParseRes), + Fields = join_states(Fields0, " AND "), + Where = join_states(Where0, " AND "), + State = + concat_states( + [#state{'query' = [{str, "SELECT "}], + res_vars = [erl_syntax:variable("__VSel")], + res = [erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(to_bool), + [erl_syntax:variable("__VSel")])]}, + Fields, + #state{'query' = [{str, " FROM "}, {str, Table}, {str, " WHERE "}]}, + Where + ]), + State. + +make_sql_upsert_mysql_select(Table, ParseRes) -> + Select = make_sql_query(make_sql_upsert_select(Table, ParseRes)), + Insert = make_sql_query(make_sql_upsert_insert(Table, ParseRes)), + Update = make_sql_query(make_sql_upsert_update(Table, ParseRes)), + erl_syntax:case_expr( + erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(sql_query_t), + [Select]), + [erl_syntax:clause( + [erl_syntax:tuple([erl_syntax:atom(selected), erl_syntax:list([])])], + none, + [erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(sql_query_t), + [Insert])]), + erl_syntax:clause( + [erl_syntax:abstract({selected, [{true}]})], + [], + [erl_syntax:atom(ok)]), + erl_syntax:clause( + [erl_syntax:tuple([erl_syntax:atom(selected), erl_syntax:underscore()])], + none, + [erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(sql_query_t), + [Update])]), + erl_syntax:clause( + [erl_syntax:variable("__SelectRes")], + none, + [erl_syntax:variable("__SelectRes")])]). + make_sql_upsert_mysql(Table, ParseRes) -> Vals = lists:map( From 2428f74fbdb01d76ff611376d2f681276e8b5dd9 Mon Sep 17 00:00:00 2001 From: sando38 Date: Sun, 23 Apr 2023 10:09:02 +0200 Subject: [PATCH 0111/1302] Dockerfile: Provide specific OTP and elixir vsn for direct compilation Ejabberd images can now be built with specific erlang/OTP and elixir vsn with the new build arguments OTP_VSN and ELIXIR_VSN. --- .github/container/Dockerfile | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index ef467329b..f04a53800 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,6 @@ #' Define default build variables -ARG ALPINE_VSN='3.17' +ARG OTP_VSN='25.3' +ARG ELIXIR_VSN='1.14.4' ARG UID='9000' ARG USER='ejabberd' ARG HOME="opt/$USER" @@ -9,7 +10,7 @@ ARG VERSION='master' ################################################################################ #' METHOD='direct' - build and install ejabberd directly from source -FROM alpine:${ALPINE_VSN} AS direct +FROM docker.io/erlang:${OTP_VSN}-alpine AS direct RUN apk -U add --no-cache \ autoconf \ @@ -17,9 +18,6 @@ RUN apk -U add --no-cache \ bash \ build-base \ curl \ - elixir \ - erlang-odbc \ - erlang-reltool \ expat-dev \ file \ gd-dev \ @@ -33,6 +31,13 @@ RUN apk -U add --no-cache \ yaml-dev \ zlib-dev +ARG ELIXIR_VSN +RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz \ + | tar -xzf - + +WORKDIR elixir-$ELIXIR_VSN +RUN make install clean + RUN mix local.hex --force \ && mix local.rebar --force @@ -66,7 +71,7 @@ RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ ################################################################################ #' METHOD='package' - install ejabberd from binary tarball package -FROM alpine:${ALPINE_VSN} AS package +FROM docker.io/erlang:${OTP_VSN}-alpine AS package COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/ WORKDIR /rootfs ARG HOME @@ -127,7 +132,7 @@ RUN chown -R $UID:$UID $HOME ################################################################################ #' METHOD='package' - install runtime dependencies -FROM alpine:${ALPINE_VSN} AS runtime-package +FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-package RUN apk -U upgrade --available --no-cache \ && apk add --no-cache \ libcap2 \ @@ -149,6 +154,11 @@ ARG HOME RUN addgroup $USER -g $UID \ && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER +RUN apk del .erlang-rundeps \ + && rm -f $(which rebar3) \ + && find /usr -type d -name 'erlang' -exec rm -rf {} + \ + && find /usr -type l -exec test ! -e {} \; -delete + ################################################################################ #' Build together production image FROM scratch AS prod From 461c1ddf3d69a8acdebd98e7694038491142b4c7 Mon Sep 17 00:00:00 2001 From: sando38 Date: Sun, 23 Apr 2023 10:57:06 +0200 Subject: [PATCH 0112/1302] Dockerfile: Rename packages to improve compatibility Rename libcap packages to improve compatibility between Alpine versions. This may be beneficial if one specifies an OTP_VSN which was built using an older Alpine base version. The alpine package libcap has been splitted into libcap2 and libcap-utils in Alpine 3.17. 'libcap' is now an alias for libcap2 and libcap-utils. We define 'so:libcap.so.2' for the runtime stage, as we only need the libraries, not the binaries. --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index f04a53800..3d0b861e7 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -85,7 +85,7 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ FROM ${METHOD} AS ejabberd RUN apk -U add --no-cache \ git \ - libcap-utils \ + libcap \ openssl WORKDIR /rootfs @@ -135,7 +135,7 @@ RUN chown -R $UID:$UID $HOME FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-package RUN apk -U upgrade --available --no-cache \ && apk add --no-cache \ - libcap2 \ + so:libcap.so.2 \ tini ################################################################################ From 33ac7916d3a40130c4b05253d85213bc5ecfd6bb Mon Sep 17 00:00:00 2001 From: sando38 Date: Sun, 23 Apr 2023 11:02:54 +0200 Subject: [PATCH 0113/1302] Dockerfile: Cosmetic changes Ommit the path to ejabberdctl as it is already located at the $PATH Also, do not copy Dockerfile into the container as this may unnecessarily trigger re-compiling of ejabberd. --- .dockerignore | 2 +- .github/container/Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.dockerignore b/.dockerignore index b825bcd1f..6abcb6de0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -43,4 +43,4 @@ Mnesia.nonode@nohost/ /ejabberd-*.rpm /ejabberd-*.run /ejabberd-*.tar.gz - +/.github/container/Dockerfile diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 3d0b861e7..107dfeeb0 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -146,7 +146,7 @@ RUN apk add --no-cache \ $(cat /tmp/runDeps) ################################################################################ -#' Finalize runtime environment +#' Remove erlang/OTP & rebar3 from base image, finalize runtime environment FROM runtime-${METHOD} AS runtime ARG USER ARG UID @@ -180,5 +180,5 @@ USER $USER VOLUME ["/$HOME"] EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 -ENTRYPOINT ["/sbin/tini","--","/usr/local/bin/ejabberdctl"] +ENTRYPOINT ["/sbin/tini","--","ejabberdctl"] CMD ["foreground"] From 8f05af78103e1e348a5f868853c98417eebd1999 Mon Sep 17 00:00:00 2001 From: sando38 Date: Mon, 24 Apr 2023 15:03:26 +0200 Subject: [PATCH 0114/1302] Dockerfile: Use Alpine as base for METHOD=package No need to use the "large" docker.io/erlang image as we do not need any erlang/otp for the binary installers. --- .github/container/Dockerfile | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 107dfeeb0..f99a80ad1 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,6 +1,10 @@ #' Define default build variables +## specifc ARGs for METHOD='direct' ARG OTP_VSN='25.3' ARG ELIXIR_VSN='1.14.4' +## specifc ARGs for METHOD='package' +ARG ALPINE_VSN='3.17' +## general ARGs ARG UID='9000' ARG USER='ejabberd' ARG HOME="opt/$USER" @@ -71,7 +75,7 @@ RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ ################################################################################ #' METHOD='package' - install ejabberd from binary tarball package -FROM docker.io/erlang:${OTP_VSN}-alpine AS package +FROM docker.io/alpine:${ALPINE_VSN} AS package COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/ WORKDIR /rootfs ARG HOME @@ -132,21 +136,29 @@ RUN chown -R $UID:$UID $HOME ################################################################################ #' METHOD='package' - install runtime dependencies -FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-package +FROM docker.io/alpine:${ALPINE_VSN} AS runtime-package RUN apk -U upgrade --available --no-cache \ && apk add --no-cache \ so:libcap.so.2 \ tini ################################################################################ -#' METHOD='direct' - install runtime dependencies -FROM runtime-package AS runtime-direct +#' METHOD='direct' - install runtime dependencies, remove erlang/OTP & rebar3 +FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-direct COPY --from=ejabberd /tmp/runDeps /tmp/runDeps -RUN apk add --no-cache \ +RUN apk -U upgrade --available --no-cache \ + && apk add --no-cache \ + so:libcap.so.2 \ + tini \ $(cat /tmp/runDeps) +RUN apk del .erlang-rundeps \ + && rm -f $(which rebar3) \ + && find /usr -type d -name 'erlang' -exec rm -rf {} + \ + && find /usr -type l -exec test ! -e {} \; -delete + ################################################################################ -#' Remove erlang/OTP & rebar3 from base image, finalize runtime environment +#' Finalize runtime environment FROM runtime-${METHOD} AS runtime ARG USER ARG UID @@ -154,11 +166,6 @@ ARG HOME RUN addgroup $USER -g $UID \ && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER -RUN apk del .erlang-rundeps \ - && rm -f $(which rebar3) \ - && find /usr -type d -name 'erlang' -exec rm -rf {} + \ - && find /usr -type l -exec test ! -e {} \; -delete - ################################################################################ #' Build together production image FROM scratch AS prod From 6155b001b44884c00e4744c4c5ec6d719db434c4 Mon Sep 17 00:00:00 2001 From: sando38 Date: Tue, 25 Apr 2023 09:33:45 +0200 Subject: [PATCH 0115/1302] Dockerfile: Reorder stages and steps for consistency Also avoid duplicated lines. --- .github/container/Dockerfile | 37 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index f99a80ad1..adbcdb98d 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -128,38 +128,37 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ > usr/local/bin/ejabberdctl \ && chmod +x usr/local/bin/* \ && scanelf --needed --nobanner --format '%n#p' --recursive $home_root_dir \ - | tr ',' '\n' | sort -u | awk 'system("[ -e $home_root_dir" $1 " ]") == 0 { next } \ - { print "so:" $1 }' > /tmp/runDeps + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e $home_root_dir" $1 " ]") == 0 { next } { print "so:" $1 }' \ + | sed -e "s|so:libc.so|so:libc.musl-$(uname -m).so.1|" \ + > /tmp/runDeps ARG UID RUN chown -R $UID:$UID $HOME ################################################################################ -#' METHOD='package' - install runtime dependencies -FROM docker.io/alpine:${ALPINE_VSN} AS runtime-package -RUN apk -U upgrade --available --no-cache \ - && apk add --no-cache \ - so:libcap.so.2 \ - tini - -################################################################################ -#' METHOD='direct' - install runtime dependencies, remove erlang/OTP & rebar3 +#' METHOD='direct' - Remove erlang/OTP & rebar3 FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-direct -COPY --from=ejabberd /tmp/runDeps /tmp/runDeps -RUN apk -U upgrade --available --no-cache \ - && apk add --no-cache \ - so:libcap.so.2 \ - tini \ - $(cat /tmp/runDeps) - RUN apk del .erlang-rundeps \ && rm -f $(which rebar3) \ && find /usr -type d -name 'erlang' -exec rm -rf {} + \ && find /usr -type l -exec test ! -e {} \; -delete ################################################################################ -#' Finalize runtime environment +#' METHOD='package' - define runtime base image +FROM docker.io/alpine:${ALPINE_VSN} AS runtime-package + +################################################################################ +#' Update alpine, finalize runtime environment FROM runtime-${METHOD} AS runtime +COPY --from=ejabberd /tmp/runDeps /tmp/runDeps +RUN apk -U upgrade --available --no-cache \ + && apk add --no-cache \ + $(cat /tmp/runDeps) \ + so:libcap.so.2 \ + tini + ARG USER ARG UID ARG HOME From c9a21175707ca93ca40843cb6273fd1e6a4d9a0f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 May 2023 09:30:51 +0200 Subject: [PATCH 0116/1302] Ignore .tool-versions in git, this file is used by asdf --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 67b66cf7e..4e8bc1de3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ \#*# .#* .edts +.tool-versions *.dump /Makefile /doc From 436074c67a3c427956d64deac7db7f56841b3fbe Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 May 2023 13:48:49 +0200 Subject: [PATCH 0117/1302] When installing module already configured, keep config as example When installing a module using ext_mod, if it has already configuration in the modules section, copy its specific config file as an example (copy file and rename it). This may happen when using the new install_contrib_modules option. --- src/ext_mod.erl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 32f1f6fc8..2df0a4df9 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -676,15 +676,22 @@ inform_module_configuration(Module, LibDir, Files1) -> Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true; (_) -> false end, Files1), - case Res of - [{ConfigPath, ok}] -> + AlreadyConfigured = lists:keymember(Module, 1, ejabberd_config:get_option(modules)), + case {Res, AlreadyConfigured} of + {[{ConfigPath, ok}], false} -> FullConfigPath = filename:join(LibDir, ConfigPath), io:format("Module ~p has been installed and started.~n" "It's configured in the file:~n ~s~n" "Configure the module in that file, or remove it~n" "and configure in your main ejabberd.yml~n", [Module, FullConfigPath]); - [] -> + {[{ConfigPath, ok}], true} -> + FullConfigPath = filename:join(LibDir, ConfigPath), + file:rename(FullConfigPath, FullConfigPath++".example"), + io:format("Module ~p has been installed and started.~n" + "The ~p configuration in your ejabberd.yml is used.~n", + [Module, Module]); + {[], _} -> io:format("Module ~p has been installed.~n" "Now you can configure it in your ejabberd.yml~n", [Module]) From 480b42b36df582dd4eea4983d020ba9f33c03acc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Jun 2023 23:05:52 +0200 Subject: [PATCH 0118/1302] Run Dialyzer again with Erlang/OTP 26, and let's solve its problems --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 575070886..7d8a86988 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,7 +149,6 @@ jobs: - run: make options - run: make xref - run: make dialyzer - if: matrix.otp != '26.0-rc3' - name: Check Production Release run: | From b3eeac637f2cb02d4519ad850818ed38f4618f2d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Jun 2023 23:07:44 +0200 Subject: [PATCH 0119/1302] Update tests to Erlang/OTP 26 and recent Elixir --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d8a86988..563fbda03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25.3', '26.0-rc3'] + otp: ['20.0', '25.3', '26'] runs-on: ubuntu-20.04 services: redis: diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 492048572..e42908d22 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -111,23 +111,31 @@ jobs: strategy: fail-fast: false matrix: - otp: ['21.3', '22.0', '25.0'] - elixir: ['1.10.3', '1.11.4', '1.12.3', '1.13.0', '1.14.0'] + otp: ['21.3', '25.0', '26'] + elixir: ['1.10.3', '1.11.4', '1.12.3', '1.13.4', '1.14.5', '1.15'] exclude: - otp: '21.3' elixir: '1.12.3' - otp: '21.3' - elixir: '1.13.0' + elixir: '1.13.4' - otp: '21.3' - elixir: '1.14.0' - - otp: '22.0' - elixir: '1.14.0' + elixir: '1.14.5' + - otp: '21.3' + elixir: '1.15' - otp: '25.0' elixir: '1.10.3' - otp: '25.0' elixir: '1.11.4' - otp: '25.0' elixir: '1.12.3' + - otp: '26' + elixir: '1.10.3' + - otp: '26' + elixir: '1.11.4' + - otp: '26' + elixir: '1.12.3' + - otp: '26' + elixir: '1.13.4' runs-on: ubuntu-20.04 steps: From 1b06f4ca4fd6b72b3e11d0e0105d18b260f8cdc7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Jun 2023 13:41:59 +0200 Subject: [PATCH 0120/1302] The warnings_as_errors compiler option is already disabled by default --- rebar.config | 3 --- 1 file changed, 3 deletions(-) diff --git a/rebar.config b/rebar.config index 3eb2914e2..bd5ee12f6 100644 --- a/rebar.config +++ b/rebar.config @@ -175,9 +175,6 @@ {coveralls_service_name, "github"}. {recursive_cmds, ['configure-deps']}. -{overrides, [ - {del, [{erl_opts, [warnings_as_errors]}]}]}. - {post_hook_configure, [{"eimp", []}, {if_var_true, pam, {"epam", []}}, {if_var_true, sip, {"esip", []}}, From 0bbc255814c7a669d568aa31b0c2f83cfdc79ffa Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Jun 2023 12:34:05 +0200 Subject: [PATCH 0121/1302] Dialyzer dirty workarounds because re:mp() is not an exported type Since Erlang/OTP 26, Dialyzer by default reports unknown types. ejabberd's type specs refer to the re:mp() type, but that isn't exported in the OTP source code, and cannot be used in any other modules. This commit provides very dirty workarounds, and any cleaner alternative is very welcomed. --- Makefile.in | 2 ++ include/logger.hrl | 2 ++ src/acl.erl | 18 +++++++++--------- src/gen_mod.erl | 2 +- src/mod_shared_roster_ldap.erl | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Makefile.in b/Makefile.in index c51811b09..c56070eec 100644 --- a/Makefile.in +++ b/Makefile.in @@ -442,7 +442,9 @@ Makefile: Makefile.in ifeq "$(REBAR_VER)" "3" dialyzer: + find src/*_opt.erl -type f \! -regex ".*git.*" -exec sed -i 's/re:mp/ tuple/g' {} \; $(REBAR) dialyzer + find src/*_opt.erl -type f \! -regex ".*git.*" -exec sed -i 's/ tuple/re:mp/g' {} \; else deps := $(wildcard $(DEPSDIR)/*/ebin) diff --git a/include/logger.hrl b/include/logger.hrl index b6f272b73..b6e7067fe 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -62,5 +62,7 @@ _ -> 'Elixir.Logger':bare_log(error, io_lib:format(Format, Args), [?MODULE]) end). +-type re_mp() :: {re_pattern, _, _, _, _}. % Copied from re.erl + %% Uncomment if you want to debug p1_fsm/gen_fsm %%-define(DBGFSM, true). diff --git a/src/acl.erl b/src/acl.erl index 70bed9909..6b8ea42bf 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -39,14 +39,14 @@ -type acl_rule() :: {user, {binary(), binary()} | binary()} | {server, binary()} | {resource, binary()} | - {user_regexp, {re:mp(), binary()} | re:mp()} | - {server_regexp, re:mp()} | - {resource_regexp, re:mp()} | - {node_regexp, {re:mp(), re:mp()}} | - {user_glob, {re:mp(), binary()} | re:mp()} | - {server_glob, re:mp()} | - {resource_glob, re:mp()} | - {node_glob, {re:mp(), re:mp()}} | + {user_regexp, {re_mp(), binary()} | re_mp()} | + {server_regexp, re_mp()} | + {resource_regexp, re_mp()} | + {node_regexp, {re_mp(), re_mp()}} | + {user_glob, {re_mp(), binary()} | re_mp()} | + {server_glob, re_mp()} | + {resource_glob, re_mp()} | + {node_glob, {re_mp(), re_mp()}} | {shared_group, {binary(), binary()} | binary()} | {ip, ip_mask()}. -type access() :: [{action(), [access_rule()]}]. @@ -348,7 +348,7 @@ node_validator(UV, SV) -> %%%=================================================================== %%% Aux %%%=================================================================== --spec match_regexp(iodata(), re:mp()) -> boolean(). +-spec match_regexp(iodata(), re_mp()) -> boolean(). match_regexp(Data, RegExp) -> re:run(Data, RegExp) /= nomatch. diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 75af9cab9..a3c8cb4fa 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -431,7 +431,7 @@ is_equal_opt(Opt, NewOpts, OldOpts) -> %%%=================================================================== -spec format_module_error(atom(), start | reload, non_neg_integer(), opts(), error | exit | throw, any(), - [erlang:stack_item()]) -> iolist(). + [tuple()]) -> iolist(). format_module_error(Module, Fun, Arity, Opts, Class, Reason, St) -> case {Class, Reason} of {error, {bad_return, Module, {error, _} = Err}} -> diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 67dcdf4e4..665f52827 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -72,7 +72,7 @@ user_desc = <<"">> :: binary(), user_uid = <<"">> :: binary(), uid_format = <<"">> :: binary(), - uid_format_re :: undefined | re:mp(), + uid_format_re :: undefined | re_mp(), filter = <<"">> :: binary(), ufilter = <<"">> :: binary(), rfilter = <<"">> :: binary(), From 19070e4b04bbe8650db4c4c4f5bb695ee3ef44bc Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Jun 2023 17:36:06 +0200 Subject: [PATCH 0122/1302] Add sections to rebar.config to organize its content --- rebar.config | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/rebar.config b/rebar.config index bd5ee12f6..a4828a4fa 100644 --- a/rebar.config +++ b/rebar.config @@ -18,6 +18,10 @@ %%% %%%---------------------------------------------------------------------- +%%% +%%% Dependencies +%%% + {deps, [{base64url, ".*", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, @@ -101,6 +105,10 @@ xmpp, yconf]}}. +%%% +%%% Compile +%%% + {erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl", "src/gen_mod.erl", "src/mod_muc_room.erl", "src/mod_push.erl", "src/xmpp_socket.erl"]}. @@ -149,6 +157,10 @@ {keep_build_info, true}. +%%% +%%% Test +%%% + {xref_warnings, false}. {xref_checks, [deprecated_function_calls]}. @@ -184,6 +196,10 @@ {"fast_yaml", []}, {"stringprep", []}]}. +%%% +%%% OTP Release +%%% + {relx, [{release, {ejabberd, {cmd, "grep {vsn, vars.config | sed 's|{vsn, \"||;s|\"}.||' | tr -d '\012'"}}, [ejabberd]}, {sys_config, "./rel/sys.config"}, From 397a08afcac985a66d3397bc1310d85704f1e56e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Jun 2023 17:33:34 +0200 Subject: [PATCH 0123/1302] Move configure options from the Test to the Compile section --- rebar.config | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rebar.config b/rebar.config index a4828a4fa..def6f349c 100644 --- a/rebar.config +++ b/rebar.config @@ -109,6 +109,17 @@ %%% Compile %%% +{recursive_cmds, ['configure-deps']}. + +{post_hook_configure, [{"eimp", []}, + {if_var_true, pam, {"epam", []}}, + {if_var_true, sip, {"esip", []}}, + {if_var_true, zlib, {"ezlib", []}}, + {"fast_tls", []}, + {"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]}, + {"fast_yaml", []}, + {"stringprep", []}]}. + {erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl", "src/gen_mod.erl", "src/mod_muc_room.erl", "src/mod_push.erl", "src/xmpp_socket.erl"]}. @@ -185,16 +196,6 @@ {cover_export_enabled, true}. {coveralls_coverdata, "_build/test/cover/ct.coverdata"}. {coveralls_service_name, "github"}. -{recursive_cmds, ['configure-deps']}. - -{post_hook_configure, [{"eimp", []}, - {if_var_true, pam, {"epam", []}}, - {if_var_true, sip, {"esip", []}}, - {if_var_true, zlib, {"ezlib", []}}, - {"fast_tls", []}, - {"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]}, - {"fast_yaml", []}, - {"stringprep", []}]}. %%% %%% OTP Release From 3263e81972cf828ee3e1ed5e237d2933f1981033 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Jun 2023 13:38:22 +0200 Subject: [PATCH 0124/1302] Move Xref and Dialyzer options from workflows to rebar.config And also include some more applications in Dialyzer plt_extra_apps, which apparently is required since Erlang 26. --- .github/workflows/ci.yml | 13 ------------- .github/workflows/runtime.yml | 8 -------- rebar.config | 29 ++++++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 563fbda03..8252d0633 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,19 +100,6 @@ jobs: sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ libsqlite3-dev libwebp-dev libyaml-dev - - name: Prepare rebar - run: | - echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2} - ]}.' >>rebar.config - echo '{xref_checks, [deprecated_function_calls, deprecated_functions, - locals_not_used, undefined_function_calls, undefined_functions]}. - % Disabled: exports_not_used,' >>rebar.config - echo '{dialyzer, [{get_warnings, true}, {plt_extra_apps, [cache_tab, - eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, - mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config - echo "{ct_opts, [{keep_logs, 20}]}." >>rebar.config - - name: Remove syntax_tools from release run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index e42908d22..a27203ba3 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -62,14 +62,6 @@ jobs: make update make - - name: Prepare rebar - run: | - echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2} - ]}.' >>rebar.config - echo '{xref_checks, [deprecated_function_calls, deprecated_functions, - locals_not_used, undefined_function_calls, undefined_functions]}. - % Disabled: exports_not_used,' >>rebar.config - - run: make xref - name: Test rel (rebar2) diff --git a/rebar.config b/rebar.config index def6f349c..0f954ae85 100644 --- a/rebar.config +++ b/rebar.config @@ -174,7 +174,16 @@ {xref_warnings, false}. -{xref_checks, [deprecated_function_calls]}. +{if_rebar3, + {xref_checks, + [deprecated_function_calls, deprecated_functions, locals_not_used, + undefined_function_calls, undefined_functions]} +}. +{if_not_rebar3, + {xref_checks, + [deprecated_function_calls, deprecated_functions, + undefined_function_calls, undefined_functions]} +}. {xref_exclusions, [ "(\"gen_transport\":_/_)", @@ -189,9 +198,27 @@ {if_var_false, sqlite, "(\"sqlite3\":_/_)"}, {if_var_false, zlib, "(\"ezlib\":_/_)"}]}. +{xref_ignores, [{eldap_filter_yecc, return_error, 2} ]}. + {eunit_compile_opts, [{i, "tools"}, {i, "include"}]}. +{dialyzer, [{get_warnings, false}, % Show warnings of dependencies + {if_version_above, "25", + {plt_extra_apps, + [asn1, odbc, public_key, stdlib, syntax_tools, + eredis, idna, jiffy, luerl, jose, + cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, + sqlite3, stringprep, stun, xmpp, yconf]}, + {plt_extra_apps, % For Erlang/OTP 25 and older + [cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, + sqlite3, stringprep, stun, xmpp, yconf]} + } ]}. + +{ct_opts, [{keep_logs, 20}]}. + {cover_enabled, true}. {cover_export_enabled, true}. {coveralls_coverdata, "_build/test/cover/ct.coverdata"}. From c333cc0776a3670f5aa56169f3c663b8fe803a5e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 May 2023 17:09:09 +0200 Subject: [PATCH 0125/1302] New option install_contrib_modules This option is read during ejabberd start or config reload. It installs the listed modules which aren't yet installed, as long as allow_contrib_modules is not disabled. Edit ejabberd.yml and configure the desired ejabberd-contrib modules, add them in the install_contrib_modules option, finally start ejabberd (or reload config). --- src/ejabberd_config.erl | 20 +++++++++++- src/ejabberd_option.erl | 5 +++ src/ejabberd_options.erl | 4 +++ src/ejabberd_options_doc.erl | 9 ++++++ src/ext_mod.erl | 59 ++++++++++++++++++++++++++++-------- 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 2745f5545..ff823554e 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -502,7 +502,13 @@ read_file(File, Opts) -> end, case Ret of {ok, Y} -> - validate(Y); + InstalledModules = maybe_install_contrib_modules(Y), + ValResult = validate(Y), + case InstalledModules of + [] -> ok; + _ -> spawn(fun() -> timer:sleep(5000), ?MODULE:reload() end) + end, + ValResult; Err -> Err end. @@ -527,6 +533,18 @@ read_erlang_file(File, _) -> Err end. +-spec maybe_install_contrib_modules(term()) -> [atom()]. +maybe_install_contrib_modules(Options) -> + case {lists:keysearch(allow_contrib_modules, 1, Options), + lists:keysearch(install_contrib_modules, 1, Options)} of + {Allow, {value, {_, InstallContribModules}}} + when (Allow == false) or + (Allow == {value, {allow_contrib_modules, true}}) -> + ext_mod:install_contrib_modules(InstallContribModules, Options); + _ -> + [] + end. + -spec validate(term()) -> {ok, [{atom(), term()}]} | error_return(). validate(Y1) -> case pre_validate(Y1) of diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index b71f088c6..81f4bab7f 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -52,6 +52,7 @@ -export([host_config/0]). -export([hosts/0]). -export([include_config_file/0, include_config_file/1]). +-export([install_contrib_modules/0]). -export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]). -export([jwt_jid_field/0, jwt_jid_field/1]). -export([jwt_key/0, jwt_key/1]). @@ -449,6 +450,10 @@ include_config_file() -> include_config_file(Host) -> ejabberd_config:get_option({include_config_file, Host}). +-spec install_contrib_modules() -> [atom()]. +install_contrib_modules() -> + ejabberd_config:get_option({install_contrib_modules, global}). + -spec jwt_auth_only_rule() -> atom(). jwt_auth_only_rule() -> jwt_auth_only_rule(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index c2af225cf..06087921d 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -183,6 +183,8 @@ opt_type(hosts) -> econf:non_empty(econf:list(econf:domain(), [unique])); opt_type(include_config_file) -> econf:any(); +opt_type(install_contrib_modules) -> + econf:list(econf:atom()); opt_type(language) -> econf:lang(); opt_type(ldap_backups) -> @@ -517,6 +519,7 @@ options() -> {access_rules, []}, {acme, #{}}, {allow_contrib_modules, true}, + {install_contrib_modules, []}, {allow_multiple_connections, false}, {anonymous_protocol, sasl_anon}, {api_permissions, @@ -736,6 +739,7 @@ globals() -> fqdn, hosts, host_config, + install_contrib_modules, listen, loglevel, log_rotate_count, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 0e125e950..9d80e721b 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -303,6 +303,8 @@ doc() -> #{value => "true | false", desc => ?T("Whether to allow installation of third-party modules or not. " + "See https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib" + "[ejabberd-contrib] documentation section. " "The default value is 'true'.")}}, {allow_multiple_connections, #{value => "true | false", @@ -670,6 +672,13 @@ doc() -> "file 'Filename'. The options that do not match this " "criteria are not accepted. The default value is to include " "all options.")}}]}, + {install_contrib_modules, + #{value => "[Module, ...]", + desc => + ?T("Modules to install from " + "https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib" + "[ejabberd-contrib] at start time. " + "The default value is an empty list of modules: '[]'.")}}, {jwt_auth_only_rule, #{value => ?T("AccessName"), desc => diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 2df0a4df9..34c37af19 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -33,6 +33,7 @@ installed_command/0, installed/0, installed/1, install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0, add_sources/1, add_sources/2, del_sources/1, modules_dir/0, + install_contrib_modules/2, config_dir/0, get_commands_spec/0]). -export([modules_configs/0, module_ebin_dir/1]). -export([compile_erlang_file/2, compile_elixir_file/2]). @@ -215,10 +216,13 @@ installed_command() -> [short_spec(Item) || Item <- installed()]. install(Module) when is_atom(Module) -> - install(misc:atom_to_binary(Module)); + install(misc:atom_to_binary(Module), undefined); install(Package) when is_binary(Package) -> + install(Package, undefined). + +install(Package, Config) when is_binary(Package) -> Spec = [S || {Mod, S} <- available(), misc:atom_to_binary(Mod)==Package], - case {Spec, installed(Package), is_contrib_allowed()} of + case {Spec, installed(Package), is_contrib_allowed(Config)} of {_, _, false} -> {error, not_allowed}; {[], _, _} -> @@ -227,10 +231,10 @@ install(Package) when is_binary(Package) -> {error, conflict}; {[Attrs], _, _} -> Module = misc:binary_to_atom(Package), - case compile_and_install(Module, Attrs) of + case compile_and_install(Module, Attrs, Config) of ok -> code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]), - ejabberd_config:reload(), + ejabberd_config_reload(Config), copy_commit_json(Package, Attrs), case erlang:function_exported(Module, post_install, 0) of true -> Module:post_install(); @@ -242,6 +246,14 @@ install(Package) when is_binary(Package) -> end end. +ejabberd_config_reload(Config) when is_list(Config) -> + %% Don't reload config when ejabberd is starting + %% because it will be reloaded after installing + %% all the external modules from install_contrib_modules + ok; +ejabberd_config_reload(undefined) -> + ejabberd_config:reload(). + uninstall(Module) when is_atom(Module) -> uninstall(misc:atom_to_binary(Module)); uninstall(Package) when is_binary(Package) -> @@ -483,7 +495,13 @@ modules_spec(Dir, Path) -> short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) -> {Module, proplists:get_value(summary, Attrs, "")}. -is_contrib_allowed() -> +is_contrib_allowed(Config) when is_list(Config) -> + case lists:keyfind(allow_contrib_modules, 1, Config) of + false -> true; + {_, false} -> false; + {_, true} -> true + end; +is_contrib_allowed(undefined) -> ejabberd_option:allow_contrib_modules(). %% -- build functions @@ -526,7 +544,7 @@ check_sources(Module) -> _ -> {error, Result} end. -compile_and_install(Module, Spec) -> +compile_and_install(Module, Spec, Config) -> SrcDir = module_src_dir(Module), LibDir = module_lib_dir(Module), case filelib:is_dir(SrcDir) of @@ -534,7 +552,7 @@ compile_and_install(Module, Spec) -> case compile_deps(SrcDir) of ok -> case compile(SrcDir) of - ok -> install(Module, Spec, SrcDir, LibDir); + ok -> install(Module, Spec, SrcDir, LibDir, Config); Error -> Error end; Error -> @@ -543,7 +561,7 @@ compile_and_install(Module, Spec) -> false -> Path = proplists:get_value(url, Spec, ""), case add_sources(Module, Path) of - ok -> compile_and_install(Module, Spec); + ok -> compile_and_install(Module, Spec, Config); Error -> Error end end. @@ -648,7 +666,7 @@ compile_elixir_file(_, File) -> {error, {compilation_failed, File}}. -endif. -install(Module, Spec, SrcDir, LibDir) -> +install(Module, Spec, SrcDir, LibDir, Config) -> {ok, CurDir} = file:get_cwd(), file:set_cwd(SrcDir), Files1 = [{File, copy(File, filename:join(LibDir, File))} @@ -660,7 +678,7 @@ install(Module, Spec, SrcDir, LibDir) -> Errors = lists:dropwhile(fun({_, ok}) -> true; (_) -> false end, Files1++Files2++Files3), - inform_module_configuration(Module, LibDir, Files1), + inform_module_configuration(Module, LibDir, Files1, Config), Result = case Errors of [{F, {error, E}}|_] -> {error, {F, E}}; @@ -672,11 +690,11 @@ install(Module, Spec, SrcDir, LibDir) -> file:set_cwd(CurDir), Result. -inform_module_configuration(Module, LibDir, Files1) -> +inform_module_configuration(Module, LibDir, Files1, Config) -> Res = lists:filter(fun({[$c, $o, $n, $f |_], ok}) -> true; (_) -> false end, Files1), - AlreadyConfigured = lists:keymember(Module, 1, ejabberd_config:get_option(modules)), + AlreadyConfigured = lists:keymember(Module, 1, get_modules(Config)), case {Res, AlreadyConfigured} of {[{ConfigPath, ok}], false} -> FullConfigPath = filename:join(LibDir, ConfigPath), @@ -697,6 +715,12 @@ inform_module_configuration(Module, LibDir, Files1) -> [Module]) end. +get_modules(Config) when is_list(Config) -> + {modules, Modules} = lists:keyfind(modules, 1, Config), + Modules; +get_modules(undefined) -> + ejabberd_config:get_option(modules). + %% -- minimalist rebar spec parser, only support git fetch_rebar_deps(SrcDir) -> @@ -1220,3 +1244,14 @@ list_modules_parse_uninstall(Query) -> end, installed()), ok. + +install_contrib_modules(Modules, Config) -> + lists:filter(fun(Module) -> + case install(misc:atom_to_binary(Module), Config) of + {error, conflict} -> + false; + ok -> + true + end + end, + Modules). From 16f758e13f4c0f99da6b083ec178bd7d7020cec3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Jun 2023 17:27:48 +0200 Subject: [PATCH 0126/1302] Support to provide only the dependency name This is used in ejabberd-contrib repository's ci.yml, and useful for a custom development that doesn't require rebar2 support. --- rebar.config.script | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index a2b1e065f..9a26b6472 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -232,7 +232,8 @@ LibDir = fun(Name, Suffix) -> GlobalDepsFilter = fun(Deps) -> DepNames = lists:map(fun({DepName, _, _}) -> DepName; - ({DepName, _}) -> DepName + ({DepName, _}) -> DepName; + (DepName) -> DepName end, Deps), lists:filtermap(fun(Dep) -> case LibDir(atom_to_list(Dep), "") of @@ -357,7 +358,8 @@ ProcessRelx = fun(Relx, Deps) -> _ -> [] end, DepApps = lists:map(fun({DepName, _, _}) -> DepName; - ({DepName, _}) -> DepName + ({DepName, _}) -> DepName; + (DepName) -> DepName end, Deps), [{release, NameVersion, DefaultApps ++ VarsApps ++ ProfileApps ++ DepApps} | RelxTail] end, From f40a7b1c77304a00004c733b14d97f3129ca8e82 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 12 Jun 2023 22:27:15 +0200 Subject: [PATCH 0127/1302] OAuth: Handle badpass error message --- src/ejabberd_oauth.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index ba369c1f2..63d1a6f36 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -753,6 +753,7 @@ json_error(Code, Error, Reason) -> json_response(Code, Body). json_error_desc(access_denied) -> <<"Access denied">>; +json_error_desc(badpass) -> <<"Bad password">>; json_error_desc(unsupported_grant_type) -> <<"Unsupported grant type">>; json_error_desc(invalid_scope) -> <<"Invalid scope">>. From d2c54fd5fee2e35cb136feb4db440416f8d19d6e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Jun 2023 11:42:00 +0200 Subject: [PATCH 0128/1302] Fix calling ejabberdctl command with wrong number of arguments with Erlang 26 In Erlang up to 25.3, the lists:zip arguments were [A1, A2] Since Erlang 26.0, the arguments are: [A1, A2, fail] https://github.com/erlang/otp/commit/93748a8d841efc8f8246074ca721607efecebe2d --- src/ejabberd_ctl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 314dd7d98..93f372372 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -336,7 +336,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) -> CI2, Version), format_result(Result, ResultFormat); - {'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} -> + {'EXIT', {function_clause,[{lists,zip,[A1,A2|_], _} | _]}} -> {NumCompa, TextCompa} = case {length(A1), length(A2)} of {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; From dcc8149f58e651d862ac5017537d0d515b7c5df5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Jun 2023 00:56:19 +0200 Subject: [PATCH 0129/1302] New command to halt ejabberd abruptly with an error status code Used for processone/ejabberd-contrib#97 --- src/ejabberd_admin.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 443d8164e..a6ea8a04b 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -116,6 +116,10 @@ get_commands_spec() -> desc = "Stop ejabberd gracefully", module = ?MODULE, function = stop, args = [], result = {res, rescode}}, + #ejabberd_commands{name = halt, tags = [server], + desc = "Halt ejabberd abruptly with status code 1", + module = ejabberd, function = halt, + args = [], result = {res, rescode}}, #ejabberd_commands{name = restart, tags = [server], desc = "Restart ejabberd gracefully", module = ?MODULE, function = restart, From ffbcf19156b5808e0640193ee8439a9a01354c87 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Jun 2023 00:39:05 +0200 Subject: [PATCH 0130/1302] Halt ejabberd if a command in CTL_ON_ fails during ejabberd startup See processone/ejabberd-contrib#97 --- .github/container/ejabberdctl.template | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 1f4d902ec..84969f7ee 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -283,6 +283,12 @@ post_waiter_loop() TAIL=${LIST#* ; } echo ":> ejabberdctl $HEAD" $0 $HEAD + ctlstatus=$? + if [ $ctlstatus -ne 0 ] ; then + echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..." + $0 halt > /dev/null + exit $ctlstatus + fi [ "$HEAD" = "$TAIL" ] || post_waiter_loop $TAIL } From 54314e5bb973521a5676c1a802c7163d31db994d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 4 Jul 2023 18:18:49 +0200 Subject: [PATCH 0131/1302] Better error handling in mod_muc_rtbl Should fix issue #4050 --- src/mod_muc_rtbl.erl | 86 +++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 20 deletions(-) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index d5fe3bbfd..739d2ff61 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -26,6 +26,7 @@ -author("pawel@process-one.net"). -behaviour(gen_mod). +-behavior(gen_server). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -33,12 +34,21 @@ -include("mod_muc_room.hrl"). %% API --export([start/2, stop/1, mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). +-export([start/2, stop/1, init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3, + mod_options/1, mod_opt_type/1, mod_doc/0, depends/2]). -export([pubsub_event_handler/1, muc_presence_filter/3, muc_process_iq/2]). -record(muc_rtbl, {host_id, blank = blank}). +-record(rtbl_state, {host, subscribed = false, retry_timer}). start(Host, _Opts) -> + gen_server:start({local, gen_mod:get_module_proc(Host, ?MODULE)}, ?MODULE, [Host], []). + +stop(Host) -> + gen_server:stop({local, gen_mod:get_module_proc(Host, ?MODULE)}). + +init([Host]) -> ejabberd_mnesia:create(?MODULE, muc_rtbl, [{ram_copies, [node()]}, {local_content, true}, @@ -50,32 +60,62 @@ start(Host, _Opts) -> ?MODULE, muc_presence_filter, 50), ejabberd_hooks:add(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), - request_initial_items(Host). + request_initial_items(Host), + {ok, #rtbl_state{host = Host}}. -stop(Host) -> +handle_call(_Request, _From, State) -> + {noreply, State}. + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info({iq_reply, IQReply, initial_items}, State) -> + State2 = parse_initial_items(State, IQReply), + {noreply, State2}; +handle_info({iq_reply, IQReply, subscription}, State) -> + State2 = parse_subscription(State, IQReply), + {noreply, State2}; +handle_info(_Request, State) -> + {noreply, State}. + +terminate(_Reason, #rtbl_state{host = Host, subscribed = Sub, retry_timer = Timer}) -> ejabberd_hooks:delete(local_send_to_resource_hook, Host, ?MODULE, pubsub_event_handler, 50), ejabberd_hooks:delete(muc_filter_presence, Host, ?MODULE, muc_presence_filter, 50), ejabberd_hooks:delete(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), - Jid = service_jid(Host), - IQ = #iq{type = set, from = Jid, to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), - sub_els = [ - #pubsub{unsubscribe = - #ps_unsubscribe{jid = Jid, node = mod_muc_rtbl_opt:rtbl_node(Host)}}]}, - ejabberd_router:route_iq(IQ, fun parse_subscribe_result/1). + case Sub of + true -> + Jid = service_jid(Host), + IQ = #iq{type = set, from = Jid, to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), + sub_els = [ + #pubsub{unsubscribe = + #ps_unsubscribe{jid = Jid, node = mod_muc_rtbl_opt:rtbl_node(Host)}}]}, + ejabberd_router:route_iq(IQ, fun(_) -> ok end); + _ -> + ok + end, + misc:cancel_timer(Timer). + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. request_initial_items(Host) -> IQ = #iq{type = get, from = service_jid(Host), to = jid:make(mod_muc_rtbl_opt:rtbl_server(Host)), sub_els = [ #pubsub{items = #ps_items{node = mod_muc_rtbl_opt:rtbl_node(Host)}}]}, - ejabberd_router:route_iq(IQ, fun parse_initial_items/1). + ejabberd_router:route_iq(IQ, initial_items, self()). -parse_initial_items(#iq{type = error} = IQ) -> - ?WARNING_MSG("Fetching initial list failed: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]); -parse_initial_items(#iq{from = From, to = #jid{lserver = Host} = To, type = result} = IQ) -> +parse_initial_items(State, timeout) -> + ?WARNING_MSG("Fetching initial list failed: fetch timeout. Retrying in 60 seconds", []), + State#rtbl_state{retry_timer = erlang:send_after(60000, self(), fetch_list)}; +parse_initial_items(State, #iq{type = error} = IQ) -> + ?WARNING_MSG("Fetching initial list failed: ~p. Retrying in 60 seconds", + [xmpp:format_stanza_error(xmpp:get_error(IQ))]), + State#rtbl_state{retry_timer = erlang:send_after(60000, self(), fetch_list)}; +parse_initial_items(State, #iq{from = From, to = #jid{lserver = Host} = To, type = result} = IQ) -> case xmpp:get_subtag(IQ, #pubsub{}) of #pubsub{items = #ps_items{node = Node, items = Items}} -> Added = lists:foldl( @@ -86,16 +126,22 @@ parse_initial_items(#iq{from = From, to = #jid{lserver = Host} = To, type = resu SubIQ = #iq{type = set, from = To, to = From, sub_els = [ #pubsub{subscribe = #ps_subscribe{jid = To, node = Node}}]}, - ejabberd_router:route_iq(SubIQ, fun parse_subscribe_result/1), - notify_rooms(Host, Added); + ejabberd_router:route_iq(SubIQ, subscription, self()), + notify_rooms(Host, Added), + State#rtbl_state{retry_timer = undefined, subscribed = true}; _ -> - ?WARNING_MSG("Fetching initial list failed: invalid result payload", []) + ?WARNING_MSG("Fetching initial list failed: invalid result payload", []), + State#rtbl_state{retry_timer = undefined} end. -parse_subscribe_result(#iq{type = error} = IQ) -> - ?WARNING_MSG("Subscription error: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]); -parse_subscribe_result(_) -> - ok. +parse_subscription(State, timeout) -> + ?WARNING_MSG("Subscription error: request timeout", []), + State#rtbl_state{subscribed = false}; +parse_subscription(State, #iq{type = error} = IQ) -> + ?WARNING_MSG("Subscription error: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]), + State#rtbl_state{subscribed = false}; +parse_subscription(State, _) -> + State. pubsub_event_handler(#message{from = #jid{luser = <<>>, lserver = SServer}, to = #jid{luser = <<>>, lserver = Server, From 6272c0e9011a6b866277f1ceee73d909a39cb61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Mon, 10 Jul 2023 16:18:06 +0200 Subject: [PATCH 0132/1302] Improve ejabberdctl script Improved handling of ERLANG_OPTS and fixed hanging process when running some ejabberdctl commands as root, such as debug or foreground. --- ejabberdctl.template | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 758a85bca..a549b9393 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -95,7 +95,7 @@ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFI $(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" -EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" +EJABBERD_OPTS="$ERLANG_OPTS -mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH @@ -121,19 +121,19 @@ set_dist_client() exec_cmd() { case $EXEC_CMD in - as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; + as_install_user) su -s /bin/sh -c 'exec "$0" "$@"' "$INSTALLUSER" -- "$@" ;; as_current_user) "$@" ;; esac } exec_erl() { NODE=$1; shift - exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" + exec_cmd "$ERL" ${S:--}name "$NODE" "$@" } exec_iex() { NODE=$1; shift - exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" + exec_cmd "$IEX" -${S:--}name "$NODE" "$@" } # usage @@ -321,7 +321,7 @@ case $1 in PEER=${2:-$ERLANG_NODE} [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client - exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; From e66ba2e4245a36a3bec6c374004c29824153554e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Jul 2023 10:13:59 +0200 Subject: [PATCH 0133/1302] Improve ejabberdctl script: Copy recent commit 6272c0e90 to the container --- .github/container/ejabberdctl.template | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 84969f7ee..b78771851 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -96,7 +96,7 @@ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFI $(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" -EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" +EJABBERD_OPTS="$ERLANG_OPTS -mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH @@ -129,8 +129,8 @@ run_cmd() exec_cmd() { case $EXEC_CMD in - as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; as_current_user) exec "$@" ;; + as_install_user) su -s /bin/sh -c 'exec "$0" "$@"' "$INSTALLUSER" -- "$@" ;; esac } run_erl() @@ -141,12 +141,12 @@ run_erl() exec_erl() { NODE=$1; shift - exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" + exec_cmd "$ERL" ${S:--}name "$NODE" "$@" } exec_iex() { NODE=$1; shift - exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" + exec_cmd "$IEX" -${S:--}name "$NODE" "$@" } # usage @@ -364,7 +364,7 @@ case $1 in PEER=${2:-$ERLANG_NODE} [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client - exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; From 8d9ee8e35b687ace00f64e258a98d8c1481e6dbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 06:05:32 +0000 Subject: [PATCH 0134/1302] Bump ex_doc from 0.29.4 to 0.30.1 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.29.4 to 0.30.1. - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.29.4...v0.30.1) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- mix.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index bb660f38f..c053aae0c 100644 --- a/mix.lock +++ b/mix.lock @@ -1,12 +1,12 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"}, "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, + "ex_doc": {:hex, :ex_doc, "0.30.1", "a0f3b598d3c2cb3af48af39e59fa66ac8d4033740409b11dd753a3f30f8f8f7a", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2e2216e84aa33e5803f8898d762b0f5e76bf2de3a08d1f40ac5f74456dd5057c"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, @@ -16,10 +16,10 @@ "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "luerl": {:hex, :luerl, "1.0.0", "1b68c30649323590d5339b967b419260500ffe520cd3abc1987482a82d3b5a6c", [:rebar3], [], "hexpm", "c17bc45cb4b0845ec975387f9a5d8c81ab60456698527a29c96f78992af86bd1"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, "p1_mysql": {:hex, :p1_mysql, "1.0.21", "5972add935e7b1b03d981fa88a0d01e96de357443eaf96ca2fb62e465a717f47", [:rebar3], [], "hexpm", "16f197adb99dab034139c429b256d65948a4057d3e4d553adbe5ce3236c4aabf"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, From 78f81de252dc932cd47b91d1a84ca8e8f0647498 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Jul 2023 13:32:24 +0200 Subject: [PATCH 0135/1302] Improve support to stop external modules written in Elixir --- src/ext_mod.erl | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 34c37af19..81b71def4 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -236,8 +236,9 @@ install(Package, Config) when is_binary(Package) -> code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]), ejabberd_config_reload(Config), copy_commit_json(Package, Attrs), - case erlang:function_exported(Module, post_install, 0) of - true -> Module:post_install(); + ModuleRuntime = get_runtime_module_name(Module), + case erlang:function_exported(ModuleRuntime, post_install, 0) of + true -> ModuleRuntime:post_install(); _ -> ok end; Error -> @@ -260,14 +261,15 @@ uninstall(Package) when is_binary(Package) -> case installed(Package) of true -> Module = misc:binary_to_atom(Package), - case erlang:function_exported(Module, pre_uninstall, 0) of - true -> Module:pre_uninstall(); + ModuleRuntime = get_runtime_module_name(Module), + case erlang:function_exported(ModuleRuntime, pre_uninstall, 0) of + true -> ModuleRuntime:pre_uninstall(); _ -> ok end, - [catch gen_mod:stop_module(Host, Module) + [catch gen_mod:stop_module(Host, ModuleRuntime) || Host <- ejabberd_option:hosts()], - code:purge(Module), - code:delete(Module), + code:purge(ModuleRuntime), + code:delete(ModuleRuntime), [code:del_path(PathDelete) || PathDelete <- [module_ebin_dir(Module)|module_deps_dirs(Module)]], delete_path(module_lib_dir(Module)), ejabberd_config:reload(); @@ -1019,6 +1021,26 @@ get_module_status(Module) -> unknown end. +%% When a module named mod_whatever in ejabberd-modules +%% is written in Elixir, its runtime name is 'Elixir.ModWhatever' +get_runtime_module_name(Module) -> + case is_elixir_module(Module) of + true -> elixir_module_name(Module); + false -> Module + end. + +is_elixir_module(Module) -> + LibDir = module_src_dir(Module), + Lib = filename:join(LibDir, "lib"), + Src = filename:join(LibDir, "src"), + case {filelib:wildcard(Lib++"/*.{ex}"), + filelib:wildcard(Src++"/*.{erl}")} of + {[_ | _], []} -> + true; + {[], [_ | _]} -> + false + end. + %% Converts mod_some_thing to Elixir.ModSomeThing elixir_module_name(ModAtom) -> list_to_atom("Elixir." ++ elixir_module_name("_" ++ atom_to_list(ModAtom), [])). From 2ef9fbc11144248b043172ffbe903651cc8f5761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 17 Jul 2023 18:40:22 +0200 Subject: [PATCH 0136/1302] Add stun app to cond_apps in mix.exs --- mix.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 490ec4a49..348215e81 100644 --- a/mix.exs +++ b/mix.exs @@ -154,7 +154,8 @@ defmodule Ejabberd.MixProject do {config(:sip), :esip}, {config(:odbc), :odbc}, {config(:pgsql), :p1_pgsql}, - {config(:sqlite), :sqlite3}], do: + {config(:sqlite), :sqlite3}, + {config(:stun), :stun}], do: app end From d349e3a88e9a8410e744fcd58913c83309474746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 18 Jul 2023 11:14:57 +0200 Subject: [PATCH 0137/1302] Revert "Add stun app to cond_apps in mix.exs" Looks like this is required only when using older mix versions. This reverts commit 2ef9fbc11144248b043172ffbe903651cc8f5761. --- mix.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 348215e81..490ec4a49 100644 --- a/mix.exs +++ b/mix.exs @@ -154,8 +154,7 @@ defmodule Ejabberd.MixProject do {config(:sip), :esip}, {config(:odbc), :odbc}, {config(:pgsql), :p1_pgsql}, - {config(:sqlite), :sqlite3}, - {config(:stun), :stun}], do: + {config(:sqlite), :sqlite3}], do: app end From 82887747877175e354bb8f9538ea2b31db6d7c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Tue, 18 Jul 2023 15:31:10 +0200 Subject: [PATCH 0138/1302] Add EJABBERD_OPTS in ejabberdctl.cfg & revert "Improve ejabberdctl script" EJABBERD_OPTS is used to pass options to erl only when starting ejabberd, to enable -heart for example. This partially reverts commit 6272c0e9011a6b866277f1ceee73d909a39cb61d. --- ejabberdctl.cfg.example | 19 +++++++++++++++---- ejabberdctl.template | 8 ++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/ejabberdctl.cfg.example b/ejabberdctl.cfg.example index 8b15933db..da9e1bd32 100644 --- a/ejabberdctl.cfg.example +++ b/ejabberdctl.cfg.example @@ -108,10 +108,8 @@ #. #' ERL_OPTIONS: Additional Erlang options # -# The next variable allows to specify additional options passed to erlang while -# starting ejabberd. Some useful options are -noshell, -detached, -heart. When -# ejabberd is started from an init.d script options -noshell and -detached are -# added implicitly. See erl(1) for more info. +# The next variable allows to specify additional options passed to +# erlang. See erl(1) for more info. # # It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you # want to add local modules in this path. @@ -120,6 +118,19 @@ # #ERL_OPTIONS="" +#. +#' EJABBERD_OPTS: Additional Erlang options to start ejabberd +# +# The next variable allows to specify additional options passed to erlang while +# starting ejabberd. Some useful options are -noshell, -detached, -heart. When +# ejabberd is started from an init.d script options -noshell and -detached are +# added implicitly. See erl(1) for more info. +# +# Default: "" +# +#EJABBERD_OPTS="" +EJABBERD_OPTS="-heart -env HEART_BEAT_TIMEOUT 120 -env ERL_CRASH_DUMP_SECONDS 60" + #. #' ERLANG_NODE: Erlang node name # diff --git a/ejabberdctl.template b/ejabberdctl.template index a549b9393..4085492ac 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -95,7 +95,7 @@ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFI $(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" -EJABBERD_OPTS="$ERLANG_OPTS -mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" +EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH @@ -128,12 +128,12 @@ exec_cmd() exec_erl() { NODE=$1; shift - exec_cmd "$ERL" ${S:--}name "$NODE" "$@" + exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" } exec_iex() { NODE=$1; shift - exec_cmd "$IEX" -${S:--}name "$NODE" "$@" + exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" } # usage @@ -321,7 +321,7 @@ case $1 in PEER=${2:-$ERLANG_NODE} [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client - exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" \ + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; From 8a740d50874ddf5e6a0304e577247b98f14e8766 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 14:49:07 +0200 Subject: [PATCH 0139/1302] make-binaries: Set kernel version for all builds Specify the same min. supported kernel version for builds against musl libc and glibc. --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 273ad117f..175152ee3 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -258,6 +258,7 @@ create_common_config() CT_CC_LANG_CXX=y CT_ARCH_64=y CT_KERNEL_LINUX=y + CT_LINUX_V_3_16=y CT_LOG_PROGRESS_BAR=n EOF } @@ -271,7 +272,6 @@ create_gnu_config() create_common_config "$file" cat >>"$file" <<-'EOF' - CT_LINUX_V_3_16=y CT_GLIBC_V_2_19=y CT_GLIBC_KERNEL_VERSION_NONE=y EOF From 0b6cb77b3cffed07356d04aa0db809017f72f2cc Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 14:52:18 +0200 Subject: [PATCH 0140/1302] make-binaries: Omit unnecessary glibc setting The CT_GLIBC_KERNEL_VERSION_NONE option is about the minimum kernel version supported by the toolchain's glibc. The default is to stick to the version of the kernel headers, which should be just fine. --- tools/make-binaries | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 175152ee3..20622efc3 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -273,7 +273,6 @@ create_gnu_config() cat >>"$file" <<-'EOF' CT_GLIBC_V_2_19=y - CT_GLIBC_KERNEL_VERSION_NONE=y EOF } #. From f1b0a9cb32d591f67019a07f14101790d026c508 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 14:54:04 +0200 Subject: [PATCH 0141/1302] make-binaries: Don't hard-code musl version The default is to link against the most-recent musl version available within the crosstool-ng toolchain, which is currently 1.2.2. Unlike with glibc, there's no point in sticking to some older version. --- tools/make-binaries | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 20622efc3..1f3788278 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -287,7 +287,6 @@ create_musl_config() cat >>"$file" <<-'EOF' CT_EXPERIMENTAL=y CT_LIBC_MUSL=y - CT_MUSL_V_1_2_2=y EOF } #. From ec860797471ee0c16dc529ee27128f712f6f63e4 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 14:56:01 +0200 Subject: [PATCH 0142/1302] make-binaries: Don't duplicate config entries --- tools/make-binaries | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 1f3788278..7148243ec 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -297,7 +297,6 @@ create_x64_config() local file="$1" local libc="$2" - create_common_config "$file" create_${libc}_config "$file" cat >>"$file" <<-'EOF' @@ -312,7 +311,6 @@ create_arm64_config() local file="$1" local libc="$2" - create_common_config "$file" create_${libc}_config "$file" cat >>"$file" <<-'EOF' From 121acd1da738fdb8d70c8d6c986234e2720abdc7 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 15:06:40 +0200 Subject: [PATCH 0143/1302] make-binaries: Apply minor simplifications --- tools/make-binaries | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 7148243ec..b4750a743 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -118,7 +118,6 @@ rel_tar="$rel_name-$mix_vsn.tar.gz" ct_jobs=$(nproc) src_dir="$root_dir/src" platform=$(gcc -dumpmachine) -platform_libc=$(echo $platform | sed "s/\-/\ /g" | awk '{print $NF}') targets='x86_64-linux-gnu aarch64-linux-gnu' build_start=$(date '+%F %T') have_current_deps='false' @@ -347,10 +346,10 @@ add_otp_path() if [ "$mode" = 'native' ] then native_otp_bin="$prefix/bin" - # for github runners to build for non-native systems - # https://github.com/marketplace/actions/setup-erlang-otp-with-optional-elixir-and-mix-and-or-rebar3 - elif [ ! -z "${INSTALL_DIR_FOR_OTP-}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR-}" ] - then + elif [ ! -z "${INSTALL_DIR_FOR_OTP+x}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR+x}" ] + then + # For github runners to build for non-native systems: + # https://github.com/erlef/setup-beam#environment-variables native_otp_bin="$INSTALL_DIR_FOR_OTP/bin" native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin" export PATH="$native_elixir_bin:$PATH" @@ -472,9 +471,9 @@ build_toolchain() { local target="$1" local prefix="$2" - local libc="$3" local arch=$(arch_name "$target") - + local libc="${target##*-}" + if [ -d "$prefix" ] then info "Using existing toolchain in $prefix ..." @@ -513,8 +512,8 @@ build_deps() local mode="$1" local target="$2" local prefix="$3" - local libc="$4" local arch="$(arch_name "$target")" + local libc="${target##*-}" local target_src_dir="$prefix/src" local saved_path="$PATH" @@ -721,8 +720,8 @@ build_rel() local mode="$1" local target="$2" local prefix="$3" - local libc="$4" local arch="$(arch_name "$target")" + local libc="${target##*-}" local rel_dir="$PWD/_build/prod" local target_data_dir="$prefix/$rel_name" local target_dst_dir="$prefix/$rel_name-$rel_vsn" @@ -761,7 +760,7 @@ build_rel() fi if [ $have_current_deps = false ] - then build_deps "$mode" "$target" "$prefix" "$libc" + then build_deps "$mode" "$target" "$prefix" fi add_otp_path "$mode" "$prefix" @@ -908,16 +907,15 @@ export LC_ALL='C.UTF-8' # Elixir insists on a UTF-8 environment. for target in $targets do - libc="$(echo $target | sed "s/\-/\ /g" | awk '{print $NF}')" - prefix="$build_dir/$(arch_name "$target")-$libc" + prefix="$build_dir/$target" toolchain_dir="$ct_prefix_dir/$target" - if [ "$(uname -m)-linux-$platform_libc" = "$target" ] + if [ "$platform" = "$target" ] then mode='native' else mode='cross' fi - build_toolchain "$target" "$toolchain_dir" "$libc" - build_rel "$mode" "$target" "$prefix" "$libc" + build_toolchain "$target" "$toolchain_dir" + build_rel "$mode" "$target" "$prefix" done save_built_dep_vsns From a657a6d2f6d6a95853d21e21be0f7eda4d1b9f82 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 15:35:09 +0200 Subject: [PATCH 0144/1302] make-binaries: Apply another minor simplification --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index b4750a743..1fa10bf17 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -346,7 +346,7 @@ add_otp_path() if [ "$mode" = 'native' ] then native_otp_bin="$prefix/bin" - elif [ ! -z "${INSTALL_DIR_FOR_OTP+x}" ] && [ ! -z "${INSTALL_DIR_FOR_ELIXIR+x}" ] + elif [ -n "${INSTALL_DIR_FOR_OTP+x}" ] && [ -n "${INSTALL_DIR_FOR_ELIXIR+x}" ] then # For github runners to build for non-native systems: # https://github.com/erlef/setup-beam#environment-variables From 0a5eda0777f475d9ebf9078d26f2170860b05cdb Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 15:51:27 +0200 Subject: [PATCH 0145/1302] make-binaries: Fix check for current Expat version --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 1fa10bf17..b2c9d0f7b 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -189,7 +189,7 @@ check_configured_dep_vsns() 'zlib-\([1-9][0-9.]*\)\.tar\.gz' check_vsn 'Expat' "$expat_vsn" \ 'https://github.com/libexpat/libexpat/releases' \ - '[0-9]\]\([1-9][0-9.]*\)' + '\([1-9]\.[0-9]*\.[0-9]*\)' check_vsn 'Termcap' "$termcap_vsn" \ 'https://ftp.gnu.org/gnu/termcap/' \ 'termcap-\([1-9][0-9.]*\)\.tar\.gz' From 11dc0c1774c0e19c366e80dfb3a8ee4d7e9d831e Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 15:52:32 +0200 Subject: [PATCH 0146/1302] make-binaries: Bump dependency versions --- tools/make-binaries | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index b2c9d0f7b..5e267254f 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,16 +70,16 @@ termcap_vsn='1.3.1' expat_vsn='2.5.0' zlib_vsn='1.2.13' yaml_vsn='0.2.5' -ssl_vsn='1.1.1t' -otp_vsn='25.3' -elixir_vsn='1.14.3' -pam_vsn='1.5.2' -png_vsn='1.6.39' +ssl_vsn='1.1.1u' +otp_vsn='25.3.2.4' +elixir_vsn='1.15.4' +pam_vsn='1.5.3' +png_vsn='1.6.40' jpeg_vsn='9e' -webp_vsn='1.3.0' +webp_vsn='1.3.1' gd_vsn='2.3.3' odbc_vsn='2.3.11' -sqlite_vsn='3410100' +sqlite_vsn='3420000' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From bf9b257eab0f55e460ca8bb51f0dd2de244aef97 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 16:08:25 +0200 Subject: [PATCH 0147/1302] make-binaries: Don't build Linux-PAM examples Building the examples shipped with Linux-PAM 1.5.3 fails with musl libc: https://github.com/linux-pam/linux-pam/commit/5374f677e4cae669eb9accf2449178b602e8a40a --- tools/make-binaries | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 5e267254f..9a0259461 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -624,7 +624,8 @@ build_deps() info "Building Linux-PAM $pam_vsn for $arch ..." cd "$target_src_dir/$pam_dir" $configure --prefix="$prefix" --includedir="$prefix/include/security" \ - --enable-static --disable-shared --disable-doc --enable-db=no \ + --enable-static --disable-shared --disable-doc --disable-examples \ + --enable-db=no \ CFLAGS="$CFLAGS -O3 -fPIC" make make install From d109d7f0c51226feb22b84e0608dc6703e76f224 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 23 Jul 2023 16:34:21 +0200 Subject: [PATCH 0148/1302] make-binaries: Revert to Linux-PAM 1.5.2 Linux-PAM's --disable-examples flag doesn't seem to have the expected effect. --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 9a0259461..ddc6a09ab 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -73,7 +73,7 @@ yaml_vsn='0.2.5' ssl_vsn='1.1.1u' otp_vsn='25.3.2.4' elixir_vsn='1.15.4' -pam_vsn='1.5.3' +pam_vsn='1.5.2' png_vsn='1.6.40' jpeg_vsn='9e' webp_vsn='1.3.1' From c03af0afb3591c364b2a702c46c82d2a8261ed5f Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 24 Jul 2023 00:11:32 +0200 Subject: [PATCH 0149/1302] make-binaries: Remove outdated workaround Remove a workaround for an issue that has been fixed in Erlang/OTP 25: https://github.com/erlang/otp/pull/5558 --- tools/make-binaries | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index ddc6a09ab..442edfda4 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -684,9 +684,6 @@ build_deps() export erl_xcomp_sysroot="$prefix" fi cd "$target_src_dir/$otp_dir" - # Don't link against libnsl: https://github.com/erlang/otp/pull/5558 - sed -i -e '/LIBS="-lnsl/d' -e '/LIBS="-lsocket/d' \ - 'lib/erl_interface/configure' # The additional CFLAGS/LIBS below are required by --enable-static-nifs. # The "-ldl" flag specifically is only needed for ODBC, though. $configure \ From 9c6fe98f76ac8845b9574b4d14e5831a99e13af2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Jul 2023 10:55:41 +0200 Subject: [PATCH 0150/1302] Partially revert "Improve ejabberdctl script: Copy recent commit 6272c0e90 to the container" This reverts commit e66ba2e4245a36a3bec6c374004c29824153554e following the revert in commit 82887747877175e354bb8f9538ea2b31db6d7c51 --- .github/container/ejabberdctl.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index b78771851..a0e2f0573 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -96,7 +96,7 @@ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFI $(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" -EJABBERD_OPTS="$ERLANG_OPTS -mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" +EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" # export global variables export EJABBERD_CONFIG_PATH @@ -141,12 +141,12 @@ run_erl() exec_erl() { NODE=$1; shift - exec_cmd "$ERL" ${S:--}name "$NODE" "$@" + exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" } exec_iex() { NODE=$1; shift - exec_cmd "$IEX" -${S:--}name "$NODE" "$@" + exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" } # usage @@ -364,7 +364,7 @@ case $1 in PEER=${2:-$ERLANG_NODE} [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client - exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" \ + exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; From db03c7428c2298454ed202af1e39ad4eeadc958c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Jul 2023 16:50:06 +0200 Subject: [PATCH 0151/1302] Web Admin roster page: move the AddJID textbox to top (#4067) --- src/mod_roster.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 7ea3dac81..892c6cc30 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -1127,11 +1127,11 @@ user_roster(User, Server, Query, Lang) -> ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - (FItems ++ - [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>), + ( [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>), ?C(<<" ">>), ?INPUTT(<<"submit">>, <<"addjid">>, - ?T("Add Jabber ID"))]))]. + ?T("Add Jabber ID"))] + ++ FItems))]. build_contact_jid_td(RosterJID) -> ContactJID = jid:make(RosterJID), From 3710dc1e3be25b7061b72867b5f06336873896a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 25 Jul 2023 16:52:49 +0200 Subject: [PATCH 0152/1302] Use prepared statement with mysql --- mix.exs | 2 +- rebar.config | 2 +- src/ejabberd_sql.erl | 27 ++++++++++++++++++++++++++- src/ejabberd_sql_pt.erl | 17 +++++++++++++---- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 490ec4a49..8a1615778 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below('22', true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, - {config(:mysql), {:p1_mysql, "~> 1.0.20"}}, + {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: diff --git a/rebar.config b/rebar.config index 0f954ae85..386187d4e 100644 --- a/rebar.config +++ b/rebar.config @@ -63,7 +63,7 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.21"}}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.22"}}}}, diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 35b77d3d9..99f74953e 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -683,7 +683,14 @@ sql_query_internal(#sql_query{} = Query) -> pgsql_sql_query(Query) end; mysql -> - generic_sql_query(Query); + case {Query#sql_query.flags, ejabberd_option:sql_prepared_statements(State#state.host)} of + {1, _} -> + generic_sql_query(Query); + {_, false} -> + generic_sql_query(Query); + _ -> + mysql_prepared_execute(Query, State) + end; sqlite -> sqlite_sql_query(Query) end @@ -862,6 +869,24 @@ pgsql_execute_sql_query(SQLQuery, State) -> Res = pgsql_execute_to_odbc(ExecuteRes), sql_query_format_res(Res, SQLQuery). +mysql_prepared_execute(#sql_query{hash = Hash} = Query, State) -> + ValEsc = #sql_escape{like_escape = fun() -> ignore end, _ = fun(X) -> X end}, + TypesEsc = #sql_escape{string = fun(_) -> string end, + integer = fun(_) -> integer end, + boolean = fun(_) -> bool end, + in_array_string = fun(_) -> string end, + like_escape = fun() -> ignore end}, + Val = [X || X <- (Query#sql_query.args)(ValEsc), X /= ignore], + Types = [X || X <- (Query#sql_query.args)(TypesEsc), X /= ignore], + QueryFn = fun() -> + PrepEsc = #sql_escape{like_escape = fun() -> <<>> end, _ = fun(_) -> <<"?">> end}, + (Query#sql_query.format_query)((Query#sql_query.args)(PrepEsc)) + end, + QueryTimeout = query_timeout(State#state.host), + Res = p1_mysql_conn:prepared_query(State#state.db_ref, QueryFn, Hash, Val, Types, + self(), [{timeout, QueryTimeout - 1000}]), + Res2 = mysql_to_odbc(Res), + sql_query_format_res(Res2, Query). sql_query_format_res({selected, _, Rows}, SQLQuery) -> Res = diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 5d72fde24..61f9e421c 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -42,7 +42,8 @@ used_vars = [], use_new_schema, need_timestamp_pass = false, - need_array_pass = false}). + need_array_pass = false, + has_list = false}). -define(QUERY_RECORD, "sql_query"). @@ -268,9 +269,12 @@ parse1([$@, $( | S], Acc, State) -> Convert = case Type of integer -> - erl_syntax:application( - erl_syntax:atom(binary_to_integer), - [EVar]); + erl_syntax:if_expr([ + erl_syntax:clause( + [erl_syntax:application(erl_syntax:atom(is_binary), [EVar])], + [erl_syntax:application(erl_syntax:atom(binary_to_integer), [EVar])]), + erl_syntax:clause([erl_syntax:atom(true)], [EVar]) + ]); string -> EVar; timestamp -> @@ -339,6 +343,7 @@ parse1([$%, $( | S], Acc, State) -> erl_syntax:variable(Name)]), State2#state{'query' = [[{var, Var, Type}] | State2#state.'query'], need_array_pass = true, + has_list = true, args = [[Convert, ConvertArr] | State2#state.args], params = [Var | State2#state.params], param_pos = State2#state.param_pos + 1, @@ -467,6 +472,7 @@ make_sql_query(State, Type) -> Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}), SHash = <<"Q", (integer_to_binary(Hash))/binary>>, Query = pack_query(State#state.'query'), + Flags = case State#state.has_list of true -> 1; _ -> 0 end, EQuery = lists:flatmap( fun({str, S}) -> @@ -515,6 +521,9 @@ make_sql_query(State, Type) -> none, [erl_syntax:tuple(State#state.res)] )])), + erl_syntax:record_field( + erl_syntax:atom(flags), + erl_syntax:abstract(Flags)), erl_syntax:record_field( erl_syntax:atom(loc), erl_syntax:abstract({get(?MOD), State#state.loc})) From da7fe59834b5ba1f254f783235caaf36f2c4f183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 25 Jul 2023 18:24:24 +0200 Subject: [PATCH 0153/1302] Commit changes to ejabberd_sql.hrl as well --- include/ejabberd_sql.hrl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index c690261ef..c622efcd7 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -34,12 +34,14 @@ format_query :: fun(), format_res :: fun(), args :: fun(), + flags :: pos_integer(), loc :: {module(), pos_integer()}}). -else. -record(sql_query, {hash :: binary(), format_query :: fun(), format_res :: fun(), args :: fun(), + flags :: pos_integer(), loc :: {module(), {pos_integer(), pos_integer()}}}). -endif. From 84ee724aa3018794e471ec0b6d8ef7dad3f5f144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 25 Jul 2023 18:49:45 +0200 Subject: [PATCH 0154/1302] Fix dialyzer warning --- include/ejabberd_sql.hrl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index c622efcd7..1734b1cb1 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -34,14 +34,14 @@ format_query :: fun(), format_res :: fun(), args :: fun(), - flags :: pos_integer(), + flags :: non_neg_integer(), loc :: {module(), pos_integer()}}). -else. -record(sql_query, {hash :: binary(), format_query :: fun(), format_res :: fun(), args :: fun(), - flags :: pos_integer(), + flags :: non_neg_integer(), loc :: {module(), {pos_integer(), pos_integer()}}}). -endif. From a7c3368635011dcb8049483e57b49d776df14ec6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jul 2023 13:23:13 +0200 Subject: [PATCH 0155/1302] Move example Perl extauth script from ejabberd git to Docs site --- examples/extauth/check_pass_null.pl | 66 ----------------------------- 1 file changed, 66 deletions(-) delete mode 100755 examples/extauth/check_pass_null.pl diff --git a/examples/extauth/check_pass_null.pl b/examples/extauth/check_pass_null.pl deleted file mode 100755 index cbf179202..000000000 --- a/examples/extauth/check_pass_null.pl +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/perl - -use Unix::Syslog qw(:macros :subs); - -my $domain = $ARGV[0] || "example.com"; - -while(1) - { - # my $rin = '',$rout; - # vec($rin,fileno(STDIN),1) = 1; - # $ein = $rin; - # my $nfound = select($rout=$rin,undef,undef,undef); - - my $buf = ""; - syslog LOG_INFO,"waiting for packet"; - my $nread = sysread STDIN,$buf,2; - do { syslog LOG_INFO,"port closed"; exit; } unless $nread == 2; - my $len = unpack "n",$buf; - my $nread = sysread STDIN,$buf,$len; - - my ($op,$user,$host,$password) = split /:/,$buf; - #$user =~ s/\./\//og; - my $jid = "$user\@$domain"; - my $result; - - syslog(LOG_INFO,"request (%s)", $op); - - SWITCH: - { - $op eq 'auth' and do - { - $result = 1; - },last SWITCH; - - $op eq 'setpass' and do - { - $result = 1; - },last SWITCH; - - $op eq 'isuser' and do - { - # password is null. Return 1 if the user $user\@$domain exitst. - $result = 1; - },last SWITCH; - - $op eq 'tryregister' and do - { - $result = 1; - },last SWITCH; - - $op eq 'removeuser' and do - { - # password is null. Return 1 if the user $user\@$domain exitst. - $result = 1; - },last SWITCH; - - $op eq 'removeuser3' and do - { - $result = 1; - },last SWITCH; - }; - my $out = pack "nn",2,$result ? 1 : 0; - syswrite STDOUT,$out; - } - -closelog; From b501ee2b8d2427b328d20d7a2118949a58ff9655 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jul 2023 13:37:05 +0200 Subject: [PATCH 0156/1302] Remove obsolete example files, and add link in Docs to the archived copies --- examples/mtr/ejabberd | 75 ------------------------------ examples/mtr/ejabberd-netbsd.sh | 81 --------------------------------- examples/mtr/ejabberd.cfg | 65 -------------------------- 3 files changed, 221 deletions(-) delete mode 100644 examples/mtr/ejabberd delete mode 100644 examples/mtr/ejabberd-netbsd.sh delete mode 100644 examples/mtr/ejabberd.cfg diff --git a/examples/mtr/ejabberd b/examples/mtr/ejabberd deleted file mode 100644 index 4328b0697..000000000 --- a/examples/mtr/ejabberd +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# PROVIDE: ejabberd -# REQUIRE: DAEMON -# KEYWORD: shutdown -# - -HOME=/usr/pkg/jabber D=/usr/pkg/jabber/ejabberd export HOME - -name="ejabberd" -rcvar=$name - -if [ -r /etc/rc.conf ] -then - . /etc/rc.conf -else - eval ${rcvar}=YES -fi - -# $flags from environment overrides ${rcvar}_flags -if [ -n "${flags}" ] -then - eval ${rcvar}_flags="${flags}" -fi - -checkyesno() -{ - eval _value=\$${1} - case $_value in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0 ;; - [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1 ;; - *) - echo "\$${1} is not set properly." - return 1 - ;; - esac -} - -cmd=${1:-start} -case ${cmd} in -force*) - cmd=${cmd#force} - eval ${rcvar}=YES - ;; -esac - -if checkyesno ${rcvar} -then -else - exit 0 -fi - -case ${cmd} in -start) - if [ -x $D/src ]; then - echo "Starting ${name}." - cd $D/src - ERL_MAX_PORTS=32000 export ERL_MAX_PORTS - ulimit -n $ERL_MAX_PORTS - su jabber -c "/usr/pkg/bin/erl -sname ejabberd -s ejabberd -heart -detached -sasl sasl_error_logger '{file, \"ejabberd-sasl.log\"}' &" \ - 1>/dev/null 2>&1 - fi - ;; -stop) - echo "rpc:call('ejabberd@`hostname -s`', init, stop, [])." | \ - su jabber -c "/usr/pkg/bin/erl -sname ejabberdstop" - ;; -restart) - echo "rpc:call('ejabberd@`hostname -s`', init, restart, [])." | \ - su jabber -c "/usr/pkg/bin/erl -sname ejabberdrestart" - ;; -*) - echo "Usage: $0 {start|stop|restart}" - exit 1 -esac diff --git a/examples/mtr/ejabberd-netbsd.sh b/examples/mtr/ejabberd-netbsd.sh deleted file mode 100644 index 31d01b6b8..000000000 --- a/examples/mtr/ejabberd-netbsd.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/sh - -echo '1. fetch, compile, and install erlang' - -if [ ! pkg_info erlang 1>/dev/null 2>&1 ]; then - cd /usr/pkgsrc/lang/erlang - make fetch-list|sh - make - make install -fi -if pkg_info erlang | grep -q erlang-9.1nb1; then -else - echo "erlang-9.1nb1 not installed" 1>&2 - exit 1 -fi - - -echo '2. install crypt_drv.so' - -if [ ! -d /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib ] ; then - mkdir -p /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi -if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/crypto_drv.so ]; then - cp work/otp*/lib/crypto/priv/*/*/crypto_drv.so \ - /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi - - -echo '3. compile and install elibcrypto.so' - -if [ ! -f /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib/elibcrypto.so ]; then -cd /usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/crypto/c_src -ld -r -u CRYPTO_set_mem_functions -u MD5 -u MD5_Init -u MD5_Update \ - -u MD5_Final -u SHA1 -u SHA1_Init -u SHA1_Update -u SHA1_Final \ - -u des_set_key -u des_ncbc_encrypt -u des_ede3_cbc_encrypt \ - -L/usr/lib -lcrypto -o ../priv/obj/i386--netbsdelf/elibcrypto.o -cc -shared \ - -L/usr/pkgsrc/lang/erlang/work/otp_src_R9B-1/lib/erl_interface/obj/i386--netbsdelf \ - -o ../priv/obj/i386--netbsdelf/elibcrypto.so \ - ../priv/obj/i386--netbsdelf/elibcrypto.o -L/usr/lib -lcrypto -cp ../priv/obj/i386--netbsdelf/elibcrypto.so \ - /usr/pkg/lib/erlang/lib/crypto-1.1.2.1/priv/lib -fi - - -echo '4. compile and install ssl_esock' - -if [ ! -f /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/bin/ssl_esock ]; then - cd /usr/pkg/lib/erlang/lib/ssl-2.3.5/priv/obj/ - make -fi - - -echo '5. initial ejabberd configuration' - -cd /usr/pkg/jabber/ejabberd/src -./configure - - -echo '6. edit ejabberd Makefiles' - -for M in Makefile mod_*/Makefile; do - if [ ! -f $M.orig ]; then - mv $M $M.orig - sed -e s%/usr/local%/usr/pkg%g < $M.orig > $M - fi -done - - -echo '7. compile ejabberd' - -gmake -for A in mod_muc mod_pubsub; do - (cd $A; gmake) -done - - -echo '' -echo 'now edit ejabberd.cfg' -echo '' -echo 'to start ejabberd: erl -sname ejabberd -s ejabberd' diff --git a/examples/mtr/ejabberd.cfg b/examples/mtr/ejabberd.cfg deleted file mode 100644 index b1023ee0e..000000000 --- a/examples/mtr/ejabberd.cfg +++ /dev/null @@ -1,65 +0,0 @@ -% jabber.dbc.mtview.ca.us - -override_acls. - -{acl, admin, {user, "mrose", "jabber.dbc.mtview.ca.us"}}. - - -{access, announce, [{allow, admin}, - {deny, all}]}. -{access, c2s, [{deny, blocked}, - {allow, all}]}. -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. -{access, configure, [{allow, admin}, - {deny, all}]}. -{access, disco_admin, [{allow, admin}, - {deny, all}]}. -{access, muc_admin, [{allow, admin}, - {deny, all}]}. -{access, register, [{deny, all}]}. -{access, s2s_shaper, [{fast, all}]}. - - -{auth_method, internal}. -{host, "jabber.dbc.mtview.ca.us"}. -{outgoing_s2s_port, 5269}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{welcome_message, none}. - - -{listen, [{5222, ejabberd_c2s, - [{access, c2s}, - {shaper, c2s_shaper}]}, - {5223, ejabberd_c2s, - [{access, c2s}, - {shaper, c2s_shaper}, - {ssl, [{certfile, "/etc/openssl/certs/ejabberd.pem"}]}]}, - {5269, ejabberd_s2s_in, - [{shaper, s2s_shaper}]}]}. - - -{modules, [ - {mod_register, []}, - {mod_roster, []}, - {mod_privacy, []}, - {mod_configure, []}, - {mod_disco, []}, - {mod_stats, []}, - {mod_vcard, []}, - {mod_offline, []}, - {mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]}, - {mod_private, []}, - {mod_muc, []}, - {mod_pubsub, []}, - {mod_time, []}, - {mod_last, []}, - {mod_version, []} - ]}. - - - -% Local Variables: -% mode: erlang -% End: From 5a9099f49cd79bc0a30f31ebb3f40cd7c0d65ff8 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 31 Jul 2023 16:26:39 +0300 Subject: [PATCH 0157/1302] Extend gen_mod API to simplify hooks and IQ handlers registration --- src/gen_mod.erl | 62 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index a3c8cb4fa..8abf9e771 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -49,6 +49,7 @@ -record(ejabberd_module, {module_host = {undefined, <<"">>} :: {atom(), binary()}, opts = [] :: opts() | '_' | '$2', + registrations = [] :: [registration()], order = 0 :: integer()}). -type opts() :: #{atom() => term()}. @@ -57,7 +58,14 @@ value => string() | binary()}. -type opt_doc() :: {atom(), opt_desc()} | {atom(), opt_desc(), [opt_doc()]}. --callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}. +-type component() :: ejabberd_sm | ejabberd_local. +-type registration() :: + {hook, atom(), module(), atom(), integer()} | + {iq_handler, component(), binary(), module(), atom()}. + +-callback start(binary(), opts()) -> + ok | {ok, pid()} | + {ok, [registration()]} | {error, term()}. -callback stop(binary()) -> any(). -callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}. -callback mod_opt_type(atom()) -> econf:validator(). @@ -155,6 +163,10 @@ start_module(Host, Module, Opts, Order) -> try case Module:start(Host, Opts) of ok -> ok; {ok, Pid} when is_pid(Pid) -> {ok, Pid}; + {ok, Registrations} when is_list(Registrations) -> + store_options(Host, Module, Opts, Registrations, Order), + add_registrations(Host, Registrations), + ok; Err -> ets:delete(ejabberd_modules, {Module, Host}), erlang:error({bad_return, Module, Err}) @@ -246,9 +258,21 @@ update_module(Host, Module, Opts) -> -spec store_options(binary(), module(), opts(), integer()) -> true. store_options(Host, Module, Opts, Order) -> + case ets:lookup(ejabberd_modules, {Module, Host}) of + [M] -> + store_options( + Host, Module, Opts, M#ejabberd_module.registrations, Order); + [] -> + store_options(Host, Module, Opts, [], Order) + end. + +-spec store_options(binary(), module(), opts(), [registration()], integer()) -> true. +store_options(Host, Module, Opts, Registrations, Order) -> ets:insert(ejabberd_modules, #ejabberd_module{module_host = {Module, Host}, - opts = Opts, order = Order}). + opts = Opts, + registrations = Registrations, + order = Order}). maybe_halt_ejabberd() -> case is_app_running(ejabberd) of @@ -281,16 +305,21 @@ stop_modules(Host) -> stop_module_keep_config(Host, Module) end, Modules). --spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}. +-spec stop_module(binary(), atom()) -> error | ok. stop_module(Host, Module) -> - case stop_module_keep_config(Host, Module) of - error -> error; - ok -> ok - end. + stop_module_keep_config(Host, Module). -spec stop_module_keep_config(binary(), atom()) -> error | ok. stop_module_keep_config(Host, Module) -> ?DEBUG("Stopping ~ts at ~ts", [Module, Host]), + Registrations = + case ets:lookup(ejabberd_modules, {Module, Host}) of + [M] -> + M#ejabberd_module.registrations; + [] -> + [] + end, + del_registrations(Host, Registrations), try Module:stop(Host) of _ -> ets:delete(ejabberd_modules, {Module, Host}), @@ -303,6 +332,25 @@ stop_module_keep_config(Host, Module) -> error end. +-spec add_registrations(binary(), [registration()]) -> ok. +add_registrations(Host, Registrations) -> + lists:foreach( + fun({hook, Hook, Module, Function, Seq}) -> + ejabberd_hooks:add(Hook, Host, Module, Function, Seq); + ({iq_handler, Component, NS, Module, Function}) -> + gen_iq_handler:add_iq_handler( + Component, Host, NS, Module, Function) + end, Registrations). + +-spec del_registrations(binary(), [registration()]) -> ok. +del_registrations(Host, Registrations) -> + lists:foreach( + fun({hook, Hook, Module, Function, Seq}) -> + ejabberd_hooks:delete(Hook, Host, Module, Function, Seq); + ({iq_handler, Component, NS, _Module, _Function}) -> + gen_iq_handler:remove_iq_handler(Component, Host, NS) + end, Registrations). + -spec get_opt(atom(), opts()) -> any(). get_opt(Opt, Opts) -> maps:get(Opt, Opts). From 03ffbe00c156e011a7ce63ec5c00f5aa38b118ed Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 31 Jul 2023 16:28:54 +0300 Subject: [PATCH 0158/1302] Update mod_disco to the new gen_mod API --- src/mod_disco.erl | 62 ++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 2b77cf762..8f8093793 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -51,16 +51,6 @@ -export_type([features_acc/0, items_acc/0]). start(Host, Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_DISCO_ITEMS, ?MODULE, - process_local_iq_items), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_DISCO_INFO, ?MODULE, - process_local_iq_info), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_DISCO_ITEMS, ?MODULE, process_sm_iq_items), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_DISCO_INFO, ?MODULE, process_sm_iq_info), catch ets:new(disco_extra_domains, [named_table, ordered_set, public, {heir, erlang:group_leader(), none}]), @@ -69,45 +59,23 @@ start(Host, Opts) -> register_extra_domain(Host, Domain) end, ExtraDomains), - ejabberd_hooks:add(disco_local_items, Host, ?MODULE, - get_local_services, 100), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, - get_local_features, 100), - ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, - get_local_identity, 100), - ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, - get_sm_items, 100), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - get_sm_features, 100), - ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 100), - ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, - 100), - ok. + {ok, [{iq_handler, ejabberd_local, ?NS_DISCO_ITEMS, + ?MODULE, process_local_iq_items}, + {iq_handler, ejabberd_local, ?NS_DISCO_INFO, + ?MODULE, process_local_iq_info}, + {iq_handler, ejabberd_sm, ?NS_DISCO_ITEMS, + ?MODULE, process_sm_iq_items}, + {iq_handler, ejabberd_sm, ?NS_DISCO_INFO, + ?MODULE, process_sm_iq_info}, + {hook, disco_local_items, ?MODULE, get_local_services, 100}, + {hook, disco_local_features, ?MODULE, get_local_features, 100}, + {hook, disco_local_identity, ?MODULE, get_local_identity, 100}, + {hook, disco_sm_items, ?MODULE, get_sm_items, 100}, + {hook, disco_sm_features, ?MODULE, get_sm_features, 100}, + {hook, disco_sm_identity, ?MODULE, get_sm_identity, 100}, + {hook, disco_info, ?MODULE, get_info, 100}]}. stop(Host) -> - ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 100), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, - get_sm_features, 100), - ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, - get_sm_items, 100), - ejabberd_hooks:delete(disco_local_identity, Host, - ?MODULE, get_local_identity, 100), - ejabberd_hooks:delete(disco_local_features, Host, - ?MODULE, get_local_features, 100), - ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, - get_local_services, 100), - ejabberd_hooks:delete(disco_info, Host, ?MODULE, - get_info, 100), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_DISCO_ITEMS), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_DISCO_INFO), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_DISCO_ITEMS), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_DISCO_INFO), catch ets:match_delete(disco_extra_domains, {{'_', Host}}), ok. From 4bd77797fc562221c081f84aacc60b0d01a20d7c Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 1 Aug 2023 18:14:08 +0300 Subject: [PATCH 0159/1302] Add shorter forms for gen_mod hook/iq_handler API --- src/gen_mod.erl | 31 +++++++++++++++++++++---------- src/mod_disco.erl | 26 +++++++++++--------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 8abf9e771..4b9f1fc4b 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -60,7 +60,9 @@ -type component() :: ejabberd_sm | ejabberd_local. -type registration() :: + {hook, atom(), atom(), integer()} | {hook, atom(), module(), atom(), integer()} | + {iq_handler, component(), binary(), atom()} | {iq_handler, component(), binary(), module(), atom()}. -callback start(binary(), opts()) -> @@ -165,7 +167,7 @@ start_module(Host, Module, Opts, Order) -> {ok, Pid} when is_pid(Pid) -> {ok, Pid}; {ok, Registrations} when is_list(Registrations) -> store_options(Host, Module, Opts, Registrations, Order), - add_registrations(Host, Registrations), + add_registrations(Host, Module, Registrations), ok; Err -> ets:delete(ejabberd_modules, {Module, Host}), @@ -319,7 +321,7 @@ stop_module_keep_config(Host, Module) -> [] -> [] end, - del_registrations(Host, Registrations), + del_registrations(Host, Module, Registrations), try Module:stop(Host) of _ -> ets:delete(ejabberd_modules, {Module, Host}), @@ -332,21 +334,30 @@ stop_module_keep_config(Host, Module) -> error end. --spec add_registrations(binary(), [registration()]) -> ok. -add_registrations(Host, Registrations) -> +-spec add_registrations(binary(), module(), [registration()]) -> ok. +add_registrations(Host, Module, Registrations) -> lists:foreach( - fun({hook, Hook, Module, Function, Seq}) -> + fun({hook, Hook, Function, Seq}) -> ejabberd_hooks:add(Hook, Host, Module, Function, Seq); - ({iq_handler, Component, NS, Module, Function}) -> + ({hook, Hook, Module1, Function, Seq}) -> + ejabberd_hooks:add(Hook, Host, Module1, Function, Seq); + ({iq_handler, Component, NS, Function}) -> gen_iq_handler:add_iq_handler( - Component, Host, NS, Module, Function) + Component, Host, NS, Module, Function); + ({iq_handler, Component, NS, Module1, Function}) -> + gen_iq_handler:add_iq_handler( + Component, Host, NS, Module1, Function) end, Registrations). --spec del_registrations(binary(), [registration()]) -> ok. -del_registrations(Host, Registrations) -> +-spec del_registrations(binary(), module(), [registration()]) -> ok. +del_registrations(Host, Module, Registrations) -> lists:foreach( - fun({hook, Hook, Module, Function, Seq}) -> + fun({hook, Hook, Function, Seq}) -> ejabberd_hooks:delete(Hook, Host, Module, Function, Seq); + ({hook, Hook, Module1, Function, Seq}) -> + ejabberd_hooks:delete(Hook, Host, Module1, Function, Seq); + ({iq_handler, Component, NS, _Function}) -> + gen_iq_handler:remove_iq_handler(Component, Host, NS); ({iq_handler, Component, NS, _Module, _Function}) -> gen_iq_handler:remove_iq_handler(Component, Host, NS) end, Registrations). diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 8f8093793..ed4a37caa 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -59,21 +59,17 @@ start(Host, Opts) -> register_extra_domain(Host, Domain) end, ExtraDomains), - {ok, [{iq_handler, ejabberd_local, ?NS_DISCO_ITEMS, - ?MODULE, process_local_iq_items}, - {iq_handler, ejabberd_local, ?NS_DISCO_INFO, - ?MODULE, process_local_iq_info}, - {iq_handler, ejabberd_sm, ?NS_DISCO_ITEMS, - ?MODULE, process_sm_iq_items}, - {iq_handler, ejabberd_sm, ?NS_DISCO_INFO, - ?MODULE, process_sm_iq_info}, - {hook, disco_local_items, ?MODULE, get_local_services, 100}, - {hook, disco_local_features, ?MODULE, get_local_features, 100}, - {hook, disco_local_identity, ?MODULE, get_local_identity, 100}, - {hook, disco_sm_items, ?MODULE, get_sm_items, 100}, - {hook, disco_sm_features, ?MODULE, get_sm_features, 100}, - {hook, disco_sm_identity, ?MODULE, get_sm_identity, 100}, - {hook, disco_info, ?MODULE, get_info, 100}]}. + {ok, [{iq_handler, ejabberd_local, ?NS_DISCO_ITEMS, process_local_iq_items}, + {iq_handler, ejabberd_local, ?NS_DISCO_INFO, process_local_iq_info}, + {iq_handler, ejabberd_sm, ?NS_DISCO_ITEMS, process_sm_iq_items}, + {iq_handler, ejabberd_sm, ?NS_DISCO_INFO, process_sm_iq_info}, + {hook, disco_local_items, get_local_services, 100}, + {hook, disco_local_features, get_local_features, 100}, + {hook, disco_local_identity, get_local_identity, 100}, + {hook, disco_sm_items, get_sm_items, 100}, + {hook, disco_sm_features, get_sm_features, 100}, + {hook, disco_sm_identity, get_sm_identity, 100}, + {hook, disco_info, get_info, 100}]}. stop(Host) -> catch ets:match_delete(disco_extra_domains, From 07d428260355ddb229c83220cda1fc228f4c4a90 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 2 Aug 2023 17:48:14 +0200 Subject: [PATCH 0160/1302] Update XMPP library which supports XEP-0421 (3397) --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 8a1615778..b11687cc5 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.6.2"}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "3d3d98b996dd7569c57df5fb966b9dfb985c58f3", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 386187d4e..70e51fcc1 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.6.2"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3d3d98b996dd7569c57df5fb966b9dfb985c58f3"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 25411333da0fffce3c29eae91324b59cd78f9501 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 2 Aug 2023 13:42:09 +0200 Subject: [PATCH 0161/1302] Add support for XEP-0421 Occupant Id in anonymous MUC rooms (#3397) --- src/mod_muc.erl | 6 ++- src/mod_muc_occupantid.erl | 105 +++++++++++++++++++++++++++++++++++++ src/mod_muc_room.erl | 6 +++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/mod_muc_occupantid.erl diff --git a/src/mod_muc.erl b/src/mod_muc.erl index e1da09a7e..3af28e156 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -715,6 +715,10 @@ process_disco_info(#iq{type = get, from = From, to = To, lang = Lang, true -> [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2]; false -> [] end, + OccupantIdFeatures = case gen_mod:is_loaded(ServerHost, mod_muc_occupantid) of + true -> [?NS_OCCUPANT_ID]; + false -> [] + end, RSMFeatures = case RMod:rsm_supported() of true -> [?NS_RSM]; false -> [] @@ -725,7 +729,7 @@ process_disco_info(#iq{type = get, from = From, to = To, lang = Lang, end, Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE - | RegisterFeatures ++ RSMFeatures ++ MAMFeatures], + | RegisterFeatures ++ RSMFeatures ++ MAMFeatures ++ OccupantIdFeatures], Name = mod_muc_opt:name(ServerHost), Identity = #identity{category = <<"conference">>, type = <<"text">>, diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl new file mode 100644 index 000000000..ccb4a2134 --- /dev/null +++ b/src/mod_muc_occupantid.erl @@ -0,0 +1,105 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_muc_occupantid.erl +%%% Author : Badlop +%%% Purpose : Add Occupant Ids to stanzas in anonymous MUC rooms (XEP-0421) +%%% Created : +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_muc_occupantid). + +-author('badlop@process-one.net'). + +-protocol({xep, 421, '0.1.0'}). + +-behaviour(gen_mod). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). +-include("mod_muc_room.hrl"). + +-export([start/2, stop/1, + mod_options/1, mod_doc/0, depends/2]). +-export([filter_packet/3]). + +%%% +%%% gen_mod +%%% + +start(Host, _Opts) -> + ejabberd_hooks:add(muc_filter_presence, Host, + ?MODULE, filter_packet, 10), + ejabberd_hooks:add(muc_filter_message, Host, + ?MODULE, filter_packet, 10), + ok. + +stop(Host) -> + ejabberd_hooks:delete(muc_filter_presence, Host, + ?MODULE, filter_packet, 10), + ejabberd_hooks:delete(muc_filter_message, Host, + ?MODULE, filter_packet, 10), + ok. + +%%% +%%% Hooks +%%% + +filter_packet(Packet, State, _Nick) -> + case (State#state.config)#config.anonymous of + true -> + add_occupantid_packet(Packet, State#state.jid); + false -> + Packet + end. + +%%% +%%% XEP-0421 Occupant-id +%%% + +add_occupantid_packet(Packet, RoomJid) -> + From = xmpp:get_from(Packet), + OccupantId = calculate_occupantid(From, RoomJid), + OccupantElement = #occupant_id{id = OccupantId}, + xmpp:set_subtag(Packet, OccupantElement). + +calculate_occupantid(From, RoomJid) -> + Term = {jid:remove_resource(From), RoomJid, erlang:get_cookie()}, + misc:term_to_base64(crypto:hash(sha256, io_lib:format("~p", [Term]))). + +%%% +%%% Doc +%%% + +mod_options(_Host) -> + []. + +mod_doc() -> + #{desc => + [?T("This module implements " + "https://xmpp.org/extensions/xep-0421.html" + "[XEP-0421: Anonymous unique occupant identifiers for MUCs]."), "", + ?T("When the module is enabled, the feature is enabled " + "in all semi-anonymous rooms."), "", + ?T("This module is available since ejabberd 23.xx.")] + }. + +depends(_, _) -> + [{mod_muc, hard}]. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f6c5c4bb3..9c9b328d7 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4362,6 +4362,12 @@ make_disco_info(_From, StateData) -> true -> [?NS_MUCSUB]; false -> [] end + ++ case gen_mod:is_loaded(StateData#state.server_host, mod_muc_occupantid) of + true -> + [?NS_OCCUPANT_ID]; + _ -> + [] + end ++ case {gen_mod:is_loaded(StateData#state.server_host, mod_mam), Config#config.mam} of {true, true} -> From 20a8654be2eb6575bff0e2d75fbe9216e85b7508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 3 Aug 2023 13:06:01 +0200 Subject: [PATCH 0162/1302] Reset scram fields when setting plain password in ejabberd_auth_sql Setting scram password, then disabling scram and setting plain password again, will make us think we are still using scramed password and crash when trying to process it as such. This makes sure that when set plain password we don't leave parts from old scram password. --- src/ejabberd_auth_sql.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 566152695..1871d3a71 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -161,7 +161,10 @@ set_password_t(LUser, LServer, Password) -> "users", ["!username=%(LUser)s", "!server_host=%(LServer)s", - "password=%(Password)s"]). + "password=%(Password)s", + "serverkey=''", + "salt=''", + "iterationcount=0"]). get_password_scram(LServer, LUser) -> ejabberd_sql:sql_query( From 60002fc145c826c4ad5662bf08185bcbc6a26056 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 4 Aug 2023 18:53:50 +0300 Subject: [PATCH 0163/1302] Update some modules to the new gen_mod API --- src/mod_adhoc.erl | 55 ++++++++-------------------------- src/mod_avatar.erl | 23 +++++--------- src/mod_block_strangers.erl | 20 ++++--------- src/mod_blocking.erl | 12 ++++---- src/mod_carboncopy.erl | 29 +++++++----------- src/mod_configure.erl | 56 ++++++++-------------------------- src/mod_last.erl | 33 +++++--------------- src/mod_legacy_auth.erl | 15 ++++------ src/mod_metrics.erl | 32 ++++++++------------ src/mod_mqtt_bridge.erl | 5 ++-- src/mod_offline.erl | 60 ++++++++++--------------------------- src/mod_pres_counter.erl | 10 ++----- src/mod_privacy.erl | 33 +++++--------------- src/mod_private.erl | 14 ++++----- src/mod_register.erl | 26 +++++----------- src/mod_roster.erl | 53 ++++++++------------------------ src/mod_s2s_dialback.erl | 56 +++++++++------------------------- src/mod_service_log.erl | 15 +++------- src/mod_shared_roster.erl | 59 ++++++++---------------------------- src/mod_sic.erl | 21 +++++-------- src/mod_stats.erl | 11 ++++--- src/mod_stream_mgmt.erl | 48 +++++++++-------------------- src/mod_time.erl | 10 +++---- src/mod_vcard_xupdate.erl | 21 ++++--------- 24 files changed, 200 insertions(+), 517 deletions(-) diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 3075366c4..8a76e2bfc 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -42,49 +42,20 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_COMMANDS, ?MODULE, process_local_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_COMMANDS, ?MODULE, process_sm_iq), - ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, - get_local_identity, 99), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, - get_local_features, 99), - ejabberd_hooks:add(disco_local_items, Host, ?MODULE, - get_local_commands, 99), - ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 99), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - get_sm_features, 99), - ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, - get_sm_commands, 99), - ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, - ping_item, 100), - ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, - ping_command, 100). +start(_Host, _Opts) -> + {ok, [{iq_handler, ejabberd_local, ?NS_COMMANDS, process_local_iq}, + {iq_handler, ejabberd_sm, ?NS_COMMANDS, process_sm_iq}, + {hook, disco_local_identity, get_local_identity, 99}, + {hook, disco_local_features, get_local_features, 99}, + {hook, disco_local_items, get_local_commands, 99}, + {hook, disco_sm_identity, get_sm_identity, 99}, + {hook, disco_sm_features, get_sm_features, 99}, + {hook, disco_sm_items, get_sm_commands, 99}, + {hook, adhoc_local_items, ping_item, 100}, + {hook, adhoc_local_commands, ping_command, 100}]}. -stop(Host) -> - ejabberd_hooks:delete(adhoc_local_commands, Host, - ?MODULE, ping_command, 100), - ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, - ping_item, 100), - ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, - get_sm_commands, 99), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, - get_sm_features, 99), - ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 99), - ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, - get_local_commands, 99), - ejabberd_hooks:delete(disco_local_features, Host, - ?MODULE, get_local_features, 99), - ejabberd_hooks:delete(disco_local_identity, Host, - ?MODULE, get_local_identity, 99), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_COMMANDS), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_COMMANDS). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index 58b4a8af6..ae7e74ff0 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -43,23 +43,14 @@ %%%=================================================================== %%% API %%%=================================================================== -start(Host, _Opts) -> - ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, - pubsub_publish_item, 50), - ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, - vcard_iq_convert, 30), - ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, - vcard_iq_publish, 100), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - get_sm_features, 50). +start(_Host, _Opts) -> + {ok, [{hook, pubsub_publish_item, pubsub_publish_item, 50}, + {hook, vcard_iq_set, vcard_iq_convert, 30}, + {hook, vcard_iq_set, vcard_iq_publish, 100}, + {hook, disco_sm_features, get_sm_features, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, - pubsub_publish_item, 50), - ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30), - ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, - get_sm_features, 50). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index 8595b4aa0..7859e0291 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -45,21 +45,13 @@ %%%=================================================================== %%% Callbacks and hooks %%%=================================================================== -start(Host, _Opts) -> - ejabberd_hooks:add(user_receive_packet, Host, - ?MODULE, filter_packet, 25), - ejabberd_hooks:add(roster_in_subscription, Host, - ?MODULE, filter_subscription, 25), - ejabberd_hooks:add(offline_message_hook, Host, - ?MODULE, filter_offline_msg, 25). +start(_Host, _Opts) -> + {ok, [{hook, user_receive_packet, filter_packet, 25}, + {hook, roster_in_subscription, filter_subscription, 25}, + {hook, offline_message_hook, filter_offline_msg, 25}]}. -stop(Host) -> - ejabberd_hooks:delete(user_receive_packet, Host, - ?MODULE, filter_packet, 25), - ejabberd_hooks:delete(roster_in_subscription, Host, - ?MODULE, filter_subscription, 25), - ejabberd_hooks:delete(offline_message_hook, Host, - ?MODULE, filter_offline_msg, 25). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index 75f9aa0b9..efc8f6e11 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -37,14 +37,12 @@ -include("mod_privacy.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_BLOCKING, ?MODULE, process_iq). +start(_Host, _Opts) -> + {ok, [{hook, disco_local_features, disco_features, 50}, + {iq_handler, ejabberd_sm, ?NS_BLOCKING, process_iq}]}. -stop(Host) -> - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index c9fe73b0a..1976e5827 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -48,25 +48,18 @@ -type direction() :: sent | received. -type c2s_state() :: ejabberd_c2s:state(). -start(Host, _Opts) -> - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), - %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) - ejabberd_hooks:add(user_send_packet,Host, ?MODULE, user_send_packet, 89), - ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), - ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), - ejabberd_hooks:add(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler). +start(_Host, _Opts) -> + {ok, [{hook, disco_local_features, disco_features, 50}, + %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) + {hook, user_send_packet, user_send_packet, 89}, + {hook, user_receive_packet, user_receive_packet, 89}, + {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, c2s_session_resumed, c2s_session_resumed, 50}, + {hook, c2s_session_opened, c2s_session_opened, 50}, + {iq_handler, ejabberd_sm, ?NS_CARBONS_2, iq_handler}]}. -stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2), - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), - %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) - ejabberd_hooks:delete(user_send_packet,Host, ?MODULE, user_send_packet, 89), - ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), - ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), - ejabberd_hooks:delete(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 3aec8e106..a36439577 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -44,50 +44,20 @@ -include("translate.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -start(Host, _Opts) -> - ejabberd_hooks:add(disco_local_items, Host, ?MODULE, - get_local_items, 50), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, - get_local_features, 50), - ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, - get_local_identity, 50), - ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, - get_sm_items, 50), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - get_sm_features, 50), - ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 50), - ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, - adhoc_local_items, 50), - ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, - adhoc_local_commands, 50), - ejabberd_hooks:add(adhoc_sm_items, Host, ?MODULE, - adhoc_sm_items, 50), - ejabberd_hooks:add(adhoc_sm_commands, Host, ?MODULE, - adhoc_sm_commands, 50), - ok. +start(_Host, _Opts) -> + {ok, [{hook, disco_local_items, get_local_items, 50}, + {hook, disco_local_features, get_local_features, 50}, + {hook, disco_local_identity, get_local_identity, 50}, + {hook, disco_sm_items, get_sm_items, 50}, + {hook, disco_sm_features, get_sm_features, 50}, + {hook, disco_sm_identity, get_sm_identity, 50}, + {hook, adhoc_local_items, adhoc_local_items, 50}, + {hook, adhoc_local_commands, adhoc_local_commands, 50}, + {hook, adhoc_sm_items, adhoc_sm_items, 50}, + {hook, adhoc_sm_commands, adhoc_sm_commands, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(adhoc_sm_commands, Host, ?MODULE, - adhoc_sm_commands, 50), - ejabberd_hooks:delete(adhoc_sm_items, Host, ?MODULE, - adhoc_sm_items, 50), - ejabberd_hooks:delete(adhoc_local_commands, Host, - ?MODULE, adhoc_local_commands, 50), - ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, - adhoc_local_items, 50), - ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, - get_sm_identity, 50), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, - get_sm_features, 50), - ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, - get_sm_items, 50), - ejabberd_hooks:delete(disco_local_identity, Host, - ?MODULE, get_local_identity, 50), - ejabberd_hooks:delete(disco_local_features, Host, - ?MODULE, get_local_features, 50), - ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, - get_local_items, 50). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_last.erl b/src/mod_last.erl index be7abd561..55cc43657 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -62,32 +62,15 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_LAST, ?MODULE, process_local_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_LAST, ?MODULE, process_sm_iq), - ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, - privacy_check_packet, 30), - ejabberd_hooks:add(register_user, Host, ?MODULE, - register_user, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:add(unset_presence_hook, Host, ?MODULE, - on_presence_update, 50). + {ok, [{iq_handler, ejabberd_local, ?NS_LAST, process_local_iq}, + {iq_handler, ejabberd_sm, ?NS_LAST, process_sm_iq}, + {hook, privacy_check_packet, privacy_check_packet, 30}, + {hook, register_user, register_user, 50}, + {hook, remove_user, remove_user, 50}, + {hook, unset_presence_hook, on_presence_update, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(register_user, Host, ?MODULE, - register_user, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:delete(unset_presence_hook, Host, - ?MODULE, on_presence_update, 50), - ejabberd_hooks:delete(privacy_check_packet, Host, ?MODULE, - privacy_check_packet, 30), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_LAST), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_LAST). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl index 98daa18ad..57f659dec 100644 --- a/src/mod_legacy_auth.erl +++ b/src/mod_legacy_auth.erl @@ -37,17 +37,12 @@ %%%=================================================================== %%% API %%%=================================================================== -start(Host, _Opts) -> - ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, - c2s_unauthenticated_packet, 50), - ejabberd_hooks:add(c2s_pre_auth_features, Host, ?MODULE, - c2s_stream_features, 50). +start(_Host, _Opts) -> + {ok, [{hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50}, + {hook, c2s_pre_auth_features, c2s_stream_features, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, - c2s_unauthenticated_packet, 50), - ejabberd_hooks:delete(c2s_pre_auth_features, Host, ?MODULE, - c2s_stream_features, 50). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl index e3dd397f7..38822bef5 100644 --- a/src/mod_metrics.erl +++ b/src/mod_metrics.erl @@ -49,27 +49,19 @@ %% API %%==================================================================== -start(Host, _Opts) -> - ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, offline_message_hook, 20), - ejabberd_hooks:add(sm_register_connection_hook, Host, ?MODULE, sm_register_connection_hook, 20), - ejabberd_hooks:add(sm_remove_connection_hook, Host, ?MODULE, sm_remove_connection_hook, 20), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, user_send_packet, 20), - ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, user_receive_packet, 20), - ejabberd_hooks:add(s2s_send_packet, Host, ?MODULE, s2s_send_packet, 20), - ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 20), - ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 20), - ejabberd_hooks:add(register_user, Host, ?MODULE, register_user, 20). +start(_Host, _Opts) -> + {ok, [{hook, offline_message_hook, offline_message_hook, 20}, + {hook, sm_register_connection_hook, sm_register_connection_hook, 20}, + {hook, sm_remove_connection_hook, sm_remove_connection_hook, 20}, + {hook, user_send_packet, user_send_packet, 20}, + {hook, user_receive_packet, user_receive_packet, 20}, + {hook, s2s_send_packet, s2s_send_packet, 20}, + {hook, s2s_receive_packet, s2s_receive_packet, 20}, + {hook, remove_user, remove_user, 20}, + {hook, register_user, register_user, 20}]}. -stop(Host) -> - ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, offline_message_hook, 20), - ejabberd_hooks:delete(sm_register_connection_hook, Host, ?MODULE, sm_register_connection_hook, 20), - ejabberd_hooks:delete(sm_remove_connection_hook, Host, ?MODULE, sm_remove_connection_hook, 20), - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, user_send_packet, 20), - ejabberd_hooks:delete(user_receive_packet, Host, ?MODULE, user_receive_packet, 20), - ejabberd_hooks:delete(s2s_send_packet, Host, ?MODULE, s2s_send_packet, 20), - ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 20), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 20), - ejabberd_hooks:delete(register_user, Host, ?MODULE, register_user, 20). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 4cf8a5891..74a771d82 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -32,13 +32,12 @@ %%%=================================================================== %%% API %%%=================================================================== -start(Host, Opts) -> +start(_Host, Opts) -> User = mod_mqtt_bridge_opt:replication_user(Opts), start_servers(User, element(1, mod_mqtt_bridge_opt:servers(Opts))), - ejabberd_hooks:add(mqtt_publish, Host, ?MODULE, mqtt_publish_hook, 50). + {ok, [{hook, mqtt_publish, mqtt_publish_hook, 50}]}. stop(Host) -> - ejabberd_hooks:delete(mqtt_publish, Host, ?MODULE, mqtt_publish_hook, 50), stop_servers(element(1, mod_mqtt_bridge_opt:servers(Host))), ok. diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 4840ced5f..d7de2ccd4 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -120,51 +120,23 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, - store_packet, 50), - ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), - ejabberd_hooks:add(remove_user, Host, - ?MODULE, remove_user, 50), - ejabberd_hooks:add(disco_sm_features, Host, - ?MODULE, get_sm_features, 50), - ejabberd_hooks:add(disco_local_features, Host, - ?MODULE, get_sm_features, 50), - ejabberd_hooks:add(disco_sm_identity, Host, - ?MODULE, get_sm_identity, 50), - ejabberd_hooks:add(disco_sm_items, Host, - ?MODULE, get_sm_items, 50), - ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), - ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), - ejabberd_hooks:add(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, Host, - ?MODULE, webadmin_user, 50), - ejabberd_hooks:add(webadmin_user_parse_query, Host, - ?MODULE, webadmin_user_parse_query, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE, - ?MODULE, handle_offline_query). + {ok, [{hook, offline_message_hook, store_packet, 50}, + {hook, c2s_self_presence, c2s_self_presence, 50}, + {hook, remove_user, remove_user, 50}, + {hook, disco_sm_features, get_sm_features, 50}, + {hook, disco_local_features, get_sm_features, 50}, + {hook, disco_sm_identity, get_sm_identity, 50}, + {hook, disco_sm_items, get_sm_items, 50}, + {hook, disco_info, get_info, 50}, + {hook, c2s_handle_info, c2s_handle_info, 50}, + {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, webadmin_page_host, webadmin_page, 50}, + {hook, webadmin_user, webadmin_user, 50}, + {hook, webadmin_user_parse_query, webadmin_user_parse_query, 50}, + {iq_handler, ejabberd_sm, ?NS_FLEX_OFFLINE, handle_offline_query}]}. -stop(Host) -> - ejabberd_hooks:delete(offline_message_hook, Host, - ?MODULE, store_packet, 50), - ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, c2s_self_presence, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_sm_features, 50), - ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 50), - ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 50), - ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 50), - ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, - ?MODULE, webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, Host, - ?MODULE, webadmin_user, 50), - ejabberd_hooks:delete(webadmin_user_parse_query, Host, - ?MODULE, webadmin_user_parse_query, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl index 7359260bc..04319d1c2 100644 --- a/src/mod_pres_counter.erl +++ b/src/mod_pres_counter.erl @@ -37,14 +37,10 @@ -record(pres_counter, {dir, start, count, logged = false}). -start(Host, _Opts) -> - ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, - check_packet, 25), - ok. +start(_Host, _Opts) -> + {ok, [{hook, privacy_check_packet, check_packet, 25}]}. -stop(Host) -> - ejabberd_hooks:delete(privacy_check_packet, Host, - ?MODULE, check_packet, 25), +stop(_Host) -> ok. reload(_Host, _NewOpts, _OldOpts) -> diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 37f1eb8bc..25717cadb 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -73,32 +73,15 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, - disco_features, 50), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send_packet, 50), - ejabberd_hooks:add(privacy_check_packet, Host, ?MODULE, - check_packet, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_PRIVACY, ?MODULE, process_iq). + {ok, [{hook, disco_local_features, disco_features, 50}, + {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, user_send_packet, user_send_packet, 50}, + {hook, privacy_check_packet, check_packet, 50}, + {hook, remove_user, remove_user, 50}, + {iq_handler, ejabberd_sm, ?NS_PRIVACY, process_iq}]}. -stop(Host) -> - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, - disco_features, 50), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - user_send_packet, 50), - ejabberd_hooks:delete(privacy_check_packet, Host, - ?MODULE, check_packet, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_PRIVACY). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_private.erl b/src/mod_private.erl index 52768291d..d5e252669 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -62,17 +62,13 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), - ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq), - ejabberd_commands:register_commands(?MODULE, get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + {ok, [{hook, remove_user, remove_user, 50}, + {hook, disco_sm_features, get_sm_features, 50}, + {hook, pubsub_publish_item, pubsub_publish_item, 50}, + {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. stop(Host) -> - ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), - ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE), case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> ejabberd_commands:unregister_commands(get_commands_spec()); diff --git a/src/mod_register.erl b/src/mod_register.erl index 7b05d4fff..c7edb66df 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -43,29 +43,17 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_REGISTER, ?MODULE, process_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_REGISTER, ?MODULE, process_iq), - ejabberd_hooks:add(c2s_pre_auth_features, Host, ?MODULE, - stream_feature_register, 50), - ejabberd_hooks:add(c2s_unauthenticated_packet, Host, - ?MODULE, c2s_unauthenticated_packet, 50), +start(_Host, _Opts) -> ejabberd_mnesia:create(?MODULE, mod_register_ip, [{ram_copies, [node()]}, {local_content, true}, {attributes, [key, value]}]), - ok. + {ok, [{iq_handler, ejabberd_local, ?NS_REGISTER, process_iq}, + {iq_handler, ejabberd_sm, ?NS_REGISTER, process_iq}, + {hook, c2s_pre_auth_features, stream_feature_register, 50}, + {hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(c2s_pre_auth_features, Host, - ?MODULE, stream_feature_register, 50), - ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, - ?MODULE, c2s_unauthenticated_packet, 50), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_REGISTER), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_REGISTER). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 892c6cc30..ad5fc0f77 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -91,48 +91,19 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_hooks:add(roster_get, Host, ?MODULE, - get_user_roster_items, 50), - ejabberd_hooks:add(roster_in_subscription, Host, - ?MODULE, in_subscription, 50), - ejabberd_hooks:add(roster_out_subscription, Host, - ?MODULE, out_subscription, 50), - ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, - get_jid_info, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, - c2s_self_presence, 50), - ejabberd_hooks:add(c2s_post_auth_features, Host, - ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, - webadmin_page, 50), - ejabberd_hooks:add(webadmin_user, Host, ?MODULE, - webadmin_user, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_ROSTER, ?MODULE, process_iq). + {ok, [{hook, roster_get, get_user_roster_items, 50}, + {hook, roster_in_subscription, in_subscription, 50}, + {hook, roster_out_subscription, out_subscription, 50}, + {hook, roster_get_jid_info, get_jid_info, 50}, + {hook, remove_user, remove_user, 50}, + {hook, c2s_self_presence, c2s_self_presence, 50}, + {hook, c2s_post_auth_features, get_versioning_feature, 50}, + {hook, webadmin_page_host, webadmin_page, 50}, + {hook, webadmin_user, webadmin_user, 50}, + {iq_handler, ejabberd_sm, ?NS_ROSTER, process_iq}]}. -stop(Host) -> - ejabberd_hooks:delete(roster_get, Host, ?MODULE, - get_user_roster_items, 50), - ejabberd_hooks:delete(roster_in_subscription, Host, - ?MODULE, in_subscription, 50), - ejabberd_hooks:delete(roster_out_subscription, Host, - ?MODULE, out_subscription, 50), - ejabberd_hooks:delete(roster_get_jid_info, Host, - ?MODULE, get_jid_info, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, 50), - ejabberd_hooks:delete(c2s_self_presence, Host, ?MODULE, - c2s_self_presence, 50), - ejabberd_hooks:delete(c2s_post_auth_features, - Host, ?MODULE, get_versioning_feature, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, - webadmin_page, 50), - ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, - webadmin_user, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_ROSTER). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index cd0a9b8f6..527388d87 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -40,49 +40,21 @@ %%%=================================================================== %%% API %%%=================================================================== -start(Host, _Opts) -> - ejabberd_hooks:add(s2s_out_init, Host, ?MODULE, s2s_out_init, 50), - ejabberd_hooks:add(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50), - ejabberd_hooks:add(s2s_in_pre_auth_features, Host, ?MODULE, - s2s_in_features, 50), - ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE, - s2s_in_features, 50), - ejabberd_hooks:add(s2s_in_handle_recv, Host, ?MODULE, - s2s_in_recv, 50), - ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE, - s2s_in_packet, 50), - ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE, - s2s_in_packet, 50), - ejabberd_hooks:add(s2s_out_packet, Host, ?MODULE, - s2s_out_packet, 50), - ejabberd_hooks:add(s2s_out_downgraded, Host, ?MODULE, - s2s_out_downgraded, 50), - ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, - s2s_out_auth_result, 50), - ejabberd_hooks:add(s2s_out_tls_verify, Host, ?MODULE, - s2s_out_tls_verify, 50). +start(_Host, _Opts) -> + {ok, [{hook, s2s_out_init, s2s_out_init, 50}, + {hook, s2s_out_closed, s2s_out_closed, 50}, + {hook, s2s_in_pre_auth_features, s2s_in_features, 50}, + {hook, s2s_in_post_auth_features, s2s_in_features, 50}, + {hook, s2s_in_handle_recv, s2s_in_recv, 50}, + {hook, s2s_in_unauthenticated_packet, s2s_in_packet, 50}, + {hook, s2s_in_authenticated_packet, s2s_in_packet, 50}, + {hook, s2s_out_packet, s2s_out_packet, 50}, + {hook, s2s_out_downgraded, s2s_out_downgraded, 50}, + {hook, s2s_out_auth_result, s2s_out_auth_result, 50}, + {hook, s2s_out_tls_verify, s2s_out_tls_verify, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(s2s_out_init, Host, ?MODULE, s2s_out_init, 50), - ejabberd_hooks:delete(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50), - ejabberd_hooks:delete(s2s_in_pre_auth_features, Host, ?MODULE, - s2s_in_features, 50), - ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE, - s2s_in_features, 50), - ejabberd_hooks:delete(s2s_in_handle_recv, Host, ?MODULE, - s2s_in_recv, 50), - ejabberd_hooks:delete(s2s_in_unauthenticated_packet, Host, ?MODULE, - s2s_in_packet, 50), - ejabberd_hooks:delete(s2s_in_authenticated_packet, Host, ?MODULE, - s2s_in_packet, 50), - ejabberd_hooks:delete(s2s_out_packet, Host, ?MODULE, - s2s_out_packet, 50), - ejabberd_hooks:delete(s2s_out_downgraded, Host, ?MODULE, - s2s_out_downgraded, 50), - ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, - s2s_out_auth_result, 50), - ejabberd_hooks:delete(s2s_out_tls_verify, Host, ?MODULE, - s2s_out_tls_verify, 50). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl index c387ce04c..b7686b693 100644 --- a/src/mod_service_log.erl +++ b/src/mod_service_log.erl @@ -36,18 +36,11 @@ -include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -start(Host, _Opts) -> - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - log_user_send, 50), - ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, - log_user_receive, 50), - ok. +start(_Host, _Opts) -> + {ok, [{hook, user_send_packet, log_user_send, 50}, + {hook, user_receive_packet, log_user_receive, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - log_user_send, 50), - ejabberd_hooks:delete(user_receive_packet, Host, - ?MODULE, log_user_receive, 50), +stop(_Host) -> ok. depends(_Host, _Opts) -> diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 579992739..9f94086c1 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -85,53 +85,20 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, - webadmin_menu, 70), - ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, - webadmin_page, 50), - ejabberd_hooks:add(roster_get, Host, ?MODULE, - get_user_roster, 70), - ejabberd_hooks:add(roster_in_subscription, Host, - ?MODULE, in_subscription, 30), - ejabberd_hooks:add(roster_out_subscription, Host, - ?MODULE, out_subscription, 30), - ejabberd_hooks:add(roster_get_jid_info, Host, ?MODULE, - get_jid_info, 70), - ejabberd_hooks:add(roster_process_item, Host, ?MODULE, - process_item, 50), - ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, - c2s_self_presence, 50), - ejabberd_hooks:add(unset_presence_hook, Host, ?MODULE, - unset_presence, 50), - ejabberd_hooks:add(register_user, Host, ?MODULE, - register_user, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50). + {ok, [{hook, webadmin_menu_host, webadmin_menu, 70}, + {hook, webadmin_page_host, webadmin_page, 50}, + {hook, roster_get, get_user_roster, 70}, + {hook, roster_in_subscription, in_subscription, 30}, + {hook, roster_out_subscription, out_subscription, 30}, + {hook, roster_get_jid_info, get_jid_info, 70}, + {hook, roster_process_item, process_item, 50}, + {hook, c2s_self_presence, c2s_self_presence, 50}, + {hook, unset_presence_hook, unset_presence, 50}, + {hook, register_user, register_user, 50}, + {hook, remove_user, remove_user, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, - webadmin_menu, 70), - ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, - webadmin_page, 50), - ejabberd_hooks:delete(roster_get, Host, ?MODULE, - get_user_roster, 70), - ejabberd_hooks:delete(roster_in_subscription, Host, - ?MODULE, in_subscription, 30), - ejabberd_hooks:delete(roster_out_subscription, Host, - ?MODULE, out_subscription, 30), - ejabberd_hooks:delete(roster_get_jid_info, Host, - ?MODULE, get_jid_info, 70), - ejabberd_hooks:delete(roster_process_item, Host, - ?MODULE, process_item, 50), - ejabberd_hooks:delete(c2s_self_presence, Host, - ?MODULE, c2s_self_presence, 50), - ejabberd_hooks:delete(unset_presence_hook, Host, - ?MODULE, unset_presence, 50), - ejabberd_hooks:delete(register_user, Host, ?MODULE, - register_user, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, - 50). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 94f82e10b..0079caf51 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -38,21 +38,14 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_0, - ?MODULE, process_local_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_0, - ?MODULE, process_sm_iq), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_SIC_1, - ?MODULE, process_local_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_SIC_1, - ?MODULE, process_sm_iq). +start(_Host, _Opts) -> + {ok, [{iq_handler, ejabberd_local, ?NS_SIC_0, process_local_iq}, + {iq_handler, ejabberd_sm, ?NS_SIC_0, process_sm_iq}, + {iq_handler, ejabberd_local, ?NS_SIC_1, process_local_iq}, + {iq_handler, ejabberd_sm, ?NS_SIC_1, process_sm_iq}]}. -stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_0), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_0), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SIC_1), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_SIC_1). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 492a486b9..216c25f8b 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -38,15 +38,14 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_STATS, - ?MODULE, process_iq). +start(_Host, _Opts) -> + {ok, [{iq_handler, ejabberd_local, ?NS_STATS, process_iq}]}. -stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_STATS). +stop(_Host) -> + ok. reload(Host, NewOpts, _OldOpts) -> - start(Host, NewOpts). + ok. depends(_Host, _Opts) -> []. diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 7b02b2e48..e87109a5f 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -61,42 +61,22 @@ %%%=================================================================== %%% API %%%=================================================================== -start(Host, Opts) -> +start(_Host, Opts) -> init_cache(Opts), - ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE, - c2s_stream_started, 50), - ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE, - c2s_stream_features, 50), - ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, - c2s_unauthenticated_packet, 50), - ejabberd_hooks:add(c2s_unbinded_packet, Host, ?MODULE, - c2s_unbinded_packet, 50), - ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE, - c2s_authenticated_packet, 50), - ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, c2s_handle_send, 50), - ejabberd_hooks:add(c2s_handle_recv, Host, ?MODULE, c2s_handle_recv, 50), - ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), - ejabberd_hooks:add(c2s_handle_call, Host, ?MODULE, c2s_handle_call, 50), - ejabberd_hooks:add(c2s_closed, Host, ?MODULE, c2s_closed, 50), - ejabberd_hooks:add(c2s_terminated, Host, ?MODULE, c2s_terminated, 50). + {ok, [{hook, c2s_stream_started, c2s_stream_started, 50}, + {hook, c2s_post_auth_features, c2s_stream_features, 50}, + {hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50}, + {hook, c2s_unbinded_packet, c2s_unbinded_packet, 50}, + {hook, c2s_authenticated_packet, c2s_authenticated_packet, 50}, + {hook, c2s_handle_send, c2s_handle_send, 50}, + {hook, c2s_handle_recv, c2s_handle_recv, 50}, + {hook, c2s_handle_info, c2s_handle_info, 50}, + {hook, c2s_handle_call, c2s_handle_call, 50}, + {hook, c2s_closed, c2s_closed, 50}, + {hook, c2s_terminated, c2s_terminated, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE, - c2s_stream_started, 50), - ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE, - c2s_stream_features, 50), - ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, - c2s_unauthenticated_packet, 50), - ejabberd_hooks:delete(c2s_unbinded_packet, Host, ?MODULE, - c2s_unbinded_packet, 50), - ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE, - c2s_authenticated_packet, 50), - ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, c2s_handle_send, 50), - ejabberd_hooks:delete(c2s_handle_recv, Host, ?MODULE, c2s_handle_recv, 50), - ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), - ejabberd_hooks:delete(c2s_handle_call, Host, ?MODULE, c2s_handle_call, 50), - ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, c2s_closed, 50), - ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, c2s_terminated, 50). +stop(_Host) -> + ok. reload(_Host, NewOpts, _OldOpts) -> init_cache(NewOpts), diff --git a/src/mod_time.erl b/src/mod_time.erl index c954761c2..9e580e304 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -39,13 +39,11 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -start(Host, _Opts) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_TIME, ?MODULE, process_local_iq). +start(_Host, _Opts) -> + {ok, [{iq_handler, ejabberd_local, ?NS_TIME, process_local_iq}]}. -stop(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, - ?NS_TIME). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index d169b78f4..3cf67ad09 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -48,22 +48,13 @@ start(Host, Opts) -> init_cache(Host, Opts), - ejabberd_hooks:add(c2s_self_presence, Host, ?MODULE, - update_presence, 100), - ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send_packet, 50), - ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_set, - 90), - ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50). + {ok, [{hook, c2s_self_presence, update_presence, 100}, + {hook, user_send_packet, user_send_packet, 50}, + {hook, vcard_iq_set, vcard_set, 90}, + {hook, remove_user, remove_user, 50}]}. -stop(Host) -> - ejabberd_hooks:delete(c2s_self_presence, Host, - ?MODULE, update_presence, 100), - ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - user_send_packet, 50), - ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, - vcard_set, 90), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50). +stop(_Host) -> + ok. reload(Host, NewOpts, _OldOpts) -> init_cache(Host, NewOpts). From a9347cd248d58fc2886323f33445d3423edb412f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 4 Aug 2023 20:54:02 +0300 Subject: [PATCH 0164/1302] Fix unused variable warnings in mod_stats --- src/mod_stats.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 216c25f8b..83c179e62 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -44,7 +44,7 @@ start(_Host, _Opts) -> stop(_Host) -> ok. -reload(Host, NewOpts, _OldOpts) -> +reload(_Host, _NewOpts, _OldOpts) -> ok. depends(_Host, _Opts) -> From 26ed6539ba7754f08b38a1ca76391036ef8eda54 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 8 Aug 2023 20:17:10 +0200 Subject: [PATCH 0165/1302] mod_push_keepalive: Delay 'wake_on_start' Delay the 'wake_on_start' notifications until ejabberd is fully initialized. This makes sure no s2s connections are initiated before certificates are loaded. Many thanks to Friedrich Altheide for reporting the issue. --- src/mod_push_keepalive.erl | 43 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 9463ec7d9..44d4d8aec 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -32,8 +32,9 @@ -export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2]). -export([mod_doc/0]). %% ejabberd_hooks callbacks. --export([c2s_session_pending/1, c2s_session_resumed/1, c2s_copy_session/2, - c2s_handle_cast/2, c2s_handle_info/2, c2s_stanza/3]). +-export([ejabberd_started/0, c2s_session_pending/1, c2s_session_resumed/1, + c2s_copy_session/2, c2s_handle_cast/2, c2s_handle_info/2, + c2s_stanza/3]). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -47,13 +48,7 @@ %% gen_mod callbacks. %%-------------------------------------------------------------------- -spec start(binary(), gen_mod:opts()) -> ok. -start(Host, Opts) -> - case mod_push_keepalive_opt:wake_on_start(Opts) of - true -> - wake_all(Host); - false -> - ok - end, +start(Host, _Opts) -> register_hooks(Host). -spec stop(binary()) -> ok. @@ -61,14 +56,8 @@ stop(Host) -> unregister_hooks(Host). -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. -reload(Host, NewOpts, OldOpts) -> - case {mod_push_keepalive_opt:wake_on_start(NewOpts), - mod_push_keepalive_opt:wake_on_start(OldOpts)} of - {true, false} -> - wake_all(Host); - _ -> - ok - end. +reload(_Host, _NewOpts, _OldOpts) -> + ok. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> @@ -146,7 +135,10 @@ register_hooks(Host) -> ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50). + c2s_stanza, 50), + % Wait for ejabberd_pkix before running our ejabberd_started/0, so that we + % don't initiate s2s connections before certificates are loaded: + ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 90). -spec unregister_hooks(binary()) -> ok. unregister_hooks(Host) -> @@ -161,7 +153,14 @@ unregister_hooks(Host) -> ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, c2s_handle_info, 50), ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50). + c2s_stanza, 50), + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_hooks:delete( + ejabberd_started, ?MODULE, ejabberd_started, 90); + true -> + ok + end. %%-------------------------------------------------------------------- %% Hook callbacks. @@ -233,6 +232,12 @@ c2s_handle_info(#{push_enabled := true, mgmt_state := pending, c2s_handle_info(State, _) -> State. +-spec ejabberd_started() -> ok. +ejabberd_started() -> + [wake_all(Host) || Host <- ejabberd_config:get_option(hosts), + mod_push_keepalive_opt:wake_on_start(Host)], + ok. + %%-------------------------------------------------------------------- %% Internal functions. %%-------------------------------------------------------------------- From f0db7623d1169fdb631b012b2c5e41e96d42c2c9 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 9 Aug 2023 00:11:43 +0200 Subject: [PATCH 0166/1302] mod_push_keepalive: Don't let hook crash Check whether mod_push_keepalive is loaded for a given host before querying the module configuration for that host. This avoids a hook crash in the case where the module is enabled for some but not all hosts. --- src/mod_push_keepalive.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 44d4d8aec..3d38e1d89 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -234,8 +234,11 @@ c2s_handle_info(State, _) -> -spec ejabberd_started() -> ok. ejabberd_started() -> - [wake_all(Host) || Host <- ejabberd_config:get_option(hosts), - mod_push_keepalive_opt:wake_on_start(Host)], + Pred = fun(Host) -> + gen_mod:is_loaded(Host, ?MODULE) andalso + mod_push_keepalive_opt:wake_on_start(Host) + end, + [wake_all(Host) || Host <- ejabberd_config:get_option(hosts), Pred(Host)], ok. %%-------------------------------------------------------------------- From 6c7e85d3d8b767f798bf3433d08f12f4bd15b0d6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 9 Aug 2023 01:54:12 +0200 Subject: [PATCH 0167/1302] ejabberd_systemd: Avoid using gen_server timeout Don't (ab)use the gen_server timeout mechanism for pinging the systemd watchdog. Under certain conditions (e.g., the process receiving sys messages), the gen_server timeout might not be triggered as expected. Fixes #4054, fixes #4058, --- src/ejabberd_systemd.erl | 84 ++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/src/ejabberd_systemd.erl b/src/ejabberd_systemd.erl index 6a15c56c2..48c79dc51 100644 --- a/src/ejabberd_systemd.erl +++ b/src/ejabberd_systemd.erl @@ -44,9 +44,8 @@ {socket :: gen_udp:socket() | undefined, destination :: inet:local_address() | undefined, interval :: pos_integer() | undefined, - last_ping :: integer() | undefined}). + timer :: reference() | undefined}). --type watchdog_timeout() :: pos_integer() | hibernate. -type state() :: #state{}. %%-------------------------------------------------------------------- @@ -71,8 +70,7 @@ stopping() -> %%-------------------------------------------------------------------- %% gen_server callbacks. %%-------------------------------------------------------------------- --spec init(any()) - -> {ok, state()} | {ok, state(), watchdog_timeout()} | {stop, term()}. +-spec init(any()) -> {ok, state()} | {stop, term()}. init(_Opts) -> process_flag(trap_exit, true), case os:getenv("NOTIFY_SOCKET") of @@ -84,17 +82,10 @@ init(_Opts) -> Destination = {local, Path}, case gen_udp:open(0, [local]) of {ok, Socket} -> - Interval = get_watchdog_interval(), State = #state{socket = Socket, destination = Destination, - interval = Interval}, - if is_integer(Interval), Interval > 0 -> - ?INFO_MSG("Watchdog notifications enabled", []), - {ok, set_last_ping(State), Interval}; - true -> - ?INFO_MSG("Watchdog notifications disabled", []), - {ok, State} - end; + interval = get_watchdog_interval()}, + {ok, maybe_start_timer(State)}; {error, Reason} -> ?CRITICAL_MSG("Cannot open IPC socket: ~p", [Reason]), {stop, Reason} @@ -105,47 +96,48 @@ init(_Opts) -> end. -spec handle_call(term(), {pid(), term()}, state()) - -> {reply, {error, badarg}, state(), watchdog_timeout()}. + -> {reply, {error, badarg}, state()}. handle_call(Request, From, State) -> ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), - {reply, {error, badarg}, State, get_timeout(State)}. + {reply, {error, badarg}, State}. --spec handle_cast({notify, binary()} | term(), state()) - -> {noreply, state(), watchdog_timeout()}. +-spec handle_cast({notify, binary()} | term(), state()) -> {noreply, state()}. handle_cast({notify, Notification}, #state{destination = undefined} = State) -> ?DEBUG("No NOTIFY_SOCKET, dropping ~s notification", [Notification]), - {noreply, State, get_timeout(State)}; + {noreply, State}; handle_cast({notify, Notification}, State) -> try notify(State, Notification) catch _:Err -> ?ERROR_MSG("Cannot send ~s notification: ~p", [Notification, Err]) end, - {noreply, State, get_timeout(State)}; + {noreply, State}; handle_cast(Msg, State) -> ?ERROR_MSG("Got unexpected message: ~p", [Msg]), - {noreply, State, get_timeout(State)}. + {noreply, State}. --spec handle_info(timeout | term(), state()) - -> {noreply, state(), watchdog_timeout()}. -handle_info(timeout, #state{interval = Interval} = State) +-spec handle_info(ping_watchdog | term(), state()) -> {noreply, state()}. +handle_info(ping_watchdog , #state{interval = Interval} = State) when is_integer(Interval), Interval > 0 -> try notify(State, <<"WATCHDOG=1">>) catch _:Err -> ?ERROR_MSG("Cannot ping watchdog: ~p", [Err]) end, - {noreply, set_last_ping(State), Interval}; + {noreply, start_timer(State)}; handle_info(Info, State) -> ?ERROR_MSG("Got unexpected info: ~p", [Info]), - {noreply, State, get_timeout(State)}. + {noreply, State}. -spec terminate(normal | shutdown | {shutdown, term()} | term(), state()) -> ok. -terminate(Reason, #state{socket = undefined}) -> +terminate(Reason, #state{socket = Socket} = State) -> ?DEBUG("Terminating ~s (~p)", [?MODULE, Reason]), - ok; -terminate(Reason, #state{socket = Socket}) -> - ?DEBUG("Closing socket and terminating ~s (~p)", [?MODULE, Reason]), - ok = gen_udp:close(Socket). + cancel_timer(State), + case Socket of + undefined -> + ok; + _Socket -> + gen_udp:close(Socket) + end. -spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, State, _Extra) -> @@ -166,24 +158,22 @@ get_watchdog_interval() -> undefined end. --spec get_timeout(state()) -> watchdog_timeout(). -get_timeout(#state{interval = undefined}) -> - ?DEBUG("Watchdog interval is undefined, hibernating", []), - hibernate; -get_timeout(#state{interval = Interval, last_ping = LastPing}) -> - case Interval - (erlang:monotonic_time(millisecond) - LastPing) of - Timeout when Timeout > 0 -> - ?DEBUG("Calculated new timeout value: ~B", [Timeout]), - Timeout; - _ -> - ?DEBUG("Calculated new timeout value: 1", []), - 1 - end. +-spec maybe_start_timer(state()) -> state(). +maybe_start_timer(#state{interval = Interval} = State) + when is_integer(Interval), Interval > 0 -> + ?INFO_MSG("Watchdog notifications enabled", []), + start_timer(State); +maybe_start_timer(State) -> + ?INFO_MSG("Watchdog notifications disabled", []), + State. --spec set_last_ping(state()) -> state(). -set_last_ping(State) -> - LastPing = erlang:monotonic_time(millisecond), - State#state{last_ping = LastPing}. +-spec start_timer(state()) -> state(). +start_timer(#state{interval = Interval} = State) -> + State#state{timer = erlang:send_after(Interval, self(), ping_watchdog)}. + +-spec cancel_timer(state()) -> ok. +cancel_timer(#state{timer = Timer}) -> + misc:cancel_timer(Timer). -spec notify(state(), binary()) -> ok. notify(#state{socket = Socket, destination = Destination}, From caf3807bccbb99a4a3e6cc35cb3e11fed23e25d2 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 9 Aug 2023 01:58:48 +0200 Subject: [PATCH 0168/1302] ejabberd_systemd: Add a few debug messages --- src/ejabberd_systemd.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_systemd.erl b/src/ejabberd_systemd.erl index 48c79dc51..30ed6e4ea 100644 --- a/src/ejabberd_systemd.erl +++ b/src/ejabberd_systemd.erl @@ -117,7 +117,7 @@ handle_cast(Msg, State) -> {noreply, State}. -spec handle_info(ping_watchdog | term(), state()) -> {noreply, state()}. -handle_info(ping_watchdog , #state{interval = Interval} = State) +handle_info(ping_watchdog, #state{interval = Interval} = State) when is_integer(Interval), Interval > 0 -> try notify(State, <<"WATCHDOG=1">>) catch _:Err -> @@ -169,10 +169,12 @@ maybe_start_timer(State) -> -spec start_timer(state()) -> state(). start_timer(#state{interval = Interval} = State) -> + ?DEBUG("Pinging watchdog in ~B milliseconds", [Interval]), State#state{timer = erlang:send_after(Interval, self(), ping_watchdog)}. -spec cancel_timer(state()) -> ok. cancel_timer(#state{timer = Timer}) -> + ?DEBUG("Cancelling watchdog timer", []), misc:cancel_timer(Timer). -spec notify(state(), binary()) -> ok. @@ -183,4 +185,5 @@ notify(#state{socket = Socket, destination = Destination}, -spec cast_notification(binary()) -> ok. cast_notification(Notification) -> + ?DEBUG("Closing NOTIFY_SOCKET", []), gen_server:cast(?MODULE, {notify, Notification}). From eeacace02a5dd9b78de0193070374e0e8fed5d09 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 9 Aug 2023 17:08:45 +0300 Subject: [PATCH 0169/1302] Update some modules to the new gen_mod API --- src/gen_mod.erl | 1 + src/mod_jidprep.erl | 35 ++++-------------- src/mod_muc_occupantid.erl | 15 +++----- src/mod_push.erl | 73 +++++++------------------------------- 4 files changed, 24 insertions(+), 100 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 4b9f1fc4b..d649da6a8 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -64,6 +64,7 @@ {hook, atom(), module(), atom(), integer()} | {iq_handler, component(), binary(), atom()} | {iq_handler, component(), binary(), module(), atom()}. +-export_type([registration/0]). -callback start(binary(), opts()) -> ok | {ok, pid()} | diff --git a/src/mod_jidprep.erl b/src/mod_jidprep.erl index c1b2ad1ec..9eaad095a 100644 --- a/src/mod_jidprep.erl +++ b/src/mod_jidprep.erl @@ -46,15 +46,14 @@ %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- --spec start(binary(), gen_mod:opts()) -> ok. -start(Host, _Opts) -> - register_iq_handlers(Host), - register_hooks(Host). +-spec start(binary(), gen_mod:opts()) -> {ok, [gen_mod:registration()]}. +start(_Host, _Opts) -> + {ok, [{iq_handler, ejabberd_local, ?NS_JIDPREP_0, process_iq}, + {hook, disco_local_features, disco_local_features, 50}]}. -spec stop(binary()) -> ok. -stop(Host) -> - unregister_hooks(Host), - unregister_iq_handlers(Host). +stop(_Host) -> + ok. -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(_Host, _NewOpts, _OldOpts) -> @@ -88,19 +87,6 @@ mod_doc() -> "be used to control who is allowed to use this " "service. The default value is 'local'.")}}]}. -%%-------------------------------------------------------------------- -%% Register/unregister hooks. -%%-------------------------------------------------------------------- --spec register_hooks(binary()) -> ok. -register_hooks(Host) -> - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, - disco_local_features, 50). - --spec unregister_hooks(binary()) -> ok. -unregister_hooks(Host) -> - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, - disco_local_features, 50). - %%-------------------------------------------------------------------- %% Service discovery. %%-------------------------------------------------------------------- @@ -123,15 +109,6 @@ disco_local_features(Acc, _From, _To, _Node, _Lang) -> %%-------------------------------------------------------------------- %% IQ handlers. %%-------------------------------------------------------------------- --spec register_iq_handlers(binary()) -> ok. -register_iq_handlers(Host) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_JIDPREP_0, ?MODULE, process_iq). - --spec unregister_iq_handlers(binary()) -> ok. -unregister_iq_handlers(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_JIDPREP_0). - -spec process_iq(iq()) -> iq(). process_iq(#iq{type = set, lang = Lang} = IQ) -> Txt = ?T("Value 'set' of 'type' attribute is not allowed"), diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index ccb4a2134..60cb811f8 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -44,18 +44,11 @@ %%% gen_mod %%% -start(Host, _Opts) -> - ejabberd_hooks:add(muc_filter_presence, Host, - ?MODULE, filter_packet, 10), - ejabberd_hooks:add(muc_filter_message, Host, - ?MODULE, filter_packet, 10), - ok. +start(_Host, _Opts) -> + {ok, [{hook, muc_filter_presence, filter_packet, 10}, + {hook, muc_filter_message, filter_packet, 10}]}. -stop(Host) -> - ejabberd_hooks:delete(muc_filter_presence, Host, - ?MODULE, filter_packet, 10), - ejabberd_hooks:delete(muc_filter_message, Host, - ?MODULE, filter_packet, 10), +stop(_Host) -> ok. %%% diff --git a/src/mod_push.erl b/src/mod_push.erl index c911bb6ac..b515adf53 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -91,19 +91,26 @@ %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- --spec start(binary(), gen_mod:opts()) -> ok. +-spec start(binary(), gen_mod:opts()) -> {ok, [gen_mod:registration()]}. start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - register_iq_handlers(Host), - register_hooks(Host), - ejabberd_commands:register_commands(?MODULE, get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + {ok, [{iq_handler, ejabberd_sm, ?NS_PUSH_0, process_iq}, + {hook, disco_sm_features, disco_sm_features, 50}, + {hook, c2s_session_pending, c2s_session_pending, 50}, + {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, c2s_session_resumed, c2s_session_resumed, 50}, + {hook, c2s_handle_cast, c2s_handle_cast, 50}, + {hook, c2s_handle_send, c2s_stanza, 50}, + {hook, store_mam_message, mam_message, 50}, + {hook, offline_message_hook, offline_message, 55}, + {hook, remove_user, remove_user, 50}]}. + -spec stop(binary()) -> ok. stop(Host) -> - unregister_hooks(Host), - unregister_iq_handlers(Host), case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> ejabberd_commands:unregister_commands(get_commands_spec()); @@ -248,51 +255,6 @@ delete_old_sessions(Days) -> Reason end. -%%-------------------------------------------------------------------- -%% Register/unregister hooks. -%%-------------------------------------------------------------------- --spec register_hooks(binary()) -> ok. -register_hooks(Host) -> - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, - disco_sm_features, 50), - ejabberd_hooks:add(c2s_session_pending, Host, ?MODULE, - c2s_session_pending, 50), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), - ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE, - c2s_handle_cast, 50), - ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50), - ejabberd_hooks:add(store_mam_message, Host, ?MODULE, - mam_message, 50), - ejabberd_hooks:add(offline_message_hook, Host, ?MODULE, - offline_message, 55), - ejabberd_hooks:add(remove_user, Host, ?MODULE, - remove_user, 50). - --spec unregister_hooks(binary()) -> ok. -unregister_hooks(Host) -> - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, - disco_sm_features, 50), - ejabberd_hooks:delete(c2s_session_pending, Host, ?MODULE, - c2s_session_pending, 50), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), - ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE, - c2s_handle_cast, 50), - ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50), - ejabberd_hooks:delete(store_mam_message, Host, ?MODULE, - mam_message, 50), - ejabberd_hooks:delete(offline_message_hook, Host, ?MODULE, - offline_message, 55), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, - remove_user, 50). - %%-------------------------------------------------------------------- %% Service discovery. %%-------------------------------------------------------------------- @@ -311,15 +273,6 @@ disco_sm_features(Acc, _From, _To, _Node, _Lang) -> %%-------------------------------------------------------------------- %% IQ handlers. %%-------------------------------------------------------------------- --spec register_iq_handlers(binary()) -> ok. -register_iq_handlers(Host) -> - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PUSH_0, - ?MODULE, process_iq). - --spec unregister_iq_handlers(binary()) -> ok. -unregister_iq_handlers(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUSH_0). - -spec process_iq(iq()) -> iq(). process_iq(#iq{type = get, lang = Lang} = IQ) -> Txt = ?T("Value 'get' of 'type' attribute is not allowed"), From c4563c429cb1cea7dd616df3b9a03d6ddd818dc6 Mon Sep 17 00:00:00 2001 From: sando38 Date: Sat, 12 Aug 2023 14:46:30 +0200 Subject: [PATCH 0170/1302] Dockerfile: add missing dependency for mssql databases --- .github/container/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index adbcdb98d..94a9422f2 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -157,7 +157,9 @@ RUN apk -U upgrade --available --no-cache \ && apk add --no-cache \ $(cat /tmp/runDeps) \ so:libcap.so.2 \ - tini + so:libtdsodbc.so.0 \ + tini \ + && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so ARG USER ARG UID From c5afd0322e344f1749f49fedbe9f37938a6c73f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 14 Aug 2023 17:12:16 +0200 Subject: [PATCH 0171/1302] Properly parse mysql version even if it doesn't have type tag --- src/ejabberd_sql.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 99f74953e..c5c06d078 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -1166,6 +1166,14 @@ get_db_version(#state{db_type = mysql} = State) -> _ -> 0 end, State#state{db_version = {V, TypeA, Flags}}; + {match, [V1, V2, V3]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + Flags = case V of + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> 0 + end, + State#state{db_version = {V, unknown, Flags}}; _ -> ?WARNING_MSG("Error parsing mysql version: ~p", [SVersion]), State From 16473ab691fbc48f24b6992a5e15add257cbad88 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Jun 2023 16:47:20 +0200 Subject: [PATCH 0172/1302] When sending message on behalf of user, trigger user_send_packet (#3990) This way, MAM and CarbonCopy get triggered. This is useful for transports like Slidge. --- src/mod_privilege.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 3862b33c7..f59247789 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -403,6 +403,9 @@ forward_message(#message{to = To} = Msg) -> #message{} = NewMsg -> case NewMsg#message.from of #jid{lresource = <<"">>, lserver = ServerHost} -> + FromJID = NewMsg#message.from, + State = #{jid => FromJID}, + ejabberd_hooks:run_fold(user_send_packet, FromJID#jid.lserver, {NewMsg, State}, []), ejabberd_router:route(NewMsg); _ -> Lang = xmpp:get_lang(Msg), From 550a586d2a44e938873ea5d33eae3e2d90774aa3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Jul 2023 10:55:56 +0200 Subject: [PATCH 0173/1302] New listener option unix_socket, useful when setting unix socket files (#4059) listen: - port: "unix://tmp/asd/socket" unix_socket: mode: '0775' owner: 117 group: 135 --- src/ejabberd_listener.erl | 85 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index fcfe44ab3..26fddb8c8 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -108,6 +108,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> {Port2, ExtraOpts} = case Port of <<"unix:", Path/binary>> -> SO = lists:keydelete(ip, 1, SockOpts), + setup_provisional_udsocket_dir(Path), file:delete(Path), {0, [{ip, {local, Path}} | SO]}; _ -> @@ -119,6 +120,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> {reuseaddr, true} | ExtraOpts2]) of {ok, Socket} -> + set_definitive_udsocket(Port, Opts), case inet:sockname(Socket) of {ok, {Addr, Port1}} -> proc_lib:init_ack({ok, self()}), @@ -149,6 +151,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> case listen_tcp(Port, SockOpts) of {ok, ListenSocket} -> + set_definitive_udsocket(Port, Opts), case inet:sockname(ListenSocket) of {ok, {Addr, Port1}} -> proc_lib:init_ack({ok, self()}), @@ -186,8 +189,10 @@ listen_tcp(Port, SockOpts) -> {Port2, ExtraOpts} = case Port of <<"unix:", Path/binary>> -> SO = lists:keydelete(ip, 1, SockOpts), + Prov = setup_provisional_udsocket_dir(Path), file:delete(Path), - {0, [{ip, {local, Path}} | SO]}; + file:delete(Prov), + {0, [{ip, {local, Prov}} | SO]}; _ -> {Port, SockOpts} end, @@ -205,6 +210,72 @@ listen_tcp(Port, SockOpts) -> Err end. +%%% +%%% Unix Domain Socket utility functions +%%% + +setup_provisional_udsocket_dir(DefinitivePath) -> + ProvisionalPath = get_provisional_udsocket_path(DefinitivePath), + SocketDir = filename:dirname(ProvisionalPath), + file:make_dir(SocketDir), + file:change_mode(SocketDir, 8#00700), + ?DEBUG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", + [ProvisionalPath, DefinitivePath]), + ProvisionalPath. + +get_provisional_udsocket_path(Path) -> + MnesiaDir = mnesia:system_info(directory), + SocketDir = filename:join(MnesiaDir, "socket"), + PathBase64 = misc:term_to_base64(Path), + PathBuild = filename:join(SocketDir, PathBase64), + %% Shorthen the path, a long path produces a crash when opening the socket. + binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}). + +get_definitive_udsocket_path(<<"unix", _>> = Unix) -> + Unix; +get_definitive_udsocket_path(ProvisionalPath) -> + PathBase64 = filename:basename(ProvisionalPath), + {term, Path} = misc:base64_to_term(PathBase64), + Path. + +set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> + Prov = get_provisional_udsocket_path(Path), + timer:sleep(5000), + Usd = maps:get(unix_socket, Opts), + case maps:get(mode, Usd, undefined) of + undefined -> ok; + Mode -> ok = file:change_mode(Prov, Mode) + end, + case maps:get(owner, Usd, undefined) of + undefined -> ok; + Owner -> + try + ok = file:change_owner(Prov, Owner) + catch + error:{badmatch, {error, eperm}} -> + ?ERROR_MSG("Error trying to set owner ~p for socket ~p", [Owner, Prov]), + throw({error_setting_socket_owner, Owner, Prov}) + end + end, + case maps:get(group, Usd, undefined) of + undefined -> ok; + Group -> + try + ok = file:change_group(Prov, Group) + catch + error:{badmatch, {error, eperm}} -> + ?ERROR_MSG("Error trying to set group ~p for socket ~p", [Group, Prov]), + throw({error_setting_socket_group, Group, Prov}) + end + end, + file:rename(Prov, Path); +set_definitive_udsocket(_Port, _Opts) -> + ok. + +%%% +%%% +%%% + -spec split_opts(transport(), opts()) -> {opts(), [gen_tcp:option()]}. split_opts(Transport, Opts) -> maps:fold( @@ -493,8 +564,11 @@ format_error(Reason) -> -spec format_endpoint(endpoint()) -> string(). format_endpoint({Port, IP, _Transport}) -> case Port of + <<"unix:", _/binary>> -> + Port; Unix when is_binary(Unix) -> - <<"unix:", Unix/binary>>; + Def = get_definitive_udsocket_path(Unix), + <<"unix:", Def/binary>>; _ -> IPStr = case tuple_size(IP) of 4 -> inet:ntoa(IP); @@ -699,6 +773,12 @@ listen_opt_type(shaper) -> econf:shaper(); listen_opt_type(access) -> econf:acl(); +listen_opt_type(unix_socket) -> + econf:options( + #{group => econf:non_neg_int(), + owner => econf:non_neg_int(), + mode => econf:octal()}, + [unique, {return, map}]); listen_opt_type(use_proxy_protocol) -> econf:bool(). @@ -709,5 +789,6 @@ listen_options() -> {accept_interval, 0}, {send_timeout, 15000}, {backlog, 128}, + {unix_socket, #{}}, {use_proxy_protocol, false}, {supervisor, true}]. From 66df953da1f3f8ac09f252a9a6357a148c43001f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 2 Aug 2023 11:39:48 +0200 Subject: [PATCH 0174/1302] Fix usage of plugins option, which produced default_node_config ignore (#4070) --- src/mod_pubsub.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index b8f4c4905..8563ba2d1 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3367,9 +3367,10 @@ get_option(Options, Var, Def) -> -spec node_options(host(), binary()) -> [{atom(), any()}]. node_options(Host, Type) -> DefaultOpts = node_plugin_options(Host, Type), - case config(Host, plugins) of - [Type|_] -> config(Host, default_node_config, DefaultOpts); - _ -> DefaultOpts + case lists:member(Type, config(Host, plugins)) of + true -> + config(Host, default_node_config, DefaultOpts); + false -> DefaultOpts end. -spec node_plugin_options(host(), binary()) -> [{atom(), any()}]. From 2bd61abd7195dfff7fe8026c71db3e6fa0d3bc6d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Aug 2023 13:28:02 +0200 Subject: [PATCH 0175/1302] Convert allow_private_message MUC room option to allowpm (#3736) --- include/mod_muc_room.hrl | 2 +- src/mod_muc.erl | 20 +++++++++++------ src/mod_muc_admin.erl | 2 +- src/mod_muc_log.erl | 4 ++-- src/mod_muc_mnesia.erl | 24 ++++++++++++++++++++- src/mod_muc_room.erl | 46 ++++++++++++++++++++++++---------------- test/muc_tests.erl | 2 +- 7 files changed, 70 insertions(+), 30 deletions(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index 559076ae2..f65c346c6 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -38,7 +38,7 @@ description = <<"">> :: binary(), allow_change_subj = true :: boolean(), allow_query_users = true :: boolean(), - allow_private_messages = true :: boolean(), + allowpm = anyone :: anyone | participants | moderators | none, allow_private_messages_from_visitors = anyone :: anyone | moderators | nobody , allow_visitor_status = true :: boolean(), allow_visitor_nickchange = true :: boolean(), diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 3af28e156..f7531df53 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1193,6 +1193,13 @@ opts_to_binary(Opts) -> {subject, iolist_to_binary(Subj)}; ({subject_author, Author}) -> {subject_author, iolist_to_binary(Author)}; + ({allow_private_messages, Value}) -> % ejabberd 23.04 or older + Value2 = case Value of + true -> anyone; + false -> none; + _ -> Value + end, + {allowpm, Value2}; ({AffOrRole, Affs}) when (AffOrRole == affiliation) or (AffOrRole == role) -> {affiliations, lists:map( fun({{U, S, R}, Aff}) -> @@ -1289,7 +1296,8 @@ mod_opt_type(cleanup_affiliations_on_start) -> mod_opt_type(default_room_options) -> econf:options( #{allow_change_subj => econf:bool(), - allow_private_messages => econf:bool(), + allowpm => + econf:enum([anyone, participants, moderators, none]), allow_private_messages_from_visitors => econf:enum([anyone, moderators, nobody]), allow_query_users => econf:bool(), @@ -1373,7 +1381,7 @@ mod_options(Host) -> {cleanup_affiliations_on_start, false}, {default_room_options, [{allow_change_subj,true}, - {allow_private_messages,true}, + {allowpm,anyone}, {allow_query_users,true}, {allow_user_invites,false}, {allow_visitor_nickchange,true}, @@ -1667,11 +1675,11 @@ mod_doc() -> desc => ?T("Allow occupants to change the subject. " "The default value is 'true'.")}}, - {allow_private_messages, - #{value => "true | false", + {allowpm, + #{value => "anyone | participants | moderators | none", desc => - ?T("Occupants can send private messages to other occupants. " - "The default value is 'true'.")}}, + ?T("Who can send private messages. " + "The default value is 'anyone'.")}}, {allow_query_users, #{value => "true | false", desc => diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 234f065d3..5264eaa4b 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1276,7 +1276,7 @@ room_diagnostics(Name, Service) -> change_option(Option, Value, Config) -> case Option of allow_change_subj -> Config#config{allow_change_subj = Value}; - allow_private_messages -> Config#config{allow_private_messages = Value}; + allowpm -> Config#config{allowpm = Value}; allow_private_messages_from_visitors -> Config#config{allow_private_messages_from_visitors = Value}; allow_query_users -> Config#config{allow_query_users = Value}; allow_subscription -> Config#config{allow_subscription = Value}; diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 057e4edcd..d5a8d461f 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -811,8 +811,8 @@ get_roomconfig_text(members_by_default, Lang) -> tr(Lang, ?T("Default users as participants")); get_roomconfig_text(allow_change_subj, Lang) -> tr(Lang, ?T("Allow users to change the subject")); -get_roomconfig_text(allow_private_messages, Lang) -> - tr(Lang, ?T("Allow users to send private messages")); +get_roomconfig_text(allowpm, Lang) -> + tr(Lang, ?T("Who can send private messages")); get_roomconfig_text(allow_private_messages_from_visitors, Lang) -> tr(Lang, ?T("Allow visitors to send private messages to")); get_roomconfig_text(allow_query_users, Lang) -> diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index 4a5192d2f..4b422f998 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -401,6 +401,15 @@ need_transform({muc_room, {N, H}, _}) when is_list(N) orelse is_list(H) -> ?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []), true; +need_transform({muc_room, {_N, _H}, Opts}) -> + case lists:keymember(allow_private_messages, 1, Opts) of + true -> + ?INFO_MSG("Mnesia table 'muc_room' will be converted to allowpm", []), + true; + false -> + false + end; + need_transform({muc_registered, {{U, S}, H}, Nick}) when is_list(U) orelse is_list(S) orelse is_list(H) orelse is_list(Nick) -> ?INFO_MSG("Mnesia table 'muc_registered' will be converted to binary", []), @@ -408,9 +417,22 @@ need_transform({muc_registered, {{U, S}, H}, Nick}) need_transform(_) -> false. -transform(#muc_room{name_host = {N, H}, opts = Opts} = R) -> +transform({muc_room, {N, H}, Opts} = R) + when is_list(N) orelse is_list(H) -> R#muc_room{name_host = {iolist_to_binary(N), iolist_to_binary(H)}, opts = mod_muc:opts_to_binary(Opts)}; +transform(#muc_room{opts = Opts} = R) -> + Opts2 = case lists:keyfind(allow_private_messages, 1, Opts) of + {_, Value} when is_boolean(Value) -> + Value2 = case Value of + true -> anyone; + false -> none + end, + lists:keyreplace(allow_private_messages, 1, Opts, {allowpm, Value2}); + _ -> + Opts + end, + R#muc_room{opts = Opts2}; transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick} = R) -> R#muc_registered{us_host = {{iolist_to_binary(U), iolist_to_binary(S)}, iolist_to_binary(H)}, diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 9c9b328d7..926b478e1 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -593,7 +593,7 @@ normal_state({route, ToNick, forget_message -> {next_state, normal_state, StateData}; continue_delivery -> - case {(StateData#state.config)#config.allow_private_messages, + case {is_user_allowed_private_message(From, StateData), is_user_online(From, StateData) orelse is_subscriber(From, StateData) orelse is_user_allowed_message_nonparticipant(From, StateData)} of @@ -630,7 +630,7 @@ normal_state({route, ToNick, ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID)) end, ToJIDs); true -> - ErrText = ?T("It is not allowed to send private messages"), + ErrText = ?T("You are not allowed to send private messages"), Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end @@ -641,7 +641,7 @@ normal_state({route, ToNick, Err = xmpp:err_not_acceptable(ErrText, Lang), ejabberd_router:route_error(Packet, Err); {false, _} -> - ErrText = ?T("It is not allowed to send private messages"), + ErrText = ?T("You are not allowed to send private messages"), Err = xmpp:err_forbidden(ErrText, Lang), ejabberd_router:route_error(Packet, Err) end, @@ -1329,6 +1329,24 @@ is_user_allowed_message_nonparticipant(JID, _ -> false end. +-spec is_user_allowed_private_message(jid(), state()) -> boolean(). +is_user_allowed_private_message(JID, StateData) -> + case {(StateData#state.config)#config.allowpm, + get_role(JID, StateData)} of + {anyone, _} -> + true; + {participants, moderator} -> + true; + {participants, participant} -> + true; + {moderators, moderator} -> + true; + {none, _} -> + false; + {_, _} -> + false + end. + %% @doc Get information of this participant, or default values. %% If the JID is not a participant, return values for a service message. -spec get_participant_data(jid(), state()) -> {binary(), role()}. @@ -3788,7 +3806,7 @@ get_config(Lang, StateData, From) -> {moderatedroom, Config#config.moderated}, {members_by_default, Config#config.members_by_default}, {changesubject, Config#config.allow_change_subj}, - {allow_private_messages, Config#config.allow_private_messages}, + {allowpm, Config#config.allowpm}, {allow_private_messages_from_visitors, Config#config.allow_private_messages_from_visitors}, {allow_query_users, Config#config.allow_query_users}, @@ -3859,8 +3877,8 @@ set_config(Opts, Config, ServerHost, Lang) -> ({roomdesc, Desc}, C) -> C#config{description = Desc}; ({changesubject, V}, C) -> C#config{allow_change_subj = V}; ({allow_query_users, V}, C) -> C#config{allow_query_users = V}; - ({allow_private_messages, V}, C) -> - C#config{allow_private_messages = V}; + ({allowpm, V}, C) -> + C#config{allowpm = V}; ({allow_private_messages_from_visitors, V}, C) -> C#config{allow_private_messages_from_visitors = V}; ({allow_visitor_status, V}, C) -> C#config{allow_visitor_status = V}; @@ -4026,9 +4044,9 @@ set_opts2([{Opt, Val} | Opts], StateData) -> StateData#state{config = (StateData#state.config)#config{allow_query_users = Val}}; - allow_private_messages -> + allowpm -> StateData#state{config = - (StateData#state.config)#config{allow_private_messages + (StateData#state.config)#config{allowpm = Val}}; allow_private_messages_from_visitors -> StateData#state{config = @@ -4221,7 +4239,7 @@ make_opts(StateData, Hibernation) -> [?MAKE_CONFIG_OPT(#config.title), ?MAKE_CONFIG_OPT(#config.description), ?MAKE_CONFIG_OPT(#config.allow_change_subj), ?MAKE_CONFIG_OPT(#config.allow_query_users), - ?MAKE_CONFIG_OPT(#config.allow_private_messages), + ?MAKE_CONFIG_OPT(#config.allowpm), ?MAKE_CONFIG_OPT(#config.allow_private_messages_from_visitors), ?MAKE_CONFIG_OPT(#config.allow_visitor_status), ?MAKE_CONFIG_OPT(#config.allow_visitor_nickchange), @@ -4481,20 +4499,12 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang, -spec iq_disco_info_extras(binary(), state(), boolean()) -> xdata(). iq_disco_info_extras(Lang, StateData, Static) -> Config = StateData#state.config, - AllowPM = case Config#config.allow_private_messages of - false -> none; - true -> - case Config#config.allow_private_messages_from_visitors of - nobody -> participants; - _ -> anyone - end - end, Fs1 = [{roomname, Config#config.title}, {description, Config#config.description}, {changesubject, Config#config.allow_change_subj}, {allowinvites, Config#config.allow_user_invites}, {allow_query_users, Config#config.allow_query_users}, - {allowpm, AllowPM}, + {allowpm, Config#config.allowpm}, {lang, Config#config.lang}], Fs2 = case Config#config.pubsub of Node when is_binary(Node), Node /= <<"">> -> diff --git a/test/muc_tests.erl b/test/muc_tests.erl index 0cfbc9967..21a8488a6 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -1242,7 +1242,7 @@ config_private_messages_master(Config) -> [104] = set_config(Config, [{allow_private_messages_from_visitors, nobody}]), wait_for_slave(Config), [104] = set_config(Config, [{allow_private_messages_from_visitors, anyone}, - {allow_private_messages, false}]), + {allowpm, none}]), ct:comment("Fail trying to send a private message"), send(Config, #message{to = PeerNickJID, type = chat}), #message{from = PeerNickJID, type = error} = ErrMsg = recv_message(Config), From ffa07c649ba185afbc79e00352b0f9bff9865a53 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 15 Aug 2023 12:44:41 +0200 Subject: [PATCH 0176/1302] Store the subject author JID, and run muc_filter_message when sending subject (#3397) When changing the room subject, store the original author JID, so later it can be provided in the hook and mod_room_occupantid can use it to calculate and provide the occupant id This is noticeable when a new occupant joins an existing room, and receives the room subject. --- include/mod_muc_room.hrl | 2 +- src/mod_muc.erl | 6 ++++-- src/mod_muc_room.erl | 20 +++++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index f65c346c6..54773415c 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -125,7 +125,7 @@ roles = #{} :: roles(), history = #lqueue{} :: lqueue(), subject = [] :: [text()], - subject_author = <<"">> :: binary(), + subject_author = {<<"">>, #jid{}} :: {binary(), jid()}, hats_users = #{} :: map(), % FIXME on OTP 21+: #{ljid() => #{binary() => binary()}}, just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), diff --git a/src/mod_muc.erl b/src/mod_muc.erl index f7531df53..c77977387 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1191,8 +1191,10 @@ opts_to_binary(Opts) -> {password, iolist_to_binary(Pass)}; ({subject, [C|_] = Subj}) when is_integer(C), C >= 0, C =< 255 -> {subject, iolist_to_binary(Subj)}; - ({subject_author, Author}) -> - {subject_author, iolist_to_binary(Author)}; + ({subject_author, {AuthorNick, AuthorJID}}) -> + {subject_author, {iolist_to_binary(AuthorNick), AuthorJID}}; + ({subject_author, AuthorNick}) -> % ejabberd 23.04 or older + {subject_author, {iolist_to_binary(AuthorNick), #jid{}}}; ({allow_private_messages, Value}) -> % ejabberd 23.04 or older Value2 = case Value of true -> anyone; diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 926b478e1..22ac26184 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1053,7 +1053,7 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData true -> NSD = StateData#state{subject = Subject, - subject_author = FromNick}, + subject_author = {FromNick, From}}, store_room(NSD), {NSD, true}; _ -> {StateData, false} @@ -3000,14 +3000,24 @@ send_history(JID, History, StateData) -> end, History). -spec send_subject(jid(), state()) -> ok. -send_subject(JID, #state{subject_author = Nick} = StateData) -> +send_subject(JID, #state{subject_author = {Nick, AuthorJID}} = StateData) -> Subject = case StateData#state.subject of [] -> [#text{}]; [_|_] = S -> S end, - Packet = #message{from = jid:replace_resource(StateData#state.jid, Nick), + Packet = #message{from = AuthorJID, to = JID, type = groupchat, subject = Subject}, - ejabberd_router:route(Packet). + case ejabberd_hooks:run_fold(muc_filter_message, + StateData#state.server_host, + Packet, + [StateData, Nick]) of + drop -> + ok; + NewPacket1 -> + FromRoomNick = jid:replace_resource(StateData#state.jid, Nick), + NewPacket2 = xmpp:set_from(NewPacket1, FromRoomNick), + ejabberd_router:route(NewPacket2) + end. -spec check_subject(message()) -> [text()]. check_subject(#message{subject = [_|_] = Subj, body = [], @@ -4294,7 +4304,7 @@ expand_opts(CompactOpts) -> {Pos+1, [{Field, Val}|Opts]} end end, {2, []}, Fields), - SubjectAuthor = proplists:get_value(subject_author, CompactOpts, <<"">>), + SubjectAuthor = proplists:get_value(subject_author, CompactOpts, {<<"">>, #jid{}}), Subject = proplists:get_value(subject, CompactOpts, <<"">>), Subscribers = proplists:get_value(subscribers, CompactOpts, []), HibernationTime = proplists:get_value(hibernation_time, CompactOpts, 0), From 86fc2f157eebde860de630058dc4aa12fc1f5aed Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 4 Aug 2023 13:45:19 +0200 Subject: [PATCH 0177/1302] Always add the occupant id, even in non-anonymous rooms (#3397) --- src/mod_muc_occupantid.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 60cb811f8..4d08a4513 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -56,12 +56,7 @@ stop(_Host) -> %%% filter_packet(Packet, State, _Nick) -> - case (State#state.config)#config.anonymous of - true -> - add_occupantid_packet(Packet, State#state.jid); - false -> - Packet - end. + add_occupantid_packet(Packet, State#state.jid). %%% %%% XEP-0421 Occupant-id From 3479f88dab533adfeda26aeb2da1939198833dd7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 4 Aug 2023 16:07:14 +0200 Subject: [PATCH 0178/1302] Pass MUC room private messages over the muc_filter_message too (#3397) --- src/mod_muc_room.erl | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 22ac26184..1c1e8ee0a 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -622,13 +622,20 @@ normal_state({route, ToNick, jid:replace_resource(StateData#state.jid, FromNick), X = #muc_user{}, - PrivMsg = xmpp:set_from( - xmpp:set_subtag(Packet, X), - FromNickJID), - lists:foreach( - fun(ToJID) -> - ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID)) - end, ToJIDs); + Packet2 = xmpp:set_subtag(Packet, X), + case ejabberd_hooks:run_fold(muc_filter_message, + StateData#state.server_host, + Packet2, + [StateData, FromNick]) of + drop -> + ok; + Packet3 -> + PrivMsg = xmpp:set_from(Packet3, FromNickJID), + lists:foreach( + fun(ToJID) -> + ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID)) + end, ToJIDs) + end; true -> ErrText = ?T("You are not allowed to send private messages"), Err = xmpp:err_forbidden(ErrText, Lang), From 7683691f5a688ecd4fdbffc7b42e4449eaa0194f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Aug 2023 17:57:54 +0200 Subject: [PATCH 0179/1302] mod_muc_occupantid: New mnesia table to store rooms salts (#3397) --- src/mod_muc_occupantid.erl | 41 +++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 4d08a4513..647cb61a3 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -38,15 +38,17 @@ -export([start/2, stop/1, mod_options/1, mod_doc/0, depends/2]). --export([filter_packet/3]). +-export([filter_packet/3, remove_room/3]). %%% %%% gen_mod %%% start(_Host, _Opts) -> + create_table(), {ok, [{hook, muc_filter_presence, filter_packet, 10}, - {hook, muc_filter_message, filter_packet, 10}]}. + {hook, muc_filter_message, filter_packet, 10}, + {hook, remove_room, remove_room, 50}]}. stop(_Host) -> ok. @@ -58,6 +60,9 @@ stop(_Host) -> filter_packet(Packet, State, _Nick) -> add_occupantid_packet(Packet, State#state.jid). +remove_room(_LServer, Name, Host) -> + delete_salt(jid:make(Name, Host)). + %%% %%% XEP-0421 Occupant-id %%% @@ -69,9 +74,39 @@ add_occupantid_packet(Packet, RoomJid) -> xmpp:set_subtag(Packet, OccupantElement). calculate_occupantid(From, RoomJid) -> - Term = {jid:remove_resource(From), RoomJid, erlang:get_cookie()}, + Term = {jid:remove_resource(From), get_salt(RoomJid)}, misc:term_to_base64(crypto:hash(sha256, io_lib:format("~p", [Term]))). +%%% +%%% Table storing rooms' salt +%%% + +-record(muc_occupant_id, {room_jid, salt}). + +create_table() -> + ejabberd_mnesia:create(?MODULE, muc_occupant_id, + [{ram_copies, [node()]}, + {local_content, true}, + {attributes, record_info(fields, muc_occupant_id)}, + {type, set}]). + + +get_salt(RoomJid) -> + case mnesia:dirty_read(muc_occupant_id, RoomJid) of + [] -> + Salt = p1_rand:get_string(), + ok = write_salt(RoomJid, Salt), + Salt; + [#muc_occupant_id{salt = Salt}] -> + Salt + end. + +write_salt(RoomJid, Salt) -> + mnesia:dirty_write(#muc_occupant_id{room_jid = RoomJid, salt = Salt}). + +delete_salt(RoomJid) -> + mnesia:dirty_delete(muc_occupant_id, RoomJid). + %%% %%% Doc %%% From ff24700156524148ebc29f932c8b55e06efd6dc3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 7 Aug 2023 12:32:36 +0200 Subject: [PATCH 0180/1302] Fix support to retract a MUC room message Now this works as expected https://xmpp.org/extensions/xep-0425.html#example-4 --- src/mod_mam.erl | 10 +++++++--- src/mod_muc_room.erl | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 7c40b17fc..652a0bbcb 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -354,11 +354,15 @@ remove_mam_for_user_with_peer(User, Server, Peer) -> {error, <<"Invalid peer JID">>} end. --spec remove_message_from_archive(User :: binary(), Server :: binary(), StanzaId :: integer()) -> +-spec remove_message_from_archive( + User :: binary() | {User :: binary(), Host :: binary()}, + Server :: binary(), StanzaId :: integer()) -> ok | {error, binary()}. -remove_message_from_archive(User, Server, StanzaId) -> +remove_message_from_archive(User, Server, StanzaId) when is_binary(User) -> + remove_message_from_archive({User, Server}, Server, StanzaId); +remove_message_from_archive({User, Host}, Server, StanzaId) -> Mod = gen_mod:db_mod(Server, ?MODULE), - case Mod:remove_from_archive(User, Server, StanzaId) of + case Mod:remove_from_archive(User, Host, StanzaId) of ok -> ok; {error, Bin} when is_binary(Bin) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 1c1e8ee0a..e23f46737 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1130,7 +1130,8 @@ process_groupchat_message(#message{from = From, lang = Lang} = Packet, StateData -spec check_message_for_retractions(Packet :: message(), State :: state()) -> state(). check_message_for_retractions(Packet, - #state{config = Config, jid = JID, server_host = Server} = State) -> + #state{config = Config, room = Room, host = Host, + server_host = Server} = State) -> case xmpp:get_subtag(Packet, #fasten_apply_to{}) of #fasten_apply_to{id = ID} = F -> case xmpp:get_subtag(F, #message_retract{}) of @@ -1140,8 +1141,7 @@ check_message_for_retractions(Packet, {NewState, StanzaId} when is_integer(StanzaId) -> case Config#config.mam of true -> - JIDs = jid:encode(JID), - mod_mam:remove_message_from_archive(JIDs, Server, StanzaId), + mod_mam:remove_message_from_archive({Room, Host}, Server, StanzaId), NewState; _ -> NewState @@ -5140,7 +5140,8 @@ process_iq_moderate(_From, #iq{type = get}, _ApplyTo, _Moderate, _StateData) -> process_iq_moderate(From, #iq{type = set, lang = Lang}, #fasten_apply_to{id = Id}, #message_moderate{reason = Reason}, - #state{config = Config, jid = JID, server_host = Server} = StateData) -> + #state{config = Config, room = Room, host = Host, + jid = JID, server_host = Server} = StateData) -> FAffiliation = get_affiliation(From, StateData), FRole = get_role(From, StateData), IsModerator = FRole == moderator orelse FAffiliation == owner orelse @@ -5154,8 +5155,7 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, StanzaId -> case Config#config.mam of true -> - JIDs = jid:encode(JID), - mod_mam:remove_message_from_archive(JIDs, Server, StanzaId); + mod_mam:remove_message_from_archive({Room, Host}, Server, StanzaId); _ -> ok end, From 83e51c815dcb3953aff1a883963736a1d4c0f2b1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 7 Aug 2023 13:05:06 +0200 Subject: [PATCH 0181/1302] Pass also MUC room retract messages over the muc_filter_message (#3397) --- src/mod_muc_room.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index e23f46737..b6483ddab 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5160,12 +5160,18 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, ok end, By = jid:replace_resource(JID, find_nick_by_jid(From, StateData)), - Packet = #message{type = groupchat, + Packet0 = #message{type = groupchat, + from = From, sub_els = [ #fasten_apply_to{id = Id, sub_els = [ #message_moderated{by = By, reason = Reason, retract = #message_retract{}} ]}]}, + {FromNick, _Role} = get_participant_data(From, StateData), + Packet = ejabberd_hooks:run_fold(muc_filter_message, + StateData#state.server_host, + Packet0, + [StateData, FromNick]), send_wrapped_multiple(JID, get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_MESSAGES, StateData), Packet, ?NS_MUCSUB_NODES_MESSAGES, StateData), From f8af3a00059863fe9adaab9fd16567f252511c99 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Aug 2023 12:37:05 +0200 Subject: [PATCH 0182/1302] create_room_with_opts: fix typo and move examples to args_example (#4080) --- src/mod_muc_admin.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 5264eaa4b..e68a275ef 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -162,14 +162,15 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = create_room_with_opts, tags = [muc_room], desc = "Create a MUC room name@service in host with given options", - longdesc = "To set affilitions string value must have format 'Type:JID,Type:JID' " - "for example 'owner:bob@example.com,member:peter@example.com'. Subscribers can be " - "define with string 'JID:Nick:Node1:Node2,JID:Nick:Node3' for example " - "'bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages'.", + longdesc = + "The syntax of affiliations is: 'Type:JID,Type:JID'. " + "The syntax of subscribers is: 'JID:Nick:Node:Node2,JID:Nick:Node' ", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], args_example = ["room1", "muc.example.com", "localhost", - [{"members_only","true"}, {"subscribers", "bob@example.com:Bob:messages"}]], + [{"members_only","true"}, + {"affiliations", "owner:bob@example.com,member:peter@example.com"}, + {"subscribers", "bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages"}]], args = [{name, binary}, {service, binary}, {host, binary}, {options, {list, From a84fbd6a74ad1b58b421e1683956b475adf00d71 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Aug 2023 13:59:43 +0200 Subject: [PATCH 0183/1302] Improve syntax of many command descriptions for the Docs site --- src/ejabberd_admin.erl | 22 +++++++++-------- src/ejabberd_commands.erl | 4 +-- src/mod_admin_extra.erl | 51 +++++++++++++++++++++------------------ src/mod_mam.erl | 8 +++--- src/mod_muc_admin.erl | 36 ++++++++++++++------------- 5 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index a6ea8a04b..6ead50ff9 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -140,8 +140,8 @@ get_commands_spec() -> desc = "Inform users and rooms, wait, and stop the server", longdesc = "Provide the delay in seconds, and the " "announcement quoted, for example: \n" - "ejabberdctl stop_kindly 60 " - "\\\"The server will stop in one minute.\\\"", + "`ejabberdctl stop_kindly 60 " + "\\\"The server will stop in one minute.\\\"`", module = ?MODULE, function = stop_kindly, args_desc = ["Seconds to wait", "Announcement to send, with quotes"], args_example = [60, <<"Server will stop now.">>], @@ -292,8 +292,9 @@ get_commands_spec() -> args = [{host, binary}], result = {res, rescode}}, #ejabberd_commands{name = import_prosody, tags = [mnesia, sql], desc = "Import data from Prosody", - longdesc = "Note: this requires ejabberd compiled with --enable-lua " - "and include the optional 'luerl' library.", + longdesc = "Note: this requires ejabberd to be " + "[compiled with `--enable-lua`](http://localhost:8098/admin/installation/#configure) " + "(which installs the `luerl` library).", module = prosody2ejabberd, function = from_dir, args_desc = ["Full path to the Prosody data directory"], args_example = ["/var/lib/prosody/datadump/"], @@ -371,7 +372,7 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = set_master, tags = [cluster], desc = "Set master node of the clustered Mnesia tables", - longdesc = "If you provide as nodename \"self\", this " + longdesc = "If you provide as nodename `self`, this " "node will be set as its own master.", module = ?MODULE, function = set_master, args_desc = ["Name of the erlang node that will be considered master of this node"], @@ -399,7 +400,7 @@ get_commands_spec() -> "binary backup file the internal Mnesia " "database. This will consume a lot of memory if " "you have a large database, you may prefer " - "'install_fallback'.", + "http://./#install-fallback[install_fallback].", module = ?MODULE, function = restore_mnesia, args_desc = ["Full path to the backup file"], args_example = ["/var/lib/ejabberd/database.backup"], @@ -421,8 +422,9 @@ get_commands_spec() -> longdesc = "Restore immediately. This is not " "recommended for big databases, as it will " "consume much time, memory and processor. In " - "that case it's preferable to use 'backup' and " - "'install_fallback'.", + "that case it's preferable to use " + "http://./#backup[backup] and " + "http://./#install-fallback[install_fallback].", module = ?MODULE, function = load_mnesia, args_desc = ["Full path to the text file"], args_example = ["/var/lib/ejabberd/database.txt"], @@ -444,8 +446,8 @@ get_commands_spec() -> "restore the database at the next ejabberd " "start. This means that, after running this " "command, you have to restart ejabberd. This " - "command requires less memory than - 'restore'.", + "command requires less memory than " + "http://./#restore[restore].", module = ?MODULE, function = install_fallback_mnesia, args_desc = ["Full path to the fallback file"], args_example = ["/var/lib/ejabberd/database.fallback"], diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 1b3e968cf..e61569777 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -73,7 +73,7 @@ get_commands_spec() -> "documentation should be stored", "Regexp matching names of commands or modules " "that will be included inside generated document", - "Comma separated list of languages (chosen from java, perl, xmlrpc, json)" + "Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) " "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succeeded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], @@ -87,7 +87,7 @@ get_commands_spec() -> "documentation should be stored", "Regexp matching names of commands or modules " "that will be included inside generated document", - "Comma separated list of languages (chosen from java, perl, xmlrpc, json)" + "Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) " "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succeeded", args_example = ["/home/me/docs/api.html", "mod_admin", "java,json"], diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 9b88176fb..7cc54939c 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -169,10 +169,12 @@ get_commands_spec() -> #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], desc = "Delete users that didn't log in last days, or that never logged", longdesc = "To protect admin accounts, configure this for example:\n" + "```\n" "access_rules:\n" " protect_old_users:\n" " - allow: admin\n" - " - deny: all\n", + " - deny: all\n" + "```\n", module = ?MODULE, function = delete_old_users, args = [{days, integer}], args_example = [30], @@ -183,10 +185,12 @@ get_commands_spec() -> #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge], desc = "Delete users that didn't log in last days in vhost, or that never logged", longdesc = "To protect admin accounts, configure this for example:\n" + "```\n" "access_rules:\n" " delete_old_users:\n" " - deny: admin\n" - " - allow: all\n", + " - allow: all\n" + "```\n", module = ?MODULE, function = delete_old_users_vhost, args = [{host, binary}, {days, integer}], args_example = [<<"myserver.com">>, 30], @@ -215,7 +219,8 @@ get_commands_spec() -> result_desc = "Status code: 0 on success, 1 otherwise"}, #ejabberd_commands{name = check_password_hash, tags = [accounts], desc = "Check if the password hash is correct", - longdesc = "Allows hash methods from crypto application", + longdesc = "Allows hash methods from the Erlang/OTP " + "[crypto](https://www.erlang.org/doc/man/crypto) application.", module = ?MODULE, function = check_password_hash, args = [{user, binary}, {host, binary}, {passwordhash, binary}, {hashmethod, binary}], @@ -390,14 +395,14 @@ get_commands_spec() -> "and its presence (show and status message) " "for a given user.", longdesc = - "The 'jid' value contains the user jid " - "with resource.\nThe 'show' value contains " + "The `jid` value contains the user JID " + "with resource.\n\nThe `show` value contains " "the user presence flag. It can take " - "limited values:\n - available\n - chat " - "(Free for chat)\n - away\n - dnd (Do " - "not disturb)\n - xa (Not available, " - "extended away)\n - unavailable (Not " - "connected)\n\n'status' is a free text " + "limited values:\n\n - `available`\n - `chat` " + "(Free for chat)\n - `away`\n - `dnd` (Do " + "not disturb)\n - `xa` (Not available, " + "extended away)\n - `unavailable` (Not " + "connected)\n\n`status` is a free text " "defined by the user client.", module = ?MODULE, function = get_presence, args = [{user, binary}, {host, binary}], @@ -421,8 +426,8 @@ get_commands_spec() -> args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>, <<"available">>,<<"away">>,<<"BB">>, <<"7">>], args_desc = ["User name", "Server name", "Resource", - "Type: available, error, probe...", - "Show: away, chat, dnd, xa.", "Status text", + "Type: `available`, `error`, `probe`...", + "Show: `away`, `chat`, `dnd`, `xa`.", "Status text", "Priority, provide this value as an integer"], result = {res, rescode}}, @@ -485,7 +490,7 @@ get_commands_spec() -> #ejabberd_commands{name = add_rosteritem, tags = [roster], desc = "Add an item to a user's roster (supports ODBC)", - longdesc = "Group can be several groups separated by ; for example: \"g1;g2;g3\"", + longdesc = "Group can be several groups separated by `;` for example: `g1;g2;g3`", module = ?MODULE, function = add_rosteritem, args = [{localuser, binary}, {localhost, binary}, {user, binary}, {host, binary}, @@ -616,7 +621,7 @@ get_commands_spec() -> #ejabberd_commands{name = get_last, tags = [last], desc = "Get last activity information", longdesc = "Timestamp is UTC and XEP-0082 format, for example: " - "2017-02-23T22:25:28.063062Z ONLINE", + "`2017-02-23T22:25:28.063062Z ONLINE`", module = ?MODULE, function = get_last, args = [{user, binary}, {host, binary}], args_example = [<<"user1">>,<<"myserver.com">>], @@ -630,7 +635,7 @@ get_commands_spec() -> #ejabberd_commands{name = set_last, tags = [last], desc = "Set last activity information", longdesc = "Timestamp is the seconds since " - "1970-01-01 00:00:00 UTC, for example: date +%s", + "`1970-01-01 00:00:00 UTC`. For example value see `date +%s`", module = ?MODULE, function = set_last, args = [{user, binary}, {host, binary}, {timestamp, integer}, {status, binary}], args_example = [<<"user1">>,<<"myserver.com">>, 1500045311, <<"GoSleeping">>], @@ -657,11 +662,11 @@ get_commands_spec() -> desc = "Create a Shared Roster Group", longdesc = "If you want to specify several group " "identifiers in the Display argument,\n" - "put \\ \" around the argument and\nseparate the " - "identifiers with \\ \\ n\n" + "put `\\ \"` around the argument and\nseparate the " + "identifiers with `\\ \\ n`\n" "For example:\n" - " ejabberdctl srg_create group3 myserver.com " - "name desc \\\"group1\\\\ngroup2\\\"", + " `ejabberdctl srg_create group3 myserver.com " + "name desc \\\"group1\\\\ngroup2\\\"`", note = "changed in 21.07", module = ?MODULE, function = srg_create, args = [{group, binary}, {host, binary}, @@ -734,7 +739,7 @@ get_commands_spec() -> #ejabberd_commands{name = send_message, tags = [stanza], desc = "Send a message to a local or remote bare of full JID", longdesc = "When sending a groupchat message to a MUC room, " - "FROM must be the full JID of a room occupant, " + "`from` must be the full JID of a room occupant, " "or the bare JID of a MUC service admin, " "or the bare JID of a MUC/Sub subscribed user.", module = ?MODULE, function = send_message, @@ -742,13 +747,13 @@ get_commands_spec() -> {subject, binary}, {body, binary}], args_example = [<<"headline">>, <<"admin@localhost">>, <<"user1@localhost">>, <<"Restart">>, <<"In 5 minutes">>], - args_desc = ["Message type: normal, chat, headline, groupchat", "Sender JID", + args_desc = ["Message type: `normal`, `chat`, `headline`, `groupchat`", "Sender JID", "Receiver JID", "Subject, or empty string", "Body"], result = {res, rescode}}, #ejabberd_commands{name = send_stanza_c2s, tags = [stanza], desc = "Send a stanza from an existing C2S session", - longdesc = "USER@HOST/RESOURCE must be an existing C2S session." - " As an alternative, use send_stanza instead.", + longdesc = "`user`@`host`/`resource` must be an existing C2S session." + " As an alternative, use http://./#send-stanza[send_stanza] instead.", module = ?MODULE, function = send_stanza_c2s, args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}], args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>, diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 652a0bbcb..77dce5416 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1468,9 +1468,9 @@ get_commands_spec() -> [#ejabberd_commands{name = delete_old_mam_messages, tags = [purge], desc = "Delete MAM messages older than DAYS", longdesc = "Valid message TYPEs: " - "\"chat\", \"groupchat\", \"all\".", + "`chat`, `groupchat`, `all`.", module = ?MODULE, function = delete_old_messages, - args_desc = ["Type of messages to delete (chat, groupchat, all)", + args_desc = ["Type of messages to delete (`chat`, `groupchat`, `all`)", "Days to keep messages"], args_example = [<<"all">>, 31], args = [{type, binary}, {days, integer}], @@ -1479,10 +1479,10 @@ get_commands_spec() -> desc = "Delete MAM messages older than DAYS", note = "added in 22.05", longdesc = "Valid message TYPEs: " - "\"chat\", \"groupchat\", \"all\".", + "`chat`, `groupchat`, `all`.", module = ?MODULE, function = delete_old_messages_batch, args_desc = ["Name of host where messages should be deleted", - "Type of messages to delete (chat, groupchat, all)", + "Type of messages to delete (`chat`, `groupchat`, `all`)", "Days to keep messages", "Number of messages to delete per batch", "Desired rate of messages to delete per minute"], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index e68a275ef..5c7453e2c 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -163,8 +163,8 @@ get_commands_spec() -> #ejabberd_commands{name = create_room_with_opts, tags = [muc_room], desc = "Create a MUC room name@service in host with given options", longdesc = - "The syntax of affiliations is: 'Type:JID,Type:JID'. " - "The syntax of subscribers is: 'JID:Nick:Node:Node2,JID:Nick:Node' ", + "The syntax of `affiliations` is: `Type:JID,Type:JID`. " + "The syntax of `subscribers` is: `JID:Nick:Node:Node2:Node3,JID:Nick:Node`.", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], args_example = ["room1", "muc.example.com", "localhost", @@ -192,9 +192,9 @@ get_commands_spec() -> desc = "List the rooms that are unused for many days in the service", longdesc = "The room recent history is used, so it's recommended " " to wait a few days after service start before running this." - " The MUC service argument can be 'global' to get all hosts.", + " The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_unused_list, - args_desc = ["MUC service, or 'global' for all", "Number of days"], + args_desc = ["MUC service, or `global` for all", "Number of days"], args_example = ["muc.example.com", 31], result_desc = "List of unused rooms", result_example = ["room1@muc.example.com", "room2@muc.example.com"], @@ -205,9 +205,9 @@ get_commands_spec() -> desc = "Destroy the rooms that are unused for many days in the service", longdesc = "The room recent history is used, so it's recommended " " to wait a few days after service start before running this." - " The MUC service argument can be 'global' to get all hosts.", + " The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_unused_destroy, - args_desc = ["MUC service, or 'global' for all", "Number of days"], + args_desc = ["MUC service, or `global` for all", "Number of days"], args_example = ["muc.example.com", 31], result_desc = "List of unused rooms that has been destroyed", result_example = ["room1@muc.example.com", "room2@muc.example.com"], @@ -217,9 +217,9 @@ get_commands_spec() -> #ejabberd_commands{name = rooms_empty_list, tags = [muc], desc = "List the rooms that have no messages in archive", - longdesc = "The MUC service argument can be 'global' to get all hosts.", + longdesc = "The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_empty_list, - args_desc = ["MUC service, or 'global' for all"], + args_desc = ["MUC service, or `global` for all"], args_example = ["muc.example.com"], result_desc = "List of empty rooms", result_example = ["room1@muc.example.com", "room2@muc.example.com"], @@ -228,9 +228,9 @@ get_commands_spec() -> result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = rooms_empty_destroy, tags = [muc], desc = "Destroy the rooms that have no messages in archive", - longdesc = "The MUC service argument can be 'global' to get all hosts.", + longdesc = "The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_empty_destroy, - args_desc = ["MUC service, or 'global' for all"], + args_desc = ["MUC service, or `global` for all"], args_example = ["muc.example.com"], result_desc = "List of empty rooms that have been destroyed", result_example = ["room1@muc.example.com", "room2@muc.example.com"], @@ -295,11 +295,11 @@ get_commands_spec() -> longdesc = "Since ejabberd 20.12, this command is " "asynchronous: the API call may return before the " "server has send all the invitations.\n\n" - "Password and Message can also be: none. " - "Users JIDs are separated with : ", + "Password and Message can also be: `none`. " + "Users JIDs are separated with `:`.", module = ?MODULE, function = send_direct_invitation, - args_desc = ["Room name", "MUC service", "Password, or none", - "Reason text, or none", "Users JIDs separated with : characters"], + args_desc = ["Room name", "MUC service", "Password, or `none`", + "Reason text, or `none`", "Users JIDs separated with `:` characters"], args_example = [<<"room1">>, <<"muc.example.com">>, <<>>, <<"Check this out!">>, "user2@localhost:user3@example.com"], @@ -333,7 +333,7 @@ get_commands_spec() -> desc = "Subscribe to a MUC conference", module = ?MODULE, function = subscribe_room, args_desc = ["User JID", "a user's nick", - "the room to subscribe", "nodes separated by commas: ,"], + "the room to subscribe", "nodes separated by commas: `,`"], args_example = ["tom@localhost", "Tom", "room1@conference.localhost", "urn:xmpp:mucsub:nodes:messages,urn:xmpp:mucsub:nodes:affiliations"], result_desc = "The list of nodes that has subscribed", @@ -345,11 +345,13 @@ get_commands_spec() -> #ejabberd_commands{name = subscribe_room_many, tags = [muc_room], desc = "Subscribe several users to a MUC conference", note = "added in 22.05", - longdesc = "This command accept up to 50 users at once (this is configurable with `subscribe_room_many_max_users` option)", + longdesc = "This command accepts up to 50 users at once " + "(this is configurable with the *`mod_muc_admin`* option " + "`subscribe_room_many_max_users`)", module = ?MODULE, function = subscribe_room_many, args_desc = ["Users JIDs and nicks", "the room to subscribe", - "nodes separated by commas: ,"], + "nodes separated by commas: `,`"], args_example = [[{"tom@localhost", "Tom"}, {"jerry@localhost", "Jerry"}], "room1@conference.localhost", From 9f08b4aa1587c0e463e880ee2916d77e479a6c08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 06:42:16 +0000 Subject: [PATCH 0184/1302] Bump ex_doc from 0.30.1 to 0.30.5 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.30.1 to 0.30.5. - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.30.1...v0.30.5) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index c053aae0c..99ea68031 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.30.1", "a0f3b598d3c2cb3af48af39e59fa66ac8d4033740409b11dd753a3f30f8f8f7a", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2e2216e84aa33e5803f8898d762b0f5e76bf2de3a08d1f40ac5f74456dd5057c"}, + "ex_doc": {:hex, :ex_doc, "0.30.5", "aa6da96a5c23389d7dc7c381eba862710e108cee9cfdc629b7ec021313900e9e", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "88a1e115dcb91cefeef7e22df4a6ebbe4634fbf98b38adcbc25c9607d6d9d8e6"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, From 2dc843cdddf8efd92e50ff4aafb3c910c7f18618 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 4 Oct 2022 15:41:44 +0200 Subject: [PATCH 0185/1302] mod_privilege: Don't fail to edit roster (#3942) --- src/mod_privilege.erl | 19 ++++++++++++------- src/mod_roster.erl | 12 +++++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index f59247789..51a82769f 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -217,19 +217,24 @@ process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From, process_message(_Stanza) -> ok. --spec roster_access(boolean(), iq()) -> boolean(). -roster_access(true, _) -> - true; -roster_access(false, #iq{from = From, to = To, type = Type}) -> +-spec roster_access({true, iq()} | false, iq()) -> {true, iq()} | false. +roster_access({true, _IQ} = Acc, _) -> + Acc; +roster_access(false, #iq{from = From, to = To, type = Type} = IQ) -> Host = From#jid.lserver, ServerHost = To#jid.lserver, Permissions = get_permissions(ServerHost), case maps:find(Host, Permissions) of {ok, Access} -> Permission = proplists:get_value(roster, Access, none), - (Permission == both) - orelse (Permission == get andalso Type == get) - orelse (Permission == set andalso Type == set); + case (Permission == both) + orelse (Permission == get andalso Type == get) + orelse (Permission == set andalso Type == set) of + true -> + {true, xmpp:put_meta(IQ, privilege_from, To)}; + false -> + false + end; error -> %% Component is disconnected false diff --git a/src/mod_roster.erl b/src/mod_roster.erl index ad5fc0f77..273b21380 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -128,8 +128,8 @@ process_iq(#iq{lang = Lang, to = To} = IQ) -> false -> Txt = ?T("Query to another users is forbidden"), xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); - true -> - process_local_iq(IQ) + {true, IQ1} -> + process_local_iq(IQ1) end. -spec process_local_iq(iq()) -> iq(). @@ -147,7 +147,13 @@ process_local_iq(#iq{type = set, from = From, lang = Lang, Txt = ?T("Duplicated groups are not allowed by RFC6121"), xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); false -> - #jid{lserver = LServer} = From, + From1 = case xmpp:get_meta(IQ, privilege_from, none) of + #jid{} = PrivFrom -> + PrivFrom; + none -> + From + end, + #jid{lserver = LServer} = From1, Access = mod_roster_opt:access(LServer), case acl:match_rule(LServer, Access, From) of deny -> From b29f87a97886d4e9214abb165607490ea23f64f8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Aug 2023 13:14:02 +0200 Subject: [PATCH 0186/1302] Result of running: make doap options --- ejabberd.doap | 9 +++++++++ src/mod_muc_opt.erl | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ejabberd.doap b/ejabberd.doap index 662dba347..47560781d 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -665,6 +665,15 @@ mod_private + + + + 0.1.0 + + + mod_muc_occupantid + + diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl index c22a9594d..0d397abbb 100644 --- a/src/mod_muc_opt.erl +++ b/src/mod_muc_opt.erl @@ -86,7 +86,7 @@ db_type(Opts) when is_map(Opts) -> db_type(Host) -> gen_mod:get_module_opt(Host, mod_muc, db_type). --spec default_room_options(gen_mod:opts() | global | binary()) -> [{atom(),'anyone' | 'false' | 'moderators' | 'nobody' | 'true' | 'undefined' | binary() | ['moderator' | 'participant' | 'visitor'] | pos_integer() | tuple()}]. +-spec default_room_options(gen_mod:opts() | global | binary()) -> [{atom(),'anyone' | 'false' | 'moderators' | 'nobody' | 'none' | 'participants' | 'true' | 'undefined' | binary() | ['moderator' | 'participant' | 'visitor'] | pos_integer() | tuple()}]. default_room_options(Opts) when is_map(Opts) -> gen_mod:get_opt(default_room_options, Opts); default_room_options(Host) -> From 00c76003cbd3ad81762bd581e878f15b6fc51036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 18 Aug 2023 11:46:26 +0200 Subject: [PATCH 0187/1302] Add ability to force alternative upsert implementation in mysql --- src/ejabberd_option.erl | 8 ++++++++ src/ejabberd_options.erl | 3 +++ src/ejabberd_sql.erl | 12 ++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 81f4bab7f..6ea63e561 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -143,6 +143,7 @@ -export([sm_use_cache/0, sm_use_cache/1]). -export([sql_connect_timeout/0, sql_connect_timeout/1]). -export([sql_database/0, sql_database/1]). +-export([sql_flags/0, sql_flags/1]). -export([sql_keepalive_interval/0, sql_keepalive_interval/1]). -export([sql_odbc_driver/0, sql_odbc_driver/1]). -export([sql_password/0, sql_password/1]). @@ -964,6 +965,13 @@ sql_database() -> sql_database(Host) -> ejabberd_config:get_option({sql_database, Host}). +-spec sql_flags() -> ['mysql_alternative_upsert']. +sql_flags() -> + sql_flags(global). +-spec sql_flags(global | binary()) -> ['mysql_alternative_upsert']. +sql_flags(Host) -> + ejabberd_config:get_option({sql_flags, Host}). + -spec sql_keepalive_interval() -> 'undefined' | pos_integer(). sql_keepalive_interval() -> sql_keepalive_interval(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 06087921d..9f48839bb 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -424,6 +424,8 @@ opt_type(sql_username) -> econf:binary(); opt_type(sql_prepared_statements) -> econf:bool(); +opt_type(sql_flags) -> + econf:list_or_single(econf:enum([mysql_alternative_upsert]), [sorted, unique]); opt_type(trusted_proxies) -> econf:either(all, econf:list(econf:ip_mask())); opt_type(use_cache) -> @@ -708,6 +710,7 @@ options() -> {sql_start_interval, timer:seconds(30)}, {sql_username, <<"ejabberd">>}, {sql_prepared_statements, true}, + {sql_flags, []}, {trusted_proxies, []}, {validate_stream, false}, {websocket_origin, []}, diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index c5c06d078..a07dac67c 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -1148,7 +1148,11 @@ get_db_version(#state{db_type = pgsql} = State) -> ?WARNING_MSG("Error getting pgsql version: ~p", [Res]), State end; -get_db_version(#state{db_type = mysql} = State) -> +get_db_version(#state{db_type = mysql, host = Host} = State) -> + DefaultUpsert = case lists:member(mysql_alternative_upsert, ejabberd_option:sql_flags(Host)) of + true -> 1; + _ -> 0 + end, case mysql_to_odbc(p1_mysql_conn:squery(State#state.db_ref, [<<"select version();">>], self(), [{timeout, 5000}, @@ -1160,10 +1164,10 @@ get_db_version(#state{db_type = mysql} = State) -> V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), TypeA = binary_to_atom(Type, utf8), Flags = case TypeA of - 'MariaDB' -> 0; + 'MariaDB' -> DefaultUpsert; _ when V >= 5007026 andalso V < 8000000 -> 1; _ when V >= 8000020 -> 1; - _ -> 0 + _ -> DefaultUpsert end, State#state{db_version = {V, TypeA, Flags}}; {match, [V1, V2, V3]} -> @@ -1171,7 +1175,7 @@ get_db_version(#state{db_type = mysql} = State) -> Flags = case V of _ when V >= 5007026 andalso V < 8000000 -> 1; _ when V >= 8000020 -> 1; - _ -> 0 + _ -> DefaultUpsert end, State#state{db_version = {V, unknown, Flags}}; _ -> From c0e77749376c9e31f486a5e19159873a4ece8a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 24 Aug 2023 11:54:26 +0200 Subject: [PATCH 0188/1302] Don't always store messages passed through muc_filter_message Recently we added new places where we call muc_filter_message to add occupandid info to messages, but this also made them be stored in archive as mod_mam uses that hook for getting sent messages - in case of those messages we shouldn't be doing this. This should fix issue #4083 --- src/mod_mam.erl | 2 ++ src/mod_muc_room.erl | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 77dce5416..58e400ade 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -465,6 +465,8 @@ offline_message({_Action, #message{from = Peer, to = To} = Pkt} = Acc) -> -spec muc_filter_message(message(), mod_muc_room:state(), binary()) -> message(). +muc_filter_message(#message{meta = #{mam_ignore := true}} = Pkt, _MUCState, _FromNick) -> + Pkt; muc_filter_message(#message{from = From} = Pkt, #state{config = Config, jid = RoomJID} = MUCState, FromNick) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index b6483ddab..34575733b 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -625,12 +625,12 @@ normal_state({route, ToNick, Packet2 = xmpp:set_subtag(Packet, X), case ejabberd_hooks:run_fold(muc_filter_message, StateData#state.server_host, - Packet2, + xmpp:put_meta(Packet2, mam_ignore, true), [StateData, FromNick]) of drop -> ok; Packet3 -> - PrivMsg = xmpp:set_from(Packet3, FromNickJID), + PrivMsg = xmpp:set_from(xmpp:del_meta(Packet3, mam_ignore), FromNickJID), lists:foreach( fun(ToJID) -> ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID)) @@ -3016,7 +3016,7 @@ send_subject(JID, #state{subject_author = {Nick, AuthorJID}} = StateData) -> to = JID, type = groupchat, subject = Subject}, case ejabberd_hooks:run_fold(muc_filter_message, StateData#state.server_host, - Packet, + xmpp:put_meta(Packet, mam_ignore, true), [StateData, Nick]) of drop -> ok; @@ -5169,9 +5169,9 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, ]}]}, {FromNick, _Role} = get_participant_data(From, StateData), Packet = ejabberd_hooks:run_fold(muc_filter_message, - StateData#state.server_host, - Packet0, - [StateData, FromNick]), + StateData#state.server_host, + xmpp:put_meta(Packet0, mam_ignore, true), + [StateData, FromNick]), send_wrapped_multiple(JID, get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_MESSAGES, StateData), Packet, ?NS_MUCSUB_NODES_MESSAGES, StateData), From a01de8d944c7b47c7aefe5d2bac5f45eaa54207c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 18 Aug 2023 18:04:02 +0200 Subject: [PATCH 0189/1302] Fix small bug introduced in 5d549dc When providing a client-id as explained in https://www.process-one.net/blog/understanding-ejabberd-oauth-support-roadmap/ ejabberd crashed with an error exception error: no function clause matching ejabberd_oauth:get_client_identity( as reported in https://stackoverflow.com/questions/76922951/ejabberd-oauth-api-http-1-1-502-bad-gateway --- src/ejabberd_oauth.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 63d1a6f36..83eceb1ba 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -244,10 +244,9 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. - get_client_identity(<<"">>, Ctx) -> {ok, {Ctx, {client, unknown_client}}}; -get_client_identity({client, ClientID}, Ctx) -> +get_client_identity(ClientID, Ctx) when is_binary(ClientID) -> {ok, {Ctx, {client, ClientID}}}. verify_redirection_uri(_ClientID, RedirectURI, Ctx) -> From a7c3c9b77dd5b687364cecd0bd4b93ed9b4aeaa9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 24 Aug 2023 17:33:12 +0200 Subject: [PATCH 0190/1302] Pass ERLANG_OPTS when calling erl to parse the INET_DIST_INTERFACE (#4066) This is required when running ejabberdctl in binary installers and INET_DIST_INTERFACE was configured, because some boot files were removed --- .github/container/ejabberdctl.template | 2 +- ejabberdctl.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index a0e2f0573..8407ac5a3 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -77,7 +77,7 @@ if [ -n "$FIREWALL_WINDOW" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" fi if [ -n "$INET_DIST_INTERFACE" ] ; then - INET_DIST_INTERFACE2=$("$ERL" -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) + INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi diff --git a/ejabberdctl.template b/ejabberdctl.template index 4085492ac..f1af26ca1 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -76,7 +76,7 @@ if [ -n "$FIREWALL_WINDOW" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" fi if [ -n "$INET_DIST_INTERFACE" ] ; then - INET_DIST_INTERFACE2=$("$ERL" -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) + INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi From 6d596063de0cd021e008f247fd9da76e22764382 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Aug 2023 11:55:45 +0200 Subject: [PATCH 0191/1302] Elixir 1.15 removed support for --app Removing that argument does not affect iexlive at all For reference: https://github.com/elixir-lang/elixir/commit/e1eecb8ca698712f5cff76f52ff49b381ce64aa9 --- .github/container/ejabberdctl.template | 2 +- ejabberdctl.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 8407ac5a3..dc9e99bc6 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -358,7 +358,7 @@ case $1 in ;; iexlive) livewarning - exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" --app ejabberd + exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" ;; ping) PEER=${2:-$ERLANG_NODE} diff --git a/ejabberdctl.template b/ejabberdctl.template index f1af26ca1..d255bcbaa 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -315,7 +315,7 @@ case $1 in ;; iexlive) livewarning - exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" --app ejabberd + exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" ;; ping) PEER=${2:-$ERLANG_NODE} From 40333066d6c99267393114b9d6850b23a5aa9eb2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Aug 2023 13:43:06 +0200 Subject: [PATCH 0192/1302] Update syntax of function calls as recommended by Elixir compiler --- lib/ejabberd/config/config.ex | 4 ++-- lib/mix/tasks/deps.tree.ex | 6 +++--- mix.exs | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/ejabberd/config/config.ex b/lib/ejabberd/config/config.ex index a1b91858a..df508fb4d 100644 --- a/lib/ejabberd/config/config.ex +++ b/lib/ejabberd/config/config.ex @@ -36,8 +36,8 @@ defmodule Ejabberd.Config do case force do true -> - Ejabberd.Config.Store.stop - Ejabberd.Config.Store.start_link + Ejabberd.Config.Store.stop() + Ejabberd.Config.Store.start_link() do_init(file_path) false -> if not init_already_executed, do: do_init(file_path) diff --git a/lib/mix/tasks/deps.tree.ex b/lib/mix/tasks/deps.tree.ex index a3439c40b..e93b4aa48 100644 --- a/lib/mix/tasks/deps.tree.ex +++ b/lib/mix/tasks/deps.tree.ex @@ -14,15 +14,15 @@ defmodule Mix.Tasks.Ejabberd.Deps.Tree do def run(_argv) do # First we need to start manually the store to be available # during the compilation of the config file. - Ejabberd.Config.Store.start_link + Ejabberd.Config.Store.start_link() Ejabberd.Config.init(:ejabberd_config.path()) - Mix.shell.info "ejabberd modules" + Mix.shell().info "ejabberd modules" Ejabberd.Config.Store.get(:modules) |> Enum.reverse # Because of how mods are stored inside the store |> format_mods - |> Mix.shell.info + |> Mix.shell().info end defp format_mods(mods) when is_list(mods) do diff --git a/mix.exs b/mix.exs index b11687cc5..d190e9da0 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Ejabberd.MixProject do elixir: elixir_required_version(), elixirc_paths: ["lib"], compile_path: ".", - compilers: [:asn1] ++ Mix.compilers, + compilers: [:asn1] ++ Mix.compilers(), erlc_options: erlc_options(), erlc_paths: ["asn1", "src"], # Elixir tests are starting the part of ejabberd they need @@ -260,7 +260,7 @@ defmodule Ejabberd.MixProject do end # Mix/Elixir lower than 1.11.0 use config/releases.exs instead of runtime.exs - case Version.match?(System.version, "~> 1.11") do + case Version.match?(System.version(), "~> 1.11") do true -> :ok false -> @@ -339,7 +339,7 @@ defmodule Mix.Tasks.Compile.Asn1 do def run(args) do {opts, _, _} = OptionParser.parse(args, switches: [force: :boolean]) - project = Mix.Project.config + project = Mix.Project.config() source_paths = project[:asn1_paths] || ["asn1"] dest_paths = project[:asn1_target] || ["src"] mappings = Enum.zip(source_paths, dest_paths) @@ -361,7 +361,7 @@ defmodule Mix.Tasks.Compile.Asn1 do end def manifests, do: [manifest()] - defp manifest, do: Path.join(Mix.Project.manifest_path, @manifest) + defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest) def clean, do: Erlang.clean(manifest()) end From 2a6ea7926026dfd0a09fb4a315dec9040f596d51 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Aug 2023 12:30:09 +0200 Subject: [PATCH 0193/1302] When building OTP release with mix, keep ERLANG_NODE=ejabberd@localhost This updates the mix.exs code from commit 8ca12d4 (ejabberd 21.07) Consequently, no need in make-binaries to update ERLANG_NODE. The ecs docker image 23.04 got a similar change in https://github.com/processone/docker-ejabberd/pull/73/commits/f81905d5e315d2eb813a715b51d4a8351a7d4e6b --- mix.exs | 3 +-- tools/make-binaries | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index d190e9da0..bb82b6471 100644 --- a/mix.exs +++ b/mix.exs @@ -271,8 +271,7 @@ defmodule Ejabberd.MixProject do Mix.Generator.copy_template("ejabberdctl.example1", "ejabberdctl.example2", assigns) execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2> ejabberdctl.example2a") Mix.Generator.copy_template("ejabberdctl.example2a", "ejabberdctl.example2b", assigns) - execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2b > ejabberdctl.example3") - execute.("sed -e 's|^ERLANG_NODE=ejabberd@localhost|ERLANG_NODE=ejabberd|g' ejabberdctl.example3 > ejabberdctl.example4") + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2b > ejabberdctl.example4") execute.("sed -e 's|^ERLANG_OPTS=\"|ERLANG_OPTS=\"-boot ../releases/#{release.version}/start_clean -boot_var RELEASE_LIB ../lib |' ejabberdctl.example4 > ejabberdctl.example5") execute.("sed -e 's|^INSTALLUSER=|ERL_OPTIONS=\"-setcookie \\$\\(cat \"\\${SCRIPT_DIR%/*}/releases/COOKIE\")\"\\nINSTALLUSER=|g' ejabberdctl.example5 > ejabberdctl.example6") Mix.Generator.copy_template("ejabberdctl.example6", "#{ro}/bin/ejabberdctl", assigns) diff --git a/tools/make-binaries b/tools/make-binaries index 442edfda4..d0398d6d3 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -409,7 +409,6 @@ edit_ejabberdctl() sed -i \ -e "2iexport TERM='internal'" \ -e '/ERL_OPTIONS=/d' \ - -e 's|^ERLANG_NODE=ejabberd$|ERLANG_NODE=ejabberd@localhost|' \ -e 's|_DIR:=".*}/|_DIR:="/opt/ejabberd/|' \ -e 's|/database|/database/$ERLANG_NODE|' \ "$code_dir/bin/${rel_name}ctl" From 19e2e169b179b123ee9f6459b5cf4adf351dac6d Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Aug 2023 17:23:31 +0200 Subject: [PATCH 0194/1302] Let "ejabberdctl etop" work in a release (if observer is available) --- ejabberdctl.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index d255bcbaa..d128cd6e9 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -305,8 +305,8 @@ case $1 in ;; etop) set_dist_client - exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \ - -s erlang halt -output text + exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" -s etop \ + -output text ;; iexdebug) debugwarning From 57d404a99be374e7b7a8bbc75ea0b5b65189c87b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 30 Aug 2023 11:45:45 +0200 Subject: [PATCH 0195/1302] Remove mailing list link, it had no activity and was removed long ago --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9972df564..0b3fa7809 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,6 @@ Community There are several places to get in touch with other ejabberd developers and administrators: - ejabberd XMPP chatroom: [ejabberd@conference.process-one.net][muc] -- [Mailing list][list] - [GitHub Discussions][discussions] - [Stack Overflow][stackoverflow] @@ -108,7 +107,6 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [hubecs]: https://hub.docker.com/r/ejabberd/ecs/ [im]: https://ejabberd.im/ [issues]: https://github.com/processone/ejabberd/issues -[list]: https://lists.jabber.ru/mailman/listinfo/ejabberd [localization]: https://docs.ejabberd.im/developer/extending-ejabberd/localization/ [mqtt]: https://mqtt.org/ [muc]: xmpp:ejabberd@conference.process-one.net From 706424f0d24a5abe08e3238eb9e537f724ce3074 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Sep 2023 11:17:54 +0200 Subject: [PATCH 0196/1302] Fix crash when loading room from DB older than ffa07c6, 23.04 Before commit ffa07c6, the subject_author was just the author nick, since that commit, subject_author contains {author nick, JID}. --- src/mod_muc_room.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 34575733b..46a4c5fc3 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4201,7 +4201,10 @@ set_opts2([{Opt, Val} | Opts], StateData) -> is_list(Val) -> Val end, StateData#state{subject = Subj}; - subject_author -> StateData#state{subject_author = Val}; + subject_author when is_tuple(Val) -> + StateData#state{subject_author = Val}; + subject_author when is_binary(Val) -> % ejabberd 23.04 or older + StateData#state{subject_author = {Val, #jid{}}}; hats_users -> Hats = maps:from_list( lists:map(fun({U, H}) -> {U, maps:from_list(H)} end, From 7522c29f25c0d88496ef86b73ee62f3d532067fe Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 8 Sep 2023 12:29:44 +0200 Subject: [PATCH 0197/1302] Update xmpp version to send roomconfig_changesubject in disco info (#4085) --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index bb82b6471..4f065cd8d 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "3d3d98b996dd7569c57df5fb966b9dfb985c58f3", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ef62a043e93c0f472b987847a2a6718714772dcc", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 70e51fcc1..c974b0001 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3d3d98b996dd7569c57df5fb966b9dfb985c58f3"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "ef62a043e93c0f472b987847a2a6718714772dcc"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 2782430887fdf5975e93d9bf94ee16c217859ce8 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 8 Sep 2023 19:49:17 +0200 Subject: [PATCH 0198/1302] mod_push: Add 'notify_on' option If the new 'notify_on' option is set to 'messages', notifications are only triggered by actual chat messages with a body (or encrypted payload). The default behavior remains to generate a notification on any kind of stanzas. Thanks to EISST International Ltd for sponsoring this work. --- src/mod_push.erl | 41 +++++++++++++++++++++++++++++------------ src/mod_push_opt.erl | 7 +++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/mod_push.erl b/src/mod_push.erl index b515adf53..7979125c5 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -135,6 +135,8 @@ depends(_Host, _Opts) -> []. -spec mod_opt_type(atom()) -> econf:validator(). +mod_opt_type(notify_on) -> + econf:enum([messages, all]); mod_opt_type(include_sender) -> econf:bool(); mod_opt_type(include_body) -> @@ -154,7 +156,8 @@ mod_opt_type(cache_life_time) -> -spec mod_options(binary()) -> [{atom(), any()}]. mod_options(Host) -> - [{include_sender, false}, + [{notify_on, all}, + {include_sender, false}, {include_body, <<"New message">>}, {db_type, ejabberd_config:default_db(Host, ?MODULE)}, {use_cache, ejabberd_option:use_cache(Host)}, @@ -175,7 +178,16 @@ mod_doc() -> "notification delivery to the user's mobile device using " "platform-dependant backend services such as FCM or APNS."), opts => - [{include_sender, + [{notify_on, + #{value => "messages | all", + desc => + ?T("If this option is set to 'messages', notifications are " + "generated only for actual chat messages with a body text " + "(or some encrypted payload). If it's set to 'all', any " + "kind of XMPP stanza will trigger a notification. If " + "unsure, it's strongly recommended to stick to 'all', " + "which is the default value.")}}, + {include_sender, #{value => "true | false", desc => ?T("If this option is set to 'true', the sender's JID " @@ -510,16 +522,21 @@ notify(LUser, LServer, Clients, Pkt, Dir) -> notify(LServer, PushLJID, Node, XData, Pkt0, Dir, HandleResponse) -> Pkt = unwrap_message(Pkt0), From = jid:make(LServer), - Summary = make_summary(LServer, Pkt, Dir), - Item = #ps_item{sub_els = [#push_notification{xdata = Summary}]}, - PubSub = #pubsub{publish = #ps_publish{node = Node, items = [Item]}, - publish_options = XData}, - IQ = #iq{type = set, - from = From, - to = jid:make(PushLJID), - id = p1_rand:get_string(), - sub_els = [PubSub]}, - ejabberd_router:route_iq(IQ, HandleResponse). + case {make_summary(LServer, Pkt, Dir), mod_push_opt:notify_on(LServer)} of + {undefined, messages} -> + ?DEBUG("Suppressing notification for stanza without payload", []), + ok; + {Summary, _NotifyOn} -> + Item = #ps_item{sub_els = [#push_notification{xdata = Summary}]}, + PubSub = #pubsub{publish = #ps_publish{node = Node, items = [Item]}, + publish_options = XData}, + IQ = #iq{type = set, + from = From, + to = jid:make(PushLJID), + id = p1_rand:get_string(), + sub_els = [PubSub]}, + ejabberd_router:route_iq(IQ, HandleResponse) + end. %%-------------------------------------------------------------------- %% Miscellaneous. diff --git a/src/mod_push_opt.erl b/src/mod_push_opt.erl index 6ab94b9c7..db6c55389 100644 --- a/src/mod_push_opt.erl +++ b/src/mod_push_opt.erl @@ -9,6 +9,7 @@ -export([db_type/1]). -export([include_body/1]). -export([include_sender/1]). +-export([notify_on/1]). -export([use_cache/1]). -spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer(). @@ -47,6 +48,12 @@ include_sender(Opts) when is_map(Opts) -> include_sender(Host) -> gen_mod:get_module_opt(Host, mod_push, include_sender). +-spec notify_on(gen_mod:opts() | global | binary()) -> 'all' | 'messages'. +notify_on(Opts) when is_map(Opts) -> + gen_mod:get_opt(notify_on, Opts); +notify_on(Host) -> + gen_mod:get_module_opt(Host, mod_push, notify_on). + -spec use_cache(gen_mod:opts() | global | binary()) -> boolean(). use_cache(Opts) when is_map(Opts) -> gen_mod:get_opt(use_cache, Opts); From 6c573cc9fd27a85c0551fe08b90acf903ac4ff97 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 9 Sep 2023 17:33:16 +0200 Subject: [PATCH 0199/1302] mod_push: Set 'last-message-sender' to bare JID If the mod_push option 'include_sender' is set to 'true', use the bare JID rather than the full JID for the 'last-message-sender' field. --- src/mod_push.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_push.erl b/src/mod_push.erl index 7979125c5..8a6901efb 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -682,7 +682,7 @@ drop_online_sessions(LUser, LServer, Clients) -> -spec make_summary(binary(), xmpp_element() | xmlel() | none, direction()) -> xdata() | undefined. -make_summary(Host, #message{from = From} = Pkt, recv) -> +make_summary(Host, #message{from = From0} = Pkt, recv) -> case {mod_push_opt:include_sender(Host), mod_push_opt:include_body(Host)} of {false, false} -> @@ -702,6 +702,7 @@ make_summary(Host, #message{from = From} = Pkt, recv) -> end, Fields2 = case IncludeSender of true -> + From = jid:remove_resource(From0), [{'last-message-sender', From} | Fields1]; false -> Fields1 From 10882af7c8106df4aee4fbc09a7c5deb24673791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 06:36:36 +0000 Subject: [PATCH 0200/1302] Bump ex_doc from 0.30.5 to 0.30.6 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.30.5 to 0.30.6. - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.30.5...v0.30.6) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 99ea68031..3f37a1b72 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, - "ex_doc": {:hex, :ex_doc, "0.30.5", "aa6da96a5c23389d7dc7c381eba862710e108cee9cfdc629b7ec021313900e9e", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "88a1e115dcb91cefeef7e22df4a6ebbe4634fbf98b38adcbc25c9607d6d9d8e6"}, + "ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, @@ -30,6 +30,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.7", "d6bdcf0aa72c927fbe8b779fc4ef1f3916c5450b2ff136c800a7a0361fb1ddff", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3fb1f07aaa630b2276e83d267557d1ceb3d2ce52d1145de71864160210655852"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.6.2", "8045dfea83e8996415b9a5161f685cb97dc3c40c0b9c46763a5eca2408017221", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "db2ee6115961fe159bc2629093797ac4535083176817cdbe2ae186a0ff540fde"}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "3d3d98b996dd7569c57df5fb966b9dfb985c58f3", [ref: "3d3d98b996dd7569c57df5fb966b9dfb985c58f3"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } From 1b7b23fab626d3bb8eec7c837aebabb2381ec7f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 06:16:03 +0000 Subject: [PATCH 0201/1302] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/container.yml | 4 ++-- .github/workflows/installers.yml | 2 +- .github/workflows/runtime.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8252d0633..9378d1946 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Test shell scripts if: matrix.otp == '25.3' diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index e471356fd..d46eeae65 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -24,7 +24,7 @@ jobs: packages: write steps: - name: Check out repository code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -75,7 +75,7 @@ jobs: mv ejabberd-*.tar.gz tarballs - name: Checkout ejabberd-contrib - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: processone/ejabberd-contrib path: .ejabberd-modules/sources/ejabberd-contrib diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 38b211e28..cba7777b9 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -41,7 +41,7 @@ jobs: gem install --no-document --user-install fpm echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH - name: Check out repository code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build binary archives diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index a27203ba3..4b62858f8 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -42,7 +42,7 @@ jobs: steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Prepare libraries run: | @@ -132,7 +132,7 @@ jobs: steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get specific Erlang/OTP uses: erlef/setup-beam@v1 From 11fdd417dd98e782bf8a541e1890d29895991ac6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:37:41 +0000 Subject: [PATCH 0202/1302] Bump docker/login-action from 2 to 3 Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index d46eeae65..cace2a4ad 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -81,7 +81,7 @@ jobs: path: .ejabberd-modules/sources/ejabberd-contrib - name: Log in to the Container registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From 7d2cfd2aaa7c4b680ab4c9eaf00ce4fcb9418a2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:37:44 +0000 Subject: [PATCH 0203/1302] Bump docker/metadata-action from 4 to 5 Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index cace2a4ad..1c0889765 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -93,7 +93,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} labels: | From d40250c3d62d691b190ec48dfa344cf540abe721 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:37:47 +0000 Subject: [PATCH 0204/1302] Bump docker/setup-qemu-action from 2 to 3 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 1c0889765..371dfe001 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -102,7 +102,7 @@ jobs: org.opencontainers.image.vendor=ProcessOne - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 From 4b3fa13163bc86fe243024a7dd7713fdc1cd8fe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:37:50 +0000 Subject: [PATCH 0205/1302] Bump docker/setup-buildx-action from 2 to 3 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 371dfe001..b1043557a 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -105,7 +105,7 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build and push Docker image uses: docker/build-push-action@v4 From 4735372682587357ecc2759fc833e0e508d05b44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 06:37:53 +0000 Subject: [PATCH 0206/1302] Bump docker/build-push-action from 4 to 5 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index b1043557a..0461cb9b8 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -108,7 +108,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build and push Docker image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: build-args: | METHOD=package From 426fd14b117daf9f5c3d46d47fd71f31fccda012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 20 Sep 2023 14:06:23 +0200 Subject: [PATCH 0207/1302] Make sure that policy=user commands have host instead of server arg in docs We renamed them in ejabberd_command, but forgot to update doc geenerator. --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 5f2cb0e04..178e75a54 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -472,7 +472,7 @@ generate_html_output(File, RegExp, Languages) -> ok. maybe_add_policy_arguments(#ejabberd_commands{args=Args1, policy=user}=Cmd) -> - Args2 = [{user, binary}, {server, binary} | Args1], + Args2 = [{user, binary}, {host, binary} | Args1], Cmd#ejabberd_commands{args = Args2}; maybe_add_policy_arguments(Cmd) -> Cmd. From 739a231259087f4dc56bd936af37b4130cff6750 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Sep 2023 12:45:27 +0200 Subject: [PATCH 0208/1302] Improve get_roster command to return groups in a list instead of newlines --- src/mod_admin_extra.erl | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 7cc54939c..ef78ec1ad 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -574,7 +574,10 @@ get_commands_spec() -> ]}} }}}, #ejabberd_commands{name = get_roster, tags = [roster], - desc = "Get roster of a local user", + desc = "Get list of contacts in a local user roster", + longdesc = + "Subscription can be: \"none\", \"from\", \"to\", \"both\". " + "Pending can be: \"in\", \"out\", \"none\".", policy = user, module = ?MODULE, function = get_roster, args = [], @@ -583,8 +586,8 @@ get_commands_spec() -> {jid, string}, {nick, string}, {subscription, string}, - {ask, string}, - {group, string} + {pending, string}, + {groups, {list, {group, string}}} ]}}}}}, #ejabberd_commands{name = push_roster, tags = [roster], desc = "Push template roster from file to a user", @@ -1334,25 +1337,16 @@ get_roster(User, Server) -> make_roster_xmlrpc(Items) end. -%% Note: if a contact is in several groups, the contact is returned -%% several times, each one in a different group. make_roster_xmlrpc(Roster) -> - lists:foldl( - fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask} = Item, Res) -> + lists:map( + fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups} = Item) -> JIDS = jid:encode(JID), Subs = atom_to_list(Sub), Asks = atom_to_list(Ask), - Groups = case Item#roster_item.groups of - [] -> [<<>>]; - Gs -> Gs - end, - ItemsX = [{JIDS, Nick, Subs, Asks, Group} || Group <- Groups], - ItemsX ++ Res + {JIDS, Nick, Subs, Asks, Groups} end, - [], Roster). - %%----------------------------- %% Push Roster from file %%----------------------------- From 8d39431d68bf4f60a076648c082b1c2518acc437 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Sep 2023 12:23:13 +0200 Subject: [PATCH 0209/1302] ejabberd_ctl: Improve printing lists in results When formatting the results of a command: - If the top of the result is a list, split elements with newline as usual - If it's a list in one of the resulting lines, split elements with ; --- src/ejabberd_ctl.erl | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 93f372372..83ca6ce4a 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -335,7 +335,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) -> ArgsFormatted, CI2, Version), - format_result(Result, ResultFormat); + format_result_preliminary(Result, ResultFormat); {'EXIT', {function_clause,[{lists,zip,[A1,A2|_], _} | _]}} -> {NumCompa, TextCompa} = case {length(A1), length(A2)} of @@ -385,6 +385,11 @@ format_arg2(Arg, Parse)-> %% Format result %%----------------------------- +format_result_preliminary(Result, {A, {list, B}}) -> + format_result(Result, {A, {top_result_list, B}}); +format_result_preliminary(Result, ResultFormat) -> + format_result(Result, ResultFormat). + format_result({error, ErrorAtom}, _) -> {io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)}; @@ -421,6 +426,16 @@ format_result(Code, {_Name, rescode}) -> format_result({Code, Text}, {_Name, restuple}) -> {io_lib:format("~ts", [Text]), make_status(Code)}; +format_result([], {_Name, {top_result_list, _ElementsDef}}) -> + ""; +format_result([FirstElement | Elements], {_Name, {top_result_list, ElementsDef}}) -> + [format_result(FirstElement, ElementsDef) | + lists:map( + fun(Element) -> + ["\n" | format_result(Element, ElementsDef)] + end, + Elements)]; + %% The result is a list of something: [something()] format_result([], {_Name, {list, _ElementsDef}}) -> ""; @@ -430,7 +445,7 @@ format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> %% If there are more elements, put always first a newline character lists:map( fun(Element) -> - ["\n" | format_result(Element, ElementsDef)] + [";" | format_result(Element, ElementsDef)] end, Elements)]; @@ -755,7 +770,10 @@ print_usage_help(MaxC, ShCode) -> "\n", "Please note that 'ejabberdctl' shows all ejabberd commands,\n", "even those that cannot be used in the shell with ejabberdctl.\n", - "Those commands can be identified because their description starts with: *"], + "Those commands can be identified because their description starts with: *\n", + "\n", + "Some commands return lists, like get_roster and get_user_subscriptions.\n", + "In those commands, the elements in the list are separated with: ;\n"], ArgsDef = [], C = #ejabberd_commands{ name = help, From 245c9ae44684d28d3aaa0d7674464031fb173049 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Sep 2023 13:41:44 +0200 Subject: [PATCH 0210/1302] ejabberd_ctl: Support policy=user in the help and return proper arguments --- src/ejabberd_ctl.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 83ca6ce4a..e244fef26 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -835,6 +835,11 @@ filter_commands_regexp(All, Glob) -> end, All). +maybe_add_policy_arguments(Args, user) -> + [{user, binary}, {host, binary} | Args]; +maybe_add_policy_arguments(Args, _) -> + Args. + -spec print_usage_command(Cmd::string(), MaxC::integer(), ShCode::boolean(), Version::integer()) -> ok. print_usage_command(Cmd, MaxC, ShCode, Version) -> @@ -847,13 +852,15 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> tags = TagsAtoms, definer = Definer, desc = Desc, - args = ArgsDef, + args = ArgsDefPreliminary, + policy = Policy, longdesc = LongDesc, result = ResultDef} = C, NameFmt = [" ", ?B("Command Name"), ": ", ?C(Cmd), "\n"], %% Initial indentation of result is 13 = length(" Arguments: ") + ArgsDef = maybe_add_policy_arguments(ArgsDefPreliminary, Policy), Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef], ArgsMargin = lists:duplicate(13, $\s), ArgsListFmt = case Args of From cd421f98d7b927ba400bcab71b59b665f1c82854 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Sep 2023 13:55:34 +0200 Subject: [PATCH 0211/1302] ejabberdctl: Document how to stop a debug shell: control+g MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit control+g is the correct way to enter shell break mode, as documented in https://www.erlang.org/doc/apps/erts/tty The ejabberdctl script included in installers use the included VT100, and that may break when hitting control+c. In that scenario let's explicitly recommend to not use control+c. Thanks to Holger Weiß for the report. --- .github/container/ejabberdctl.template | 6 ++++-- ejabberdctl.template | 6 ++++-- tools/make-binaries | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index dc9e99bc6..1a8df3e12 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -162,9 +162,11 @@ debugwarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To detach this shell from ejabberd, press:" - echo " control+c, control+c" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" echo "" + #vt100 echo "Please do NOT use control+c in this debug shell !" + #vt100 echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" diff --git a/ejabberdctl.template b/ejabberdctl.template index d128cd6e9..1a7403e71 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -149,9 +149,11 @@ debugwarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To detach this shell from ejabberd, press:" - echo " control+c, control+c" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" echo "" + #vt100 echo "Please do NOT use control+c in this debug shell !" + #vt100 echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" diff --git a/tools/make-binaries b/tools/make-binaries index d0398d6d3..9840cb5f6 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -411,6 +411,7 @@ edit_ejabberdctl() -e '/ERL_OPTIONS=/d' \ -e 's|_DIR:=".*}/|_DIR:="/opt/ejabberd/|' \ -e 's|/database|/database/$ERLANG_NODE|' \ + -e 's|#vt100 ||' \ "$code_dir/bin/${rel_name}ctl" } #. From a5341963156cebe383395d84a3f993f3a7dd983f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Sep 2023 16:50:55 +0200 Subject: [PATCH 0212/1302] Fix unused variable, forgotten in the recent commit 739a231 --- src/mod_admin_extra.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index ef78ec1ad..04559e8d0 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1339,7 +1339,7 @@ get_roster(User, Server) -> make_roster_xmlrpc(Roster) -> lists:map( - fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups} = Item) -> + fun(#roster_item{jid = JID, name = Nick, subscription = Sub, ask = Ask, groups = Groups}) -> JIDS = jid:encode(JID), Subs = atom_to_list(Sub), Asks = atom_to_list(Ask), From ab431b378a6ccec0bbbe98a47738d5da071d3c5c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Sep 2023 15:54:32 +0200 Subject: [PATCH 0213/1302] WebAdmin: Show a warning when visiting webadmin with non-privileged account (4089) --- src/ejabberd_web_admin.erl | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index ac3d4ec9b..ab109699f 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -397,13 +397,15 @@ logo_fill() -> %%%% process_admin process_admin(global, #request{path = [], lang = Lang}, AJID) -> + MenuItems = get_menu_items(global, cluster, Lang, AJID, 0), + Disclaimer = maybe_disclaimer_not_admin(MenuItems, AJID, Lang), make_xhtml((?H1GL((translate:translate(Lang, ?T("Administration"))), <<"">>, <<"Contents">>)) - ++ + ++ Disclaimer ++ [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} - <- get_menu_items(global, cluster, Lang, AJID, 0)])], + <- MenuItems])], global, Lang, AJID, 0); process_admin(Host, #request{path = [], lang = Lang}, AJID) -> make_xhtml([?XCT(<<"h1">>, ?T("Administration")), @@ -573,14 +575,16 @@ term_to_id(T) -> base64:encode((term_to_binary(T))). %%%% list_vhosts list_vhosts(Lang, JID) -> + list_vhosts2(Lang, list_vhosts_allowed(JID)). + +list_vhosts_allowed(JID) -> Hosts = ejabberd_option:hosts(), - HostsAllowed = lists:filter(fun (Host) -> + lists:filter(fun (Host) -> any_rules_allowed(Host, [configure, webadmin_view], JID) end, - Hosts), - list_vhosts2(Lang, HostsAllowed). + Hosts). list_vhosts2(Lang, Hosts) -> SHosts = lists:sort(Hosts), @@ -616,6 +620,17 @@ list_vhosts2(Lang, Hosts) -> end, SHosts)))])]. +maybe_disclaimer_not_admin(MenuItems, AJID, Lang) -> + case {MenuItems, list_vhosts_allowed(AJID)} of + {[_], []} -> + [?XREST("Apparently your account has no administration rights in this server. " + "Please check how to grant admin rights in: " + "https://docs.ejabberd.im/admin/installation/#administration-account") + ]; + _ -> + [] + end. + %%%================================== %%%% list_users From abe0817553b07cc9f60117fabb580cc231c456d1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Sep 2023 18:31:25 +0200 Subject: [PATCH 0214/1302] Mention what software versions are used to build the container image --- CONTAINER.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTAINER.md b/CONTAINER.md index f97749a85..7c7c233ef 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -19,6 +19,7 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. This document explains how to use the `ejabberd` container image available in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), built using the files in `.github/container/`. +This image is based in Alpine 3.17, includes Erlang/OTP 25.3 and Elixir 1.14.4. Alternatively, there is also the `ecs` container image available in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), From ac47b7b8cbf432f400394bb03bc29a25d569cb17 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 26 Sep 2023 23:21:45 +0200 Subject: [PATCH 0215/1302] ejabberd_web_admin: Make text translatable This also fixes a type issue. --- src/ejabberd_web_admin.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index ab109699f..eeb30dd29 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -623,9 +623,9 @@ list_vhosts2(Lang, Hosts) -> maybe_disclaimer_not_admin(MenuItems, AJID, Lang) -> case {MenuItems, list_vhosts_allowed(AJID)} of {[_], []} -> - [?XREST("Apparently your account has no administration rights in this server. " - "Please check how to grant admin rights in: " - "https://docs.ejabberd.im/admin/installation/#administration-account") + [?XREST(?T("Apparently your account has no administration rights in this server. " + "Please check how to grant admin rights in: " + "https://docs.ejabberd.im/admin/installation/#administration-account")) ]; _ -> [] From b70bef77cb6b4bb9db1e4be735727685f6edaa45 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 26 Sep 2023 23:55:01 +0200 Subject: [PATCH 0216/1302] make-binaries: Bump crosstool-NG version to 1.26.0 --- .github/workflows/installers.yml | 2 +- tools/make-binaries | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index cba7777b9..4a026c31c 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -27,7 +27,7 @@ jobs: uses: actions/cache@v3 with: path: ~/build/ - key: ${{runner.os}}-ct-ng-1.25.0 + key: ${{runner.os}}-ct-ng-1.26.0 - name: Install prerequisites run: | sudo apt-get -qq update diff --git a/tools/make-binaries b/tools/make-binaries index 9840cb5f6..b0c2daad6 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -65,7 +65,7 @@ fi rel_name='ejabberd' rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:]') mix_vsn=$(mix_version "$rel_vsn") -crosstool_vsn='1.25.0' +crosstool_vsn='1.26.0' termcap_vsn='1.3.1' expat_vsn='2.5.0' zlib_vsn='1.2.13' @@ -497,8 +497,6 @@ build_toolchain() cd "$root_dir" create_${arch}_config 'defconfig' "$libc" ct-ng defconfig - sed -i -e "s|^CT_ZLIB_VERSION=.*|CT_ZLIB_VERSION=\"$zlib_vsn\"|" .config - sed -i -e 's|^CT_ZLIB_MIRRORS=.*|CT_ZLIB_MIRRORS="https://github.com/madler/zlib/releases/download/v${CT_ZLIB_VERSION} https://www.zlib.net/ https://www.zlib.net/fossils/"|' .config ct-ng build CT_PREFIX="$ct_prefix_dir" CT_JOBS="$ct_jobs" rm -rf '.config' '.build' 'build.log' cd "$OLDPWD" From ad00553bf8fa2379356ddbc39c28f29fd7f4546d Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 27 Sep 2023 00:17:30 +0200 Subject: [PATCH 0217/1302] make-binaries: Bump Erlang/OTP version to 26.1 --- .github/workflows/ci.yml | 2 +- tools/make-binaries | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9378d1946..b71dbccfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25.3', '26'] + otp: ['20.0', '25.3', '26.1'] runs-on: ubuntu-20.04 services: redis: diff --git a/tools/make-binaries b/tools/make-binaries index b0c2daad6..be10cd1ff 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.5.0' zlib_vsn='1.2.13' yaml_vsn='0.2.5' ssl_vsn='1.1.1u' -otp_vsn='25.3.2.4' +otp_vsn='26.1' elixir_vsn='1.15.4' pam_vsn='1.5.2' png_vsn='1.6.40' From b16530bb6a88350f8f1d318bec4a03b9a1b85ba2 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 27 Sep 2023 00:28:46 +0200 Subject: [PATCH 0218/1302] make-binaries: Bump dependency versions --- tools/make-binaries | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index be10cd1ff..22b8fa110 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -68,18 +68,18 @@ mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.26.0' termcap_vsn='1.3.1' expat_vsn='2.5.0' -zlib_vsn='1.2.13' +zlib_vsn='1.3' yaml_vsn='0.2.5' -ssl_vsn='1.1.1u' +ssl_vsn='1.1.1w' otp_vsn='26.1' -elixir_vsn='1.15.4' +elixir_vsn='1.15.6' pam_vsn='1.5.2' png_vsn='1.6.40' jpeg_vsn='9e' -webp_vsn='1.3.1' +webp_vsn='1.3.2' gd_vsn='2.3.3' -odbc_vsn='2.3.11' -sqlite_vsn='3420000' +odbc_vsn='2.3.12' +sqlite_vsn='3430100' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From accb0bc35a94245f98ac672b7b48209d9c553f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 27 Sep 2023 18:29:56 +0200 Subject: [PATCH 0219/1302] Report support of config-node-max in pep --- src/node_pep.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node_pep.erl b/src/node_pep.erl index 237096a62..88e1225b3 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -82,6 +82,7 @@ features() -> <<"auto-create">>, <<"auto-subscribe">>, <<"config-node">>, + <<"config-node-max">>, <<"delete-nodes">>, <<"delete-items">>, <<"filtered-notifications">>, From c3e0b746d7c0548853f2d8f9b19e45fa24dffb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 27 Sep 2023 18:30:17 +0200 Subject: [PATCH 0220/1302] Add pubsub_delete_item hook --- src/mod_pubsub.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 8563ba2d1..e39a4a93e 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -1975,6 +1975,9 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, + ServerHost = serverhost(Host), + ejabberd_hooks:run(pubsub_delete_item, ServerHost, + [ServerHost, Node, Publisher, service_jid(Host), ItemId]), broadcast_retract_items(Host, Node, Nidx, Type, Options, [ItemId], ForceNotify), case get_cached_item(Host, Nidx) of #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx); From 765770aaa5c13267677293223802e63638e873ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 27 Sep 2023 18:36:30 +0200 Subject: [PATCH 0221/1302] Add support for xep-0402 - PEP Native Bookmarks --- mix.exs | 2 +- rebar.config | 2 +- src/mod_private.erl | 176 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 166 insertions(+), 14 deletions(-) diff --git a/mix.exs b/mix.exs index 4f065cd8d..e1f19e8d7 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ef62a043e93c0f472b987847a2a6718714772dcc", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "68cb07d5d0f36d5c51bfea496c638f3ee9b36027", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index c974b0001..eaf513c2f 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "ef62a043e93c0f472b987847a2a6718714772dcc"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "68cb07d5d0f36d5c51bfea496c638f3ee9b36027"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_private.erl b/src/mod_private.erl index d5e252669..ccb3f5bfb 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -29,13 +29,15 @@ -protocol({xep, 49, '1.2'}). -protocol({xep, 411, '0.2.0', '18.12', "", ""}). +-protocol({xep, 402, '1.1.3', '23.09', "", ""}). -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0, remove_user/2, get_data/2, get_data/3, export/1, mod_doc/0, import/5, import_start/2, mod_opt_type/1, set_data/2, - mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6]). + mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6, + pubsub_delete_item/5]). -export([get_commands_spec/0, bookmarks_to_pep/2]). @@ -44,6 +46,7 @@ -include("mod_private.hrl"). -include("ejabberd_commands.hrl"). -include("translate.hrl"). +-include("pubsub.hrl"). -define(PRIVATE_CACHE, private_cache). @@ -66,6 +69,7 @@ start(Host, Opts) -> {ok, [{hook, remove_user, remove_user, 50}, {hook, disco_sm_features, get_sm_features, 50}, {hook, pubsub_publish_item, pubsub_publish_item, 50}, + {hook, pubsub_delete_item, pubsub_delete_item, 50}, {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. stop(Host) -> @@ -150,7 +154,7 @@ get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> get_sm_features(Acc, _From, To, <<"">>, _Lang) -> case gen_mod:is_loaded(To#jid.lserver, mod_pubsub) of true -> - {result, [?NS_BOOKMARKS_CONVERSION_0 | + {result, [?NS_BOOKMARKS_CONVERSION_0, ?NS_PEP_BOOKMARKS_COMPAT, ?NS_PEP_BOOKMARKS_COMPAT_PEP | case Acc of {result, Features} -> Features; empty -> [] @@ -207,17 +211,21 @@ filter_xmlels(Els) -> -spec set_data(jid(), [{binary(), xmlel()}]) -> ok | {error, _}. set_data(JID, Data) -> - set_data(JID, Data, true). + set_data(JID, Data, true, true). --spec set_data(jid(), [{binary(), xmlel()}], boolean()) -> ok | {error, _}. -set_data(JID, Data, Publish) -> +-spec set_data(jid(), [{binary(), xmlel()}], boolean(), boolean()) -> ok | {error, _}. +set_data(JID, Data, PublishPepStorageBookmarks, PublishPepXmppBookmarks) -> {LUser, LServer, _} = jid:tolower(JID), Mod = gen_mod:db_mod(LServer, ?MODULE), case Mod:set_data(LUser, LServer, Data) of ok -> delete_cache(Mod, LUser, LServer, Data), - case Publish of - true -> publish_data(JID, Data); + case PublishPepStorageBookmarks of + true -> publish_pep_storage_bookmarks(JID, Data); + false -> ok + end, + case PublishPepXmppBookmarks of + true -> publish_pep_xmpp_bookmarks(JID, Data); false -> ok end; {error, _} = Err -> @@ -278,8 +286,8 @@ remove_user(User, Server) -> %%%=================================================================== %%% Pubsub %%%=================================================================== --spec publish_data(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}. -publish_data(JID, Data) -> +-spec publish_pep_storage_bookmarks(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}. +publish_pep_storage_bookmarks(JID, Data) -> {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), case gen_mod:is_loaded(LServer, mod_pubsub) of true -> @@ -299,16 +307,154 @@ publish_data(JID, Data) -> ok end. +-spec publish_pep_xmpp_bookmarks(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}. +publish_pep_xmpp_bookmarks(JID, Data) -> + {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), + case gen_mod:is_loaded(LServer, mod_pubsub) of + true -> + case lists:keyfind(?NS_STORAGE_BOOKMARKS, 1, Data) of + {_, Bookmarks0} -> + Bookmarks = case xmpp:decode(Bookmarks0) of + #bookmark_storage{conference = C} -> C; + _ -> [] + end, + PubOpts = [{persist_items, true}, + {access_model, whitelist}, + {max_items, max}, + {notify_retract,true}, + {notify_delete,true}, + {send_last_published_item, never}], + case mod_pubsub:get_items(LBJID, ?NS_PEP_BOOKMARKS) of + PepBookmarks when is_list(PepBookmarks) -> + put(mod_private_pep_update, true), + PepBookmarksMap = lists:foldl(fun pubsub_item_to_map/2, #{}, PepBookmarks), + ToDelete = + lists:foldl( + fun(#bookmark_conference{jid = BookmarkJID} = Bookmark, Map2) -> + PB = storage_bookmark_to_xmpp_bookmark(Bookmark), + case maps:take(jid:tolower(BookmarkJID), Map2) of + {StoredBookmark, Map3} when StoredBookmark == PB -> + Map3; + {_, Map4} -> + mod_pubsub:publish_item( + LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all), + Map4; + _ -> + mod_pubsub:publish_item( + LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all), + Map2 + end + end, PepBookmarksMap, Bookmarks), + maps:fold( + fun(DeleteJid, _, _) -> + mod_pubsub:delete_item(LBJID, ?NS_PEP_BOOKMARKS, JID, jid:encode(DeleteJid)) + end, ok, ToDelete), + erase(mod_private_pep_update), + ok; + {error, #stanza_error{reason = 'item-not-found'}} -> + put(mod_private_pep_update, true), + lists:foreach( + fun(#bookmark_conference{jid = BookmarkJID} = Bookmark) -> + PB = storage_bookmark_to_xmpp_bookmark(Bookmark), + mod_pubsub:publish_item( + LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all) + end, Bookmarks), + erase(mod_private_pep_update), + ok; + _ -> + ok + end; + _ -> + ok + end; + false -> + ok + end. + -spec pubsub_publish_item(binary(), binary(), jid(), jid(), binary(), [xmlel()]) -> any(). pubsub_publish_item(LServer, ?NS_STORAGE_BOOKMARKS, #jid{luser = LUser, lserver = LServer} = From, #jid{luser = LUser, lserver = LServer}, _ItemId, [Payload|_]) -> - set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], false); + set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], false, true); +pubsub_publish_item(LServer, ?NS_PEP_BOOKMARKS, + #jid{luser = LUser, lserver = LServer} = From, + #jid{luser = LUser, lserver = LServer}, + _ItemId, _Payload) -> + NotRecursion = get(mod_private_pep_update) == undefined, + case mod_pubsub:get_items({LUser, LServer, <<>>}, ?NS_PEP_BOOKMARKS) of + Bookmarks when is_list(Bookmarks), NotRecursion -> + Bookmarks2 = lists:filtermap(fun pubsub_item_to_storage_bookmark/1, Bookmarks), + Payload = xmpp:encode(#bookmark_storage{conference = Bookmarks2}), + set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], true, false); + _ -> + ok + end; pubsub_publish_item(_, _, _, _, _, _) -> ok. +-spec pubsub_delete_item(binary(), binary(), jid(), jid(), binary()) -> any(). +pubsub_delete_item(LServer, ?NS_PEP_BOOKMARKS, + #jid{luser = LUser, lserver = LServer} = From, + #jid{luser = LUser, lserver = LServer}, + _ItemId) -> + NotRecursion = get(mod_private_pep_update) == undefined, + case mod_pubsub:get_items({LUser, LServer, <<>>}, ?NS_PEP_BOOKMARKS) of + Bookmarks when is_list(Bookmarks), NotRecursion -> + Bookmarks2 = lists:filtermap(fun pubsub_item_to_storage_bookmark/1, Bookmarks), + Payload = xmpp:encode(#bookmark_storage{conference = Bookmarks2}), + set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], true, false); + _ -> + ok + end; +pubsub_delete_item(_, _, _, _, _) -> + ok. + +-spec pubsub_item_to_storage_bookmark(#pubsub_item{}) -> {true, bookmark_conference()} | false. +pubsub_item_to_storage_bookmark(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}) -> + case xmpp:decode(B) of + #pep_bookmarks_conference{name = Name, autojoin = AutoJoin, nick = Nick, + password = Password} -> + try jid:decode(Id) of + #jid{} = Jid -> + {true, #bookmark_conference{jid = Jid, name = Name, autojoin = AutoJoin, nick = Nick, + password = Password}} + catch _:_ -> + false + end; + _ -> + false + end; +pubsub_item_to_storage_bookmark(_) -> + false. + +-spec storage_bookmark_to_xmpp_bookmark(bookmark_conference()) -> pep_bookmarks_conference(). +storage_bookmark_to_xmpp_bookmark(#bookmark_conference{name = Name, autojoin = AutoJoin, nick = Nick, + password = Password}) -> + #pep_bookmarks_conference{name = Name, autojoin = AutoJoin, nick = Nick, + password = Password}. + +-spec pubsub_item_to_map(#pubsub_item{}, map()) -> map(). +pubsub_item_to_map(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}, Map) -> + ?INFO_MSG("DECODING ~p", [B]), + case xmpp:decode(B) of + #pep_bookmarks_conference{} = B2 -> + try jid:decode(Id) of + #jid{} = Jid -> + maps:put(jid:tolower(Jid), B2#pep_bookmarks_conference{extensions = undefined}, Map) + catch _:_ -> + Map + end; + _ -> + Map + end; +pubsub_item_to_map(_, Map) -> + Map. + %%%=================================================================== %%% Commands %%%=================================================================== @@ -344,11 +490,17 @@ bookmarks_to_pep(User, Server) -> case Res of {ok, El} -> Data = [{?NS_STORAGE_BOOKMARKS, El}], - case publish_data(jid:make(User, Server), Data) of + case publish_pep_storage_bookmarks(jid:make(User, Server), Data) of ok -> - {ok, <<"Bookmarks exported to PEP node">>}; + case publish_pep_xmpp_bookmarks(jid:make(User, Server), Data) of + ok -> + {ok, <<"Bookmarks exported to PEP node">>}; + {error, Err} -> + {error, xmpp:format_stanza_error(Err)} + end; {error, Err} -> {error, xmpp:format_stanza_error(Err)} + end; _ -> {error, <<"Cannot retrieve bookmarks from private XML storage">>} From d55955f7d8f81d491a0707852f119a615a87df46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 27 Sep 2023 19:38:14 +0200 Subject: [PATCH 0222/1302] Clean also urn:xmpp:bookmarks:1 after mod_private tests --- test/private_tests.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/private_tests.erl b/test/private_tests.erl index d194a0632..25f55b1a6 100644 --- a/test/private_tests.erl +++ b/test/private_tests.erl @@ -95,7 +95,11 @@ test_published(Config) -> #iq{type = result, sub_els = []} = send_recv(Config, #iq{type = set, - sub_els = [#pubsub_owner{delete = {Node, <<>>}}]}); + sub_els = [#pubsub_owner{delete = {Node, <<>>}}]}), + #iq{type = result, sub_els = []} = + send_recv(Config, + #iq{type = set, + sub_els = [#pubsub_owner{delete = {?NS_PEP_BOOKMARKS, <<>>}}]}); false -> ok end, From 9ba645503b28be21c65fe2c9a24dcbb574e7081c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 27 Sep 2023 23:02:26 +0200 Subject: [PATCH 0223/1302] Fix dialyzer warning --- src/mod_private.erl | 52 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index ccb3f5bfb..3b229ae74 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -328,42 +328,45 @@ publish_pep_xmpp_bookmarks(JID, Data) -> PepBookmarks when is_list(PepBookmarks) -> put(mod_private_pep_update, true), PepBookmarksMap = lists:foldl(fun pubsub_item_to_map/2, #{}, PepBookmarks), - ToDelete = + {ToDelete, Ret} = lists:foldl( - fun(#bookmark_conference{jid = BookmarkJID} = Bookmark, Map2) -> + fun(#bookmark_conference{jid = BookmarkJID} = Bookmark, {Map2, Ret2}) -> PB = storage_bookmark_to_xmpp_bookmark(Bookmark), case maps:take(jid:tolower(BookmarkJID), Map2) of {StoredBookmark, Map3} when StoredBookmark == PB -> - Map3; + {Map3, Ret2}; {_, Map4} -> - mod_pubsub:publish_item( - LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, - jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all), - Map4; + {Map4, + err_ret(Ret2, mod_pubsub:publish_item( + LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all))}; _ -> - mod_pubsub:publish_item( - LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, - jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all), - Map2 + {Map2, + err_ret(Ret2, mod_pubsub:publish_item( + LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all))} end - end, PepBookmarksMap, Bookmarks), + end, {PepBookmarksMap, ok}, Bookmarks), + Ret4 = maps:fold( - fun(DeleteJid, _, _) -> - mod_pubsub:delete_item(LBJID, ?NS_PEP_BOOKMARKS, JID, jid:encode(DeleteJid)) - end, ok, ToDelete), + fun(DeleteJid, _, Ret3) -> + err_ret(Ret3, mod_pubsub:delete_item(LBJID, ?NS_PEP_BOOKMARKS, + JID, jid:encode(DeleteJid))) + end, Ret, ToDelete), erase(mod_private_pep_update), - ok; + Ret4; {error, #stanza_error{reason = 'item-not-found'}} -> put(mod_private_pep_update, true), - lists:foreach( - fun(#bookmark_conference{jid = BookmarkJID} = Bookmark) -> + Ret7 = + lists:foldl( + fun(#bookmark_conference{jid = BookmarkJID} = Bookmark, Ret5) -> PB = storage_bookmark_to_xmpp_bookmark(Bookmark), - mod_pubsub:publish_item( + err_ret(Ret5, mod_pubsub:publish_item( LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, - jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all) - end, Bookmarks), + jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all)) + end, ok, Bookmarks), erase(mod_private_pep_update), - ok; + Ret7; _ -> ok end; @@ -374,6 +377,11 @@ publish_pep_xmpp_bookmarks(JID, Data) -> ok end. +err_ret({error, _} = E, _) -> + E; +err_ret(ok, E) -> + E. + -spec pubsub_publish_item(binary(), binary(), jid(), jid(), binary(), [xmlel()]) -> any(). pubsub_publish_item(LServer, ?NS_STORAGE_BOOKMARKS, From f327f4cc67849edaea81893d2f13828996c7aca6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 29 Sep 2023 17:32:17 +0200 Subject: [PATCH 0224/1302] make-binaries: Suppress curl's info messages --- tools/make-binaries | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 22b8fa110..0e4e8728b 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -367,7 +367,7 @@ create_data_dir() mkdir "$data_dir" "$data_dir/database" "$data_dir/logs" mv "$code_dir/conf" "$data_dir" chmod 'o-rwx' "$data_dir/"* - curl --no-progress-meter -o "$data_dir/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' + curl -sS -o "$data_dir/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' sed -i '/^loglevel:/a\ \ ca_file: /opt/ejabberd/conf/cacert.pem\ @@ -877,23 +877,23 @@ else info 'Downloading dependencies ...' cd "$src_dir" - curl --no-progress-meter -LO "https://github.com/crosstool-ng/crosstool-ng/releases/download/$crosstool_dir/$crosstool_tar" - curl --no-progress-meter -LO "https://ftp.gnu.org/gnu/termcap/$termcap_tar" - curl --no-progress-meter -LO "https://github.com/libexpat/libexpat/releases/download/R_$(printf '%s' "$expat_vsn" | sed 's/\./_/g')/$expat_tar" - curl --no-progress-meter -LO "https://zlib.net/fossils/$zlib_tar" - curl --no-progress-meter -LO "https://pyyaml.org/download/libyaml/$yaml_tar" - curl --no-progress-meter -LO "https://www.openssl.org/source/$ssl_tar" - curl --no-progress-meter -LO "https://github.com/erlang/otp/releases/download/OTP-$otp_vsn/$otp_tar" - curl --no-progress-meter -LO "https://github.com/elixir-lang/elixir/archive/v$elixir_vsn.tar.gz" - curl --no-progress-meter -LO "https://github.com/linux-pam/linux-pam/releases/download/v$pam_vsn/$pam_tar" - curl --no-progress-meter -LO "https://download.sourceforge.net/libpng/$png_tar" - curl --no-progress-meter -LO "https://www.ijg.org/files/$jpeg_tar" - curl --no-progress-meter -LO "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/$webp_tar" - curl --no-progress-meter -LO "https://github.com/libgd/libgd/releases/download/gd-$gd_vsn/$gd_tar" - curl --no-progress-meter -LO "http://www.unixodbc.org/$odbc_tar" - curl --no-progress-meter -LO "https://www.sqlite.org/$(date '+%Y')/$sqlite_tar" \ - || curl --no-progress-meter -LO "https://www.sqlite.org/$(date -d '1 year ago' '+%Y')/$sqlite_tar" \ - || curl --no-progress-meter -LO "https://www.sqlite.org/$(date -d '2 years ago' '+%Y')/$sqlite_tar" + curl -sSLO "https://github.com/crosstool-ng/crosstool-ng/releases/download/$crosstool_dir/$crosstool_tar" + curl -sSLO "https://ftp.gnu.org/gnu/termcap/$termcap_tar" + curl -sSLO "https://github.com/libexpat/libexpat/releases/download/R_$(printf '%s' "$expat_vsn" | sed 's/\./_/g')/$expat_tar" + curl -sSLO "https://zlib.net/fossils/$zlib_tar" + curl -sSLO "https://pyyaml.org/download/libyaml/$yaml_tar" + curl -sSLO "https://www.openssl.org/source/$ssl_tar" + curl -sSLO "https://github.com/erlang/otp/releases/download/OTP-$otp_vsn/$otp_tar" + curl -sSLO "https://github.com/elixir-lang/elixir/archive/v$elixir_vsn.tar.gz" + curl -sSLO "https://github.com/linux-pam/linux-pam/releases/download/v$pam_vsn/$pam_tar" + curl -sSLO "https://download.sourceforge.net/libpng/$png_tar" + curl -sSLO "https://www.ijg.org/files/$jpeg_tar" + curl -sSLO "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/$webp_tar" + curl -sSLO "https://github.com/libgd/libgd/releases/download/gd-$gd_vsn/$gd_tar" + curl -sSLO "http://www.unixodbc.org/$odbc_tar" + curl -sSLO "https://www.sqlite.org/$(date '+%Y')/$sqlite_tar" \ + || curl -sSLO "https://www.sqlite.org/$(date -d '1 year ago' '+%Y')/$sqlite_tar" \ + || curl -sSLO "https://www.sqlite.org/$(date -d '2 years ago' '+%Y')/$sqlite_tar" cd "$OLDPWD" fi From d4fc54be18f65499bed44b02428865130a8b963b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 29 Sep 2023 17:33:09 +0200 Subject: [PATCH 0225/1302] make-binaries: Let curl fail on HTTP errors --- tools/make-binaries | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 0e4e8728b..548df1145 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -367,7 +367,7 @@ create_data_dir() mkdir "$data_dir" "$data_dir/database" "$data_dir/logs" mv "$code_dir/conf" "$data_dir" chmod 'o-rwx' "$data_dir/"* - curl -sS -o "$data_dir/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' + curl -fsS -o "$data_dir/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' sed -i '/^loglevel:/a\ \ ca_file: /opt/ejabberd/conf/cacert.pem\ @@ -877,23 +877,23 @@ else info 'Downloading dependencies ...' cd "$src_dir" - curl -sSLO "https://github.com/crosstool-ng/crosstool-ng/releases/download/$crosstool_dir/$crosstool_tar" - curl -sSLO "https://ftp.gnu.org/gnu/termcap/$termcap_tar" - curl -sSLO "https://github.com/libexpat/libexpat/releases/download/R_$(printf '%s' "$expat_vsn" | sed 's/\./_/g')/$expat_tar" - curl -sSLO "https://zlib.net/fossils/$zlib_tar" - curl -sSLO "https://pyyaml.org/download/libyaml/$yaml_tar" - curl -sSLO "https://www.openssl.org/source/$ssl_tar" - curl -sSLO "https://github.com/erlang/otp/releases/download/OTP-$otp_vsn/$otp_tar" - curl -sSLO "https://github.com/elixir-lang/elixir/archive/v$elixir_vsn.tar.gz" - curl -sSLO "https://github.com/linux-pam/linux-pam/releases/download/v$pam_vsn/$pam_tar" - curl -sSLO "https://download.sourceforge.net/libpng/$png_tar" - curl -sSLO "https://www.ijg.org/files/$jpeg_tar" - curl -sSLO "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/$webp_tar" - curl -sSLO "https://github.com/libgd/libgd/releases/download/gd-$gd_vsn/$gd_tar" - curl -sSLO "http://www.unixodbc.org/$odbc_tar" - curl -sSLO "https://www.sqlite.org/$(date '+%Y')/$sqlite_tar" \ - || curl -sSLO "https://www.sqlite.org/$(date -d '1 year ago' '+%Y')/$sqlite_tar" \ - || curl -sSLO "https://www.sqlite.org/$(date -d '2 years ago' '+%Y')/$sqlite_tar" + curl -fsSLO "https://github.com/crosstool-ng/crosstool-ng/releases/download/$crosstool_dir/$crosstool_tar" + curl -fsSLO "https://ftp.gnu.org/gnu/termcap/$termcap_tar" + curl -fsSLO "https://github.com/libexpat/libexpat/releases/download/R_$(printf '%s' "$expat_vsn" | sed 's/\./_/g')/$expat_tar" + curl -fsSLO "https://zlib.net/fossils/$zlib_tar" + curl -fsSLO "https://pyyaml.org/download/libyaml/$yaml_tar" + curl -fsSLO "https://www.openssl.org/source/$ssl_tar" + curl -fsSLO "https://github.com/erlang/otp/releases/download/OTP-$otp_vsn/$otp_tar" + curl -fsSLO "https://github.com/elixir-lang/elixir/archive/v$elixir_vsn.tar.gz" + curl -fsSLO "https://github.com/linux-pam/linux-pam/releases/download/v$pam_vsn/$pam_tar" + curl -fsSLO "https://download.sourceforge.net/libpng/$png_tar" + curl -fsSLO "https://www.ijg.org/files/$jpeg_tar" + curl -fsSLO "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/$webp_tar" + curl -fsSLO "https://github.com/libgd/libgd/releases/download/gd-$gd_vsn/$gd_tar" + curl -fsSLO "http://www.unixodbc.org/$odbc_tar" + curl -fsSLO "https://www.sqlite.org/$(date '+%Y')/$sqlite_tar" \ + || curl -fsSLO "https://www.sqlite.org/$(date -d '1 year ago' '+%Y')/$sqlite_tar" \ + || curl -fsSLO "https://www.sqlite.org/$(date -d '2 years ago' '+%Y')/$sqlite_tar" cd "$OLDPWD" fi From 762e4951f2d51c8d2f50c53627fea7c7fa5a86b1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 29 Sep 2023 19:01:50 +0200 Subject: [PATCH 0226/1302] make-binaries: Bump Erlang/OTP version to 26.1.1 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 548df1145..0df721331 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.5.0' zlib_vsn='1.3' yaml_vsn='0.2.5' ssl_vsn='1.1.1w' -otp_vsn='26.1' +otp_vsn='26.1.1' elixir_vsn='1.15.6' pam_vsn='1.5.2' png_vsn='1.6.40' From a63d3bf0d6a05715de374ca438b2d3f23927bf7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Oct 2023 08:32:24 +0200 Subject: [PATCH 0227/1302] Fix crash in mod_private bookmarks converter This should fix issue reported in #4092 --- src/mod_private.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 3b229ae74..07cfe12df 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -379,8 +379,10 @@ publish_pep_xmpp_bookmarks(JID, Data) -> err_ret({error, _} = E, _) -> E; -err_ret(ok, E) -> - E. +err_ret(ok, {error, _} = E) -> + E; +err_ret(_, _) -> + ok. -spec pubsub_publish_item(binary(), binary(), jid(), jid(), binary(), [xmlel()]) -> any(). From 86465c418d0f21e4f0f791b1f337d7b9097f66ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Oct 2023 14:39:30 +0200 Subject: [PATCH 0228/1302] Try to fill xep-0402 bookmarks from private storage on first access --- src/mod_private.erl | 19 ++++++++++++++++++- src/mod_pubsub.erl | 4 +++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 07cfe12df..701491eea 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -37,7 +37,7 @@ remove_user/2, get_data/2, get_data/3, export/1, mod_doc/0, import/5, import_start/2, mod_opt_type/1, set_data/2, mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6, - pubsub_delete_item/5]). + pubsub_delete_item/5, pubsub_tree_call/4]). -export([get_commands_spec/0, bookmarks_to_pep/2]). @@ -70,6 +70,7 @@ start(Host, Opts) -> {hook, disco_sm_features, get_sm_features, 50}, {hook, pubsub_publish_item, pubsub_publish_item, 50}, {hook, pubsub_delete_item, pubsub_delete_item, 50}, + {hook, pubsub_tree_call, pubsub_tree_call, 50}, {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. stop(Host) -> @@ -442,6 +443,22 @@ pubsub_item_to_storage_bookmark(#pubsub_item{itemid = {Id, _}, payload = [#xmlel pubsub_item_to_storage_bookmark(_) -> false. +-spec pubsub_tree_call(Res :: any(), _Tree::any(), atom(), any()) -> any(). +pubsub_tree_call({error, #stanza_error{reason = 'item-not-found'}} = Res, Tree, get_node, + [{User, Server, _}, ?NS_PEP_BOOKMARKS] = Args) -> + case get(mod_private_in_pubsub_tree_call) of + undefined -> + put(mod_private_in_pubsub_tree_call, true), + bookmarks_to_pep(User, Server), + Res2 = apply(Tree, get_node, Args), + erase(mod_private_in_pubsub_tree_call), + Res2; + _ -> + Res + end; +pubsub_tree_call(Res, _Tree, _Function, _Args) -> + Res. + -spec storage_bookmark_to_xmpp_bookmark(bookmark_conference()) -> pep_bookmarks_conference(). storage_bookmark_to_xmpp_bookmark(#bookmark_conference{name = Name, autojoin = AutoJoin, nick = Nick, password = Password}) -> diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index e39a4a93e..7cc1d0170 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3806,7 +3806,9 @@ tree_call({_User, Server, _Resource}, Function, Args) -> tree_call(Host, Function, Args) -> Tree = tree(Host), ?DEBUG("Tree_call apply(~ts, ~ts, ~p) @ ~ts", [Tree, Function, Args, Host]), - case apply(Tree, Function, Args) of + Res = apply(Tree, Function, Args), + Res2 = ejabberd_hooks:run_fold(pubsub_tree_call, Host, Res, [Tree, Function, Args]), + case Res2 of {error, #stanza_error{}} = Err -> Err; {error, {virtual, _}} = Err -> From a3f4a05b0cfab8917788caf60e29b504b4022ac1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 3 Oct 2023 17:56:27 +0200 Subject: [PATCH 0229/1302] make-binaries: Don't use non-prefixed cross tools Omit the directory that contains cross compilation tools without "$target-" prefix from the PATH. Having it in the path might lead to problems when native tools are needed during cross compilation. For actual cross compilation, the prefixed tools should always be used anyway. --- tools/make-binaries | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 0df721331..51d2261e7 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -724,16 +724,7 @@ build_rel() local target_dst_tar="$rel_name-$rel_vsn-linux-$libc-$arch.tar.gz" local saved_path="$PATH" - # - # The "$ct_prefix_dir/$target/$target/bin" directory contains cross - # compilation tools without "$target-" prefix. We add it to the PATH, - # just in case tools are called without prefix somewhere. However, we - # try to use the prefixed tools everywhere, so it should be possible to - # omit this directory from the path if desired. See also: - # - # https://stackoverflow.com/a/24243789 - # - export PATH="$ct_prefix_dir/$target/bin:$ct_prefix_dir/$target/$target/bin:$PATH" + export PATH="$ct_prefix_dir/$target/bin:$PATH" export CC="$target-gcc" export CXX="$target-g++" export CPP="$target-cpp" From 62d3d7a32d2dbae84dc16a85cd389ab814cefc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 4 Oct 2023 17:16:29 +0200 Subject: [PATCH 0230/1302] Relay pubsub iq queries to muc members without using bare jid We do something similar for vcard queries, this allows target server to respond to those queries by target server, which is what we want to do in both of those cases. --- src/mod_muc_room.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 46a4c5fc3..88e2c0c0a 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -671,6 +671,10 @@ normal_state({route, ToNick, ejabberd_router:route_iq( xmpp:set_from_to(Packet, FromJID, jid:remove_resource(To)), Packet, self()); + pubsub -> + ejabberd_router:route_iq( + xmpp:set_from_to(Packet, FromJID, jid:remove_resource(To)), + Packet, self()); ping when ToNick == FromNick -> %% Self-ping optimization from XEP-0410 ejabberd_router:route(xmpp:make_iq_result(Packet)); @@ -1304,12 +1308,13 @@ process_voice_approval(From, Pkt, VoiceApproval, StateData) -> StateData end. --spec direct_iq_type(iq()) -> vcard | ping | request | response | stanza_error(). +-spec direct_iq_type(iq()) -> vcard | ping | request | response | pubsub | stanza_error(). direct_iq_type(#iq{type = T, sub_els = SubEls, lang = Lang}) when T == get; T == set -> case SubEls of [El] -> case xmpp:get_ns(El) of ?NS_VCARD when T == get -> vcard; + ?NS_PUBSUB when T == get -> pubsub; ?NS_PING when T == get -> ping; _ -> request end; From d85c125bef33b8c9c8893f276eb79387be7f3179 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 4 Oct 2023 19:38:56 +0200 Subject: [PATCH 0231/1302] ext_mod: Use the same URL that ejabberd-contrib spec files --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 81b71def4..d65576a46 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -49,7 +49,7 @@ -include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --define(REPOS, "https://github.com/processone/ejabberd-contrib"). +-define(REPOS, "git@github.com:processone/ejabberd-contrib.git"). -record(state, {}). From f9d11265d0bc34268fe03236370e9e7d8139d60a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 4 Oct 2023 19:39:27 +0200 Subject: [PATCH 0232/1302] ext_mod: Don't crash when github page response is 403 --- src/ext_mod.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index d65576a46..6833872af 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -357,6 +357,8 @@ geturl(Url) -> case httpc:request(get, {Url, [UA]}, User, [{body_format, binary}], ext_mod) of {ok, {{_, 200, _}, Headers, Response}} -> {ok, Headers, Response}; + {ok, {{_, 403, Reason}, _Headers, _Response}} -> + {error, Reason}; {ok, {{_, Code, _}, _Headers, Response}} -> {error, {Code, Response}}; {error, Reason} -> @@ -799,10 +801,14 @@ maybe_write_commit_json(Url, RepDir) -> write_commit_json(Url, RepDir) -> Url2 = string_replace(Url, "https://github.com", "https://api.github.com/repos"), BranchUrl = lists:flatten(Url2 ++ "/branches/master"), - {ok, _Headers, Body} = geturl(BranchUrl), - {ok, F} = file:open(filename:join(RepDir, "COMMIT.json"), [raw, write]), - file:write(F, Body), - file:close(F). + case geturl(BranchUrl) of + {ok, _Headers, Body} -> + {ok, F} = file:open(filename:join(RepDir, "COMMIT.json"), [raw, write]), + file:write(F, Body), + file:close(F); + {error, Reason} -> + Reason + end. find_commit_json(Attrs) -> {_, FromPath} = lists:keyfind(path, 1, Attrs), From 9534ca2da190a149e3bc1f87dd3e06cc991cb00d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 4 Oct 2023 19:39:57 +0200 Subject: [PATCH 0233/1302] ext_mod: Support when git repository name is not identical to the module name For example, ejabberd-contrib has an extra module, mod_prometheus, that is hosted in a git repository named ejabberd-prometheus-exporter --- src/ext_mod.erl | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 6833872af..ddab4407c 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -294,7 +294,7 @@ add_sources(Module, Path) when is_atom(Module), is_list(Path) -> add_sources(Package, Path) when is_binary(Package), is_list(Path) -> DestDir = sources_dir(), RepDir = filename:join(DestDir, module_name(Path)), - delete_path(RepDir), + delete_path(RepDir, binary_to_list(Package)), case filelib:ensure_dir(RepDir) of ok -> case {string:left(Path, 4), string:right(Path, 2)} of @@ -406,8 +406,9 @@ extract_github_master(Repos, DestDir) -> case extract(zip, geturl(Url++"/archive/master.zip"), DestDir) of ok -> RepDir = filename:join(DestDir, module_name(Repos)), - file:rename(RepDir++"-master", RepDir), - maybe_write_commit_json(Url, RepDir); + RepDirSpec = filename:join(DestDir, module_spec_name(RepDir)), + file:rename(RepDir++"-master", RepDirSpec), + maybe_write_commit_json(Url, RepDirSpec); Error -> Error end. @@ -442,6 +443,9 @@ delete_path(Path) -> file:delete(Path) end. +delete_path(Path, Package) -> + delete_path(filename:join(filename:dirname(Path), Package)). + modules_dir() -> DefaultDir = filename:join(getenv("HOME"), ".ejabberd-modules"), getenv("CONTRIB_MODULES_PATH", DefaultDir). @@ -480,6 +484,14 @@ module_src_dir(Package) -> module_name(Id) -> filename:basename(filename:rootname(Id)). +module_spec_name(Path) -> + case filelib:wildcard(filename:join(Path++"-master", "*.spec")) of + "" -> + module_name(Path); + ModuleName -> + filename:basename(ModuleName, ".spec") + end. + module(Id) -> misc:binary_to_atom(iolist_to_binary(module_name(Id))). From 10245b40ee4bf7fd6d741824e645c405ff2cce2b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 10 Oct 2023 13:08:13 +0200 Subject: [PATCH 0234/1302] Add support to register nick in a room (#3455) Registering a nick in the MUC service or in a room is mutually exclusive: - A nick that is registered in the service cannot be registered in any room, not even the original owner can register it. - Similarly, a nick registered in any room cannot be registered in the service. --- src/mod_muc.erl | 37 +++++++++++++++--------- src/mod_muc_mnesia.erl | 43 +++++++++++++++++++++------- src/mod_muc_room.erl | 16 ++++++++--- src/mod_muc_sql.erl | 65 ++++++++++++++++++++++++++++++++---------- 4 files changed, 118 insertions(+), 43 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index c77977387..aadd77a35 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -51,6 +51,7 @@ process_disco_items/1, process_vcard/1, process_register/1, + process_iq_register/1, process_muc_unique/1, process_mucsub/1, broadcast_service_message/3, @@ -675,29 +676,33 @@ process_vcard(#iq{lang = Lang} = IQ) -> xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). -spec process_register(iq()) -> iq(). -process_register(#iq{type = Type, from = From, to = To, lang = Lang, - sub_els = [El = #register{}]} = IQ) -> +process_register(IQ) -> + case process_iq_register(IQ) of + {result, Result} -> + xmpp:make_iq_result(IQ, Result); + {error, Err} -> + xmpp:make_error(IQ, Err) + end. + +-spec process_iq_register(iq()) -> {result, register()} | {error, stanza_error()}. +process_iq_register(#iq{type = Type, from = From, to = To, lang = Lang, + sub_els = [El = #register{}]}) -> Host = To#jid.lserver, + RegisterDestination = jid:encode(To), ServerHost = ejabberd_router:host_of_route(Host), AccessRegister = mod_muc_opt:access_register(ServerHost), case acl:match_rule(ServerHost, AccessRegister, From) of allow -> case Type of get -> - xmpp:make_iq_result( - IQ, iq_get_register_info(ServerHost, Host, From, Lang)); + {result, iq_get_register_info(ServerHost, RegisterDestination, From, Lang)}; set -> - case process_iq_register_set(ServerHost, Host, From, El, Lang) of - {result, Result} -> - xmpp:make_iq_result(IQ, Result); - {error, Err} -> - xmpp:make_error(IQ, Err) - end + process_iq_register_set(ServerHost, RegisterDestination, From, El, Lang) end; deny -> ErrText = ?T("Access denied by service policy"), Err = xmpp:err_forbidden(ErrText, Lang), - xmpp:make_error(IQ, Err) + {error, Err} end. -spec process_disco_info(iq()) -> iq(). @@ -1416,6 +1421,11 @@ mod_doc() -> "nobody else can use that nickname in any room in the MUC " "service. To register a nickname, open the Service Discovery in " "your XMPP client and register in the MUC service."), "", + ?T("It is also possible to register a nickname in a room, so " + "nobody else can use that nickname in that room. If a nick is " + "registered in the MUC service, that nick cannot be registered in " + "any room, and vice versa: a nick that is registered in a room " + "cannot be registered at the MUC service."), "", ?T("This module supports clustering and load balancing. One module " "can be started per cluster node. Rooms are distributed at " "creation time on all available MUC module instances. The " @@ -1461,11 +1471,12 @@ mod_doc() -> "modify that option.")}}, {access_register, #{value => ?T("AccessName"), + note => "improved in 23.xx", desc => ?T("This option specifies who is allowed to register nickname " - "within the Multi-User Chat service. The default is 'all' for " + "within the Multi-User Chat service and rooms. The default is 'all' for " "backward compatibility, which means that any user is allowed " - "to register any free nick.")}}, + "to register any free nick in the MUC service and in the rooms.")}}, {db_type, #{value => "mnesia | sql", desc => diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index 4b422f998..d994110ce 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -82,13 +82,19 @@ forget_room(_LServer, Host, Name) -> end, mnesia:transaction(F). -can_use_nick(_LServer, Host, JID, Nick) -> +can_use_nick(_LServer, ServiceOrRoom, JID, Nick) -> {LUser, LServer, _} = jid:tolower(JID), LUS = {LUser, LServer}, + MatchSpec = case (jid:decode(ServiceOrRoom))#jid.lserver of + ServiceOrRoom -> [{'==', {element, 2, '$1'}, ServiceOrRoom}]; + Service -> [{'orelse', + {'==', {element, 2, '$1'}, Service}, + {'==', {element, 2, '$1'}, ServiceOrRoom} }] + end, case catch mnesia:dirty_select(muc_registered, [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, - [{'==', {element, 2, '$1'}, Host}], + MatchSpec, ['$_']}]) of {'EXIT', _Reason} -> true; @@ -110,31 +116,46 @@ get_nick(_LServer, Host, From) -> [#muc_registered{nick = Nick}] -> Nick end. -set_nick(_LServer, Host, From, Nick) -> +set_nick(_LServer, ServiceOrRoom, From, Nick) -> {LUser, LServer, _} = jid:tolower(From), LUS = {LUser, LServer}, F = fun () -> case Nick of <<"">> -> - mnesia:delete({muc_registered, {LUS, Host}}), + mnesia:delete({muc_registered, {LUS, ServiceOrRoom}}), ok; _ -> + Service = (jid:decode(ServiceOrRoom))#jid.lserver, + MatchSpec = case (ServiceOrRoom == Service) of + true -> [{'==', {element, 2, '$1'}, ServiceOrRoom}]; + false -> [{'orelse', + {'==', {element, 2, '$1'}, Service}, + {'==', {element, 2, '$1'}, ServiceOrRoom} }] + end, Allow = case mnesia:select( muc_registered, - [{#muc_registered{us_host = - '$1', - nick = Nick, - _ = '_'}, - [{'==', {element, 2, '$1'}, - Host}], + [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, + MatchSpec, ['$_']}]) of + [] when (ServiceOrRoom == Service) -> + NickRegistrations = mnesia:select( + muc_registered, + [{#muc_registered{us_host = '$1', nick = Nick, _ = '_'}, + [], + ['$_']}]), + not lists:any(fun({_, {_NRUS, NRServiceOrRoom}, _Nick}) -> + Service == (jid:decode(NRServiceOrRoom))#jid.lserver end, + NickRegistrations); [] -> true; + [#muc_registered{us_host = {_U, Host}}] + when (Host == Service) and (ServiceOrRoom /= Service) -> + false; [#muc_registered{us_host = {U, _Host}}] -> U == LUS end, if Allow -> mnesia:write(#muc_registered{ - us_host = {LUS, Host}, + us_host = {LUS, ServiceOrRoom}, nick = Nick}), ok; true -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 88e2c0c0a..30df7dbaa 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -501,6 +501,8 @@ normal_state({route, <<"">>, process_iq_captcha(From, IQ, StateData); #adhoc_command{} -> process_iq_adhoc(From, IQ, StateData); + #register{} -> + mod_muc:process_iq_register(IQ); #fasten_apply_to{} = ApplyTo -> case xmpp:get_subtag(ApplyTo, #message_moderate{}) of #message_moderate{} = Moderate -> @@ -1406,7 +1408,7 @@ do_process_presence(Nick, #presence{from = From, type = available, lang = Lang} true -> case {nick_collision(From, Nick, StateData), mod_muc:can_use_nick(StateData#state.server_host, - StateData#state.host, + jid:encode(StateData#state.jid), From, Nick), {(StateData#state.config)#config.allow_visitor_nickchange, is_visitor(From, StateData)}} of @@ -2290,7 +2292,7 @@ add_new_user(From, Nick, Packet, StateData) -> andalso NConferences < MaxConferences), Collision, mod_muc:can_use_nick(StateData#state.server_host, - StateData#state.host, From, Nick), + jid:encode(StateData#state.jid), From, Nick), get_occupant_initial_role(From, Affiliation, StateData)} of {false, _, _, _} when NUsers >= MaxUsers orelse NUsers >= MaxAdminUsers -> @@ -4385,8 +4387,10 @@ maybe_forget_room(StateData) -> end). -spec make_disco_info(jid(), state()) -> disco_info(). -make_disco_info(_From, StateData) -> +make_disco_info(From, StateData) -> Config = StateData#state.config, + ServerHost = StateData#state.server_host, + AccessRegister = mod_muc_opt:access_register(ServerHost), Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_COMMANDS, ?NS_MESSAGE_MODERATE, ?NS_MESSAGE_RETRACT, ?CONFIG_OPT_TO_FEATURE((Config#config.public), @@ -4401,6 +4405,10 @@ make_disco_info(_From, StateData) -> <<"muc_moderated">>, <<"muc_unmoderated">>), ?CONFIG_OPT_TO_FEATURE((Config#config.password_protected), <<"muc_passwordprotected">>, <<"muc_unsecured">>)] + ++ case acl:match_rule(ServerHost, AccessRegister, From) of + allow -> [?NS_REGISTER]; + deny -> [] + end ++ case Config#config.allow_subscription of true -> [?NS_MUCSUB]; false -> [] @@ -4703,7 +4711,7 @@ process_iq_mucsub(From, {error, xmpp:err_conflict(ErrText, Lang)}; false -> case mod_muc:can_use_nick(StateData#state.server_host, - StateData#state.host, + jid:encode(StateData#state.jid), From, Nick) of false -> Err = case Nick of diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index 1c72a5bd2..51a42c32a 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -159,13 +159,19 @@ forget_room(LServer, Host, Name) -> end, ejabberd_sql:sql_transaction(LServer, F). -can_use_nick(LServer, Host, JID, Nick) -> +can_use_nick(LServer, ServiceOrRoom, JID, Nick) -> SJID = jid:encode(jid:tolower(jid:remove_resource(JID))), - case catch ejabberd_sql:sql_query( - LServer, - ?SQL("select @(jid)s from muc_registered " - "where nick=%(Nick)s" - " and host=%(Host)s")) of + SqlQuery = case (jid:decode(ServiceOrRoom))#jid.lserver of + ServiceOrRoom -> + ?SQL("select @(jid)s from muc_registered " + "where nick=%(Nick)s" + " and host=%(ServiceOrRoom)s"); + Service -> + ?SQL("select @(jid)s from muc_registered " + "where nick=%(Nick)s" + " and (host=%(ServiceOrRoom)s or host=%(Service)s)") + end, + case catch ejabberd_sql:sql_query(LServer, SqlQuery) of {selected, [{SJID1}]} -> SJID == SJID1; _ -> true end. @@ -258,28 +264,57 @@ get_nick(LServer, Host, From) -> _ -> error end. -set_nick(LServer, Host, From, Nick) -> +set_nick(LServer, ServiceOrRoom, From, Nick) -> JID = jid:encode(jid:tolower(jid:remove_resource(From))), F = fun () -> case Nick of <<"">> -> ejabberd_sql:sql_query_t( ?SQL("delete from muc_registered where" - " jid=%(JID)s and host=%(Host)s")), + " jid=%(JID)s and host=%(ServiceOrRoom)s")), ok; _ -> - Allow = case ejabberd_sql:sql_query_t( - ?SQL("select @(jid)s from muc_registered" - " where nick=%(Nick)s" - " and host=%(Host)s")) of - {selected, [{J}]} -> J == JID; - _ -> true + Service = (jid:decode(ServiceOrRoom))#jid.lserver, + SqlQuery = case (ServiceOrRoom == Service) of + true -> + ?SQL("select @(jid)s, @(host)s from muc_registered " + "where nick=%(Nick)s" + " and host=%(ServiceOrRoom)s"); + false -> + ?SQL("select @(jid)s, @(host)s from muc_registered " + "where nick=%(Nick)s" + " and (host=%(ServiceOrRoom)s or host=%(Service)s)") + end, + Allow = case ejabberd_sql:sql_query_t(SqlQuery) of + {selected, []} + when (ServiceOrRoom == Service) -> + %% Registering in the service... + %% check if nick is registered for some room in this service + {selected, NickRegistrations} = + ejabberd_sql:sql_query_t( + ?SQL("select @(jid)s, @(host)s from muc_registered " + "where nick=%(Nick)s")), + not lists:any(fun({_NRJid, NRServiceOrRoom}) -> + Service == (jid:decode(NRServiceOrRoom))#jid.lserver end, + NickRegistrations); + {selected, []} -> + %% Nick not registered in any service or room + true; + {selected, [{_J, Host}]} + when (Host == Service) and (ServiceOrRoom /= Service) -> + %% Registering in a room, but the nick is already registered in the service + false; + {selected, [{J, _Host}]} -> + %% Registering in room (or service) a nick that is + %% already registered in this room (or service) + %% Only the owner of this registration can use the nick + J == JID end, if Allow -> ?SQL_UPSERT_T( "muc_registered", ["!jid=%(JID)s", - "!host=%(Host)s", + "!host=%(ServiceOrRoom)s", "server_host=%(LServer)s", "nick=%(Nick)s"]), ok; From 8e324e67a4ad0a1a81085269e10989cc5c870e5a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Oct 2023 01:13:49 +0200 Subject: [PATCH 0235/1302] New Bulgarian translation (thanks to Mr. EddX) --- priv/msgs/bg.msg | 539 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 priv/msgs/bg.msg diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg new file mode 100644 index 000000000..4369cd41e --- /dev/null +++ b/priv/msgs/bg.msg @@ -0,0 +1,539 @@ +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (Добавете * в края на полето, за да съответства на подниза)"}. +{" has set the subject to: "," е задал темата на: "}. +{"# participants","# участници"}. +{"A description of the node","Описание на нода"}. +{"A friendly name for the node","Удобно име на нода"}. +{"A password is required to enter this room","Необходима е парола за влизане в тази стая"}. +{"A Web Page","Уеб страница"}. +{"Accept","Приемам"}. +{"Access denied by service policy","Достъпът е отказан спрямо политиката на услугата"}. +{"Access model","Модел на достъп"}. +{"Account doesn't exist","Профилът не съществува"}. +{"Action on user","Действие върху потребител"}. +{"Add a hat to a user","Добави шапка към потребител"}. +{"Add Jabber ID","Добави Jabber ID"}. +{"Add New","Добави нов"}. +{"Add User","Добави потребител"}. +{"Administration of ","Администриране на "}. +{"Administration","Администриране"}. +{"Administrator privileges required","Изискватт се администраторски права"}. +{"All activity","Цялата статистика"}. +{"All Users","Всички потребители"}. +{"Allow subscription","Разреши абониране"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Позволявате ли това Jabber ID да се абонира за pubsub нода?"}. +{"Allow this person to register with the room?","Позволявате ли този потребителя да се регистрира в стаята?"}. +{"Allow users to change the subject","Позволи потребителите да сменят темата"}. +{"Allow users to query other users","Позволи на потребителите да правят заявки към други потребители"}. +{"Allow users to send invites","Разреши на потребителите да изпращат покани"}. +{"Allow users to send private messages","Разреши на потребителите да изпращат лични съобщения"}. +{"Allow visitors to change nickname","Разреши на посетителите да променят псевдонима си"}. +{"Allow visitors to send private messages to","Разреши на потребителите да изпращат лични съобщения до"}. +{"Allow visitors to send status text in presence updates","Разреши на посетителите да изпращат текст за състоянието в актуализациите за присъствие"}. +{"Allow visitors to send voice requests","Разреши на посетителите да изпращат заявки за гласово повикване"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Асоциирана LDAP група, която определя членството в стая; това трябва да бъде LDAP отличително име според специфично за изпълнението/внедряването определение на група."}. +{"Announcements","Съобщения"}. +{"Answer associated with a picture","Отговор, свързан с картина"}. +{"Answer associated with a video","Отговор, свързан с видеоклип"}. +{"Answer associated with speech","Отговор, свързан с аудио клип"}. +{"Answer to a question","Отговор на въпрос"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Всеки в посочения списък от групата с контакти, може да се абонира и извлича елементи"}. +{"Anyone may associate leaf nodes with the collection","Всеки може да асоциира \"leaf\" нодове с колекцията"}. +{"Anyone may publish","Всеки може да публикува"}. +{"Anyone may subscribe and retrieve items","Всеки може да се абонира и да извлича елементи"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Всеки, който има абонамент за присъствие на двете или от: може да се абонира и да извлича елементи"}. +{"Anyone with Voice","Всеки, с възможност за гласово обаждане"}. +{"Anyone","Всеки"}. +{"April","Април"}. +{"Attribute 'channel' is required for this request","Атрибутът 'канал' е задължителен за тази заявка"}. +{"Attribute 'id' is mandatory for MIX messages","Атрибутът 'id' е задължителен за MIX съобщения"}. +{"Attribute 'jid' is not allowed here","Атрибутът 'jid' не е разрешен тук"}. +{"Attribute 'node' is not allowed here","Атрибутът 'нод' не е разрешен тук"}. +{"Attribute 'to' of stanza that triggered challenge","Атрибут 'до' на строфата, който е предизвикал предизвикателството"}. +{"August","Август"}. +{"Automatic node creation is not enabled","Автоматичното създаване на нод не е включено"}. +{"Backup Management","Управление на архивирането"}. +{"Backup of ~p","Резервно копие на ~p"}. +{"Backup to File at ","Архивиране във файл на "}. +{"Backup","Резервно копие"}. +{"Bad format","Лош формат"}. +{"Birthday","Рожден ден"}. +{"Both the username and the resource are required","Изискват се потребителското име и ресурсът"}. +{"Bytestream already activated","Bytestream вече е активиран"}. +{"Cannot remove active list","Активният списък не може да бъде премахнат"}. +{"Cannot remove default list","Не можете да премахнете списъка по подразбиране"}. +{"CAPTCHA web page","CAPTCHA уеб страница"}. +{"Challenge ID","ID на предизвикателството"}. +{"Change Password","Смяна на парола"}. +{"Change User Password","Смяна на потребителска парола"}. +{"Changing password is not allowed","Смяната на парола не е разрешена"}. +{"Changing role/affiliation is not allowed","Смяната на роля/принадлежност не е разрешена"}. +{"Channel already exists","Каналът вече съществува"}. +{"Channel does not exist","Каналът не съществува"}. +{"Channel JID","JID на канал"}. +{"Channels","Канали"}. +{"Characters not allowed:","Неразрешени символи:"}. +{"Chatroom configuration modified","Конфигурацията на стаята за чат е променена"}. +{"Chatroom is created","Стаята за чат е създадена"}. +{"Chatroom is destroyed","Стаята за чат е унищожена"}. +{"Chatroom is started","Стаята за чат е стартирана"}. +{"Chatroom is stopped","Стаята за чат е спряна"}. +{"Chatrooms","Чат стаи"}. +{"Choose a username and password to register with this server","Изберете потребителско име и парола, за да се регистрирате на този сървър"}. +{"Choose storage type of tables","Изберете тип за съхранение на таблици"}. +{"Choose whether to approve this entity's subscription.","Изберете дали да одобрите абонамента на този субект."}. +{"City","Град"}. +{"Client acknowledged more stanzas than sent by server","Клиентът потвърди повече строфи от изпратените от сървъра"}. +{"Commands","Команди"}. +{"Conference room does not exist","Конферентната стая не съществува"}. +{"Configuration of room ~s","Конфигурация на стая ~s"}. +{"Configuration","Конфигурация"}. +{"Connected Resources:","Свързани ресурси:"}. +{"Contact Addresses (normally, room owner or owners)","Адреси за контакт (обикновено собственик или собственици на стая)"}. +{"Contrib Modules","Сътруднически модули"}. +{"Country","Държава"}. +{"CPU Time:","Процесорно време:"}. +{"Current Discussion Topic","Текуща тема на дискусита"}. +{"Database failure","Грешка в базата данни"}. +{"Database Tables at ~p","Таблици на базата данни при ~p"}. +{"Database Tables Configuration at ","Конфигурация на таблиците в базата данни при "}. +{"Database","База данни"}. +{"December","Декември"}. +{"Default users as participants","Потребители по подразбиране като участници"}. +{"Delete content","Изтрий съдържанието"}. +{"Delete message of the day on all hosts","Изтрий съобщението на деня от всички нодове"}. +{"Delete message of the day","Изтрий съобщението на деня"}. +{"Delete Selected","Изтрий избраните"}. +{"Delete table","Изтрий таблицата"}. +{"Delete User","Изтрий потребителя"}. +{"Deliver event notifications","Достави известията за събития"}. +{"Deliver payloads with event notifications","Достави прикачените обекти с известията за събития"}. +{"Description:","Описание:"}. +{"Disc only copy","Копие само на диска"}. +{"'Displayed groups' not added (they do not exist!): ","'Показаните групи' не са добавени (не съществуват!): "}. +{"Displayed:","Показва се:"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Не казвайте паролата си на никого, дори на администраторите на XMPP сървъра."}. +{"Dump Backup to Text File at ","Архивиране в текстов файл при "}. +{"Dump to Text File","Архивиране в текстов файл"}. +{"Duplicated groups are not allowed by RFC6121","Дублирани групи не са разрешени от RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Динамично задаване на отговор към публикувалия елемента"}. +{"Edit Properties","Редактиране на свойства"}. +{"Either approve or decline the voice request.","Одобрете или отхвърлете заявката за гласова връзка."}. +{"Elements","Елементи"}. +{"Email Address","Имейл адрес"}. +{"Email","Илейл"}. +{"Enable hats","Активиране на шапки"}. +{"Enable logging","Активирай запис на хронологията"}. +{"Enable message archiving","Активирай архивиране на съобщенията"}. +{"Enabling push without 'node' attribute is not supported","Активиране на известията без атрибут 'нод' не се поддържа"}. +{"End User Session","Прекрати сесията на потребителя"}. +{"Enter nickname you want to register","Въведете псевдонима, който желаете да регистрирате"}. +{"Enter path to backup file","Въведете пътя към архивния файл"}. +{"Enter path to jabberd14 spool dir","Въведете пътя към jabberd14 spool директорията"}. +{"Enter path to jabberd14 spool file","Въведете пътя към jabberd14 spool файла"}. +{"Enter path to text file","Въведете пътя към текстовия файл"}. +{"Enter the text you see","Въведете текста, който виждате"}. +{"Erlang XMPP Server","Erlang XMPP сървър"}. +{"Error","Грешка"}. +{"Exclude Jabber IDs from CAPTCHA challenge","Изключи CAPTCHA предизвикателство за следните Jabber ID-та"}. +{"Export all tables as SQL queries to a file:","Експортирай всички таблици като SQL заявки във файл:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","Експортирай данните за всички потребители на сървъра в PIEFXIS файлове (XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","Експортирай данните за потребителите на този хост в PIEFXIS файлове (XEP-0227):"}. +{"External component failure","Неуспех породен от външен компонент"}. +{"External component timeout","Времето за изчакване на външен компонент изтече"}. +{"Failed to activate bytestream","Неуспешно активиране на bytestream"}. +{"Failed to extract JID from your voice request approval","Неуспешно извличане на JID от одобрението за гласова заявка"}. +{"Failed to map delegated namespace to external component","Неуспешно съпоставяне на делегирано пространство от имена с външен компонент"}. +{"Failed to parse HTTP response","Неуспешно анализиран HTTP отговор"}. +{"Failed to process option '~s'","Неуспешо обработена опция '~s'"}. +{"Family Name","Фамилно име"}. +{"FAQ Entry","Въвеждане на ЧЗВ"}. +{"February","Февруари"}. +{"File larger than ~w bytes","Файлът е по-голям от ~w байта"}. +{"Fill in the form to search for any matching XMPP User","Попълнете формата, за да търсите съвпадащ XMPP потребител"}. +{"Friday","Петък"}. +{"From ~ts","От ~ts"}. +{"From","От"}. +{"Full List of Room Admins","Пълен списък на администраторите на стаята"}. +{"Full List of Room Owners","Пълен списък на собствениците на стаята"}. +{"Full Name","Пълно име"}. +{"Get List of Online Users","Списък на онлайн потребителите"}. +{"Get List of Registered Users","Списък на регистрираните потребители"}. +{"Get Number of Online Users","Брой на онлайн потребителите"}. +{"Get Number of Registered Users","Брой на регистрираните потребители"}. +{"Get Pending","Виж чакащи"}. +{"Get User Last Login Time","Покажи времето, когато потребителят е влязъл за последно"}. +{"Get User Password","Покажи паролата на потребителя"}. +{"Get User Statistics","Покажи статистика за потребителя"}. +{"Given Name","Име"}. +{"Grant voice to this person?","Предоставяне на глас за потребителя?"}. +{"Groups that will be displayed to the members","Групи, които ще се показват на членовете"}. +{"Groups","Групи"}. +{"Group","Група"}. +{"Hat title","Заглавие на шапката"}. +{"Hat URI","URI адрес за шапка"}. +{"Hats limit exceeded","Превишен е лимитът за шапка"}. +{"Host unknown","Неизвестен хост"}. +{"Host","Хост"}. +{"HTTP File Upload","Качване на файл по HTTP"}. +{"Idle connection","Неактивна връзка"}. +{"If you don't see the CAPTCHA image here, visit the web page.","Ако не виждате CAPTCHA изображението, посетете уеб страницата."}. +{"Import Directory","Импорт на директория"}. +{"Import File","Импорт на файл"}. +{"Import user data from jabberd14 spool file:","Импорт на потребители от jabberd14 Spool файл:"}. +{"Import User from File at ","Импорт на потребител от файл на "}. +{"Import users data from a PIEFXIS file (XEP-0227):","Импорт на потребителски данни от PIEFXIS файл (XEP-0227):"}. +{"Import users data from jabberd14 spool directory:","Импорт на потребители от jabberd14 Spool директория:"}. +{"Import Users from Dir at ","Импорт на потребители от директория на "}. +{"Import Users From jabberd14 Spool Files","Импорт на потребители от jabberd14 Spool файлове"}. +{"Improper domain part of 'from' attribute","Неправилна част за домейн в атрибута 'from'"}. +{"Improper message type","Неправилен тип съобщение"}. +{"Incoming s2s Connections:","Входящи s2s връзки:"}. +{"Incorrect CAPTCHA submit","Неправилно CAPTCHA въвеждане"}. +{"Incorrect data form","Неправилна форма на данните"}. +{"Incorrect password","Грешна парола"}. +{"Incorrect value of 'action' attribute","Неправилна стойност на атрибута 'action'"}. +{"Incorrect value of 'action' in data form","Неправилна стойност на 'action' във формата за данни"}. +{"Incorrect value of 'path' in data form","Неправилна стойност на 'path' във формата за данни"}. +{"Installed Modules:","Инсталирани модули:"}. +{"Install","Инсталирай"}. +{"Insufficient privilege","Недостатъчни права"}. +{"Internal server error","Вътрешна сървърна грешка"}. +{"Invalid 'from' attribute in forwarded message","Невалиден атрибут 'from' в препратеното съобщение"}. +{"Invalid node name","Невалидно име на нода"}. +{"Invalid 'previd' value","Невалидна стойност на 'previd'"}. +{"Invitations are not allowed in this conference","Поканите не са разрешени в тази конференция"}. +{"IP addresses","IP адреси"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Не е позволено да изпращате съобщения за грешки в стаята. Участникът (~s) е изпратил съобщение за грешка (~s) и е бил отстранен от стаята"}. +{"It is not allowed to send private messages of type \"groupchat\"","Изпращането на лични съобщения от тип \"групов чат\" не е разрешено"}. +{"It is not allowed to send private messages to the conference","Изпращането на лични съобщения до конференцията не е разрешено"}. +{"Jabber ID","Jabber ID"}. +{"January","Януари"}. +{"JID normalization denied by service policy","Политиката на услугата не допуска нормализирането на JID"}. +{"JID normalization failed","Нормализирането на JID е неуспешно"}. +{"Joined MIX channels of ~ts","Свързани MIX канали на ~ts"}. +{"Joined MIX channels:","Свързани MIX канали:"}. +{"July","Юли"}. +{"June","Юни"}. +{"Just created","Току що създаден"}. +{"Label:","Етикет:"}. +{"Last Activity","Последна активност"}. +{"Last login","Последно влизане"}. +{"Last message","Последно съобщение"}. +{"Last month","Миналия месец"}. +{"Last year","Миналата година"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Най-малко значимите битове SHA-256 хеш на текст трябва да са равни на шестнайсетичния етикет"}. +{"List of rooms","Списък на стаите"}. +{"List of users with hats","Списък на потребителите с шапки"}. +{"List users with hats","Избройте потребителите с шапки"}. +{"Logging","Регистриране на събития"}. +{"Low level update script","Скрипт за актуализация на ниско ниво"}. +{"Make participants list public","Направи списъка с участниците публичен"}. +{"Make room CAPTCHA protected","Защити стаята с CAPTCHA"}. +{"Make room members-only","Направи стаята само за членове"}. +{"Make room moderated","Направи стая модерирана"}. +{"Make room password protected","Защити стаята с парола"}. +{"Make room persistent","Направи стая постоянна"}. +{"Make room public searchable","Направи стаята да е публично търсена"}. +{"Malformed username","Неправилно формирано потребителско име"}. +{"MAM preference modification denied by service policy","Промяна на предпочитанията за МАМ е отказана, поради политика на услугата"}. +{"March","Март"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Максимален # на елементи, които да се запазят, или `max` за неспецифичен лимит, различен от наложения от сървъра максимум"}. +{"Max payload size in bytes","Максимален размер на прикачения обект в байтове"}. +{"Maximum file size","Максимален размер на файла"}. +{"Maximum Number of History Messages Returned by Room","Максимален брой съобщения от хронологията, върнати от стая"}. +{"Maximum number of items to persist","Максимален брой елементи за запазване"}. +{"Maximum Number of Occupants","Максимален брой потребители"}. +{"May","Май"}. +{"Members not added (inexistent vhost!): ","Членовете не са добавени (несъществуващ vhost!): "}. +{"Membership is required to enter this room","Изисква се членство, за вход в тази стая"}. +{"Members:","Членове:"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запомнете паролата си или я запишете на лист хартия, поставен на сигурно място. В XMPP няма автоматичен начин за възстановяване на паролата в случай, че я забравите."}. +{"Memory","Памет"}. +{"Mere Availability in XMPP (No Show Value)","Наличност в XMPP (Не показвай стойност)"}. +{"Message body","Текст на съобщението"}. +{"Message not found in forwarded payload","Съобщението не е намерено в препратения прикачен елемент"}. +{"Messages from strangers are rejected","Съобщенията от непознати се отхвърлят"}. +{"Messages of type headline","Съобщения от тип заглавие"}. +{"Messages of type normal","Съобщения от тип нормален"}. +{"Middle Name","Презиме"}. +{"Minimum interval between voice requests (in seconds)","Минимален интервал между заявките за гласова комуникация (в секунди)"}. +{"Moderator privileges required","Изискват се права на модератор"}. +{"Moderators Only","Само модератори"}. +{"Moderator","Модератор"}. +{"Modified modules","Модифицирани модули"}. +{"Module failed to handle the query","Модулът не успя да обработи заявката"}. +{"Monday","Понеделник"}. +{"Multicast","Мултикаст"}. +{"Multiple elements are not allowed by RFC6121","Повече от един елемента не се разрешават от RFC6121"}. +{"Multi-User Chat","Групов чат (MUC)"}. +{"Name in the rosters where this group will be displayed","Име в списъците с контакти, където ще се показва тази група"}. +{"Name","Име"}. +{"Name:","Име:"}. +{"Natural Language for Room Discussions","Език за дискусии в стаята"}. +{"Natural-Language Room Name","Име на стаята на предпочитания език"}. +{"Neither 'jid' nor 'nick' attribute found","Атрибутите 'jid' и 'nick' не са намерени"}. +{"Neither 'role' nor 'affiliation' attribute found","Атрибути 'role' или 'affiliation' не са намерени"}. +{"Never","Никога"}. +{"New Password:","Нова парола:"}. +{"Nickname can't be empty","Псевдонимът не може да бъде празен"}. +{"Nickname Registration at ","Регистрация на псевдоним в "}. +{"Nickname ~s does not exist in the room","Псевдонимът ~s не присъства в стаята"}. +{"Nickname","Псевдоним"}. +{"No address elements found","Не е намерен адресен елемент"}. +{"No addresses element found","Не са намерени адресни елементи"}. +{"No 'affiliation' attribute found","Атрибут 'affiliation' не е намерен"}. +{"No available resource found","Не е намерен наличен ресурс"}. +{"No body provided for announce message","Не е предоставен текст за съобщение тип обява"}. +{"No child elements found","Не са открити подчинени елементи"}. +{"No data form found","Не е намерена форма за данни"}. +{"No Data","Няма данни"}. +{"No features available","Няма налични функции"}. +{"No element found","Елементът не е намерен"}. +{"No hook has processed this command","Никоя кука не е обработила тази команда"}. +{"No info about last activity found","Няма информация за последна активновт"}. +{"No 'item' element found","Елементът 'item' не е намерен"}. +{"No items found in this query","Няма намерени елементи в тази заявка"}. +{"No limit","Няма ограничение"}. +{"No module is handling this query","Нито един модул не обработва тази заявка"}. +{"No node specified","Не е посочен нод"}. +{"No 'password' found in data form","Не е намерен 'password' във формата за данни"}. +{"No 'password' found in this query","В заявката не е намерен 'password'"}. +{"No 'path' found in data form","Не е намерен 'path' във формата за данни"}. +{"No pending subscriptions found","Не са намерени чакащи абонаменти"}. +{"No privacy list with this name found","Не е намерен списък за поверителност с това име"}. +{"No private data found in this query","Няма открити лични данни в тази заявка"}. +{"No running node found","Не е намерен работещ нод"}. +{"No services available","Няма налични услуги"}. +{"No statistics found for this item","Не е налична статистика за този елемент"}. +{"No 'to' attribute found in the invitation","Атрибутът 'to' не е намерен в поканата"}. +{"Nobody","Никой"}. +{"Node already exists","Нодът вече съществува"}. +{"Node ID","ID на нода"}. +{"Node index not found","Индексът на нода не е намерен"}. +{"Node not found","Нодът не е намерен"}. +{"Node ~p","Нод ~p"}. +{"Nodeprep has failed","Nodeprep е неуспешен"}. +{"Nodes","Нодове"}. +{"Node","Нод"}. +{"None","Нито един"}. +{"Not allowed","Не е разрешено"}. +{"Not Found","Не е намерен"}. +{"Not subscribed","Няма абонамент"}. +{"Notify subscribers when items are removed from the node","Уведоми абонатите, когато елементите бъдат премахнати от нода"}. +{"Notify subscribers when the node configuration changes","Уведоми абонатите, когато конфигурацията на нода се промени"}. +{"Notify subscribers when the node is deleted","Уведоми абонатите, когато нодът бъде изтрит"}. +{"November","Ноември"}. +{"Number of answers required","Брой на необходимите отговори"}. +{"Number of occupants","Брой потребители"}. +{"Number of Offline Messages","Брой офлайн съобщения"}. +{"Number of online users","Брой онлайн потребители"}. +{"Number of registered users","Брой регистрирани потребители"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Брой секунди, след които автоматично да се изчистят елементите, или `max` за липса на конкретно ограничение, различно от наложения от сървъра максимум"}. +{"Occupants are allowed to invite others","На участниците е позволено да канят други"}. +{"Occupants are allowed to query others","Участниците могат да отправят заявки към други лица"}. +{"Occupants May Change the Subject","Участниците могат да променят темата"}. +{"October","Октомври"}. +{"Offline Messages","Офлайн съобщения"}. +{"Offline Messages:","Офлайн съобщения:"}. +{"OK","ДОБРЕ"}. +{"Old Password:","Стара парола:"}. +{"Online Users","Онлайн потребители"}. +{"Online Users:","Онлайн потребители:"}. +{"Online","Онлайн"}. +{"Only admins can see this","Само администратори могат да видят това"}. +{"Only deliver notifications to available users","Доставяне на известия само до наличните потребители"}. +{"Only or tags are allowed","Само тагове и са разрешени"}. +{"Only element is allowed in this query","Само елементът е разрешен за тази заявка"}. +{"Only members may query archives of this room","Само членовете могат да търсят архиви на тази стая"}. +{"Only moderators and participants are allowed to change the subject in this room","Само модератори и участници имат право да променят темата в тази стая"}. +{"Only moderators are allowed to change the subject in this room","Само модераторите имат право да сменят темата в тази стая"}. +{"Only moderators are allowed to retract messages","Само модераторите имат право да оттеглят съобщения"}. +{"Only moderators can approve voice requests","Само модераторите могат да одобряват гласови заявки"}. +{"Only occupants are allowed to send messages to the conference","Само обитателите имат право да изпращат съобщения до конференцията"}. +{"Only occupants are allowed to send queries to the conference","Само обитателите имат право да изпращат запитвания до конференцията"}. +{"Only publishers may publish","Само издателите могат да публикуват"}. +{"Only service administrators are allowed to send service messages","Само администраторите на услуги имат право да изпращат системни съобщения"}. +{"Only those on a whitelist may subscribe and retrieve items","Само тези в белия списък могат да се абонират и да извличат елементи"}. +{"Organization Name","Име на организацията"}. +{"Organization Unit","Отдел"}. +{"Other Modules Available:","Други налични модули:"}. +{"Outgoing s2s Connections","Изходящи s2s връзки"}. +{"Outgoing s2s Connections:","Изходящи s2s връзки:"}. +{"Owner privileges required","Изискват се привилегии на собственик"}. +{"Packet relay is denied by service policy","Предаването на пакети е отказано от политиката на услугата"}. +{"Packet","Пакет"}. +{"Participant ID","ID на участник"}. +{"Participant","Участник"}. +{"Password Verification","Проверка на паролата"}. +{"Password Verification:","Проверка на паролата:"}. +{"Password","Парола"}. +{"Password:","Парола:"}. +{"Path to Dir","Път към директория"}. +{"Path to File","Път до файл"}. +{"Pending","В очакване"}. +{"Period: ","Период: "}. +{"Persist items to storage","Запазване на елементите в хранилището"}. +{"Persistent","Постоянен"}. +{"Ping query is incorrect","Заявката за пинг е неправилна"}. +{"Ping","Пинг"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Обърнете внимание, че тези опции ще направят резервно копие само на вградената (Mnesia) база данни. Ако използвате модула ODBC, трябва да направите резервно копие на SQL базата данни отделно."}. +{"Please, wait for a while before sending new voice request","Моля, изчакайте известно време, преди да изпратите нова заявка за гласова връзка"}. +{"Pong","Понг"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Притежаването на атрибут 'ask' не е разрешено от RFC6121"}. +{"Present real Jabber IDs to","Покажи истински Jabber ID-та на"}. +{"Previous session not found","Предишната сесия не е намерена"}. +{"Previous session PID has been killed","PID от предишната сесия е унищожен"}. +{"Previous session PID has exited","Предишният PID на сесията е излязъл"}. +{"Previous session PID is dead","PID от предишната сесия не съществува"}. +{"Previous session timed out","Времето на предишната сесия изтече"}. +{"Public","Публичен"}. +{"Publish model","Модел за публикуване"}. +{"Publish-Subscribe","Публикуване-Абониране"}. +{"PubSub subscriber request","Заявка от абонат за PubSub"}. +{"Purge all items when the relevant publisher goes offline","Изчисти всички елементи, когато съответният издател премине в режим офлайн"}. +{"Push record not found","Push записът не е намерен"}. +{"Queries to the conference members are not allowed in this room","В тази зала не се допускат запитвания към членовете на конференцията"}. +{"Query to another users is forbidden","Заявка към други потребители е забранена"}. +{"RAM and disc copy","Копие в RAM и на диск"}. +{"RAM copy","Копие в RAM"}. +{"Really delete message of the day?","Наистина ли желаете да изтриете съобщението на деня?"}. +{"Receive notification from all descendent nodes","Получаване на известие от всички низходящи нодове"}. +{"Receive notification from direct child nodes only","Получаване на известия само от директни подчинени нодове"}. +{"Receive notification of new items only","Получаване на известия само за нови елементи"}. +{"Receive notification of new nodes only","Получаване на известия само за нови нодове"}. +{"Recipient is not in the conference room","Получателят не е в конферентната зала"}. +{"Register an XMPP account","Регистрирай XMPP акаунт"}. +{"Registered Users","Регистрирани потребители"}. +{"Registered Users:","Регистрирани потребители:"}. +{"Register","Регистрирай"}. +{"Remote copy","Отдалечено копие"}. +{"Remove a hat from a user","Премахни шапка от потребител"}. +{"Remove All Offline Messages","Премахни всички офлайн съобщения"}. +{"Remove User","Премахни потребител"}. +{"Remove","Премахни"}. +{"Replaced by new connection","Заменен от нова връзка"}. +{"Request has timed out","Времето за заявка изтече"}. +{"Request is ignored","Заявката е игнорирано"}. +{"Requested role","Заявена роля"}. +{"Resources","Ресурси"}. +{"Restart Service","Рестартирай услугата"}. +{"Restart","Рестартирай"}. +{"Restore Backup from File at ","Възстанови резервно копие от файл в "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","Възстановяване на бинарно копие след следващото рестартиране на ejabberd (изисква по-малко памет):"}. +{"Restore binary backup immediately:","Възстанови незабавно двоично копие:"}. +{"Restore plain text backup immediately:","Възстановете незабавно копие от обикновен текст:"}. +{"Restore","Възстанови"}. +{"Roles and Affiliations that May Retrieve Member List","Роли и принадлежности, които могат да извличат списък с членове"}. +{"Roles for which Presence is Broadcasted","Роли, за които се излъчва присъствие"}. +{"Roles that May Send Private Messages","Роли, които могат да изпращат лични съобщения"}. +{"Room Configuration","Конфигурация на стаята"}. +{"Room creation is denied by service policy","Създаването на стая е отказано поради политика на услугата"}. +{"Room description","Описание на стаята"}. +{"Room Occupants","Обитатели на стаята"}. +{"Room terminates","Стаята се прекратява"}. +{"Room title","Заглавие на стаята"}. +{"Roster groups allowed to subscribe","Групи от списъци с контакти, на които е разрешено да се абонират"}. +{"Roster of ~ts","Списък с контакти на ~ts"}. +{"Roster size","Размер на списъка с контакти"}. +{"Roster:","Списък с контакти:"}. +{"RPC Call Error","Грешка при RPC повикване"}. +{"Running Nodes","Работещи нодове"}. +{"Saturday","Събота"}. +{"Script check","Проверка на скрипт"}. +{"Search from the date","Търси от дата"}. +{"Search Results for ","Резултати от търсенето за "}. +{"Search the text","Търси текста"}. +{"Search until the date","Търси до дата"}. +{"Search users in ","Търси потребители в "}. +{"Select All","Избери всички"}. +{"Send announcement to all online users on all hosts","Изпрати съобщение до всички онлайн потребители на всички хостове"}. +{"Send announcement to all online users","Изпрати съобщение до всички онлайн потребители"}. +{"Send announcement to all users on all hosts","Изпрати съобщение до всички потребители на всички хостове"}. +{"Send announcement to all users","Изпрати съобщение до всички потребители"}. +{"September","Септември"}. +{"Server:","Сървър:"}. +{"Service list retrieval timed out","Времето за изчакване на извличането на списъка с услуги изтече"}. +{"Session state copying timed out","Времето за изчакване на копирането на състоянието на сесията изтече"}. +{"Set message of the day and send to online users","Задай съобщение на деня и го изпрати на онлайн потребителите"}. +{"Set message of the day on all hosts and send to online users","Задавай съобщение на деня на всички хостове и изпрати на онлайн потребителите"}. +{"Shared Roster Groups","Споделени групи от списъци с контакти"}. +{"Show Integral Table","Покажи интегрална таблица"}. +{"Show Ordinary Table","Покажи обикновена таблица"}. +{"Shut Down Service","Изключи услугата"}. +{"SOCKS5 Bytestreams","SOCKS5 байтови потоци"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Някои XMPP клиенти могат да съхраняват паролата Ви в компютъра, но от съображения за сигурност трябва да го правите само на личния си компютър."}. +{"Sources Specs:","Спецификации на източниците:"}. +{"Specify the access model","Задай модела за достъп"}. +{"Specify the event message type","Задай типа на съобщението за събитие"}. +{"Specify the publisher model","Задай модела на издателя"}. +{"Stanza id is not valid","Невалидно ID на строфата"}. +{"Stanza ID","ID на строфа"}. +{"Statically specify a replyto of the node owner(s)","Статично задаване на replyto на собственика(ците) на нода"}. +{"Statistics of ~p","Статистики на ~p"}. +{"Statistics","Статистики"}. +{"Stopped Nodes","Спрени нодове"}. +{"Stop","Спри"}. +{"Storage Type","Тип хранилище"}. +{"Store binary backup:","Запази бинарен архив:"}. +{"Store plain text backup:","Запази архив като обикновен текст:"}. +{"Stream management is already enabled","Управлението на потока вече е активирано"}. +{"Stream management is not enabled","Управлението на потока не е активирано"}. +{"Subject","Тема"}. +{"Submitted","Изпратено"}. +{"Submit","Изпрати"}. +{"Subscriber Address","Адрес на абоната"}. +{"Subscribers may publish","Абонатите могат да публикуват"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Заявките за абонамент трябва да бъдат одобрени и само абонатите могат да извличат елементи"}. +{"Subscriptions are not allowed","Абонаментите не са разрешени"}. +{"Subscription","Абонамент"}. +{"Sunday","Неделя"}. +{"Text associated with a picture","Текст, свързан със снимка"}. +{"Text associated with a sound","Текст, свързан със звук"}. +{"Text associated with a video","Текст, свързан с видео"}. +{"Text associated with speech","Текст, свързан с реч"}. +{"That nickname is already in use by another occupant","Този псевдоним вече се използва от друг потребител"}. +{"That nickname is registered by another person","Този псевдоним е регистриран от друго лице"}. +{"The account already exists","Профилът вече съществува"}. +{"The account was not unregistered","Профилът не е дерегистриран"}. +{"The body text of the last received message","Текстът на последното получено съобщение"}. +{"The CAPTCHA is valid.","CAPTCHA предизвикателството е валидно."}. +{"The CAPTCHA verification has failed","Проверката на CAPTCHA предизвикателството е неуспешна"}. +{"The captcha you entered is wrong","Въведеният captcha код е грешен"}. +{"The DateTime at which a leased subscription will end or has ended","Датата и часът, на който абонамент ще приключи или е приключил"}. +{"The datetime when the node was created","Датата, когато нодът е бил създаден"}. +{"The default language of the node","Езикът по подразбиране на нода"}. +{"The feature requested is not supported by the conference","Исканата функция не се поддържа от конференцията"}. +{"The JID of the node creator","JID на създателя на нода"}. +{"The JIDs of those to contact with questions","JID на лицата, с които да се свържете при въпроси"}. +{"The JIDs of those with an affiliation of owner","JID на лицата с принадлежност на собственик"}. +{"The JIDs of those with an affiliation of publisher","JID-та на лица с принадлежност към публикуващи"}. +{"The list of all online users","Списък на всички онлайн потребители"}. +{"The list of all users","Списък на всички потребители"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимален брой подчинени нодове, които могат да бъдат свързани с колекция, или `max` за липса на конкретен лимит, различен от наложения от сървъра максимум"}. +{"The minimum number of milliseconds between sending any two notification digests","Минималният брой милисекунди между изпращането на две извадки на известия"}. +{"The name of the node","Името на нода"}. +{"The node is a collection node","Нодът е от тип колекция"}. +{"The NodeID of the relevant node","NodeID на съответния нод"}. +{"The number of pending incoming presence subscription requests","Броят на чакащите входящи заявки за абонамент за присъствие"}. +{"The number of subscribers to the node","Бротят абонати на нода"}. +{"The number of unread or undelivered messages","Броят непрочетени или недоставени съобщения"}. +{"The password contains unacceptable characters","Паролата съдържа недопустими символи"}. +{"The password is too weak","Паролата е твърде слаба"}. +{"The password of your XMPP account was successfully changed.","Паролата на вашия XMPP профил беше успешно променена."}. +{"The password was not changed","Паролата не е променена"}. +{"The passwords are different","Паролите са различни"}. +{"The presence states for which an entity wants to receive notifications","Състояния на присъствие, за които даден субект желае да получава известия"}. +{"The query is only allowed from local users","Заявката е разрешена само за локални потребители"}. +{"The query must not contain elements","Заявката не може да съдържа елементи "}. +{"The room subject can be modified by participants","Темата на стаята може да бъде променяна от участниците"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Информацията за семантичния тип данни на нода, обикновено зададена от пространството на имената на прикачените данни (ако има такива)"}. +{"The sender of the last received message","Подателят на последното получено съобщение"}. +{"The stanza MUST contain only one element, one element, or one element","Строфата ТРЯБВА да съдържа само един елемент, един елемент или един елемент"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL адрес на XSL трансформацията, която може да се приложи към прикачените данни, за да се генерира подходящ елемент от тялото на съобщението."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms."}. +{"View Roster","Преглед на списъка с контакти"}. From 3851a771347fcd8e0ddc757b872dc1b401be866c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Oct 2023 01:14:49 +0200 Subject: [PATCH 0236/1302] Update German translation (thanks to nautilusx) --- priv/msgs/de.msg | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index 56dfce48c..88e1d7c2d 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -12,11 +12,6 @@ {"A Web Page","Eine Webseite"}. {"Accept","Akzeptieren"}. {"Access denied by service policy","Zugriff aufgrund der Dienstrichtlinien verweigert"}. -{"Access model of authorize","Zugriffsmodell von 'authorize'"}. -{"Access model of open","Zugriffsmodell von 'open'"}. -{"Access model of presence","Zugriffsmodell von 'presence'"}. -{"Access model of roster","Zugriffsmodell der Kontaktliste"}. -{"Access model of whitelist","Zugriffsmodell von 'whitelist'"}. {"Access model","Zugriffsmodell"}. {"Account doesn't exist","Konto existiert nicht"}. {"Action on user","Aktion auf Benutzer"}. @@ -229,11 +224,11 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer (~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum geworfen"}. {"It is not allowed to send private messages of type \"groupchat\"","Es ist nicht erlaubt private Nachrichten des Typs \"groupchat\" zu senden"}. {"It is not allowed to send private messages to the conference","Es ist nicht erlaubt private Nachrichten an die Konferenz zu senden"}. -{"It is not allowed to send private messages","Es ist nicht erlaubt private Nachrichten zu senden"}. {"Jabber ID","Jabber-ID"}. {"January","Januar"}. {"JID normalization denied by service policy","JID-Normalisierung aufgrund der Dienstrichtlinien verweigert"}. {"JID normalization failed","JID-Normalisierung fehlgeschlagen"}. +{"Joined MIX channels of ~ts","Beigetretene MIX-Channels von ~ts"}. {"Joined MIX channels:","Beigetretene MIX-Channels:"}. {"joins the room","betritt den Raum"}. {"July","Juli"}. @@ -373,6 +368,7 @@ {"Only members may query archives of this room","Nur Mitglieder dürfen den Verlauf dieses Raumes abrufen"}. {"Only moderators and participants are allowed to change the subject in this room","Nur Moderatoren und Teilnehmer dürfen das Thema in diesem Raum ändern"}. {"Only moderators are allowed to change the subject in this room","Nur Moderatoren dürfen das Thema in diesem Raum ändern"}. +{"Only moderators are allowed to retract messages","Nur Moderatoren dürfen Nachrichten zurückziehen"}. {"Only moderators can approve voice requests","Nur Moderatoren können Sprachrecht-Anforderungen genehmigen"}. {"Only occupants are allowed to send messages to the conference","Nur Teilnehmer dürfen Nachrichten an die Konferenz senden"}. {"Only occupants are allowed to send queries to the conference","Nur Teilnehmer dürfen Anfragen an die Konferenz senden"}. @@ -494,6 +490,7 @@ {"Specify the access model","Geben Sie das Zugangsmodell an"}. {"Specify the event message type","Geben Sie den Ereignisnachrichtentyp an"}. {"Specify the publisher model","Geben Sie das Veröffentlichermodell an"}. +{"Stanza id is not valid","Stanza-ID ist ungültig"}. {"Stanza ID","Stanza-ID"}. {"Statically specify a replyto of the node owner(s)","Ein 'replyto' des/der Nodebesitzer(s) statisch angeben"}. {"Statistics of ~p","Statistiken von ~p"}. From bab867305550d1a2591292d1be15edb7a170b4da Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Oct 2023 01:15:57 +0200 Subject: [PATCH 0237/1302] Update Portuguese (Brazil) translation (thanks to Wellington Uemura) --- priv/msgs/pt-br.msg | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 179688b78..934da000c 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -12,11 +12,6 @@ {"A Web Page","Uma página da web"}. {"Accept","Aceito"}. {"Access denied by service policy","Acesso negado pela política do serviço"}. -{"Access model of authorize","Modelo de acesso da autorização"}. -{"Access model of open","Modelo para acesso aberto"}. -{"Access model of presence","Modelo para acesso presença"}. -{"Access model of roster","Modelo para acesso lista"}. -{"Access model of whitelist","Modelo de acesso da lista branca"}. {"Access model","Modelo de acesso"}. {"Account doesn't exist","A conta não existe"}. {"Action on user","Ação no usuário"}. @@ -182,7 +177,7 @@ {"Get User Last Login Time","Obter a Data do Último Login"}. {"Get User Password","Obter Senha do Usuário"}. {"Get User Statistics","Obter Estatísticas do Usuário"}. -{"Given Name","Sobrenome"}. +{"Given Name","Prenome"}. {"Grant voice to this person?","Dar voz a esta pessoa?"}. {"Group","Grupo"}. {"Groups that will be displayed to the members","Os grupos que serão exibidos para os membros"}. @@ -230,7 +225,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro para a sala. O membro (~s) enviou uma mensagem de erro (~s) e foi expulso da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Não é permitido enviar mensagens privadas para a sala de conferência"}. -{"It is not allowed to send private messages","Não é permitido enviar mensagens privadas"}. {"Jabber ID","ID Jabber"}. {"January","Janeiro"}. {"JID normalization denied by service policy","Normalização JID negada por causa da política de serviços"}. @@ -250,7 +244,7 @@ {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Bits menos significativos do hash sha-256 do texto devem ser iguais ao rótulo hexadecimal"}. {"leaves the room","Sair da sala"}. {"List of rooms","Lista de salas"}. -{"List of users with hats","Lista os usuários com chapéus"}. +{"List of users with hats","Lista dos usuários com chapéus"}. {"List users with hats","Lista os usuários com chapéus"}. {"Logging","Registrando no log"}. {"Low level update script","Script de atualização low level"}. @@ -351,7 +345,7 @@ {"Notify subscribers when the node is deleted","Notificar assinantes quando o nó for eliminado se elimine"}. {"November","Novembro"}. {"Number of answers required","Quantidade de respostas necessárias"}. -{"Number of occupants","Número de participantes"}. +{"Number of occupants","Quantidade de ocupantes"}. {"Number of Offline Messages","Quantidade das mensagens offline"}. {"Number of online users","Número de usuários online"}. {"Number of registered users","Número de usuários registrados"}. @@ -375,6 +369,7 @@ {"Only members may query archives of this room","Somente os membros podem procurar nos arquivos desta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Somente os moderadores e os participamentes podem alterar o assunto desta sala"}. {"Only moderators are allowed to change the subject in this room","Somente os moderadores podem alterar o assunto desta sala"}. +{"Only moderators are allowed to retract messages","Apenas moderadores estão autorizados a retirar mensagens"}. {"Only moderators can approve voice requests","Somente moderadores podem aprovar requisições de voz"}. {"Only occupants are allowed to send messages to the conference","Somente os ocupantes podem enviar mensagens à sala de conferência"}. {"Only occupants are allowed to send queries to the conference","Somente os ocupantes podem enviar consultas à sala de conferência"}. @@ -398,6 +393,7 @@ {"Password:","Senha:"}. {"Path to Dir","Caminho para o diretório"}. {"Path to File","Caminho do arquivo"}. +{"Payload semantic type information","Informações de tipo semântico de carga útil"}. {"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. @@ -458,7 +454,7 @@ {"Room Configuration","Configuração de salas"}. {"Room creation is denied by service policy","Sala não pode ser criada devido à política do serviço"}. {"Room description","Descrição da Sala"}. -{"Room Occupants","Número de participantes"}. +{"Room Occupants","Ocupantes do quarto"}. {"Room terminates","Terminação da sala"}. {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Listar grupos autorizados"}. @@ -496,6 +492,7 @@ {"Specify the access model","Especificar os modelos de acesso"}. {"Specify the event message type","Especificar o tipo de mensagem para o evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. +{"Stanza id is not valid","A Stanza ID não é válido"}. {"Stanza ID","ID da estrofe"}. {"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. {"Statistics of ~p","Estatísticas de ~p"}. @@ -560,6 +557,7 @@ {"The query is only allowed from local users","Esta consulta só é permitida a partir de usuários locais"}. {"The query must not contain elements","A consulta não pode conter elementos "}. {"The room subject can be modified by participants","O tema da sala pode ser alterada pelos próprios participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Informações de tipo semântico dos dados no nó, geralmente especificadas pelo espaço de nomes da carga útil (se houver)"}. {"The sender of the last received message","O remetente da última mensagem que foi recebida"}. {"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. {"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. From 9acf5912423dff011e5f2da310d6972556a1c4ed Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Oct 2023 01:17:10 +0200 Subject: [PATCH 0238/1302] Run 'make translations' --- priv/msgs/ca.msg | 10 ++++------ priv/msgs/cs.msg | 1 - priv/msgs/el.msg | 6 ------ priv/msgs/eo.msg | 4 ---- priv/msgs/es.msg | 12 +++++------- priv/msgs/fr.msg | 6 ------ priv/msgs/gl.msg | 1 - priv/msgs/he.msg | 1 - priv/msgs/hu.msg | 1 - priv/msgs/id.msg | 6 ------ priv/msgs/it.msg | 1 - priv/msgs/ja.msg | 1 - priv/msgs/nl.msg | 1 - priv/msgs/no.msg | 3 --- priv/msgs/pl.msg | 1 - priv/msgs/pt.msg | 6 ------ priv/msgs/ru.msg | 1 - priv/msgs/sk.msg | 1 - priv/msgs/sq.msg | 1 - priv/msgs/sv.msg | 1 - priv/msgs/tr.msg | 1 - priv/msgs/uk.msg | 1 - priv/msgs/wa.msg | 1 - priv/msgs/zh.msg | 6 ------ 24 files changed, 9 insertions(+), 65 deletions(-) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 821da7920..5650bb966 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -12,11 +12,6 @@ {"A Web Page","Una Pàgina Web"}. {"Accept","Acceptar"}. {"Access denied by service policy","Accés denegat per la política del servei"}. -{"Access model of authorize","Model d'Accés de autoritzar"}. -{"Access model of open","Model d'Accés de obert"}. -{"Access model of presence","Model d'Accés de presència"}. -{"Access model of roster","Model d'Accés de contactes"}. -{"Access model of whitelist","Model d'Accés de llista blanca"}. {"Access model","Model d'Accés"}. {"Account doesn't exist","El compte no existeix"}. {"Action on user","Acció en l'usuari"}. @@ -230,7 +225,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No està permés enviar missatges d'error a la sala. El participant (~s) ha enviat un missatge d'error (~s) i ha sigut expulsat de la sala"}. {"It is not allowed to send private messages of type \"groupchat\"","No està permés enviar missatges del tipus \"groupchat\""}. {"It is not allowed to send private messages to the conference","No està permès l'enviament de missatges privats a la sala"}. -{"It is not allowed to send private messages","No està permés enviar missatges privats"}. {"Jabber ID","ID Jabber"}. {"January","Gener"}. {"JID normalization denied by service policy","S'ha denegat la normalització del JID per política del servei"}. @@ -375,6 +369,7 @@ {"Only members may query archives of this room","Només membres poden consultar l'arxiu de missatges d'aquesta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Només els moderadors i participants poden canviar el tema d'aquesta sala"}. {"Only moderators are allowed to change the subject in this room","Només els moderadors poden canviar el tema d'aquesta sala"}. +{"Only moderators are allowed to retract messages","Només els moderadors tenen permís per a retractar missatges"}. {"Only moderators can approve voice requests","Només els moderadors poden aprovar les peticions de veu"}. {"Only occupants are allowed to send messages to the conference","Sols els ocupants poden enviar missatges a la sala"}. {"Only occupants are allowed to send queries to the conference","Sols els ocupants poden enviar sol·licituds a la sala"}. @@ -398,6 +393,7 @@ {"Password:","Contrasenya:"}. {"Path to Dir","Ruta al directori"}. {"Path to File","Ruta al fitxer"}. +{"Payload semantic type information","Informació sobre el tipus semàntic de la carrega útil"}. {"Pending","Pendent"}. {"Period: ","Període: "}. {"Persist items to storage","Persistir elements al guardar"}. @@ -496,6 +492,7 @@ {"Specify the access model","Especificar el model d'accés"}. {"Specify the event message type","Especifica el tipus de missatge d'event"}. {"Specify the publisher model","Especificar el model del publicant"}. +{"Stanza id is not valid","L'identificador del paquet no es vàlid"}. {"Stanza ID","ID del paquet"}. {"Statically specify a replyto of the node owner(s)","Especifica estaticament una adreça on respondre al propietari del node"}. {"Statistics of ~p","Estadístiques de ~p"}. @@ -560,6 +557,7 @@ {"The query is only allowed from local users","La petició està permesa només d'usuaris locals"}. {"The query must not contain elements","La petició no pot contenir elements "}. {"The room subject can be modified by participants","El tema de la sala pot modificar-lo els participants"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La informació semàntica de les dades al node, usualment especificat pel espai de noms de la càrrega util (si n'hi ha)"}. {"The sender of the last received message","Qui ha enviat l'ultim missatge rebut"}. {"The stanza MUST contain only one element, one element, or one element","El paquet DEU contindre només un element , un element , o un element "}. {"The subscription identifier associated with the subscription request","L'identificador de subscripció associat amb la petició de subscripció"}. diff --git a/priv/msgs/cs.msg b/priv/msgs/cs.msg index c6d3b2207..c910528cf 100644 --- a/priv/msgs/cs.msg +++ b/priv/msgs/cs.msg @@ -159,7 +159,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Není povoleno posílat chybové zprávy do místnosti. Účastník (~s) odeslal chybovou zprávu (~s) a byl vyhozen z místnosti"}. {"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" "}. {"It is not allowed to send private messages to the conference","Není povoleno odesílat soukromé zprávy v této místnosti"}. -{"It is not allowed to send private messages","Je zakázáno posílat soukromé zprávy"}. {"Jabber ID","Jabber ID"}. {"January",". ledna"}. {"joins the room","vstoupil(a) do místnosti"}. diff --git a/priv/msgs/el.msg b/priv/msgs/el.msg index 1b6f2d1df..d49ae04fc 100644 --- a/priv/msgs/el.msg +++ b/priv/msgs/el.msg @@ -12,11 +12,6 @@ {"A Web Page","Μία ιστοσελίδα"}. {"Accept","Αποδοχή"}. {"Access denied by service policy","Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών"}. -{"Access model of authorize","Μοντέλο πρόσβασης της πιστοποίησης"}. -{"Access model of open","Μοντέλο πρόσβασης του ανοικτού"}. -{"Access model of presence","Μοντέλο πρόσβασης της παρουσίας"}. -{"Access model of roster","Μοντέλο πρόσβασης της Λίστας Επαφών"}. -{"Access model of whitelist","Μοντέλο πρόσβασης της Λευκής Λίστας"}. {"Access model","Καθορίστε το μοντέλο πρόσβασης"}. {"Account doesn't exist","Ο λογαριασμός δεν υπάρχει"}. {"Action on user","Eνέργεια για το χρήστη"}. @@ -219,7 +214,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Δεν επιτρέπεται η αποστολή μηνυμάτων σφάλματος στο δωμάτιο. Ο συμμετέχων (~s) έχει στείλει ένα μήνυμα σφάλματος (~s) και έχει πεταχτεί έξω από την αίθουσα"}. {"It is not allowed to send private messages of type \"groupchat\"","Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων του τύπου \"groupchat\""}. {"It is not allowed to send private messages to the conference","Δεν επιτρέπεται να στείλει προσωπικά μηνύματα για τη διάσκεψη"}. -{"It is not allowed to send private messages","Δεν επιτρέπεται η αποστολή προσωπικών μηνυμάτων"}. {"Jabber ID","Ταυτότητα Jabber"}. {"January","Ιανουάριος"}. {"JID normalization denied by service policy","Απετράπη η κανονικοποίηση του JID, λόγω της τακτικής Παροχής Υπηρεσιών"}. diff --git a/priv/msgs/eo.msg b/priv/msgs/eo.msg index a7d9ad214..c0a6be1f0 100644 --- a/priv/msgs/eo.msg +++ b/priv/msgs/eo.msg @@ -12,9 +12,6 @@ {"A Web Page","Retpaĝo"}. {"Accept","Akcepti"}. {"Access denied by service policy","Atingo rifuzita de serv-politiko"}. -{"Access model of open","Atingomodelo de malfermo"}. -{"Access model of presence","Atingomodelo de ĉeesto"}. -{"Access model of whitelist","Atingomodelo de permesolisto"}. {"Access model","Atingomodelo"}. {"Account doesn't exist","Konto ne ekzistas"}. {"Action on user","Ago je uzanto"}. @@ -163,7 +160,6 @@ {"is now known as","nun nomiĝas"}. {"It is not allowed to send private messages of type \"groupchat\"","Malpermesas sendi mesaĝojn de tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nur partoprenantoj rajtas sendi privatajn mesaĝojn al la babilejo"}. -{"It is not allowed to send private messages","Ne estas permesata sendi privatajn mesaĝojn"}. {"Jabber ID","Jabber ID"}. {"January","Januaro"}. {"joins the room","eniras la babilejo"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index abbaa9bcb..bb16ebcf4 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -12,11 +12,6 @@ {"A Web Page","Una página web"}. {"Accept","Aceptar"}. {"Access denied by service policy","Acceso denegado por la política del servicio"}. -{"Access model of authorize","Modelo de acceso de Autorizar"}. -{"Access model of open","Modelo de acceso de Abierto"}. -{"Access model of presence","Modelo de acceso de Presencia"}. -{"Access model of roster","Modelo de acceso de Roster"}. -{"Access model of whitelist","Modelo de acceso de Lista Blanca"}. {"Access model","Modelo de Acceso"}. {"Account doesn't exist","La cuenta no existe"}. {"Action on user","Acción en el usuario"}. @@ -230,7 +225,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","No está permitido enviar mensajes de error a la sala. Este participante (~s) ha enviado un mensaje de error (~s) y fue expulsado de la sala"}. {"It is not allowed to send private messages of type \"groupchat\"","No está permitido enviar mensajes privados del tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir el envio de mensajes privados a la sala"}. -{"It is not allowed to send private messages","No está permitido enviar mensajes privados"}. {"Jabber ID","Jabber ID"}. {"January","Enero"}. {"JID normalization denied by service policy","Se ha denegado la normalización del JID por política del servicio"}. @@ -290,7 +284,7 @@ {"Modified modules","Módulos modificados"}. {"Module failed to handle the query","El módulo falló al gestionar la petición"}. {"Monday","Lunes"}. -{"Multicast","Multicast"}. +{"Multicast","Multidifusión"}. {"Multiple elements are not allowed by RFC6121","No se permiten múltiples elementos en RFC6121"}. {"Multi-User Chat","Salas de Charla"}. {"Name in the rosters where this group will be displayed","Nombre del grupo con que aparecerá en las listas de contactos"}. @@ -375,6 +369,7 @@ {"Only members may query archives of this room","Solo miembros pueden consultar el archivo de mensajes de la sala"}. {"Only moderators and participants are allowed to change the subject in this room","Solo los moderadores y participantes pueden cambiar el asunto de esta sala"}. {"Only moderators are allowed to change the subject in this room","Solo los moderadores pueden cambiar el asunto de esta sala"}. +{"Only moderators are allowed to retract messages","Solo los moderadores pueden retractarse de los mensajes"}. {"Only moderators can approve voice requests","Solo los moderadores pueden aprobar peticiones de voz"}. {"Only occupants are allowed to send messages to the conference","Solo los ocupantes pueden enviar mensajes a la sala"}. {"Only occupants are allowed to send queries to the conference","Solo los ocupantes pueden enviar solicitudes a la sala"}. @@ -398,6 +393,7 @@ {"Password:","Contraseña:"}. {"Path to Dir","Ruta al directorio"}. {"Path to File","Ruta al fichero"}. +{"Payload semantic type information","Información sobre el tipo semántico de la carga útil"}. {"Pending","Pendiente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Persistir elementos al almacenar"}. @@ -496,6 +492,7 @@ {"Specify the access model","Especifica el modelo de acceso"}. {"Specify the event message type","Especifica el tipo del mensaje de evento"}. {"Specify the publisher model","Especificar el modelo del publicante"}. +{"Stanza id is not valid","El identificador de la estrofa no es válido"}. {"Stanza ID","ID del paquete"}. {"Statically specify a replyto of the node owner(s)","Especificar de forma estática un 'replyto' de dueño(s) del nodo"}. {"Statistics of ~p","Estadísticas de ~p"}. @@ -560,6 +557,7 @@ {"The query is only allowed from local users","La solicitud está permitida solo para usuarios locales"}. {"The query must not contain elements","La solicitud no debe contener elementos "}. {"The room subject can be modified by participants","El asunto de la sala puede ser modificado por los participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","La información semántica de los datos del nodo, normalmente es especificada por el espacio de los nombres de la carga útil (si existe)"}. {"The sender of the last received message","El emisor del último mensaje recibido"}. {"The stanza MUST contain only one element, one element, or one element","El paquete DEBE contener solo un elemento , un elemento , o un elemento "}. {"The subscription identifier associated with the subscription request","El identificador de suscripción asociado con la petición de suscripción"}. diff --git a/priv/msgs/fr.msg b/priv/msgs/fr.msg index aa8499c1b..1ebac0f14 100644 --- a/priv/msgs/fr.msg +++ b/priv/msgs/fr.msg @@ -12,11 +12,6 @@ {"A Web Page","Une page Web"}. {"Accept","Accepter"}. {"Access denied by service policy","L'accès au service est refusé"}. -{"Access model of authorize","Modèle d’accès de « autoriser »"}. -{"Access model of open","Modèle d’accès de « ouvrir »"}. -{"Access model of presence","Modèle d’accès de « présence »"}. -{"Access model of roster","Modèle d’accès de « liste »"}. -{"Access model of whitelist","Modèle d’accès de « liste blanche »"}. {"Access model","Modèle d’accès"}. {"Account doesn't exist","Le compte n’existe pas"}. {"Action on user","Action sur l'utilisateur"}. @@ -220,7 +215,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","L'envoyer de messages d'erreur au salon n'est pas autorisé. Le participant (~s) à envoyé un message d'erreur (~s) et à été expulsé du salon"}. {"It is not allowed to send private messages of type \"groupchat\"","Il n'est pas permis d'envoyer des messages privés de type \"groupchat\""}. {"It is not allowed to send private messages to the conference","Il n'est pas permis d'envoyer des messages privés à la conférence"}. -{"It is not allowed to send private messages","L'envoi de messages privés n'est pas autorisé"}. {"Jabber ID","Jabber ID"}. {"January","Janvier"}. {"joins the room","rejoint le salon"}. diff --git a/priv/msgs/gl.msg b/priv/msgs/gl.msg index 07b35e994..099936c33 100644 --- a/priv/msgs/gl.msg +++ b/priv/msgs/gl.msg @@ -159,7 +159,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Non está permitido enviar mensaxes de erro á sala. Este participante (~s) enviou unha mensaxe de erro (~s) e foi expulsado da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Non está permitido enviar mensaxes privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensaxes privadas á sala"}. -{"It is not allowed to send private messages","Non está permitido enviar mensaxes privadas"}. {"Jabber ID","Jabber ID"}. {"January","Xaneiro"}. {"joins the room","entra na sala"}. diff --git a/priv/msgs/he.msg b/priv/msgs/he.msg index 60c863a2f..83be202d5 100644 --- a/priv/msgs/he.msg +++ b/priv/msgs/he.msg @@ -148,7 +148,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","אין זה מותר לשלוח הודעות שגיאה לחדר. משתתף זה (~s) שלח הודעת שגיאה (~s) ונבעט מתוך החדר"}. {"It is not allowed to send private messages of type \"groupchat\"","אין זה מותר לשלוח הודעות פרטיות מן טיפוס \"groupchat\""}. {"It is not allowed to send private messages to the conference","אין זה מותר לשלוח הודעות פרטיות לועידה"}. -{"It is not allowed to send private messages","אין זה מותר לשלוח הודעות פרטיות"}. {"Jabber ID","מזהה Jabber"}. {"January","ינואר"}. {"joins the room","נכנס/ת אל החדר"}. diff --git a/priv/msgs/hu.msg b/priv/msgs/hu.msg index bf7782e49..ee9c3af0f 100644 --- a/priv/msgs/hu.msg +++ b/priv/msgs/hu.msg @@ -170,7 +170,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Nem engedélyezett hibaüzeneteket küldeni a szobába. A résztvevő (~s) hibaüzenetet (~s) küldött, és ki lett rúgva a szobából"}. {"It is not allowed to send private messages of type \"groupchat\"","Nem engedélyezett „groupchat” típusú személyes üzeneteket küldeni"}. {"It is not allowed to send private messages to the conference","Nem engedélyezett személyes üzeneteket küldeni a konferenciába"}. -{"It is not allowed to send private messages","Nem engedélyezett személyes üzeneteket küldeni"}. {"Jabber ID","Jabber-azonosító"}. {"January","január"}. {"JID normalization denied by service policy","A Jabber-azonosító normalizálása megtagadva a szolgáltatási irányelv miatt"}. diff --git a/priv/msgs/id.msg b/priv/msgs/id.msg index 1e6339279..29befa63d 100644 --- a/priv/msgs/id.msg +++ b/priv/msgs/id.msg @@ -12,11 +12,6 @@ {"A Web Page","Halaman web"}. {"Accept","Diterima"}. {"Access denied by service policy","Akses ditolak oleh kebijakan layanan"}. -{"Access model of authorize","Model akses otorisasi"}. -{"Access model of open","Model akses terbuka"}. -{"Access model of presence","Model akses kehadiran"}. -{"Access model of roster","model akses daftar kontak"}. -{"Access model of whitelist","Model akses daftar putih"}. {"Access model","Model akses"}. {"Account doesn't exist","Akun tidak ada"}. {"Action on user","Tindakan pada pengguna"}. @@ -207,7 +202,6 @@ {"is now known as","sekarang dikenal sebagai"}. {"It is not allowed to send private messages of type \"groupchat\"","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi jenis \"groupchat \""}. {"It is not allowed to send private messages to the conference","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi ke konferensi"}. -{"It is not allowed to send private messages","Hal ini tidak diperbolehkan untuk mengirim pesan pribadi"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"joins the room","bergabung ke ruangan"}. diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index 8f117c9f2..ccf6a0c2c 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -125,7 +125,6 @@ {"is now known as","è ora conosciuta/o come"}. {"It is not allowed to send private messages of type \"groupchat\"","Non è consentito l'invio di messaggi privati di tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Non è consentito l'invio di messaggi privati alla conferenza"}. -{"It is not allowed to send private messages","Non è consentito l'invio di messaggi privati"}. {"Jabber ID","Jabber ID (Jabber ID)"}. {"January","Gennaio"}. {"joins the room","entra nella stanza"}. diff --git a/priv/msgs/ja.msg b/priv/msgs/ja.msg index dbdb1ed8a..feda5992f 100644 --- a/priv/msgs/ja.msg +++ b/priv/msgs/ja.msg @@ -166,7 +166,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","このルームにエラーメッセージを送ることは許可されていません。参加者(~s)はエラーメッセージを(~s)を送信してルームからキックされました。"}. {"It is not allowed to send private messages of type \"groupchat\"","種別が\"groupchat\" であるプライベートメッセージを送信することはできません"}. {"It is not allowed to send private messages to the conference","この会議にプライベートメッセージを送信することはできません"}. -{"It is not allowed to send private messages","プライベートメッセージを送信することはできません"}. {"Jabber ID","Jabber ID"}. {"January","1月"}. {"joins the room","がチャットルームに参加しました"}. diff --git a/priv/msgs/nl.msg b/priv/msgs/nl.msg index 5a5010b89..d6092595e 100644 --- a/priv/msgs/nl.msg +++ b/priv/msgs/nl.msg @@ -129,7 +129,6 @@ {"is now known as","heet nu"}. {"It is not allowed to send private messages of type \"groupchat\"","Er mogen geen privéberichten van het type \"groupchat\" worden verzonden"}. {"It is not allowed to send private messages to the conference","Er mogen geen privéberichten naar de chatruimte worden verzonden"}. -{"It is not allowed to send private messages","Het is niet toegestaan priveberichten te sturen"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"joins the room","betrad de chatruimte"}. diff --git a/priv/msgs/no.msg b/priv/msgs/no.msg index e883518a4..98e48b6d6 100644 --- a/priv/msgs/no.msg +++ b/priv/msgs/no.msg @@ -8,8 +8,6 @@ {"A password is required to enter this room","Et passord kreves for tilgang til samtalerommet"}. {"Accept","Godta"}. {"Access denied by service policy","Tilgang nektes på grunn av en tjenesteregel"}. -{"Access model of presence","Tilgangsmodell for tilstedeværelse"}. -{"Access model of roster","Tilgangsmodell for kontaktliste"}. {"Action on user","Handling på bruker"}. {"Add Jabber ID","Legg til Jabber-ID"}. {"Add New","Legg til ny"}. @@ -141,7 +139,6 @@ {"IP addresses","IP-adresser"}. {"is now known as","er nå kjent som"}. {"It is not allowed to send private messages to the conference","Det er ikke tillatt å sende private meldinger til konferansen"}. -{"It is not allowed to send private messages","Det er ikke tillatt å sende private meldinger"}. {"Jabber ID","Jabber-ID"}. {"January","januar"}. {"JID normalization failed","JID-normalisering mislyktes"}. diff --git a/priv/msgs/pl.msg b/priv/msgs/pl.msg index 52cc374a1..1490cd22b 100644 --- a/priv/msgs/pl.msg +++ b/priv/msgs/pl.msg @@ -159,7 +159,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Użytkownik nie może wysyłać wiadomości o błędach do pokoju. Użytkownik (~s) wysłał błąd (~s) i został wyrzucony z pokoju"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie można wysyłać prywatnych wiadomości typu \"groupchat\""}. {"It is not allowed to send private messages to the conference","Nie wolno wysyłac prywatnych wiadomości na konferencję"}. -{"It is not allowed to send private messages","Wysyłanie prywatnych wiadomości jest zabronione"}. {"Jabber ID","Jabber ID"}. {"January","Styczeń"}. {"joins the room","dołącza do pokoju"}. diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index b55d78a0b..474f8e29f 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -12,11 +12,6 @@ {"A Web Page","Uma página da web"}. {"Accept","Aceito"}. {"Access denied by service policy","Acesso negado pela política de serviço"}. -{"Access model of authorize","Modelo de acesso da autorização"}. -{"Access model of open","Modelo para acesso aberto"}. -{"Access model of presence","Modelo para acesso presença"}. -{"Access model of roster","Modelo para acesso lista"}. -{"Access model of whitelist","Modelo de acesso da lista branca"}. {"Access model","Modelo de acesso"}. {"Account doesn't exist","A conta não existe"}. {"Action on user","Acção no utilizador"}. @@ -230,7 +225,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Não é permitido o envio de mensagens de erro para a sala. O membro (~s) enviou uma mensagem de erro (~s) e foi expulso da sala"}. {"It is not allowed to send private messages of type \"groupchat\"","Não é permitido enviar mensagens privadas do tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Impedir o envio de mensagens privadas para a sala"}. -{"It is not allowed to send private messages","Não é permitido enviar mensagens privadas"}. {"Jabber ID","ID Jabber"}. {"January","Janeiro"}. {"JID normalization denied by service policy","Normalização JID negada por causa da política de serviços"}. diff --git a/priv/msgs/ru.msg b/priv/msgs/ru.msg index 962c83ae8..d2e5e023c 100644 --- a/priv/msgs/ru.msg +++ b/priv/msgs/ru.msg @@ -180,7 +180,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Запрещено посылать сообщения об ошибках в эту комнату. Участник (~s) послал сообщение об ошибке (~s) и был выкинут из комнаты"}. {"It is not allowed to send private messages of type \"groupchat\"","Нельзя посылать частные сообщения типа \"groupchat\""}. {"It is not allowed to send private messages to the conference","Не разрешается посылать частные сообщения прямо в конференцию"}. -{"It is not allowed to send private messages","Запрещено посылать приватные сообщения"}. {"Jabber ID","Jabber ID"}. {"January","января"}. {"joins the room","вошёл(а) в комнату"}. diff --git a/priv/msgs/sk.msg b/priv/msgs/sk.msg index aaee00ef1..fedce900f 100644 --- a/priv/msgs/sk.msg +++ b/priv/msgs/sk.msg @@ -124,7 +124,6 @@ {"is now known as","sa premenoval(a) na"}. {"It is not allowed to send private messages of type \"groupchat\"","Nie je dovolené odoslanie súkromnej správy typu \"Skupinová správa\" "}. {"It is not allowed to send private messages to the conference","Nie je povolené odosielať súkromné správy do konferencie"}. -{"It is not allowed to send private messages","Nieje povolené posielať súkromné správy"}. {"Jabber ID","Jabber ID"}. {"January","Január"}. {"joins the room","vstúpil(a) do miestnosti"}. diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index c5efe2023..ba9f6cdfc 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -142,7 +142,6 @@ {"IP addresses","Adresa IP"}. {"is now known as","tani njihet si"}. {"It is not allowed to send private messages to the conference","Nuk lejohet të dërgohen mesazhe private te konferenca"}. -{"It is not allowed to send private messages","Nuk lejohet të dërgohen mesazhe private"}. {"Jabber ID","ID Jabber"}. {"January","Janar"}. {"JID normalization failed","Normalizimi JID dështoi"}. diff --git a/priv/msgs/sv.msg b/priv/msgs/sv.msg index afd38e2f6..08f560a3c 100644 --- a/priv/msgs/sv.msg +++ b/priv/msgs/sv.msg @@ -110,7 +110,6 @@ {"is now known as","är känd som"}. {"It is not allowed to send private messages of type \"groupchat\"","Det är inte tillåtet att skicka privata medelanden med typen \"groupchat\""}. {"It is not allowed to send private messages to the conference","Det är inte tillåtet att skicka privata medelanden till den här konferensen"}. -{"It is not allowed to send private messages","Det ar inte tillåtet att skicka privata meddelanden"}. {"Jabber ID","Jabber ID"}. {"January","Januari"}. {"joins the room","joinar rummet"}. diff --git a/priv/msgs/tr.msg b/priv/msgs/tr.msg index 8acdf3491..9b3cbd2c2 100644 --- a/priv/msgs/tr.msg +++ b/priv/msgs/tr.msg @@ -123,7 +123,6 @@ {"is now known as","isim değiştirdi :"}. {"It is not allowed to send private messages of type \"groupchat\"","\"groupchat\" tipinde özel mesajlar gönderilmesine izin verilmiyor"}. {"It is not allowed to send private messages to the conference","Konferansa özel mesajlar gönderilmesine izin verilmiyor"}. -{"It is not allowed to send private messages","Özel mesaj gönderilmesine izin verilmiyor"}. {"Jabber ID","Jabber ID"}. {"January","Ocak"}. {"joins the room","odaya katıldı"}. diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index e2b99949f..7cfbc5688 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -207,7 +207,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Не дозволяється відправляти помилкові повідомлення в кімнату. Учасник (~s) відправив помилкове повідомлення (~s), та був виганий з кімнати"}. {"It is not allowed to send private messages of type \"groupchat\"","Не дозволяється надсилати приватні повідомлення типу \"groupchat\""}. {"It is not allowed to send private messages to the conference","Не дозволяється надсилати приватні повідомлення в конференцію"}. -{"It is not allowed to send private messages","Приватні повідомлення не дозволені"}. {"Jabber ID","Jabber ID"}. {"January","січня"}. {"JID normalization failed","Помилка нормалізації JID"}. diff --git a/priv/msgs/wa.msg b/priv/msgs/wa.msg index f6b618465..ef375745c 100644 --- a/priv/msgs/wa.msg +++ b/priv/msgs/wa.msg @@ -132,7 +132,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","On n' pout nén evoyî des messaedjes d' aroke sol såle. Li pårticipan (~s) a-st evoyî on messaedje d' aroke (~s) ey a stî tapé foû."}. {"It is not allowed to send private messages of type \"groupchat\"","C' est nén possibe d' evoyî des messaedjes privés del sôre «groupchat»"}. {"It is not allowed to send private messages to the conference","On n' pout nén evoyî des messaedjes privés dins cisse conferince ci"}. -{"It is not allowed to send private messages","Ci n' est nén permetou d' evoyî des messaedjes privés"}. {"Jabber ID","ID Jabber"}. {"January","djanvî"}. {"joins the room","arive sol såle"}. diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 0320af5ac..bd295bcc6 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -12,11 +12,6 @@ {"A Web Page","网页"}. {"Accept","接受"}. {"Access denied by service policy","访问被服务策略拒绝"}. -{"Access model of authorize","授权的访问模型"}. -{"Access model of open","开启的访问模型"}. -{"Access model of presence","状态的访问模型"}. -{"Access model of roster","花名册的访问模型"}. -{"Access model of whitelist","白名单的访问模型"}. {"Access model","访问模型"}. {"Account doesn't exist","账号不存在"}. {"Action on user","对用户的动作"}. @@ -230,7 +225,6 @@ {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间"}. {"It is not allowed to send private messages of type \"groupchat\"","\"群组聊天\"类型不允许发送私聊消息"}. {"It is not allowed to send private messages to the conference","不允许向会议发送私聊消息"}. -{"It is not allowed to send private messages","不可以发送私聊消息"}. {"Jabber ID","Jabber ID"}. {"January","一月"}. {"JID normalization denied by service policy","JID规范化被服务策略拒绝"}. From 12d47455bac10b57cff44d1af37744d4ffaf0feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 11 Oct 2023 14:16:51 +0200 Subject: [PATCH 0239/1302] Add `auth_external_user_exists_check` option This makes `user_check` hook work better with authentication methods that don't have a way to determine if user exists (like is the case for jwt and cert based authentication), and as result will improve mod_offline and mod_mam handling of offline messages to those users. This reuses information stored by `mod_last` for this purpose. Should fix issue #3377. --- src/ejabberd_auth.erl | 54 +++++++++++++++++++++++------------- src/ejabberd_option.erl | 8 ++++++ src/ejabberd_options.erl | 3 ++ src/ejabberd_options_doc.erl | 7 +++++ 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 027983e61..321527eda 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -398,15 +398,29 @@ user_exists(_User, <<"">>) -> user_exists(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> - lists:any( - fun(M) -> - case db_user_exists(LUser, LServer, M) of - {error, _} -> - false; - Else -> - Else - end - end, auth_modules(LServer)); + {Exists, PerformExternalUserCheck} = + lists:foldl( + fun(M, {Exists0, PerformExternalUserCheck0}) -> + case db_user_exists(LUser, LServer, M) of + {{error, _}, Check} -> + {Exists0, PerformExternalUserCheck0 orelse Check}; + {Else, Check2} -> + {Exists0 orelse Else, PerformExternalUserCheck0 orelse Check2} + end + end, {false, false}, auth_modules(LServer)), + case (not Exists) andalso PerformExternalUserCheck andalso + ejabberd_option:auth_external_user_exists_check(Server) andalso + gen_mod:is_loaded(Server, mod_last) of + true -> + case mod_last:get_last_info(User, Server) of + not_found -> + false; + _ -> + true + end; + _ -> + Exists + end; _ -> false end. @@ -420,11 +434,11 @@ user_exists_in_other_modules_loop([], _User, _Server) -> false; user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> case db_user_exists(User, Server, AuthModule) of - true -> + {true, _} -> true; - false -> + {false, _} -> user_exists_in_other_modules_loop(AuthModules, User, Server); - {error, _} -> + {{error, _}, _} -> maybe end. @@ -628,9 +642,9 @@ db_get_password(User, Server, Mod) -> db_user_exists(User, Server, Mod) -> case db_get_password(User, Server, Mod) of {ok, _} -> - true; + {true, false}; not_found -> - false; + {false, false}; error -> case {Mod:store_type(Server), use_cache(Mod, Server)} of {external, true} -> @@ -649,18 +663,18 @@ db_user_exists(User, Server, Mod) -> end, case Val of {ok, _} -> - true; + {true, Mod /= ejabberd_auth_anonymous} ; not_found -> - false; + {false, Mod /= ejabberd_auth_anonymous}; error -> - false; + {false, Mod /= ejabberd_auth_anonymous}; {error, _} = Err -> - Err + {Err, Mod /= ejabberd_auth_anonymous} end; {external, false} -> - ets_cache:untag(Mod:user_exists(User, Server)); + {ets_cache:untag(Mod:user_exists(User, Server)), Mod /= ejabberd_auth_anonymous}; _ -> - false + {false, false} end end. diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 6ea63e561..173e412af 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -14,6 +14,7 @@ -export([auth_cache_life_time/0]). -export([auth_cache_missed/0]). -export([auth_cache_size/0]). +-export([auth_external_user_exists_check/0, auth_external_user_exists_check/1]). -export([auth_method/0, auth_method/1]). -export([auth_opts/0, auth_opts/1]). -export([auth_password_format/0, auth_password_format/1]). @@ -224,6 +225,13 @@ auth_cache_missed() -> auth_cache_size() -> ejabberd_config:get_option({auth_cache_size, global}). +-spec auth_external_user_exists_check() -> boolean(). +auth_external_user_exists_check() -> + auth_external_user_exists_check(global). +-spec auth_external_user_exists_check(global | binary()) -> boolean(). +auth_external_user_exists_check(Host) -> + ejabberd_config:get_option({auth_external_user_exists_check, Host}). + -spec auth_method() -> [atom()]. auth_method() -> auth_method(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 9f48839bb..95b3d92c3 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -81,6 +81,8 @@ opt_type(auth_password_format) -> econf:enum([plain, scram]); opt_type(auth_scram_hash) -> econf:enum([sha, sha256, sha512]); +opt_type(auth_external_user_exists_check) -> + econf:bool(); opt_type(auth_use_cache) -> econf:bool(); opt_type(c2s_cafile) -> @@ -542,6 +544,7 @@ options() -> {auth_opts, []}, {auth_password_format, plain}, {auth_scram_hash, sha}, + {auth_external_user_exists_check, true}, {auth_use_cache, fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, {c2s_cafile, undefined}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 9d80e721b..5d6e20db5 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -395,6 +395,13 @@ doc() -> "You shouldn't change this if you already have passwords generated with " "a different algorithm - users that have such passwords will not be able " "to authenticate. The default value is 'sha'.")}}, + {auth_external_user_exists_check, + #{value => "true | false", + desc => + ?T("Supplement check for user existence based on 'mod_last' data, for authentication " + "methods that don't have a way to reliable tell if user exists (like is the case for " + "'jwt' and certificate based authentication). This helps with processing offline message " + "for those users. The default value is 'true'.")}}, {auth_use_cache, #{value => "true | false", desc => From f75909db4cd6e831997bdf8afbf6b504492772a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 12 Oct 2023 13:16:13 +0200 Subject: [PATCH 0240/1302] Allow pubsub node owner to overwrite items published by other persons Owner is already permitted to delete those items, so it could do that by deleting old item, and publishing it again, so i don't see reason to not allow that overwrite. --- src/node_flat.erl | 7 +++++++ src/node_flat_sql.erl | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/node_flat.erl b/src/node_flat.erl index c64adb3ef..22fe3dd32 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -385,6 +385,13 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, []}}; + % Allow node owner to modify any item, he can also delete it and recreate + {result, #pubsub_item{creation = {CreationTime, _}} = OldItem} when Affiliation == owner-> + set_item(OldItem#pubsub_item{ + creation = {CreationTime, GenKey}, + modification = {Now, SubKey}, + payload = Payload}), + {result, {default, broadcast, []}}; {result, _} -> {error, xmpp:err_forbidden()}; _ -> diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 9a98fed65..24f6c75f4 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -257,6 +257,13 @@ publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, modification = {Now, SubKey}, payload = Payload}), {result, {default, broadcast, []}}; + % Allow node owner to modify any item, he can also delete it and recreate + {result, #pubsub_item{creation = {CreationTime, _}} = OldItem} when Affiliation == owner-> + set_item(OldItem#pubsub_item{ + creation = {CreationTime, GenKey}, + modification = {Now, SubKey}, + payload = Payload}), + {result, {default, broadcast, []}}; {result, _} -> {error, xmpp:err_forbidden()}; _ -> From 80d1e36542e3437679f68a279309ce4200589be4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 2 Oct 2023 12:06:21 +0200 Subject: [PATCH 0241/1302] mod_private: Document that it supports XEP-0402 now --- src/mod_private.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 701491eea..1342964ef 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -124,7 +124,11 @@ mod_doc() -> "might be anything, as long as it is a valid XML. " "One typical usage is storing a bookmark of all user's conferences " "(https://xmpp.org/extensions/xep-0048.html" - "[XEP-0048: Bookmarks]).")], + "[XEP-0048: Bookmarks])."), "", + ?T("It also implements the bookmark conversion described in " + "https://xmpp.org/extensions/xep-0402.html[XEP-0402: PEP Native Bookmarks]" + ", see the command " + "https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#bookmarks-to-pep[bookmarks_to_pep].")], opts => [{db_type, #{value => "mnesia | sql", From 6340d6139712edb22f575f26784bb15a1495a62e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 2 Oct 2023 10:54:24 +0200 Subject: [PATCH 0242/1302] Dcoument ejabberd version number in the new options --- src/ejabberd_admin.erl | 1 + src/ejabberd_options_doc.erl | 2 ++ src/mod_admin_extra.erl | 1 + src/mod_muc.erl | 2 +- src/mod_muc_occupantid.erl | 4 ++-- src/mod_private.erl | 2 +- src/mod_push.erl | 1 + 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 6ead50ff9..a000beb54 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -118,6 +118,7 @@ get_commands_spec() -> args = [], result = {res, rescode}}, #ejabberd_commands{name = halt, tags = [server], desc = "Halt ejabberd abruptly with status code 1", + note = "added in 23.10", module = ejabberd, function = halt, args = [], result = {res, rescode}}, #ejabberd_commands{name = restart, tags = [server], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 5d6e20db5..a4938528b 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -397,6 +397,7 @@ doc() -> "to authenticate. The default value is 'sha'.")}}, {auth_external_user_exists_check, #{value => "true | false", + note => "added in 23.10", desc => ?T("Supplement check for user existence based on 'mod_last' data, for authentication " "methods that don't have a way to reliable tell if user exists (like is the case for " @@ -681,6 +682,7 @@ doc() -> "all options.")}}]}, {install_contrib_modules, #{value => "[Module, ...]", + note => "added in 23.10", desc => ?T("Modules to install from " "https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib" diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 04559e8d0..fa292ae38 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -578,6 +578,7 @@ get_commands_spec() -> longdesc = "Subscription can be: \"none\", \"from\", \"to\", \"both\". " "Pending can be: \"in\", \"out\", \"none\".", + note = "improved in 23.10", policy = user, module = ?MODULE, function = get_roster, args = [], diff --git a/src/mod_muc.erl b/src/mod_muc.erl index aadd77a35..0ea78870b 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1471,7 +1471,7 @@ mod_doc() -> "modify that option.")}}, {access_register, #{value => ?T("AccessName"), - note => "improved in 23.xx", + note => "improved in 23.10", desc => ?T("This option specifies who is allowed to register nickname " "within the Multi-User Chat service and rooms. The default is 'all' for " diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 647cb61a3..37ebfa574 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -27,7 +27,7 @@ -author('badlop@process-one.net'). --protocol({xep, 421, '0.1.0'}). +-protocol({xep, 421, '0.1.0', '23.10', "", ""}). -behaviour(gen_mod). @@ -121,7 +121,7 @@ mod_doc() -> "[XEP-0421: Anonymous unique occupant identifiers for MUCs]."), "", ?T("When the module is enabled, the feature is enabled " "in all semi-anonymous rooms."), "", - ?T("This module is available since ejabberd 23.xx.")] + ?T("This module is available since ejabberd 23.10.")] }. depends(_, _) -> diff --git a/src/mod_private.erl b/src/mod_private.erl index 1342964ef..53393832e 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -29,7 +29,7 @@ -protocol({xep, 49, '1.2'}). -protocol({xep, 411, '0.2.0', '18.12', "", ""}). --protocol({xep, 402, '1.1.3', '23.09', "", ""}). +-protocol({xep, 402, '1.1.3', '23.10', "", ""}). -behaviour(gen_mod). diff --git a/src/mod_push.erl b/src/mod_push.erl index 8a6901efb..d6cbd66d3 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -180,6 +180,7 @@ mod_doc() -> opts => [{notify_on, #{value => "messages | all", + note => "added in 23.10", desc => ?T("If this option is set to 'messages', notifications are " "generated only for actual chat messages with a body text " From 87f18aa8d731d812087b6a26a763e4b9a3cee3a8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Oct 2023 11:07:24 +0200 Subject: [PATCH 0243/1302] Result of running: make doap options --- ejabberd.doap | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ejabberd.doap b/ejabberd.doap index 47560781d..bbdb311c1 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -638,6 +638,15 @@ mod_avatar, mod_vcard_xupdate + + + + 1.1.3 + 23.10 + + mod_private + + @@ -669,7 +678,7 @@ 0.1.0 - + 23.10 mod_muc_occupantid From ca823766576edfc3363c089e218baf1ce604a293 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Oct 2023 11:13:16 +0200 Subject: [PATCH 0244/1302] Update man page --- man/ejabberd.yml.5 | 79 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 017d9e960..64d22c8d5 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/17/2023 +.\" Date: 10/16/2023 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "04/17/2023" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "10/16/2023" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,7 +82,7 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/23\&.04/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/23\&.10/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" @@ -303,7 +303,9 @@ acme: .PP \fBallow_contrib_modules\fR: \fItrue | false\fR .RS 4 -Whether to allow installation of third\-party modules or not\&. The default value is +Whether to allow installation of third\-party modules or not\&. See +ejabberd\-contrib +documentation section\&. The default value is \fItrue\fR\&. .RE .PP @@ -396,6 +398,18 @@ Same as \fIcache_size\fR will be used\&. .RE +.sp +\fINote\fR about the next option: added in 23\&.10: +.PP +\fBauth_external_user_exists_check\fR: \fItrue | false\fR +.RS 4 +Supplement check for user existence based on +\fImod_last\fR +data, for authentication methods that don\(cqt have a way to reliable tell if user exists (like is the case for +\fIjwt\fR +and certificate based authentication)\&. This helps with processing offline message for those users\&. The default value is +\fItrue\fR\&. +.RE .PP \fBauth_method\fR: \fI[mnesia | sql | anonymous | external | jwt | ldap | pam, \&.\&.\&.]\fR .RS 4 @@ -881,6 +895,16 @@ Disallows the usage of those options in the included file \fIFilename\fR\&. The options that match this criteria are not accepted\&. The default value is an empty list\&. .RE .RE +.sp +\fINote\fR about the next option: added in 23\&.10: +.PP +\fBinstall_contrib_modules\fR: \fI[Module, \&.\&.\&.]\fR +.RS 4 +Modules to install from +ejabberd\-contrib +at start time\&. The default value is an empty list of modules: +\fI[]\fR\&. +.RE .PP \fBjwt_auth_only_rule\fR: \fIAccessName\fR .RS 4 @@ -1115,7 +1139,7 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/23\&.04/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/23\&.10/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The \fInew\fR schema allows to handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -3930,6 +3954,8 @@ This module provides support for XEP\-0045: Multi\-User Chat\&. Users can discov .sp The MUC service allows any Jabber ID to register a nickname, so nobody else can use that nickname in any room in the MUC service\&. To register a nickname, open the Service Discovery in your XMPP client and register in the MUC service\&. .sp +It is also possible to register a nickname in a room, so nobody else can use that nickname in that room\&. If a nick is registered in the MUC service, that nick cannot be registered in any room, and vice versa: a nick that is registered in a room cannot be registered at the MUC service\&. +.sp This module supports clustering and load balancing\&. One module can be started per cluster node\&. Rooms are distributed at creation time on all available MUC module instances\&. The multi\-user chat module is clustered but the rooms themselves are not clustered nor fault\-tolerant: if the node managing a set of rooms goes down, the rooms disappear and they will be recreated on an available node on first connection attempt\&. .sp .it 1 an-trap @@ -3972,12 +3998,14 @@ To configure who is allowed to modify the room option\&. The default value is \fIall\fR, which means everyone is allowed to modify that option\&. .RE +.sp +\fINote\fR about the next option: improved in 23\&.10: .PP \fBaccess_register\fR: \fIAccessName\fR .RS 4 -This option specifies who is allowed to register nickname within the Multi\-User Chat service\&. The default is +This option specifies who is allowed to register nickname within the Multi\-User Chat service and rooms\&. The default is \fIall\fR -for backward compatibility, which means that any user is allowed to register any free nick\&. +for backward compatibility, which means that any user is allowed to register any free nick in the MUC service and in the rooms\&. .RE .sp \fINote\fR about the next option: added in 22\&.05: @@ -4009,12 +4037,6 @@ Allow occupants to change the subject\&. The default value is \fItrue\fR\&. .RE .PP -\fBallow_private_messages\fR: \fItrue | false\fR -.RS 4 -Occupants can send private messages to other occupants\&. The default value is -\fItrue\fR\&. -.RE -.PP \fBallow_private_messages_from_visitors\fR: \fIanyone | moderators | nobody\fR .RS 4 Visitors can send private messages to other occupants\&. The default value is @@ -4059,6 +4081,12 @@ Allow visitors in a moderated room to request voice\&. The default value is \fItrue\fR\&. .RE .PP +\fBallowpm\fR: \fIanyone | participants | moderators | none\fR +.RS 4 +Who can send private messages\&. The default value is +\fIanyone\fR\&. +.RE +.PP \fBanonymous\fR: \fItrue | false\fR .RS 4 The room is anonymous: occupants don\(cqt see the real JIDs of other occupants\&. Note that the room moderators can always see the real JIDs of the occupants\&. The default value is @@ -4675,6 +4703,15 @@ or a conference JID is appended to the otherwise\&. There is no default value\&. .RE .RE +.SS "mod_muc_occupantid" +.sp +This module implements XEP\-0421: Anonymous unique occupant identifiers for MUCs\&. +.sp +When the module is enabled, the feature is enabled in all semi\-anonymous rooms\&. +.sp +This module is available since ejabberd 23\&.10\&. +.sp +The module has no options\&. .SS "mod_muc_rtbl" .sp This module implement Real\-time blocklists for MUC rooms\&. @@ -5197,6 +5234,8 @@ This module adds support for XEP\-0049: Private XML Storage\&. .sp Using this method, XMPP entities can store private data on the server, retrieve it whenever necessary and share it between multiple connected clients of the same user\&. The data stored might be anything, as long as it is a valid XML\&. One typical usage is storing a bookmark of all user\(cqs conferences (XEP\-0048: Bookmarks)\&. .sp +It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see the command bookmarks_to_pep\&. +.sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 @@ -5938,6 +5977,16 @@ If this option is set to \fItrue\fR, the sender\(cqs JID is included with push notifications generated for incoming messages with a body\&. The default value is \fIfalse\fR\&. .RE +.sp +\fINote\fR about the next option: added in 23\&.10: +.PP +\fBnotify_on\fR: \fImessages | all\fR +.RS 4 +If this option is set to +\fImessages\fR, notifications are generated only for actual chat messages with a body text (or some encrypted payload)\&. If it\(cqs set to +\fIall\fR, any kind of XMPP stanza will trigger a notification\&. If unsure, it\(cqs strongly recommended to stick to +\fIall\fR, which is the default value\&. +.RE .PP \fBuse_cache\fR: \fItrue | false\fR .RS 4 @@ -7748,13 +7797,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 23\&.04\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 23\&.10\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/23\&.04/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/23\&.10/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From ab6da9530d13283ec13974f3ff7313cbb8c6829a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Oct 2023 11:28:29 +0200 Subject: [PATCH 0245/1302] =?UTF-8?q?Update=20Ukrainian=20translation=20(t?= =?UTF-8?q?hanks=20to=20=D0=9E=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=20=D0=9A=D1=80=D0=B5=D0=B2=D1=81=D1=8C=D0=BA=D0=B8=D0=B9?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/uk.msg | 70 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 7cfbc5688..ea241c09a 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -34,75 +34,81 @@ {"Allow visitors to send private messages to","Дозволити відвідувачам відсилати приватні повідомлення"}. {"Allow visitors to send status text in presence updates","Дозволити відвідувачам відсилати текст статусу в оновленнях присутності"}. {"Allow visitors to send voice requests","Дозволити відвідувачам надсилати голосові запрошення"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Асоційована група LDAP, яка визначає членство в кімнаті; це повинно бути відмінне ім'я LDAP відповідно до специфічного для реалізації або специфічного для розгортання визначення групи."}. {"Announcements","Сповіщення"}. {"Answer associated with a picture","Відповідь, пов’язана зі зображенням"}. {"Answer associated with a video","Відповідь, пов'язана з відео"}. {"Answer associated with speech","Відповідь, пов'язана з мовленням"}. {"Answer to a question","Відповідь на запитання"}. -{"Anyone in the specified roster group(s) may subscribe and retrieve items","Будь-хто в зазначеному списку груп(и) може підписатися та отримати елементи"}. -{"Anyone may associate leaf nodes with the collection","Будь-хто може зв'язати вузли листів з колекцією"}. -{"Anyone may publish","Будь-хто може опублікувати"}. -{"Anyone may subscribe and retrieve items","Будь-хто може підписатися та отримати елементи"}. -{"Anyone with Voice","Усі, хто має голос"}. -{"April","квітня"}. -{"Attribute 'channel' is required for this request","Для цього запиту потрібен атрибут \"канал\""}. -{"Attribute 'id' is mandatory for MIX messages","Для MIX повідомлень потрібен атрибут \"id\""}. -{"Attribute 'jid' is not allowed here","Атрибут 'jid' тут заборонений"}. -{"Attribute 'node' is not allowed here","Атрибут \"вузол\" тут заборонений"}. -{"August","серпня"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Будь-хто в зазначеній групі (групах) реєстру може підписатися та отримувати матеріали"}. +{"Anyone may associate leaf nodes with the collection","Будь-хто може асоціювати листові вузли з колекцією"}. +{"Anyone may publish","Будь-хто може публікувати"}. +{"Anyone may subscribe and retrieve items","Будь-хто може підписатися та отримувати публікації"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Будь-хто, хто має підписку на отримання інформації про присутність в обох випадках або може підписуватись та отримувати матеріали"}. +{"Anyone with Voice","Будь-хто, хто має голос"}. +{"Anyone","Будь-хто"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Очевидно, ваш обліковий запис не має прав адміністратора на цьому сервері. Будь ласка, перевірте, як отримати права адміністратора за посиланням: https://docs.ejabberd.im/admin/installation/#administration-account"}. +{"April","Квітень"}. +{"Attribute 'channel' is required for this request","Атрибут \"канал\" є обов'язковим для цього запиту"}. +{"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' обов'язковий для MIX повідомлень"}. +{"Attribute 'jid' is not allowed here","Атрибут 'jid' заборонений"}. +{"Attribute 'node' is not allowed here","Атрибут \"вузол\" заборонений"}. +{"Attribute 'to' of stanza that triggered challenge","Атрибут \"до\" рядка, який спровокував виклик"}. +{"August","Серпень"}. {"Automatic node creation is not enabled","Автоматичне створення вузлів не ввімкнено"}. {"Backup Management","Керування резервним копіюванням"}. {"Backup of ~p","Резервне копіювання ~p"}. {"Backup to File at ","Резервне копіювання в файл на "}. {"Backup","Резервне копіювання"}. -{"Bad format","Неправильний формат"}. +{"Bad format","Невірний формат"}. {"Birthday","День народження"}. -{"Both the username and the resource are required","Потрібне ім'я користувача та ресурс"}. +{"Both the username and the resource are required","Обов'язково потрібне ім'я користувача та джерело"}. {"Bytestream already activated","Потік байтів вже активовано"}. {"Cannot remove active list","Неможливо видалити активний список"}. -{"Cannot remove default list","Неможливо видалити список за промовчанням"}. -{"CAPTCHA web page","Адреса капчі"}. +{"Cannot remove default list","Неможливо видалити список за замовчуванням"}. +{"CAPTCHA web page","Веб-сторінка CAPTCHA"}. {"Challenge ID","ID виклику"}. {"Change Password","Змінити пароль"}. -{"Change User Password","Змінити Пароль Користувача"}. +{"Change User Password","Змінити пароль користувача"}. {"Changing password is not allowed","Зміна пароля заборонена"}. {"Changing role/affiliation is not allowed","Зміна ролі/рангу заборонена"}. -{"Channel already exists","Канал уже існує"}. -{"Channel does not exist","Канал не існує"}. +{"Channel already exists","Канал вже існує"}. +{"Channel does not exist","Каналу не існує"}. +{"Channel JID","Канали JID"}. {"Channels","Канали"}. {"Characters not allowed:","Заборонені символи:"}. -{"Chatroom configuration modified","Конфігурація кімнати змінилась"}. -{"Chatroom is created","Створено кімнату"}. -{"Chatroom is destroyed","Знищено кімнату"}. -{"Chatroom is started","Запущено кімнату"}. -{"Chatroom is stopped","Зупинено кімнату"}. +{"Chatroom configuration modified","Конфігурацію кімнати змінено"}. +{"Chatroom is created","Кімнату створено"}. +{"Chatroom is destroyed","Кімнату видалено"}. +{"Chatroom is started","Кімнату запущено"}. +{"Chatroom is stopped","Кімнату зупинено"}. {"Chatrooms","Кімнати"}. -{"Choose a username and password to register with this server","Виберіть назву користувача та пароль для реєстрації на цьому сервері"}. +{"Choose a username and password to register with this server","Виберіть логін і пароль для реєстрації на цьому сервері"}. {"Choose storage type of tables","Оберіть тип збереження таблиць"}. -{"Choose whether to approve this entity's subscription.","Вирішіть, чи задовольнити запит цього об'єкту на підписку."}. +{"Choose whether to approve this entity's subscription.","Виберіть, чи підтверджувати підписку."}. {"City","Місто"}. {"Client acknowledged more stanzas than sent by server","Клієнт підтвердив більше повідомлень, ніж було відправлено сервером"}. {"Commands","Команди"}. -{"Conference room does not exist","Конференція не існує"}. +{"Conference room does not exist","Кімната для переговорів відсутня"}. {"Configuration of room ~s","Конфігурація кімнати ~s"}. {"Configuration","Конфігурація"}. {"Connected Resources:","Підключені ресурси:"}. {"Contact Addresses (normally, room owner or owners)","Контактні адреси (зазвичай, власника або власників кімнати)"}. {"Country","Країна"}. -{"CPU Time:","Процесорний час:"}. +{"CPU Time:","Час роботи процесора:"}. {"Current Discussion Topic","Поточна тема обговорення"}. -{"Database failure","Помилка база даних"}. +{"Database failure","Збій бази даних"}. {"Database Tables at ~p","Таблиці бази даних на ~p"}. {"Database Tables Configuration at ","Конфігурація таблиць бази даних на "}. {"Database","База даних"}. -{"December","грудня"}. -{"Default users as participants","Зробити користувачів учасниками за замовчуванням"}. +{"December","Грудень"}. +{"Default users as participants","Користувачі за замовчуванням як учасники"}. {"Delete content","Видалити вміст"}. {"Delete message of the day on all hosts","Видалити повідомлення дня на усіх хостах"}. {"Delete message of the day","Видалити повідомлення дня"}. -{"Delete Selected","Видалити виділені"}. +{"Delete Selected","Видалити вибране"}. {"Delete table","Видалити таблицю"}. -{"Delete User","Видалити Користувача"}. +{"Delete User","Видалити користувача"}. {"Deliver event notifications","Доставляти сповіщення про події"}. {"Deliver payloads with event notifications","Доставляти разом з повідомленнями про публікації самі публікації"}. {"Description:","Опис:"}. From d5de93b8fac8c65f7ff9953c3f6408278de45fe6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Oct 2023 11:28:51 +0200 Subject: [PATCH 0246/1302] Update other translations --- priv/msgs/ca.msg | 3 +++ priv/msgs/es.msg | 3 +++ priv/msgs/pt-br.msg | 3 +++ 3 files changed, 9 insertions(+) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 5650bb966..da668af9b 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -48,6 +48,7 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualsevol amb una subscripció de presencia de 'both' o 'from' pot subscriure's i publicar elements"}. {"Anyone with Voice","Qualsevol amb Veu"}. {"Anyone","Qualsevol"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentment el teu compte no te privilegis d'administrador en este servidor. Per favor consulta com obtindre privilegis d'administrador en: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","L'atribut 'channel' és necessari per a aquesta petició"}. {"Attribute 'id' is mandatory for MIX messages","L'atribut 'id' es necessari per a missatges MIX"}. @@ -659,6 +660,7 @@ {"Whether to allow subscriptions","Permetre subscripcions"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Si fer totes les subscripcions temporals, basat en la presencia del subscriptor"}. {"Whether to notify owners about new subscribers and unsubscribes","Si notificar als propietaris sobre noves subscripcions i desubscripcions"}. +{"Who can send private messages","Qui pot enviar missatges privats"}. {"Who may associate leaf nodes with a collection","Qui pot associar nodes fulla amb una col·lecció"}. {"Wrong parameters in the web formulary","Paràmetres incorrectes en el formulari web"}. {"Wrong xmlns","El xmlns ès incorrecte"}. @@ -670,6 +672,7 @@ {"XMPP Show Value of XA (Extended Away)","Valor 'show' de XMPP: XA (Molt Ausent)"}. {"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Node Associat Publish-Subscribe"}. {"You are being removed from the room because of a system shutdown","Has sigut expulsat de la sala perquè el sistema va a apagar-se"}. +{"You are not allowed to send private messages","No tens permés enviar missatges privats"}. {"You are not joined to the channel","No t'has unit al canal"}. {"You can later change your password using an XMPP client.","Podràs canviar la teva contrasenya més endavant utilitzant un client XMPP."}. {"You have been banned from this room","Has sigut bloquejat en aquesta sala"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index bb16ebcf4..af0f0c283 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -48,6 +48,7 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Cualquiera con una suscripción a la presencia de 'ambos' o 'de' puede suscribirse y recibir elementos"}. {"Anyone with Voice","Cualquiera con Voz"}. {"Anyone","Cualquiera"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente tu cuenta no tiene permisos de administración en este servidor. Por favor consulta cómo concederle privilegios de administrador en: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","El atributo 'channel' es necesario para esta petición"}. {"Attribute 'id' is mandatory for MIX messages","El atributo 'id' es necesario para mensajes MIX"}. @@ -659,6 +660,7 @@ {"Whether to allow subscriptions","Permitir subscripciones"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Si hacer que todas las suscripciones sean temporales, basado en la presencia del suscriptor"}. {"Whether to notify owners about new subscribers and unsubscribes","Si notificar a los dueños sobre nuevas suscripciones y desuscripciones"}. +{"Who can send private messages","Quién puede enviar mensajes privados"}. {"Who may associate leaf nodes with a collection","Quien puede asociar nodos hoja con una colección"}. {"Wrong parameters in the web formulary","Parámetros incorrectos en el formulario web"}. {"Wrong xmlns","XMLNS incorrecto"}. @@ -670,6 +672,7 @@ {"XMPP Show Value of XA (Extended Away)","Valor 'Show' de XMPP: XA (Ausente Extendido)"}. {"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Nodo Asociado de Publicar-Subscribir"}. {"You are being removed from the room because of a system shutdown","Estás siendo expulsado de la sala porque el sistema se va a detener"}. +{"You are not allowed to send private messages","No tienes permitido enviar mensajes privados"}. {"You are not joined to the channel","No has entrado en el canal"}. {"You can later change your password using an XMPP client.","Puedes cambiar tu contraseña después, usando un cliente XMPP."}. {"You have been banned from this room","Has sido bloqueado en esta sala"}. diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 934da000c..e3570f41b 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -48,6 +48,7 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente, a sua conta não tem direitos de administração neste servidor. Verifique como conceder os direitos administrativos em: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. @@ -659,6 +660,7 @@ {"Whether to allow subscriptions","Permitir subscrições"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Caso todas as assinaturas devam ser temporárias, com base na presença do assinante"}. {"Whether to notify owners about new subscribers and unsubscribes","Caso deva notificar os proprietários sobre os novos assinantes e aqueles que cancelaram a assinatura"}. +{"Who can send private messages","Quem pode enviar mensagens privadas"}. {"Who may associate leaf nodes with a collection","Quem pode associar as folhas dos nós em uma coleção"}. {"Wrong parameters in the web formulary","O formulário web está com os parâmetros errados"}. {"Wrong xmlns","Xmlns errado"}. @@ -670,6 +672,7 @@ {"XMPP Show Value of XA (Extended Away)","XMPP Exiba o valor do XA (Ausência Estendida)"}. {"XMPP URI of Associated Publish-Subscribe Node","XMPP URI da publicação do nó associado da assinatura"}. {"You are being removed from the room because of a system shutdown","Você está sendo removido da sala por causa do desligamento do sistema"}. +{"You are not allowed to send private messages","Você não tem permissão para enviar mensagens privadas"}. {"You are not joined to the channel","Você não está inscrito no canal"}. {"You can later change your password using an XMPP client.","Você pode alterar a sua senha mais tarde usando um cliente XMPP."}. {"You have been banned from this room","Você foi banido desta sala"}. From df608188836967d8eac9d651ab6bc980a8eca29b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 16 Oct 2023 14:32:38 +0200 Subject: [PATCH 0247/1302] Example configuration: Clarify direct TLS listener The ejabberd_c2s listener for port 5223 is meant to support direct TLS access rather than STARTTLS. Therefore, remove the 'starttls_required' option, which had no effect. --- ejabberd.yml.example | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 8eb038dd0..56ce5b34e 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -36,12 +36,11 @@ listen: - port: 5223 ip: "::" - tls: true module: ejabberd_c2s max_stanza_size: 262144 shaper: c2s_shaper access: c2s - starttls_required: true + tls: true - port: 5269 ip: "::" From cbfb8eb805e658d0327190c63151e2323eb11ced Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 16 Oct 2023 14:44:01 +0200 Subject: [PATCH 0248/1302] Example configuration: Specify s2s shaper Specify a shaper for incoming s2s connections (got lost in commit 91a74e3e27cf2c2f6b25888c3cc276d0ddbf5c69). Thanks to Paul Menzel for noting that it was missing. --- ejabberd.yml.example | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 56ce5b34e..df52c85be 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -46,6 +46,7 @@ listen: ip: "::" module: ejabberd_s2s_in max_stanza_size: 524288 + shaper: s2s_shaper - port: 5443 ip: "::" From ad7db90c802cb49cb93e7863103bb01151bed97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 16 Oct 2023 15:14:31 +0200 Subject: [PATCH 0249/1302] Use tagged deps --- mix.exs | 2 +- mix.lock | 10 +++++----- rebar.config | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index e1f19e8d7..64d51d6ba 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below('22', true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, - {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}, + {config(:mysql), {:p1_mysql, " >= 1.0.22"}}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: diff --git a/mix.lock b/mix.lock index 3f37a1b72..e0b91f151 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, - "esip": {:hex, :esip, "1.0.49", "7949c288d1e094cb44bff5499231939e34c2ace06de8bef950a341edb1743357", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.7", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a1379ced50c3a2a8f82a77b3184e94c3b87782e90e5ddc0d2baf5b654ecfaa11"}, + "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, "ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, @@ -21,15 +21,15 @@ "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.21", "5972add935e7b1b03d981fa88a0d01e96de357443eaf96ca2fb62e465a717f47", [:rebar3], [], "hexpm", "16f197adb99dab034139c429b256d65948a4057d3e4d553adbe5ce3236c4aabf"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.22", "593107adbce3df1bb460fd442320ff540dfba0232f45278441ef8d2de8d08163", [:rebar3], [], "hexpm", "188f04b3ba265a6e7657c67a6780a2f5f973fe86670159ef593aaf44dbd3d5a2"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.22", "f2ca59b87e8c5dbbbe84d08e307c21f078384232e8fb78a783dbeffa6d37da28", [:rebar3], [{:xmpp, "1.6.2", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "bbc38d3878c7b58ab86c257a2a2ce1bacbd68a5034ebea2735db6a70c1aa12bc"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.23", "4a8c17b642dcf5265a910d1a0b86ffb2a9dd057c7b51c3def5eacbcc4f27ced9", [:rebar3], [{:xmpp, "1.7.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "819222bcb5a74581263282ff9cdc679adeefc663dcf49ddc778aeaae90be05a6"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, - "stun": {:hex, :stun, "1.2.7", "d6bdcf0aa72c927fbe8b779fc4ef1f3916c5450b2ff136c800a7a0361fb1ddff", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3fb1f07aaa630b2276e83d267557d1ceb3d2ce52d1145de71864160210655852"}, + "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "3d3d98b996dd7569c57df5fb966b9dfb985c58f3", [ref: "3d3d98b996dd7569c57df5fb966b9dfb985c58f3"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "68cb07d5d0f36d5c51bfea496c638f3ee9b36027", [ref: "68cb07d5d0f36d5c51bfea496c638f3ee9b36027"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index eaf513c2f..32c50d08c 100644 --- a/rebar.config +++ b/rebar.config @@ -34,7 +34,7 @@ {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.49"}}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.50"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.16"}}}, @@ -63,10 +63,10 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", "150c15b96d2fb84cb00e07cc53cd97ec72b77efc"}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.22"}}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.22"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.23"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, {if_not_rebar3, %% Needed because modules are not fully migrated to new structure and mix @@ -76,8 +76,8 @@ {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.7"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "68cb07d5d0f36d5c51bfea496c638f3ee9b36027"}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.7.0"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From f6e8eb52f0edb87f932002e760699834c1266ca8 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 28 Sep 2023 05:47:43 +0300 Subject: [PATCH 0250/1302] Fix ejabberd_sql:sql_query* types --- src/ejabberd_sql.erl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index a07dac67c..1e60b6fa7 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -93,15 +93,16 @@ -endif. -type state() :: #state{}. --type sql_query_simple() :: [sql_query() | binary()] | #sql_query{} | - fun(() -> any()) | fun((atom(), _) -> any()). --type sql_query() :: sql_query_simple() | - [{atom() | {atom(), any()}, sql_query_simple()}]. --type sql_query_result() :: {updated, non_neg_integer()} | - {error, binary() | atom()} | - {selected, [binary()], [[binary()]]} | - {selected, [any()]} | - ok. +-type sql_query_simple(T) :: [sql_query(T) | binary()] | binary() | + #sql_query{} | + fun(() -> T) | fun((atom(), _) -> T). +-type sql_query(T) :: sql_query_simple(T) | + [{atom() | {atom(), any()}, sql_query_simple(T)}]. +-type sql_query_result(T) :: {updated, non_neg_integer()} | + {error, binary() | atom()} | + {selected, [binary()], [[binary()]]} | + {selected, [any()]} | + T. %%%---------------------------------------------------------------------- %%% API @@ -112,14 +113,14 @@ start_link(Host, I) -> p1_fsm:start_link({local, Proc}, ?MODULE, [Host], fsm_limit_opts() ++ ?FSMOPTS). --spec sql_query(binary(), sql_query()) -> sql_query_result(). +-spec sql_query(binary(), sql_query(T)) -> sql_query_result(T). sql_query(Host, Query) -> sql_call(Host, {sql_query, Query}). %% SQL transaction based on a list of queries %% This function automatically --spec sql_transaction(binary(), [sql_query()] | fun(() -> any())) -> - {atomic, any()} | +-spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T)) -> + {atomic, T} | {aborted, any()}. sql_transaction(Host, Queries) when is_list(Queries) -> @@ -177,7 +178,7 @@ sync_send_event(Proc, Msg, Timeout) -> {error, Reason} end. --spec sql_query_t(sql_query()) -> sql_query_result(). +-spec sql_query_t(sql_query(T)) -> sql_query_result(T). %% This function is intended to be used from inside an sql_transaction: sql_query_t(Query) -> QRes = sql_query_internal(Query), From c1af36ac2029f1f3dbcbd1fbba419895c871ddff Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 28 Sep 2023 03:37:36 +0300 Subject: [PATCH 0251/1302] Automatically create and update SQL schema --- include/ejabberd_sql.hrl | 17 + src/ejabberd_auth_sql.erl | 26 +- src/ejabberd_oauth_sql.erl | 27 + src/ejabberd_option.erl | 5 + src/ejabberd_options.erl | 4 + src/ejabberd_options_doc.erl | 5 + src/ejabberd_router_sql.erl | 19 + src/ejabberd_sm_sql.erl | 24 + src/ejabberd_sql_schema.erl | 936 ++++++++++++++++++++++++++++++++++ src/ejabberd_sql_sup.erl | 4 +- src/mod_announce_sql.erl | 19 +- src/mod_bosh_sql.erl | 16 + src/mod_caps_sql.erl | 18 +- src/mod_last_sql.erl | 18 +- src/mod_mam_sql.erl | 52 +- src/mod_mix_pam_sql.erl | 23 +- src/mod_mix_sql.erl | 59 ++- src/mod_mqtt_sql.erl | 26 +- src/mod_muc_sql.erl | 77 +++ src/mod_offline_sql.erl | 21 +- src/mod_privacy_sql.erl | 50 +- src/mod_private_sql.erl | 21 +- src/mod_proxy65_sql.erl | 21 + src/mod_pubsub_sql.erl | 86 +++- src/mod_push_sql.erl | 25 +- src/mod_roster_sql.erl | 48 +- src/mod_shared_roster_sql.erl | 33 +- src/mod_vcard_sql.erl | 72 ++- 28 files changed, 1733 insertions(+), 19 deletions(-) create mode 100644 src/ejabberd_sql_schema.erl diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index 1734b1cb1..6fe86f759 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -50,3 +50,20 @@ boolean :: fun((boolean()) -> binary()), in_array_string :: fun((binary()) -> binary()), like_escape :: fun(() -> binary())}). + + +-record(sql_index, {columns, + unique = false :: boolean()}). +-record(sql_column, {name :: binary(), + type, + default = false, + opts = []}). +-record(sql_table, {name :: binary(), + columns :: [#sql_column{}], + indices = [] :: [#sql_index{}], + post_create}). +-record(sql_schema, {version :: integer(), + tables :: [#sql_table{}], + update = []}). +-record(sql_references, {table :: binary(), + column :: binary()}). diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 1871d3a71..a1cda8cab 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -45,7 +45,31 @@ %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -start(_Host) -> ok. +start(Host) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ok. + +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"users">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"password">>, type = text}, + #sql_column{name = <<"serverkey">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"salt">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"iterationcount">>, type = integer, + default = true}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. stop(_Host) -> ok. diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index ecb1625c7..da9581575 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -41,8 +41,35 @@ -include("logger.hrl"). init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"oauth_token">>, + columns = + [#sql_column{name = <<"token">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"scope">>, type = text}, + #sql_column{name = <<"expire">>, type = bigint}], + indices = [#sql_index{ + columns = [<<"token">>], + unique = true}]}, + #sql_table{ + name = <<"oauth_client">>, + columns = + [#sql_column{name = <<"client_id">>, type = text}, + #sql_column{name = <<"client_name">>, type = text}, + #sql_column{name = <<"grant_type">>, type = text}, + #sql_column{name = <<"options">>, type = text}], + indices = [#sql_index{ + columns = [<<"client_id">>], + unique = true}]}]}]. + store(R) -> Token = R#oauth_token.token, {User, Server} = R#oauth_token.us, diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 173e412af..cc706d14e 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -162,6 +162,7 @@ -export([sql_type/0, sql_type/1]). -export([sql_username/0, sql_username/1]). -export([trusted_proxies/0]). +-export([update_sql_schema/0]). -export([use_cache/0, use_cache/1]). -export([validate_stream/0]). -export([version/0]). @@ -1096,6 +1097,10 @@ sql_username(Host) -> trusted_proxies() -> ejabberd_config:get_option({trusted_proxies, global}). +-spec update_sql_schema() -> boolean(). +update_sql_schema() -> + ejabberd_config:get_option({update_sql_schema, global}). + -spec use_cache() -> boolean(). use_cache() -> use_cache(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 95b3d92c3..3a65daaae 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -260,6 +260,8 @@ opt_type(net_ticktime) -> econf:timeout(second); opt_type(new_sql_schema) -> econf:bool(); +opt_type(update_sql_schema) -> + econf:bool(); opt_type(oauth_access) -> econf:acl(); opt_type(oauth_cache_life_time) -> @@ -607,6 +609,7 @@ options() -> {negotiation_timeout, timer:seconds(30)}, {net_ticktime, timer:seconds(60)}, {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, + {update_sql_schema, true}, {oauth_access, none}, {oauth_cache_life_time, fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, @@ -756,6 +759,7 @@ globals() -> negotiation_timeout, net_ticktime, new_sql_schema, + update_sql_schema, node_start, oauth_cache_life_time, oauth_cache_missed, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index a4938528b..fb06ab510 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -911,6 +911,11 @@ doc() -> "configuration flag '--enable-new-sql-schema' which is set " "at compile time."), [binary:part(ejabberd_config:version(), {0,5})]}}}, + {update_sql_schema, + #{value => "true | false", + desc => + ?T("Allow ejabberd to update SQL schema. " + "The default value is 'true'.")}}, {oauth_access, #{value => ?T("AccessName"), desc => ?T("By default creating OAuth tokens is not allowed. " diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index 0647ddefe..d2e369317 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -37,6 +37,8 @@ %%% API %%%=================================================================== init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'route' table...", []), case ejabberd_sql:sql_query( @@ -48,6 +50,23 @@ init() -> Err end. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"route">>, + columns = + [#sql_column{name = <<"domain">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"pid">>, type = text}, + #sql_column{name = <<"local_hint">>, type = text}], + indices = [#sql_index{ + columns = [<<"domain">>, <<"server_host">>, + <<"node">>, <<"pid">>], + unique = true}]}]}]. + register_route(Domain, ServerHost, LocalHint, _, Pid) -> PidS = misc:encode_pid(Pid), LocalHintS = enc_local_hint(LocalHint), diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index d4fd72a45..ff06d3dda 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -48,6 +48,7 @@ init() -> ?DEBUG("Cleaning SQL SM table...", []), lists:foldl( fun(Host, ok) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), case ejabberd_sql:sql_query( Host, ?SQL("delete from sm where node=%(Node)s")) of {updated, _} -> @@ -60,6 +61,29 @@ init() -> Err end, ok, ejabberd_sm:get_vh_by_backend(?MODULE)). +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"sm">>, + columns = + [#sql_column{name = <<"usec">>, type = bigint}, + #sql_column{name = <<"pid">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"resource">>, type = text}, + #sql_column{name = <<"priority">>, type = text}, + #sql_column{name = <<"info">>, type = text}], + indices = [#sql_index{ + columns = [<<"usec">>, <<"pid">>], + unique = true}, + #sql_index{ + columns = [<<"node">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>]}]}]}]. + set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R}, priority = Priority, info = Info}) -> InfoS = misc:term_to_expr(Info), diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl new file mode 100644 index 000000000..172bc41e4 --- /dev/null +++ b/src/ejabberd_sql_schema.erl @@ -0,0 +1,936 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_sql.erl +%%% Author : Alexey Shchepin +%%% Purpose : SQL schema versioning +%%% Created : 15 Aug 2023 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(ejabberd_sql_schema). + +-author('alexey@process-one.net'). + +-export([start/1, update_schema/3, + get_table_schema/2, get_table_indices/2, test/0]). + +-include("logger.hrl"). +-include("ejabberd_sql_pt.hrl"). + +start(Host) -> + case should_update_schema(Host) of + true -> + case table_exists(Host, <<"schema_version">>) of + true -> + ok; + false -> + Table = filter_table_sh(schema_table()), + Res = create_table(Host, Table), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to create table ~s: ~p", + [Table#sql_table.name, Error]), + {error, Error}; + _ -> + ok + end + end; + false -> + ok + end. + +schema_table() -> + #sql_table{ + name = <<"schema_version">>, + columns = [#sql_column{name = <<"module">>, type = text}, + #sql_column{name = <<"version">>, type = bigint}], + indices = [#sql_index{ + columns = [<<"module">>], + unique = true}]}. + +get_table_schema(Host, Table) -> + ejabberd_sql:sql_query( + Host, + fun(pgsql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select " + " @(a.attname)s, " + " @(pg_catalog.format_type(a.atttypid, a.atttypmod))s " + " from " + " pg_class t, " + " pg_attribute a " + " where " + " a.attrelid = t.oid and " + " a.attnum > 0 and " + " a.atttypid > 0 and " + " t.relkind = 'r' and " + " t.relname=%(Table)s")) + of + {selected, Cols} -> + [{Col, string_to_type(SType)} || {Col, SType} <- Cols] + end; + (sqlite, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @(i.name)s, @(i.type)s" + " from pragma_table_info(%(Table)s) as i")) + of + {selected, Cols} -> + [{Col, string_to_type(SType)} || {Col, SType} <- Cols] + end; + (mysql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @(column_name)s, @(column_type)s" + " from information_schema.columns" + " where table_name=%(Table)s and" + " table_schema=schema()" + " order by ordinal_position")) + of + {selected, Cols} -> + [{Col, string_to_type(SType)} || {Col, SType} <- Cols] + end + end). + +get_table_indices(Host, Table) -> + ejabberd_sql:sql_query( + Host, + fun(pgsql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select " + " @(i.relname)s, " + " @(a.attname)s " + " from " + " pg_class t, " + " pg_class i, " + " pg_index ix, " + " pg_attribute a " + " where " + " t.oid = ix.indrelid and " + " i.oid = ix.indexrelid and " + " a.attrelid = t.oid and " + " a.attnum = ANY(ix.indkey) and " + " t.relkind = 'r' and " + " t.relname=%(Table)s " + " order by " + " i.relname, " + " array_position(ix.indkey, a.attnum)")) + of + {selected, Cols} -> + Indices = + lists:foldr( + fun({IdxName, ColName}, Acc) -> + maps:update_with( + IdxName, + fun(Cs) -> [ColName | Cs] end, + [ColName], + Acc) + end, #{}, Cols), + maps:to_list(Indices) + end; + (sqlite, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @(i.name)s, @(c.name)s " + " from pragma_index_list(%(Table)s) as i," + " pragma_index_xinfo(i.name) as c" + " where c.cid >= 0" + " order by i.name, c.seqno")) + of + {selected, Cols} -> + Indices = + lists:foldr( + fun({IdxName, ColName}, Acc) -> + maps:update_with( + IdxName, + fun(Cs) -> [ColName | Cs] end, + [ColName], + Acc) + end, #{}, Cols), + maps:to_list(Indices) + end; + (mysql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @(index_name)s, @(column_name)s" + " from information_schema.statistics" + " where table_name=%(Table)s and" + " table_schema=schema()" + " order by index_name, seq_in_index")) + of + {selected, Cols} -> + Indices = + lists:foldr( + fun({IdxName, ColName}, Acc) -> + maps:update_with( + IdxName, + fun(Cs) -> [ColName | Cs] end, + [ColName], + Acc) + end, #{}, Cols), + maps:to_list(Indices) + end + end). + +find_index_name(Host, Table, Columns) -> + Indices = get_table_indices(Host, Table), + case lists:keyfind(Columns, 2, Indices) of + false -> + false; + {Name, _} -> + {ok, Name} + end. + +get_version(Host, Module) -> + SModule = misc:atom_to_binary(Module), + ejabberd_sql:sql_query( + Host, + ?SQL("select @(version)d" + " from schema_version" + " where module=%(SModule)s")). + +store_version(Host, Module, Version) -> + SModule = misc:atom_to_binary(Module), + ?SQL_UPSERT( + Host, + "schema_version", + ["!module=%(SModule)s", + "version=%(Version)d"]). + +table_exists(Host, Table) -> + ejabberd_sql:sql_query( + Host, + fun(pgsql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @()b exists (select * from pg_tables " + " where tablename=%(Table)s)")) + of + {selected, [{Res}]} -> + Res + end; + (sqlite, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @()b exists" + " (select 0 from pragma_table_info(%(Table)s))")) + of + {selected, [{Res}]} -> + Res + end; + (mysql, _) -> + case + ejabberd_sql:sql_query_t( + ?SQL("select @()b exists" + " (select 0 from information_schema.tables" + " where table_name=%(Table)s and" + " table_schema=schema())")) + of + {selected, [{Res}]} -> + Res + end + end). + +filter_table_sh(Table) -> + case {ejabberd_sql:use_new_schema(), Table#sql_table.name} of + {true, _} -> + Table; + {_, <<"route">>} -> + Table; + {false, _} -> + Table#sql_table{ + columns = + lists:keydelete( + <<"server_host">>, #sql_column.name, Table#sql_table.columns), + indices = + lists:map( + fun(Idx) -> + Idx#sql_index{ + columns = + lists:delete( + <<"server_host">>, Idx#sql_index.columns) + } + end, Table#sql_table.indices) + } + end. + +string_to_type(SType) -> + case string:lowercase(SType) of + <<"text">> -> text; + <<"mediumtext">> -> text; + <<"bigint">> -> bigint; + <<"bigint ", _/binary>> -> bigint; + <<"bigint(", _/binary>> -> bigint; + <<"integer">> -> integer; + <<"int">> -> integer; + <<"int(", _/binary>> -> integer; + <<"smallint">> -> smallint; + <<"smallint(", _/binary>> -> smallint; + <<"numeric">> -> numeric; + <<"decimal", _/binary>> -> numeric; + <<"bigserial">> -> bigserial; + <<"boolean">> -> boolean; + <<"tinyint(1)">> -> boolean; + <<"bytea">> -> blob; + <<"blob">> -> blob; + <<"timestamp", _/binary>> -> timestamp; + <<"character(", R/binary>> -> + {ok, [N], []} = io_lib:fread("~d)", binary_to_list(R)), + {char, N}; + <<"char(", R/binary>> -> + {ok, [N], []} = io_lib:fread("~d)", binary_to_list(R)), + {char, N}; + <<"varchar(", _/binary>> -> text; + <<"character varying(", _/binary>> -> text; + T -> + ?ERROR_MSG("Unknown SQL type '~s'", [T]), + {undefined, T} + end. + +check_columns_compatibility(RequiredColumns, Columns) -> + lists:all( + fun(#sql_column{name = Name, type = Type}) -> + %io:format("col ~p~n", [{Name, Type}]), + case lists:keyfind(Name, 1, Columns) of + false -> + false; + {_, Type2} -> + %io:format("tt ~p~n", [{Type, Type2}]), + case {Type, Type2} of + {T, T} -> true; + {text, blob} -> true; + {{text, _}, blob} -> true; + {{text, _}, text} -> true; + {{text, _}, {varchar, _}} -> true; + {text, {varchar, _}} -> true; + {{char, _}, text} -> true; + {{varchar, _}, text} -> true; + {smallint, integer} -> true; + {smallint, bigint} -> true; + {smallint, numeric} -> true; + {integer, bigint} -> true; + {integer, numeric} -> true; + {bigint, numeric} -> true; + {bigserial, integer} -> true; + {bigserial, bigint} -> true; + {bigserial, numeric} -> true; + _ -> false + end + end + end, RequiredColumns). + +guess_version(Host, Schemas) -> + LastSchema = lists:max(Schemas), + SomeTablesExist = + lists:any( + fun(Table) -> + table_exists(Host, Table#sql_table.name) + end, LastSchema#sql_schema.tables), + if + SomeTablesExist -> + CompatibleSchemas = + lists:filter( + fun(Schema) -> + lists:all( + fun(Table) -> + Table2 = filter_table_sh(Table), + CurrentColumns = + get_table_schema( + Host, Table2#sql_table.name), + check_columns_compatibility( + Table2#sql_table.columns, + CurrentColumns) + end, Schema#sql_schema.tables) + end, Schemas), + case CompatibleSchemas of + [] -> -1; + _ -> + (lists:max(CompatibleSchemas))#sql_schema.version + end; + true -> + 0 + end. + +get_current_version(Host, Module, Schemas) -> + case get_version(Host, Module) of + {selected, [{Version}]} -> + Version; + {selected, []} -> + Version = guess_version(Host, Schemas), + if + Version > 0 -> + store_version(Host, Module, Version); + true -> + ok + end, + Version + end. + +format_type(pgsql, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"text">>; + {text, _} -> <<"text">>; + bigint -> <<"bigint">>; + integer -> <<"integer">>; + smallint -> <<"smallint">>; + numeric -> <<"numeric">>; + boolean -> <<"boolean">>; + blob -> <<"bytea">>; + timestamp -> <<"timestamp">>; + {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; + bigserial -> <<"bigserial">> + end; +format_type(sqlite, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"text">>; + {text, _} -> <<"text">>; + bigint -> <<"bigint">>; + integer -> <<"integer">>; + smallint -> <<"smallint">>; + numeric -> <<"numeric">>; + boolean -> <<"boolean">>; + blob -> <<"blob">>; + timestamp -> <<"timestamp">>; + {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; + bigserial -> <<"integer primary key autoincrement">> + end; +format_type(mysql, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"text">>; + {text, big} -> <<"mediumtext">>; + {text, N} when is_integer(N), N < 191 -> + [<<"varchar(">>, integer_to_binary(N), <<")">>]; + {text, _} -> <<"text">>; + bigint -> <<"bigint">>; + integer -> <<"integer">>; + smallint -> <<"smallint">>; + numeric -> <<"numeric">>; + boolean -> <<"boolean">>; + blob -> <<"blob">>; + timestamp -> <<"timestamp">>; + {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; + bigserial -> <<"bigint auto_increment primary key">> + end. + +format_default(pgsql, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"''">>; + {text, _} -> <<"''">>; + bigint -> <<"0">>; + integer -> <<"0">>; + smallint -> <<"0">>; + numeric -> <<"0">>; + boolean -> <<"false">>; + blob -> <<"''">>; + timestamp -> <<"now()">> + %{char, N} -> <<"''">>; + %bigserial -> <<"0">> + end; +format_default(sqlite, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"''">>; + {text, _} -> <<"''">>; + bigint -> <<"0">>; + integer -> <<"0">>; + smallint -> <<"0">>; + numeric -> <<"0">>; + boolean -> <<"false">>; + blob -> <<"''">>; + timestamp -> <<"CURRENT_TIMESTAMP">> + %{char, N} -> <<"''">>; + %bigserial -> <<"0">> + end; +format_default(mysql, _DBVersion, Column) -> + case Column#sql_column.type of + text -> <<"('')">>; + {text, _} -> <<"('')">>; + bigint -> <<"0">>; + integer -> <<"0">>; + smallint -> <<"0">>; + numeric -> <<"0">>; + boolean -> <<"false">>; + blob -> <<"('')">>; + timestamp -> <<"CURRENT_TIMESTAMP">> + %{char, N} -> <<"''">>; + %bigserial -> <<"0">> + end. + +escape_name(pgsql, _DBVersion, <<"type">>) -> + <<"\"type\"">>; +escape_name(_DBType, _DBVersion, ColumnName) -> + ColumnName. + +format_column_def(DBType, DBVersion, Column) -> + [<<" ">>, + escape_name(DBType, DBVersion, Column#sql_column.name), <<" ">>, + format_type(DBType, DBVersion, Column), + <<" NOT NULL">>, + case Column#sql_column.default of + false -> []; + true -> + [<<" DEFAULT ">>, format_default(DBType, DBVersion, Column)] + end, + case lists:keyfind(sql_references, 1, Column#sql_column.opts) of + false -> []; + #sql_references{table = T, column = C} -> + [<<" REFERENCES ">>, T, <<"(">>, C, <<") ON DELETE CASCADE">>] + end]. + +format_mysql_index_column(Table, ColumnName) -> + {value, Column} = + lists:keysearch( + ColumnName, #sql_column.name, Table#sql_table.columns), + NeedsSizeLimit = + case Column#sql_column.type of + {text, N} when is_integer(N), N < 191 -> false; + {text, _} -> true; + text -> true; + _ -> false + end, + if + NeedsSizeLimit -> + [ColumnName, <<"(191)">>]; + true -> + ColumnName + end. + +format_create_index(pgsql, _DBVersion, Table, Index) -> + TableName = Table#sql_table.name, + Unique = + case Index#sql_index.unique of + true -> <<"UNIQUE ">>; + false -> <<"">> + end, + Name = [<<"i_">>, TableName, <<"_">>, + lists:join( + <<"_">>, + Index#sql_index.columns)], + [<<"CREATE ">>, Unique, <<"INDEX ">>, Name, <<" ON ">>, TableName, + <<" USING btree (">>, + lists:join( + <<", ">>, + Index#sql_index.columns), + <<");">>]; +format_create_index(sqlite, _DBVersion, Table, Index) -> + TableName = Table#sql_table.name, + Unique = + case Index#sql_index.unique of + true -> <<"UNIQUE ">>; + false -> <<"">> + end, + Name = [<<"i_">>, TableName, <<"_">>, + lists:join( + <<"_">>, + Index#sql_index.columns)], + [<<"CREATE ">>, Unique, <<"INDEX ">>, Name, <<" ON ">>, TableName, + <<"(">>, + lists:join( + <<", ">>, + Index#sql_index.columns), + <<");">>]; +format_create_index(mysql, _DBVersion, Table, Index) -> + TableName = Table#sql_table.name, + Unique = + case Index#sql_index.unique of + true -> <<"UNIQUE ">>; + false -> <<"">> + end, + Name = [<<"i_">>, TableName, <<"_">>, + lists:join( + <<"_">>, + Index#sql_index.columns)], + [<<"CREATE ">>, Unique, <<"INDEX ">>, Name, + <<" USING BTREE ON ">>, TableName, + <<"(">>, + lists:join( + <<", ">>, + lists:map( + fun(Col) -> + format_mysql_index_column(Table, Col) + end, Index#sql_index.columns)), + <<");">>]. + +format_create_table(pgsql = DBType, DBVersion, Table) -> + TableName = Table#sql_table.name, + [iolist_to_binary( + [<<"CREATE TABLE ">>, TableName, <<" (\n">>, + lists:join( + <<",\n">>, + lists:map( + fun(C) -> format_column_def(DBType, DBVersion, C) end, + Table#sql_table.columns)), + <<"\n);\n">>])] ++ + lists:map( + fun(I) -> + iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>]) + end, + Table#sql_table.indices); +format_create_table(sqlite = DBType, DBVersion, Table) -> + TableName = Table#sql_table.name, + [iolist_to_binary( + [<<"CREATE TABLE ">>, TableName, <<" (\n">>, + lists:join( + <<",\n">>, + lists:map( + fun(C) -> format_column_def(DBType, DBVersion, C) end, + Table#sql_table.columns)), + <<"\n);\n">>])] ++ + lists:map( + fun(I) -> + iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>]) + end, + Table#sql_table.indices); +format_create_table(mysql = DBType, DBVersion, Table) -> + TableName = Table#sql_table.name, + [iolist_to_binary( + [<<"CREATE TABLE ">>, TableName, <<" (\n">>, + lists:join( + <<",\n">>, + lists:map( + fun(C) -> format_column_def(DBType, DBVersion, C) end, + Table#sql_table.columns)), + <<"\n) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n">>])] ++ + lists:map( + fun(I) -> + iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>]) + end, + Table#sql_table.indices). +%format_create_table(DBType, _DBVersion, Table) -> +% ?ERROR_MSG("Can't create SQL table ~p on ~p", +% [Table#sql_table.name, DBType]), +% error. + +create_table(Host, Table) -> + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + SQLs = format_create_table(DBType, DBVersion, Table), + ?INFO_MSG("Creating table ~s:~n~s~n", + [Table#sql_table.name, SQLs]), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, SQLs), + case Table#sql_table.post_create of + undefined -> + ok; + F -> + F(DBType, DBVersion) + end + end). + +create_tables(Host, Module, Schema) -> + lists:foreach( + fun(Table) -> + Table2 = filter_table_sh(Table), + Res = create_table(Host, Table2), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to create table ~s: ~p", + [Table2#sql_table.name, Error]), + error(Error); + _ -> + ok + end + end, Schema#sql_schema.tables), + store_version(Host, Module, Schema#sql_schema.version). + +should_update_schema(Host) -> + SupportedDB = + case ejabberd_option:sql_type(Host) of + pgsql -> true; + sqlite -> true; + mysql -> true; + _ -> false + end, + case ejabberd_option:update_sql_schema() andalso SupportedDB of + true -> + case ejabberd_sql:use_new_schema() of + true -> + Host == ejabberd_config:get_myname(); + false -> + true + end; + false -> + false + end. + +update_schema(Host, Module, Schemas) -> + case should_update_schema(Host) of + true -> + Version = get_current_version(Host, Module, Schemas), + LastSchema = lists:max(Schemas), + LastVersion = LastSchema#sql_schema.version, + case Version of + _ when Version < 0 -> + ?ERROR_MSG("Can't update SQL schema for module ~p, please do it manually", [Module]); + 0 -> + create_tables(Host, Module, LastSchema); + LastVersion -> + ok; + _ when LastVersion < Version -> + ?ERROR_MSG("The current SQL schema for module ~p is ~p, but the latest known schema in the module is ~p", [Module, Version, LastVersion]); + _ -> + lists:foreach( + fun(Schema) -> + if + Schema#sql_schema.version > Version -> + do_update_schema(Host, Module, Schema); + true -> + ok + end + end, lists:sort(Schemas)) + end; + false -> + ok + end. + +do_update_schema(Host, Module, Schema) -> + lists:foreach( + fun({add_column, TableName, ColumnName}) -> + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Column} = + lists:keysearch( + ColumnName, #sql_column.name, Table#sql_table.columns), + Res = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + Def = format_column_def(DBType, DBVersion, Column), + Default = format_default(DBType, DBVersion, Column), + SQLs = + [[<<"ALTER TABLE ">>, + TableName, + <<" ADD COLUMN\n">>, + Def, + <<" DEFAULT ">>, + Default, <<";\n">>]] ++ + case Column#sql_column.default of + false -> + [[<<"ALTER TABLE ">>, + TableName, + <<" ALTER COLUMN ">>, + ColumnName, + <<" DROP DEFAULT;">>]]; + _ -> + [] + end, + ?INFO_MSG("Add column ~s/~s:~n~s~n", + [TableName, + ColumnName, + SQLs]), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, + SQLs) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({drop_column, TableName, ColumnName}) -> + Res = + ejabberd_sql:sql_query( + Host, + fun(_DBType, _DBVersion) -> + SQL = [<<"ALTER TABLE ">>, + TableName, + <<" DROP COLUMN ">>, + ColumnName, + <<";">>], + ?INFO_MSG("Drop column ~s/~s:~n~s~n", + [TableName, + ColumnName, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({create_index, TableName, Columns}) -> + {value, Table1} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Index1} = + lists:keysearch( + Columns, #sql_index.columns, Table1#sql_table.indices), + Table = filter_table_sh(Table1), + Index = + case ejabberd_sql:use_new_schema() of + true -> + Index1; + false -> + Index1#sql_index{ + columns = + lists:delete( + <<"server_host">>, Index1#sql_index.columns) + } + end, + Res = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + SQL1 = format_create_index( + DBType, DBVersion, Table, Index), + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Create index ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({drop_index, TableName, Columns1}) -> + Columns = + case ejabberd_sql:use_new_schema() of + true -> + Columns1; + false -> + lists:delete( + <<"server_host">>, Columns1) + end, + case find_index_name(Host, TableName, Columns) of + false -> + ?ERROR_MSG("Can't find an index to drop for ~s/~p", + [TableName, Columns]); + {ok, IndexName} -> + Res = + ejabberd_sql:sql_query( + Host, + fun(DBType, _DBVersion) -> + SQL = + case DBType of + mysql -> + [<<"DROP INDEX ">>, + IndexName, + <<" ON ">>, + TableName, + <<";">>]; + _ -> + [<<"DROP INDEX ">>, + IndexName, <<";">>] + end, + ?INFO_MSG("Drop index ~s/~p:~n~s~n", + [TableName, + Columns, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end + end + end, Schema#sql_schema.update), + store_version(Host, Module, Schema#sql_schema.version). + +test() -> + Schemas = + [#sql_schema{ + version = 2, + tables = + [#sql_table{ + name = <<"archive2">>, + columns = [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"timestamp">>, type = bigint}, + #sql_column{name = <<"peer">>, type = text}, + #sql_column{name = <<"bare_peer">>, type = text}, + #sql_column{name = <<"xml">>, type = {text, big}}, + #sql_column{name = <<"txt">>, type = {text, big}}, + #sql_column{name = <<"id">>, type = bigserial}, + #sql_column{name = <<"kind">>, type = text}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"origin_id">>, type = text}, + #sql_column{name = <<"type">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"bare_peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"origin_id">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"timestamp">>]} + ]}], + update = + [{add_column, <<"archive2">>, <<"origin_id">>}, + {create_index, <<"archive2">>, + [<<"server_host">>, <<"origin_id">>]}, + {drop_index, <<"archive2">>, + [<<"server_host">>, <<"origin_id">>]}, + {drop_column, <<"archive2">>, <<"origin_id">>} + ]}, + #sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"archive2">>, + columns = [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"timestamp">>, type = bigint}, + #sql_column{name = <<"peer">>, type = text}, + #sql_column{name = <<"bare_peer">>, type = text}, + #sql_column{name = <<"xml">>, type = {text, big}}, + #sql_column{name = <<"txt">>, type = {text, big}}, + #sql_column{name = <<"id">>, type = bigserial}, + #sql_column{name = <<"kind">>, type = {text, 10}}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"bare_peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"timestamp">>]} + ]}]}], + update_schema(<<"localhost">>, mod_foo, Schemas). diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index 54bc6ca87..7320174e6 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -51,7 +51,9 @@ start(Host) -> type => supervisor, modules => [?MODULE]}, case supervisor:start_child(ejabberd_db_sup, Spec) of - {ok, _} -> ok; + {ok, _} -> + ejabberd_sql_schema:start(Host), + ok; {error, {already_started, Pid}} -> %% Wait for the supervisor to fully start _ = supervisor:count_children(Pid), diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl index 20ec78295..f0d949fa1 100644 --- a/src/mod_announce_sql.erl +++ b/src/mod_announce_sql.erl @@ -40,9 +40,26 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"motd">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"xml">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. + set_motd_users(LServer, USRs) -> F = fun() -> lists:foreach( diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index 2b849a364..321f37db3 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -37,6 +37,8 @@ %%% API %%%=================================================================== init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'bosh' table...", []), case ejabberd_sql:sql_query( @@ -48,6 +50,20 @@ init() -> Err end. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"bosh">>, + columns = + [#sql_column{name = <<"sid">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"pid">>, type = text}], + indices = [#sql_index{ + columns = [<<"sid">>], + unique = true}]}]}]. + open_session(SID, Pid) -> PidS = misc:encode_pid(Pid), Node = erlang:atom_to_binary(node(Pid), latin1), diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl index 5a545e6e5..f6ea56236 100644 --- a/src/mod_caps_sql.erl +++ b/src/mod_caps_sql.erl @@ -37,9 +37,25 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"caps_features">>, + columns = + [#sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"subnode">>, type = text}, + #sql_column{name = <<"feature">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"node">>, <<"subnode">>]}]}]}]. + caps_read(LServer, {Node, SubNode}) -> case ejabberd_sql:sql_query( LServer, diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl index 98e65ee93..3d305af66 100644 --- a/src/mod_last_sql.erl +++ b/src/mod_last_sql.erl @@ -38,9 +38,25 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"last">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"seconds">>, type = text}, + #sql_column{name = <<"state">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. + get_last(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index b21e84a7b..4898944ec 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -43,9 +43,59 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"archive">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"timestamp">>, type = bigint}, + #sql_column{name = <<"peer">>, type = text}, + #sql_column{name = <<"bare_peer">>, type = text}, + #sql_column{name = <<"xml">>, type = {text, big}}, + #sql_column{name = <<"txt">>, type = {text, big}}, + #sql_column{name = <<"id">>, type = bigserial}, + #sql_column{name = <<"kind">>, type = {text, 10}}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"bare_peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"timestamp">>]} + ], + post_create = + fun(mysql, _) -> + ejabberd_sql:sql_query_t( + <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); + (_, _) -> + ok + end}, + #sql_table{ + name = <<"archive_prefs">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"def">>, type = text}, + #sql_column{name = <<"always">>, type = text}, + #sql_column{name = <<"never">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. + remove_user(LUser, LServer) -> ejabberd_sql:sql_query( LServer, diff --git a/src/mod_mix_pam_sql.erl b/src/mod_mix_pam_sql.erl index 7fc26b9e6..606d306ce 100644 --- a/src/mod_mix_pam_sql.erl +++ b/src/mod_mix_pam_sql.erl @@ -33,10 +33,29 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> - %% TODO +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"mix_pam">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"channel">>, type = text}, + #sql_column{name = <<"service">>, type = text}, + #sql_column{name = <<"id">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"username">>, <<"server_host">>, + <<"channel">>, <<"service">>], + unique = true}]}]}]. + add_channel(User, Channel, ID) -> {LUser, LServer, _} = jid:tolower(User), {Chan, Service, _} = jid:tolower(Channel), diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl index 753fd9fd0..74f341fb8 100644 --- a/src/mod_mix_sql.erl +++ b/src/mod_mix_sql.erl @@ -34,10 +34,65 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> - %% TODO +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"mix_channel">>, + columns = + [#sql_column{name = <<"channel">>, type = text}, + #sql_column{name = <<"service">>, type = text}, + #sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"domain">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"hidden">>, type = boolean}, + #sql_column{name = <<"hmac_key">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"channel">>, <<"service">>], + unique = true}, + #sql_index{ + columns = [<<"service">>]}]}, + #sql_table{ + name = <<"mix_participant">>, + columns = + [#sql_column{name = <<"channel">>, type = text}, + #sql_column{name = <<"service">>, type = text}, + #sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"domain">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"id">>, type = text}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"channel">>, <<"service">>, + <<"username">>, <<"domain">>], + unique = true}]}, + #sql_table{ + name = <<"mix_subscription">>, + columns = + [#sql_column{name = <<"channel">>, type = text}, + #sql_column{name = <<"service">>, type = {text, 75}}, + #sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"domain">>, type = {text, 75}}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"jid">>, type = text}], + indices = [#sql_index{ + columns = [<<"channel">>, <<"service">>, + <<"username">>, <<"domain">>, + <<"node">>], + unique = true}, + #sql_index{ + columns = [<<"channel">>, <<"service">>, + <<"node">>]}]}]}]. + set_channel(LServer, Channel, Service, CreatorJID, Hidden, Key) -> {User, Domain, _} = jid:tolower(CreatorJID), RawJID = jid:encode(jid:remove_resource(CreatorJID)), diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index 8db044355..a75554e1b 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -36,9 +36,33 @@ init() -> ?ERROR_MSG("Backend 'sql' is only supported for db_type", []), {error, db_failure}. -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"mqtt_pub">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"resource">>, type = text}, + #sql_column{name = <<"topic">>, type = text}, + #sql_column{name = <<"qos">>, type = smallint}, + #sql_column{name = <<"payload">>, type = blob}, + #sql_column{name = <<"payload_format">>, type = smallint}, + #sql_column{name = <<"content_type">>, type = text}, + #sql_column{name = <<"response_topic">>, type = text}, + #sql_column{name = <<"correlation_data">>, type = blob}, + #sql_column{name = <<"user_property">>, type = blob}, + #sql_column{name = <<"expiry">>, type = bigint}], + indices = [#sql_index{ + columns = [<<"topic">>, <<"server_host">>], + unique = true}]}]}]. + publish({U, LServer, R}, Topic, Payload, QoS, Props, ExpiryTime) -> PayloadFormat = encode_pfi(maps:get(payload_format_indicator, Props, binary)), ResponseTopic = maps:get(response_topic, Props, <<"">>), diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index 51a42c32a..e7eec9c0f 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -52,6 +52,7 @@ %%% API %%%=================================================================== init(Host, Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), case gen_mod:ram_db_mod(Opts, mod_muc) of ?MODULE -> clean_tables(Host); @@ -59,6 +60,82 @@ init(Host, Opts) -> ok end. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"muc_room">>, + columns = + [#sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"host">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"opts">>, type = {text, big}}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"name">>, <<"host">>], + unique = true}, + #sql_index{ + columns = [<<"host">>, <<"created_at">>]}]}, + #sql_table{ + name = <<"muc_registered">>, + columns = + [#sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"host">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"jid">>, <<"host">>], + unique = true}, + #sql_index{ + columns = [<<"nick">>]}]}, + #sql_table{ + name = <<"muc_online_room">>, + columns = + [#sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"host">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"pid">>, type = text}], + indices = [#sql_index{ + columns = [<<"name">>, <<"host">>], + unique = true}]}, + #sql_table{ + name = <<"muc_online_users">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server">>, type = {text, 75}}, + #sql_column{name = <<"resource">>, type = text}, + #sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"host">>, type = {text, 75}}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"node">>, type = text}], + indices = [#sql_index{ + columns = [<<"username">>, <<"server">>, + <<"resource">>, <<"name">>, + <<"host">>], + unique = true}]}, + #sql_table{ + name = <<"muc_room_subscribers">>, + columns = + [#sql_column{name = <<"room">>, type = text}, + #sql_column{name = <<"host">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"nodes">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"host">>, <<"room">>, <<"jid">>], + unique = true}, + #sql_index{ + columns = [<<"host">>, <<"jid">>]}, + #sql_index{ + columns = [<<"jid">>]}]}]}]. + store_room(LServer, Host, Name, Opts, ChangesHints) -> {Subs, Opts2} = case lists:keytake(subscribers, 1, Opts) of {value, {subscribers, S}, OptN} -> {S, OptN}; diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index a3b9d0545..933da38da 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -40,9 +40,28 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"spool">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"xml">>, type = {text, big}}, + #sql_column{name = <<"seq">>, type = bigserial}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>]}, + #sql_index{ + columns = [<<"created_at">>]}]}]}]. + store_message(#offline_msg{us = {LUser, LServer}} = M) -> From = M#offline_msg.from, To = M#offline_msg.to, diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index ade3bf1ad..eb53b72ed 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -42,9 +42,57 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"privacy_default_list">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"name">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}, + #sql_table{ + name = <<"privacy_list">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"id">>, type = bigserial}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"id">>], + unique = true}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"name">>], + unique = true}]}, + #sql_table{ + name = <<"privacy_list_data">>, + columns = + [#sql_column{name = <<"id">>, type = bigint, + opts = [#sql_references{ + table = <<"privacy_list">>, + column = <<"id">>}]}, + #sql_column{name = <<"t">>, type = {char, 1}}, + #sql_column{name = <<"value">>, type = text}, + #sql_column{name = <<"action">>, type = {char, 1}}, + #sql_column{name = <<"ord">>, type = numeric}, + #sql_column{name = <<"match_all">>, type = boolean}, + #sql_column{name = <<"match_iq">>, type = boolean}, + #sql_column{name = <<"match_message">>, type = boolean}, + #sql_column{name = <<"match_presence_in">>, type = boolean}, + #sql_column{name = <<"match_presence_out">>, type = boolean}], + indices = [#sql_index{columns = [<<"id">>]}]}]}]. + unset_default(LUser, LServer) -> case unset_default_privacy_list(LUser, LServer) of ok -> diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl index a1bb38c82..1b9584afe 100644 --- a/src/mod_private_sql.erl +++ b/src/mod_private_sql.erl @@ -37,9 +37,28 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"private_storage">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"namespace">>, type = text}, + #sql_column{name = <<"data">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"namespace">>], + unique = true}]}]}]. + set_data(LUser, LServer, Data) -> F = fun() -> lists:foreach( diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl index 1b47e517d..ffe9c3557 100644 --- a/src/mod_proxy65_sql.erl +++ b/src/mod_proxy65_sql.erl @@ -34,6 +34,8 @@ %%% API %%%=================================================================== init() -> + ejabberd_sql_schema:update_schema( + ejabberd_config:get_myname(), ?MODULE, schemas()), NodeS = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'proxy65' table...", []), case ejabberd_sql:sql_query( @@ -47,6 +49,25 @@ init() -> Err end. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"proxy65">>, + columns = + [#sql_column{name = <<"sid">>, type = text}, + #sql_column{name = <<"pid_t">>, type = text}, + #sql_column{name = <<"pid_i">>, type = text}, + #sql_column{name = <<"node_t">>, type = text}, + #sql_column{name = <<"node_i">>, type = text}, + #sql_column{name = <<"jid_i">>, type = text}], + indices = [#sql_index{ + columns = [<<"sid">>], + unique = true}, + #sql_index{ + columns = [<<"jid_i">>]}]}]}]. + register_stream(SID, Pid) -> PidS = misc:encode_pid(Pid), NodeS = erlang:atom_to_binary(node(Pid), latin1), diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl index 0a59d093f..2bc14b972 100644 --- a/src/mod_pubsub_sql.erl +++ b/src/mod_pubsub_sql.erl @@ -21,12 +21,96 @@ %% API -export([init/3]). +-include("ejabberd_sql_pt.hrl"). + %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _ServerHost, _Opts) -> +init(_Host, ServerHost, _Opts) -> + ejabberd_sql_schema:update_schema(ServerHost, ?MODULE, schemas()), ok. %%%=================================================================== %%% Internal functions %%%=================================================================== +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"pubsub_node">>, + columns = + [#sql_column{name = <<"host">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"parent">>, type = text, + default = true}, + #sql_column{name = <<"plugin">>, type = text}, + #sql_column{name = <<"nodeid">>, type = bigserial}], + indices = [#sql_index{ + columns = [<<"nodeid">>], + unique = true}, + #sql_index{ + columns = [<<"parent">>]}, + #sql_index{ + columns = [<<"host">>, <<"node">>], + unique = true}]}, + #sql_table{ + name = <<"pubsub_node_option">>, + columns = + [#sql_column{name = <<"nodeid">>, type = bigint, + opts = [#sql_references{ + table = <<"pubsub_node">>, + column = <<"nodeid">>}]}, + #sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"val">>, type = text}], + indices = [#sql_index{columns = [<<"nodeid">>]}]}, + #sql_table{ + name = <<"pubsub_node_owner">>, + columns = + [#sql_column{name = <<"nodeid">>, type = bigint, + opts = [#sql_references{ + table = <<"pubsub_node">>, + column = <<"nodeid">>}]}, + #sql_column{name = <<"owner">>, type = text}], + indices = [#sql_index{columns = [<<"nodeid">>]}]}, + #sql_table{ + name = <<"pubsub_state">>, + columns = + [#sql_column{name = <<"nodeid">>, type = bigint, + opts = [#sql_references{ + table = <<"pubsub_node">>, + column = <<"nodeid">>}]}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"affiliation">>, type = {char, 1}}, + #sql_column{name = <<"subscriptions">>, type = text, + default = true}, + #sql_column{name = <<"stateid">>, type = bigserial}], + indices = [#sql_index{columns = [<<"stateid">>], + unique = true}, + #sql_index{columns = [<<"jid">>]}, + #sql_index{columns = [<<"nodeid">>, <<"jid">>], + unique = true}]}, + #sql_table{ + name = <<"pubsub_item">>, + columns = + [#sql_column{name = <<"nodeid">>, type = bigint, + opts = [#sql_references{ + table = <<"pubsub_node">>, + column = <<"nodeid">>}]}, + #sql_column{name = <<"itemid">>, type = text}, + #sql_column{name = <<"publisher">>, type = text}, + #sql_column{name = <<"creation">>, type = {text, 32}}, + #sql_column{name = <<"modification">>, type = {text, 32}}, + #sql_column{name = <<"payload">>, type = {text, big}, + default = true}], + indices = [#sql_index{columns = [<<"nodeid">>, <<"itemid">>], + unique = true}, + #sql_index{columns = [<<"itemid">>]}]}, + #sql_table{ + name = <<"pubsub_subscription_opt">>, + columns = + [#sql_column{name = <<"subid">>, type = text}, + #sql_column{name = <<"opt_name">>, type = {text, 32}}, + #sql_column{name = <<"opt_value">>, type = text}], + indices = [#sql_index{columns = [<<"subid">>, <<"opt_name">>], + unique = true}]}]}]. diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index 7a433a57d..eb4f498e5 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -39,9 +39,32 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"push_session">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"timestamp">>, type = bigint}, + #sql_column{name = <<"service">>, type = text}, + #sql_column{name = <<"node">>, type = text}, + #sql_column{name = <<"xml">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"timestamp">>], + unique = true}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"service">>, <<"node">>], + unique = true}]}]}]. + store_session(LUser, LServer, NowTS, PushJID, Node, XData) -> XML = encode_xdata(XData), TS = misc:now_to_usec(NowTS), diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 67d9697f7..12caba645 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -43,9 +43,55 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"rosterusers">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"subscription">>, type = {char, 1}}, + #sql_column{name = <<"ask">>, type = {char, 1}}, + #sql_column{name = <<"askmessage">>, type = text}, + #sql_column{name = <<"server">>, type = {char, 1}}, + #sql_column{name = <<"subscribe">>, type = text}, + #sql_column{name = <<"type">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"jid">>], + unique = true}, + #sql_index{ + columns = [<<"server_host">>, <<"jid">>]}]}, + #sql_table{ + name = <<"rostergroups">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"grp">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, + <<"jid">>]}]}, + #sql_table{ + name = <<"roster_version">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"version">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}]}]. + read_roster_version(LUser, LServer) -> case ejabberd_sql:sql_query( LServer, diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl index d921e5474..485b76b13 100644 --- a/src/mod_shared_roster_sql.erl +++ b/src/mod_shared_roster_sql.erl @@ -43,9 +43,40 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"sr_group">>, + columns = + [#sql_column{name = <<"name">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"opts">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"name">>], + unique = true}]}, + #sql_table{ + name = <<"sr_user">>, + columns = + [#sql_column{name = <<"jid">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"grp">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, + <<"jid">>, <<"grp">>], + unique = true}, + #sql_index{ + columns = [<<"server_host">>, <<"grp">>]}]}]}]. + list_groups(Host) -> case ejabberd_sql:sql_query( Host, diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index 7d1c00e11..b663a4b00 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -41,9 +41,79 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> +init(Host, _Opts) -> + ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), ok. +schemas() -> + [#sql_schema{ + version = 1, + tables = + [#sql_table{ + name = <<"vcard">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"vcard">>, type = {text, big}}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}, + #sql_table{ + name = <<"vcard_search">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"lusername">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"fn">>, type = text}, + #sql_column{name = <<"lfn">>, type = text}, + #sql_column{name = <<"family">>, type = text}, + #sql_column{name = <<"lfamily">>, type = text}, + #sql_column{name = <<"given">>, type = text}, + #sql_column{name = <<"lgiven">>, type = text}, + #sql_column{name = <<"middle">>, type = text}, + #sql_column{name = <<"lmiddle">>, type = text}, + #sql_column{name = <<"nickname">>, type = text}, + #sql_column{name = <<"lnickname">>, type = text}, + #sql_column{name = <<"bday">>, type = text}, + #sql_column{name = <<"lbday">>, type = text}, + #sql_column{name = <<"ctry">>, type = text}, + #sql_column{name = <<"lctry">>, type = text}, + #sql_column{name = <<"locality">>, type = text}, + #sql_column{name = <<"llocality">>, type = text}, + #sql_column{name = <<"email">>, type = text}, + #sql_column{name = <<"lemail">>, type = text}, + #sql_column{name = <<"orgname">>, type = text}, + #sql_column{name = <<"lorgname">>, type = text}, + #sql_column{name = <<"orgunit">>, type = text}, + #sql_column{name = <<"lorgunit">>, type = text}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"lusername">>], + unique = true}, + #sql_index{ + columns = [<<"server_host">>, <<"lfn">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lfamily">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lgiven">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lmiddle">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lnickname">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lbday">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lctry">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"llocality">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lemail">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lorgname">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"lorgunit">>]}]}]}]. + stop(_Host) -> ok. From ec20691188e77b73bf38b990c5b44c3fb82c553c Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 16 Oct 2023 19:31:32 +0300 Subject: [PATCH 0252/1302] Disable update_sql_schema by default --- src/ejabberd_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 3a65daaae..75a0caa8e 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -609,7 +609,7 @@ options() -> {negotiation_timeout, timer:seconds(30)}, {net_ticktime, timer:seconds(60)}, {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, - {update_sql_schema, true}, + {update_sql_schema, false}, {oauth_access, none}, {oauth_cache_life_time, fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, From cf54608c7136e7991da1adf733650d5fb63c4b82 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Oct 2023 15:29:20 +0200 Subject: [PATCH 0253/1302] Update changelog --- CHANGELOG.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12ce1b52b..02b602824 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,113 @@ +# Version 23.10 + +Compilation +- Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum +- CI: Update tests to Erlang/OTP 26 and recent Elixir +- Move Xref and Dialyzer options from workflows to `rebar.config` +- Add sections to `rebar.config` to organize its content +- Dialyzer dirty workarounds because `re:mp()` is not an exported type +- When installing module already configured, keep config as example +- Elixir 1.15 removed support for `--app` +- Elixir: Improve support to stop external modules written in Elixir +- Elixir: Update syntax of function calls as recommended by Elixir compiler +- Elixir: When building OTP release with mix, keep `ERLANG_NODE=ejabberd@localhost` +- `ejabberdctl`: Pass `ERLANG_OPTS` when calling `erl` to parse the `INET_DIST_INTERFACE` ([#4066](https://github.com/processone/ejabberd/issues/#4066) + +Commands +- `create_room_with_opts`: Fix typo and move examples to `args_example` ([#4080](https://github.com/processone/ejabberd/issues/#4080)) +- `etop`: Let `ejabberdctl etop` work in a release (if `observer` application is available) +- `get_roster`: Command now returns groups in a list instead of newlines ([#4088](https://github.com/processone/ejabberd/issues/#4088)) +- `halt`: New command to halt ejabberd abruptly with an error status code +- `ejabberdctl`: Fix calling ejabberdctl command with wrong number of arguments with Erlang 26 +- `ejabberdctl`: Improve printing lists in results +- `ejabberdctl`: Support `policy=user` in the help and return proper arguments +- `ejabberdctl`: Document how to stop a debug shell: control+g +- `ejabberdctl`: Support policy=user in the help and return proper arguments +- `ejabberdctl`: Improve printing lists in results + +Container +- Dockerfile: Add missing dependency for mssql databases +- Dockerfile: Reorder stages and steps for consistency +- Dockerfile: Use Alpine as base for `METHOD=package` +- Dockerfile: Rename packages to improve compatibility +- Dockerfile: Provide specific OTP and elixir vsn for direct compilation +- Halt ejabberd if a command in `CTL_ON_` fails during ejabberd startup + +Core +- `auth_external_user_exists_check`: New option ([#3377](https://github.com/processone/ejabberd/issues/#3377)) +- `gen_mod`: Extend `gen_mod` API to simplify hooks and IQ handlers registration +- `gen_mod`: Add shorter forms for `gen_mod` hook/`iq_handler` API +- `gen_mod`: Update modules to the new `gen_mod` API +- `install_contrib_modules`: New option to define contrib modules to install automatically +- `unix_socket`: New listener option, useful when setting unix socket files ([#4059](https://github.com/processone/ejabberd/issues/#4059)) +- `ejabberd_systemd`: Add a few debug messages +- `ejabberd_systemd`: Avoid using `gen_server` timeout ([#4054](https://github.com/processone/ejabberd/issues/#4054))([#4058](https://github.com/processone/ejabberd/issues/#4058)) +- `ejabberd_listener`: Increase default listen queue backlog value to 128, which is the default value on both Linux and FreeBSD ([#4025](https://github.com/processone/ejabberd/issues/#4025)) +- OAuth: Handle `badpass` error message +- When sending message on behalf of user, trigger `user_send_packet` ([#3990](https://github.com/processone/ejabberd/issues/#3990)) +- Web Admin: In roster page move the `AddJID` textbox to top ([#4067](https://github.com/processone/ejabberd/issues/#4067)) +- Web Admin: Show a warning when visiting webadmin with non-privileged account ([#4089](https://github.com/processone/ejabberd/issues/#4089)) + +Docs +- Example configuration: clarify 5223 tls options; specify s2s shaper +- Make sure that `policy=user` commands have `host` instead of `server` arg in docs +- Improve syntax of many command descriptions for the Docs site +- Move example Perl extauth script from ejabberd git to Docs site +- Remove obsolete example files, and add link in Docs to the archived copies + +Installers (`make-binaries`) +- Bump Erlang/OTP version to 26.1.1, and other dependencies +- Remove outdated workaround +- Don't build Linux-PAM examples +- Fix check for current Expat version +- Apply minor simplifications +- Don't duplicate config entries +- Don't hard-code musl version +- Omit unnecessary glibc setting +- Set kernel version for all builds +- Let curl fail on HTTP errors + +Modules +- `mod_muc_log`: Add trailing backslash to URLs shown in disco info +- `mod_muc_occupantid`: New module with support for XEP-0421 Occupant Id ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- `mod_muc_rtbl`: Better error handling in ([#4050](https://github.com/processone/ejabberd/issues/#4050)) +- `mod_private`: Add support for XEP-0402 PEP Native Bookmarks +- `mod_privilege`: Don't fail to edit roster ([#3942](https://github.com/processone/ejabberd/issues/#3942)) +- `mod_pubsub`: Fix usage of `plugins` option, which produced `default_node_config` ignore ([#4070](https://github.com/processone/ejabberd/issues/#4070)) +- `mod_pubsub`: Add `pubsub_delete_item` hook +- `mod_pubsub`: Report support of `config-node-max` in pep +- `mod_pubsub`: Relay pubsub iq queries to muc members without using bare jid ([#4093](https://github.com/processone/ejabberd/issues/#4093)) +- `mod_pubsub`: Allow pubsub node owner to overwrite items published by other persons +- `mod_push_keepalive`: Delay `wake_on_start` +- `mod_push_keepalive`: Don't let hook crash +- `mod_push`: Add `notify_on` option +- `mod_push`: Set `last-message-sender` to bare JID +- `mod_register_web`: Make redirect to page that end with `/` ([#3177](https://github.com/processone/ejabberd/issues/#3177)) +- `mod_shared_roster_ldap`: Don't crash in `get_member_jid` on empty output ([#3614](https://github.com/processone/ejabberd/issues/#3614)) + +MUC +- Add support to register nick in a room ([#3455](https://github.com/processone/ejabberd/issues/#3455)) +- Convert `allow_private_message` MUC room option to `allowpm` ([#3736](https://github.com/processone/ejabberd/issues/#3736)) +- Update xmpp version to send `roomconfig_changesubject` in disco#info ([#4085](https://github.com/processone/ejabberd/issues/#4085)) +- Fix crash when loading room from DB older than ffa07c6, 23.04 +- Fix support to retract a MUC room message +- Don't always store messages passed through `muc_filter_message` ([#4083](https://github.com/processone/ejabberd/issues/#4083)) +- Pass also MUC room retract messages over the `muc_filter_message` ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Pass MUC room private messages over the `muc_filter_message` too ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Store the subject author JID, and run `muc_filter_message` when sending subject ([#3397](https://github.com/processone/ejabberd/issues/#3397)) +- Remove existing role information for users that are kicked from room ([#4035](https://github.com/processone/ejabberd/issues/#4035)) +- Expand rule "mucsub subscribers are members in members only rooms" to more places + +SQL +- Add ability to force alternative upsert implementation in mysql +- Properly parse mysql version even if it doesn't have type tag +- Use prepared statement with mysql +- Add alternate version of mysql upsert +- `ejabberd_auth_sql`: Reset scram fields when setting plain password +- `mod_privacy_sql`: Fix return values from `calculate_diff` +- `mod_privacy_sql`: Optimize `set_list` +- `mod_privacy_sql`: Use more efficient way to calculate changes in `set_privacy_list` + # Version 23.04 General: From b7166d7da079504e071f8055b42488fba3506acf Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Oct 2023 15:31:32 +0200 Subject: [PATCH 0254/1302] Set version to 23.10 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 85761366a..ff2772ccb 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.10` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From 543404bcb8a5e263ae65fd4cfddf5a54ca7397d3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Oct 2023 15:57:50 +0200 Subject: [PATCH 0255/1302] Fixing minor typos in CHANGELOG --- CHANGELOG.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b602824..cd8fcb055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Version 23.10 -Compilation +Compilation: - Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum - CI: Update tests to Erlang/OTP 26 and recent Elixir - Move Xref and Dialyzer options from workflows to `rebar.config` @@ -13,7 +13,7 @@ Compilation - Elixir: When building OTP release with mix, keep `ERLANG_NODE=ejabberd@localhost` - `ejabberdctl`: Pass `ERLANG_OPTS` when calling `erl` to parse the `INET_DIST_INTERFACE` ([#4066](https://github.com/processone/ejabberd/issues/#4066) -Commands +Commands: - `create_room_with_opts`: Fix typo and move examples to `args_example` ([#4080](https://github.com/processone/ejabberd/issues/#4080)) - `etop`: Let `ejabberdctl etop` work in a release (if `observer` application is available) - `get_roster`: Command now returns groups in a list instead of newlines ([#4088](https://github.com/processone/ejabberd/issues/#4088)) @@ -22,10 +22,8 @@ Commands - `ejabberdctl`: Improve printing lists in results - `ejabberdctl`: Support `policy=user` in the help and return proper arguments - `ejabberdctl`: Document how to stop a debug shell: control+g -- `ejabberdctl`: Support policy=user in the help and return proper arguments -- `ejabberdctl`: Improve printing lists in results -Container +Container: - Dockerfile: Add missing dependency for mssql databases - Dockerfile: Reorder stages and steps for consistency - Dockerfile: Use Alpine as base for `METHOD=package` @@ -33,7 +31,7 @@ Container - Dockerfile: Provide specific OTP and elixir vsn for direct compilation - Halt ejabberd if a command in `CTL_ON_` fails during ejabberd startup -Core +Core: - `auth_external_user_exists_check`: New option ([#3377](https://github.com/processone/ejabberd/issues/#3377)) - `gen_mod`: Extend `gen_mod` API to simplify hooks and IQ handlers registration - `gen_mod`: Add shorter forms for `gen_mod` hook/`iq_handler` API @@ -48,14 +46,14 @@ Core - Web Admin: In roster page move the `AddJID` textbox to top ([#4067](https://github.com/processone/ejabberd/issues/#4067)) - Web Admin: Show a warning when visiting webadmin with non-privileged account ([#4089](https://github.com/processone/ejabberd/issues/#4089)) -Docs +Docs: - Example configuration: clarify 5223 tls options; specify s2s shaper - Make sure that `policy=user` commands have `host` instead of `server` arg in docs - Improve syntax of many command descriptions for the Docs site - Move example Perl extauth script from ejabberd git to Docs site - Remove obsolete example files, and add link in Docs to the archived copies -Installers (`make-binaries`) +Installers (`make-binaries`): - Bump Erlang/OTP version to 26.1.1, and other dependencies - Remove outdated workaround - Don't build Linux-PAM examples @@ -67,7 +65,7 @@ Installers (`make-binaries`) - Set kernel version for all builds - Let curl fail on HTTP errors -Modules +Modules: - `mod_muc_log`: Add trailing backslash to URLs shown in disco info - `mod_muc_occupantid`: New module with support for XEP-0421 Occupant Id ([#3397](https://github.com/processone/ejabberd/issues/#3397)) - `mod_muc_rtbl`: Better error handling in ([#4050](https://github.com/processone/ejabberd/issues/#4050)) @@ -85,7 +83,7 @@ Modules - `mod_register_web`: Make redirect to page that end with `/` ([#3177](https://github.com/processone/ejabberd/issues/#3177)) - `mod_shared_roster_ldap`: Don't crash in `get_member_jid` on empty output ([#3614](https://github.com/processone/ejabberd/issues/#3614)) -MUC +MUC: - Add support to register nick in a room ([#3455](https://github.com/processone/ejabberd/issues/#3455)) - Convert `allow_private_message` MUC room option to `allowpm` ([#3736](https://github.com/processone/ejabberd/issues/#3736)) - Update xmpp version to send `roomconfig_changesubject` in disco#info ([#4085](https://github.com/processone/ejabberd/issues/#4085)) @@ -98,7 +96,7 @@ MUC - Remove existing role information for users that are kicked from room ([#4035](https://github.com/processone/ejabberd/issues/#4035)) - Expand rule "mucsub subscribers are members in members only rooms" to more places -SQL +SQL: - Add ability to force alternative upsert implementation in mysql - Properly parse mysql version even if it doesn't have type tag - Use prepared statement with mysql From aa369de280bac64d6996a06dc964d60fc94e3dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 18 Oct 2023 16:17:11 +0200 Subject: [PATCH 0256/1302] Fix xmpp dep in mix --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 64d51d6ba..eafa614fd 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "68cb07d5d0f36d5c51bfea496c638f3ee9b36027", override: true}, + {:xmpp, ">= 1.7.0"}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index e0b91f151..a50543bb5 100644 --- a/mix.lock +++ b/mix.lock @@ -30,6 +30,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "68cb07d5d0f36d5c51bfea496c638f3ee9b36027", [ref: "68cb07d5d0f36d5c51bfea496c638f3ee9b36027"]}, + "xmpp": {:hex, :xmpp, "1.7.0", "2c3034ed501c9ccb7a5e73a462a74e082b4cf9f485bb551732e56fb15106dac9", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "821bc8e2c4b288c031aa95aca17f3b215ed9bd634b7448d0f273baf8f6a46243"}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } From f48275bc112b123bb632cbd8fa52e6330ed6ac66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 19 Oct 2023 19:16:49 +0200 Subject: [PATCH 0257/1302] Only care about pep bookmarks options when creating node from scratch --- src/mod_private.erl | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 53393832e..5fb5e9c15 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -230,7 +230,7 @@ set_data(JID, Data, PublishPepStorageBookmarks, PublishPepXmppBookmarks) -> false -> ok end, case PublishPepXmppBookmarks of - true -> publish_pep_xmpp_bookmarks(JID, Data); + true -> publish_pep_native_bookmarks(JID, Data); false -> ok end; {error, _} = Err -> @@ -299,21 +299,31 @@ publish_pep_storage_bookmarks(JID, Data) -> case lists:keyfind(?NS_STORAGE_BOOKMARKS, 1, Data) of false -> ok; {_, El} -> - PubOpts = [{persist_items, true}, - {access_model, whitelist}], - case mod_pubsub:publish_item( - LBJID, LServer, ?NS_STORAGE_BOOKMARKS, JID, - <<"current">>, [El], PubOpts, all) of - {result, _} -> ok; - {error, _} = Err -> Err + case mod_pubsub:get_items(LBJID, ?NS_STORAGE_BOOKMARKS) of + {error, #stanza_error{reason = 'item-not-found'}} -> + PubOpts = [{persist_items, true}, + {access_model, whitelist}], + case mod_pubsub:publish_item( + LBJID, LServer, ?NS_STORAGE_BOOKMARKS, JID, + <<"current">>, [El], PubOpts, all) of + {result, _} -> ok; + {error, _} = Err -> Err + end; + _ -> + case mod_pubsub:publish_item( + LBJID, LServer, ?NS_STORAGE_BOOKMARKS, JID, + <<"current">>, [El], [], all) of + {result, _} -> ok; + {error, _} = Err -> Err + end end end; false -> ok end. --spec publish_pep_xmpp_bookmarks(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}. -publish_pep_xmpp_bookmarks(JID, Data) -> +-spec publish_pep_native_bookmarks(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}. +publish_pep_native_bookmarks(JID, Data) -> {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)), case gen_mod:is_loaded(LServer, mod_pubsub) of true -> @@ -323,12 +333,7 @@ publish_pep_xmpp_bookmarks(JID, Data) -> #bookmark_storage{conference = C} -> C; _ -> [] end, - PubOpts = [{persist_items, true}, - {access_model, whitelist}, - {max_items, max}, - {notify_retract,true}, - {notify_delete,true}, - {send_last_published_item, never}], + PubOpts = [{persist_items, true}, {access_model, whitelist}, {max_items, max}, {notify_retract,true}, {notify_delete,true}, {send_last_published_item, never}], case mod_pubsub:get_items(LBJID, ?NS_PEP_BOOKMARKS) of PepBookmarks when is_list(PepBookmarks) -> put(mod_private_pep_update, true), @@ -344,12 +349,12 @@ publish_pep_xmpp_bookmarks(JID, Data) -> {Map4, err_ret(Ret2, mod_pubsub:publish_item( LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, - jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all))}; + jid:encode(BookmarkJID), [xmpp:encode(PB)], [], all))}; _ -> {Map2, err_ret(Ret2, mod_pubsub:publish_item( LBJID, LServer, ?NS_PEP_BOOKMARKS, JID, - jid:encode(BookmarkJID), [xmpp:encode(PB)], PubOpts, all))} + jid:encode(BookmarkJID), [xmpp:encode(PB)], [], all))} end end, {PepBookmarksMap, ok}, Bookmarks), Ret4 = @@ -471,7 +476,6 @@ storage_bookmark_to_xmpp_bookmark(#bookmark_conference{name = Name, autojoin = A -spec pubsub_item_to_map(#pubsub_item{}, map()) -> map(). pubsub_item_to_map(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}, Map) -> - ?INFO_MSG("DECODING ~p", [B]), case xmpp:decode(B) of #pep_bookmarks_conference{} = B2 -> try jid:decode(Id) of @@ -523,7 +527,7 @@ bookmarks_to_pep(User, Server) -> Data = [{?NS_STORAGE_BOOKMARKS, El}], case publish_pep_storage_bookmarks(jid:make(User, Server), Data) of ok -> - case publish_pep_xmpp_bookmarks(jid:make(User, Server), Data) of + case publish_pep_native_bookmarks(jid:make(User, Server), Data) of ok -> {ok, <<"Bookmarks exported to PEP node">>}; {error, Err} -> From 0bdca8fd9e50b72382152b8496b1d064f673dbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 25 Oct 2023 16:46:03 +0200 Subject: [PATCH 0258/1302] Updated deps to bring support for tls-exporter channel binding --- rebar.config | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index 32c50d08c..4868d1553 100644 --- a/rebar.config +++ b/rebar.config @@ -37,7 +37,7 @@ {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.50"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.16"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "c98c1a7d190201dc4113babed91fdfedac2bf42a"}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.49"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.7.0"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3a123f2db18463d1b4fce2bd41d4e5033b77ac04"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. @@ -208,13 +208,13 @@ {plt_extra_apps, [asn1, odbc, public_key, stdlib, syntax_tools, eredis, idna, jiffy, luerl, jose, - cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, + cache_tab, eimp, esip, ezlib, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, sqlite3, stringprep, stun, xmpp, yconf]}, {plt_extra_apps, % For Erlang/OTP 25 and older - [cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, + [cache_tab, eimp, ezlib, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - sqlite3, stringprep, stun, xmpp, yconf]} + stringprep, stun, xmpp, yconf]} } ]}. {ct_opts, [{keep_logs, 20}]}. From 841d5c029905d5feabe07ab5a4db3aacfeedefb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 25 Oct 2023 20:05:27 +0200 Subject: [PATCH 0259/1302] Fix startup problem when having set EJABBERD_OPTS and logger options Make sure that logger options that we extract from config file are put in correct places when starting erl, directly after -ejabberd, custom EJABBERD_OPTS should be places after them. This fixes issue #4109 --- ejabberdctl.template | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 1a7403e71..8519374bb 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -89,11 +89,13 @@ ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump ERL_INETRC="$CONFIG_DIR"/inetrc # define ejabberd parameters -EJABBERD_OPTS="$EJABBERD_OPTS\ -$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" +LOG_OPTS= +EJABBERD_OPTS="\ +$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$EJABBERD_OPTS" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" From 36fffa5b23aec31a44c3d05b219cf88679c2a8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 25 Oct 2023 20:07:02 +0200 Subject: [PATCH 0260/1302] Revert dialyzer changes commited by mistake --- rebar.config | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 4868d1553..437393d1a 100644 --- a/rebar.config +++ b/rebar.config @@ -208,13 +208,13 @@ {plt_extra_apps, [asn1, odbc, public_key, stdlib, syntax_tools, eredis, idna, jiffy, luerl, jose, - cache_tab, eimp, esip, ezlib, fast_tls, fast_xml, fast_yaml, + cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, sqlite3, stringprep, stun, xmpp, yconf]}, {plt_extra_apps, % For Erlang/OTP 25 and older - [cache_tab, eimp, ezlib, fast_tls, fast_xml, fast_yaml, + [cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - stringprep, stun, xmpp, yconf]} + sqlite3, stringprep, stun, xmpp, yconf]} } ]}. {ct_opts, [{keep_logs, 20}]}. From c2d04bc478e8507fb00e07add720171d71cb8a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 25 Oct 2023 20:31:50 +0200 Subject: [PATCH 0261/1302] Don't set fail_if_no_peer_cert for eldap ssl client connections Looks like R26 generates error when this option is used for client connection, let's just use verify_peer/verify_none for ldap_tls_verify hard/soft options. This should fix issue #4110. --- src/eldap.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eldap.erl b/src/eldap.erl index 3a9d8974c..dc360a866 100644 --- a/src/eldap.erl +++ b/src/eldap.erl @@ -605,9 +605,9 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) -> []), CertOpts; Verify == soft -> - [{verify, verify_peer}, {fail_if_no_peer_cert, false}] ++ CertOpts ++ CacertOpts ++ DepthOpts; + [{verify, verify_none}] ++ CertOpts ++ CacertOpts ++ DepthOpts; Verify == hard -> - [{verify, verify_peer}, {fail_if_no_peer_cert, true}] ++ CertOpts ++ CacertOpts ++ DepthOpts; + [{verify, verify_peer}] ++ CertOpts ++ CacertOpts ++ DepthOpts; true -> [] end, {ok, connecting, From 81ceefe2f16cc738e3960087a3c89aadeade7cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 25 Oct 2023 20:35:25 +0200 Subject: [PATCH 0262/1302] Remove spurious line --- ejabberdctl.template | 1 - 1 file changed, 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 8519374bb..0e221f473 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -89,7 +89,6 @@ ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump ERL_INETRC="$CONFIG_DIR"/inetrc # define ejabberd parameters -LOG_OPTS= EJABBERD_OPTS="\ $(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ From 08a78a1654947046d2288a6660775feab5416b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 26 Oct 2023 11:17:17 +0200 Subject: [PATCH 0263/1302] Change logic for eldap tls_verify=soft and false Looks like originally both soft and hard worked the same way, so lets make them do that, and we also need to add {verify, verify_none} for false case otherwise it fails on R26. --- src/eldap.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eldap.erl b/src/eldap.erl index dc360a866..3676bd09a 100644 --- a/src/eldap.erl +++ b/src/eldap.erl @@ -605,10 +605,10 @@ init([Hosts, Port, Rootdn, Passwd, Opts]) -> []), CertOpts; Verify == soft -> - [{verify, verify_none}] ++ CertOpts ++ CacertOpts ++ DepthOpts; + [{verify, verify_peer}] ++ CertOpts ++ CacertOpts ++ DepthOpts; Verify == hard -> [{verify, verify_peer}] ++ CertOpts ++ CacertOpts ++ DepthOpts; - true -> [] + true -> [{verify, verify_none}] end, {ok, connecting, #eldap{hosts = Hosts, port = PortTemp, rootdn = Rootdn, From 52e7c166fca5790e79cc5d9c6deae244e21806a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 26 Oct 2023 14:31:40 +0200 Subject: [PATCH 0264/1302] Normalize pubsub `max_items` node options on read Older version used infinity value, for what never version use max, let's always return max in that case. --- src/nodetree_tree.erl | 49 ++++++++++++++++++++++++++++----------- src/nodetree_tree_sql.erl | 17 ++++++++------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index ea8d35c50..71ad4d5c6 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -73,13 +73,13 @@ get_node(Host, Node, _From) -> get_node(Host, Node) -> case mnesia:read({pubsub_node, {Host, Node}}) of - [Record] when is_record(Record, pubsub_node) -> Record; + [#pubsub_node{} = Record] -> fixup_node(Record); _ -> {error, xmpp:err_item_not_found(?T("Node not found"), ejabberd_option:language())} end. get_node(Nidx) -> case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of - [Record] when is_record(Record, pubsub_node) -> Record; + [#pubsub_node{} = Record] -> fixup_node(Record); _ -> {error, xmpp:err_item_not_found(?T("Node not found"), ejabberd_option:language())} end. @@ -87,7 +87,8 @@ get_nodes(Host) -> get_nodes(Host, infinity). get_nodes(Host, infinity) -> - mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}); + Nodes = mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}), + [fixup_node(N) || N <- Nodes]; get_nodes(Host, Limit) -> case mnesia:select( pubsub_node, @@ -96,16 +97,18 @@ get_nodes(Host, Limit) -> Node end), Limit, read) of '$end_of_table' -> []; - {Nodes, _} -> Nodes + {Nodes, _} -> [fixup_node(N) || N <- Nodes] end. get_all_nodes({_U, _S, _R} = Owner) -> Host = jid:tolower(jid:remove_resource(Owner)), - mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}); + Nodes = mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}), + [fixup_node(N) || N <- Nodes]; get_all_nodes(Host) -> - mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}) + Nodes = mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}) ++ mnesia:match_object(#pubsub_node{nodeid = {{'_', Host, '_'}, '_'}, - _ = '_'}). + _ = '_'}), + [fixup_node(N) || N <- Nodes]. get_parentnodes(Host, Node, _From) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of @@ -119,7 +122,8 @@ get_parentnodes_tree(Host, Node, _From) -> get_parentnodes_tree(Host, Node, 0, []). get_parentnodes_tree(Host, Node, Level, Acc) -> case catch mnesia:read({pubsub_node, {Host, Node}}) of - [Record] when is_record(Record, pubsub_node) -> + [#pubsub_node{} = Record0] -> + Record = fixup_node(Record0), Tree = [{Level, [Record]}|Acc], case Record#pubsub_node.parents of [Parent] -> get_parentnodes_tree(Host, Parent, Level+1, Tree); @@ -130,7 +134,8 @@ get_parentnodes_tree(Host, Node, Level, Acc) -> end. get_subnodes(Host, <<>>, infinity) -> - mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, parents = [], _ = '_'}); + Nodes = mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, parents = [], _ = '_'}), + [fixup_node(N) || N <- Nodes]; get_subnodes(Host, <<>>, Limit) -> case mnesia:select( pubsub_node, @@ -139,10 +144,10 @@ get_subnodes(Host, <<>>, Limit) -> Node end), Limit, read) of '$end_of_table' -> []; - {Nodes, _} -> Nodes + {Nodes, _} -> [fixup_node(N) || N <- Nodes] end; get_subnodes(Host, Node, infinity) -> - Q = qlc:q([N + Q = qlc:q([fixup_node(N) || #pubsub_node{nodeid = {NHost, _}, parents = Parents} = N @@ -158,9 +163,12 @@ get_subnodes(Host, Node, Limit) -> end), Limit, read) of '$end_of_table' -> []; {Nodes, _} -> - lists:filter( - fun(#pubsub_node{parents = Parents}) -> - lists:member(Node, Parents) + lists:filtermap( + fun(#pubsub_node{parents = Parents} = N2) -> + case lists:member(Node, Parents) of + true -> {true, fixup_node(N2)}; + _ -> false + end end, Nodes) end. @@ -236,3 +244,16 @@ delete_node(Host, Node) -> end, Removed), Removed. + +fixup_node(#pubsub_node{options = Options} = Node) -> + Res = lists:splitwith( + fun({max_items, infinity}) -> true; + (_) -> false + end, Options), + Options2 = case Res of + {Before, [_ | After]} -> + Before ++ [{max_items, max} | After]; + {Rest, []} -> + Rest + end, + Node#pubsub_node{options = Options2}. diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index ccb2b0e2a..608e8ea53 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -328,13 +328,16 @@ raw_to_node(Host, {Node, Parent, Type, Nidx}) -> "where nodeid=%(Nidx)d")) of {selected, ROptions} -> - DbOpts = lists:map(fun ({Key, Value}) -> - RKey = misc:binary_to_atom(Key), - Tokens = element(2, erl_scan:string(binary_to_list(<>))), - RValue = element(2, erl_parse:parse_term(Tokens)), - {RKey, RValue} - end, - ROptions), + DbOpts = lists:map( + fun({<<"max_items">>, <<"infinity">>}) -> + {max_items, max}; + ({Key, Value}) -> + RKey = misc:binary_to_atom(Key), + Tokens = element(2, erl_scan:string(binary_to_list(<>))), + RValue = element(2, erl_parse:parse_term(Tokens)), + {RKey, RValue} + end, + ROptions), Module = misc:binary_to_atom(<<"node_", Type/binary, "_sql">>), StdOpts = Module:options(), lists:foldl(fun ({Key, Value}, Acc) -> From 3bf4cf5c3f5a5f4dec12f7068efd50c40a6a5474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 26 Oct 2023 15:44:45 +0200 Subject: [PATCH 0265/1302] Fix reversed logic in node fixup function --- src/nodetree_tree.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index 71ad4d5c6..026f32014 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -247,8 +247,8 @@ delete_node(Host, Node) -> fixup_node(#pubsub_node{options = Options} = Node) -> Res = lists:splitwith( - fun({max_items, infinity}) -> true; - (_) -> false + fun({max_items, infinity}) -> false; + (_) -> true end, Options), Options2 = case Res of {Before, [_ | After]} -> From 3ed2b41a5a0e26a82de454836df7ecccde34715c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 31 Oct 2023 09:41:25 +0100 Subject: [PATCH 0266/1302] Update xmpp to bring tls-server-end-point channel binding and sasl2 codec --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 437393d1a..b38752b88 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3a123f2db18463d1b4fce2bd41d4e5033b77ac04"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "cf43573838ca6340cc6737f6485882d2d662a3ef"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 3ad30c3ff59633f695b59861d8e978ac3b22387b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 31 Oct 2023 10:15:35 +0100 Subject: [PATCH 0267/1302] Use correct commit hash for xmpp --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index b38752b88..90221ea4e 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "cf43573838ca6340cc6737f6485882d2d662a3ef"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "307deb6065476bf102d9a8eeb594524eaed36c13"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 7de0ba95d0a32d9857c1fcac5032a7f7fde4fd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 9 Nov 2023 15:43:47 +0100 Subject: [PATCH 0268/1302] Update mysql to bring sha256_password auth plugin support --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index eafa614fd..fb505e8f8 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below('22', true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, - {config(:mysql), {:p1_mysql, " >= 1.0.22"}}, + {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"}}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: diff --git a/mix.lock b/mix.lock index a50543bb5..05423248c 100644 --- a/mix.lock +++ b/mix.lock @@ -21,7 +21,7 @@ "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.22", "593107adbce3df1bb460fd442320ff540dfba0232f45278441ef8d2de8d08163", [:rebar3], [], "hexpm", "188f04b3ba265a6e7657c67a6780a2f5f973fe86670159ef593aaf44dbd3d5a2"}, + "p1_mysql": {:git, "https://github.com/processone/p1_mysql.git", "f685408b910c425b9905d4ddcdbedba717a5b48c", [ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"]}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.23", "4a8c17b642dcf5265a910d1a0b86ffb2a9dd057c7b51c3def5eacbcc4f27ced9", [:rebar3], [{:xmpp, "1.7.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "819222bcb5a74581263282ff9cdc679adeefc663dcf49ddc778aeaae90be05a6"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, diff --git a/rebar.config b/rebar.config index 90221ea4e..d296fa87f 100644 --- a/rebar.config +++ b/rebar.config @@ -63,7 +63,7 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.22"}}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", "f685408b910c425b9905d4ddcdbedba717a5b48c"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.23"}}}}, From 4ea46c57653b3d9ea9c1386d1cc3dd07163d74d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 15 Nov 2023 14:16:53 +0100 Subject: [PATCH 0269/1302] Set EJABBERD_OPTS back to "", and use previous flags as example Also try to clarify distinction between EJABBERD_OPTS and ERLANG_OPTS --- ejabberdctl.cfg.example | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ejabberdctl.cfg.example b/ejabberdctl.cfg.example index da9e1bd32..6887fb4cc 100644 --- a/ejabberdctl.cfg.example +++ b/ejabberdctl.cfg.example @@ -109,7 +109,10 @@ #' ERL_OPTIONS: Additional Erlang options # # The next variable allows to specify additional options passed to -# erlang. See erl(1) for more info. +# all commands using erlang interpreter. This applies to starting +# ejabberd server itself but also auxiliary commands like for example +# starting debug shell. See erl(1) for list of commands that can be +# used here. # # It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you # want to add local modules in this path. @@ -126,10 +129,11 @@ # ejabberd is started from an init.d script options -noshell and -detached are # added implicitly. See erl(1) for more info. # +# For example you can use value "-heart -env HEART_BEAT_TIMEOUT 120 -env ERL_CRASH_DUMP_SECONDS 60" +# # Default: "" # #EJABBERD_OPTS="" -EJABBERD_OPTS="-heart -env HEART_BEAT_TIMEOUT 120 -env ERL_CRASH_DUMP_SECONDS 60" #. #' ERLANG_NODE: Erlang node name From b0a9b58958b9f709a1e32762d476b6bab22d9202 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 16 Nov 2023 15:58:41 +0300 Subject: [PATCH 0270/1302] Use the first unique index as a primary key in ejabberd_sql_schema --- include/ejabberd_sql.hrl | 3 +- src/ejabberd_sql_schema.erl | 253 +++++++++++++++++++++++++++--------- 2 files changed, 193 insertions(+), 63 deletions(-) diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index 6fe86f759..629fc2b1c 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -53,7 +53,8 @@ -record(sql_index, {columns, - unique = false :: boolean()}). + unique = false :: boolean(), + meta = #{}}). -record(sql_column, {name :: binary(), type, default = false, diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 172bc41e4..d5d89d5f7 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -351,12 +351,11 @@ guess_version(Host, Schemas) -> fun(Schema) -> lists:all( fun(Table) -> - Table2 = filter_table_sh(Table), CurrentColumns = get_table_schema( - Host, Table2#sql_table.name), + Host, Table#sql_table.name), check_columns_compatibility( - Table2#sql_table.columns, + Table#sql_table.columns, CurrentColumns) end, Schema#sql_schema.tables) end, Schemas), @@ -568,6 +567,56 @@ format_create_index(mysql, _DBVersion, Table, Index) -> end, Index#sql_index.columns)), <<");">>]. +format_primary_key(mysql, _DBVersion, Table) -> + case lists:filter( + fun(#sql_index{meta = #{primary_key := true}}) -> true; + (_) -> false + end, Table#sql_table.indices) of + [] -> []; + [I] -> + [[<<" ">>, + <<"PRIMARY KEY (">>, + lists:join( + <<", ">>, + lists:map( + fun(Col) -> + format_mysql_index_column(Table, Col) + end, I#sql_index.columns)), + <<")">>]] + end; +format_primary_key(_DBType, _DBVersion, Table) -> + case lists:filter( + fun(#sql_index{meta = #{primary_key := true}}) -> true; + (_) -> false + end, Table#sql_table.indices) of + [] -> []; + [I] -> + [[<<" ">>, + <<"PRIMARY KEY (">>, + lists:join(<<", ">>, I#sql_index.columns), + <<")">>]] + end. + +format_add_primary_key(sqlite = DBType, DBVersion, Table, Index) -> + format_create_index(DBType, DBVersion, Table, Index); +format_add_primary_key(mysql, _DBVersion, Table, Index) -> + TableName = Table#sql_table.name, + [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + Index#sql_index.columns), + <<");">>]; +format_add_primary_key(_DBType, _DBVersion, Table, Index) -> + TableName = Table#sql_table.name, + [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + lists:map( + fun(Col) -> + format_mysql_index_column(Table, Col) + end, Index#sql_index.columns)), + <<");">>]. + format_create_table(pgsql = DBType, DBVersion, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( @@ -576,13 +625,18 @@ format_create_table(pgsql = DBType, DBVersion, Table) -> <<",\n">>, lists:map( fun(C) -> format_column_def(DBType, DBVersion, C) end, - Table#sql_table.columns)), + Table#sql_table.columns) ++ + format_primary_key(DBType, DBVersion, Table)), <<"\n);\n">>])] ++ - lists:map( - fun(I) -> - iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), - <<"\n">>]) + lists:flatmap( + fun(#sql_index{meta = #{primary_key := true}}) -> + []; + (#sql_index{meta = #{ignore := true}}) -> + []; + (I) -> + [iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>])] end, Table#sql_table.indices); format_create_table(sqlite = DBType, DBVersion, Table) -> @@ -593,13 +647,18 @@ format_create_table(sqlite = DBType, DBVersion, Table) -> <<",\n">>, lists:map( fun(C) -> format_column_def(DBType, DBVersion, C) end, - Table#sql_table.columns)), + Table#sql_table.columns) ++ + format_primary_key(DBType, DBVersion, Table)), <<"\n);\n">>])] ++ - lists:map( - fun(I) -> - iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), - <<"\n">>]) + lists:flatmap( + fun(#sql_index{meta = #{primary_key := true}}) -> + []; + (#sql_index{meta = #{ignore := true}}) -> + []; + (I) -> + [iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>])] end, Table#sql_table.indices); format_create_table(mysql = DBType, DBVersion, Table) -> @@ -610,13 +669,18 @@ format_create_table(mysql = DBType, DBVersion, Table) -> <<",\n">>, lists:map( fun(C) -> format_column_def(DBType, DBVersion, C) end, - Table#sql_table.columns)), + Table#sql_table.columns) ++ + format_primary_key(DBType, DBVersion, Table)), <<"\n) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n">>])] ++ - lists:map( - fun(I) -> - iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), - <<"\n">>]) + lists:flatmap( + fun(#sql_index{meta = #{primary_key := true}}) -> + []; + (#sql_index{meta = #{ignore := true}}) -> + []; + (I) -> + [iolist_to_binary( + [format_create_index(DBType, DBVersion, Table, I), + <<"\n">>])] end, Table#sql_table.indices). %format_create_table(DBType, _DBVersion, Table) -> @@ -644,12 +708,11 @@ create_table(Host, Table) -> create_tables(Host, Module, Schema) -> lists:foreach( fun(Table) -> - Table2 = filter_table_sh(Table), - Res = create_table(Host, Table2), + Res = create_table(Host, Table), case Res of {error, Error} -> ?ERROR_MSG("Failed to create table ~s: ~p", - [Table2#sql_table.name, Error]), + [Table#sql_table.name, Error]), error(Error); _ -> ok @@ -677,9 +740,60 @@ should_update_schema(Host) -> false end. -update_schema(Host, Module, Schemas) -> +preprocess_table(Host, Table) -> + Table1 = filter_table_sh(Table), + ImplicitPK = + case ejabberd_option:sql_type(Host) of + pgsql -> false; + sqlite -> + case lists:keyfind(bigserial, #sql_column.type, + Table1#sql_table.columns) of + false -> false; + #sql_column{name = Name} -> {ok, Name} + end; + mysql -> + case lists:keyfind(bigserial, #sql_column.type, + Table1#sql_table.columns) of + false -> false; + #sql_column{name = Name} -> {ok, Name} + end + end, + Indices = + case ImplicitPK of + false -> + {Inds, _} = + lists:mapfoldl( + fun(#sql_index{unique = true} = I, false) -> + {I#sql_index{ + meta = (I#sql_index.meta)#{primary_key => true}}, + true}; + (I, Acc) -> + {I, Acc} + end, false, Table1#sql_table.indices), + Inds; + {ok, CN} -> + lists:map( + fun(#sql_index{columns = [CN1]} = I) when CN == CN1 -> + I#sql_index{ + meta = (I#sql_index.meta)#{ignore => true}}; + (I) -> I + end, + Table1#sql_table.indices) + end, + Table1#sql_table{indices = Indices}. + +preprocess_schemas(Host, Schemas) -> + lists:map( + fun(Schema) -> + Schema#sql_schema{ + tables = lists:map(fun(T) -> preprocess_table(Host, T) end, + Schema#sql_schema.tables)} + end, Schemas). + +update_schema(Host, Module, RawSchemas) -> case should_update_schema(Host) of true -> + Schemas = preprocess_schemas(Host, RawSchemas), Version = get_current_version(Host, Module, Schemas), LastSchema = lists:max(Schemas), LastVersion = LastSchema#sql_schema.version, @@ -730,7 +844,7 @@ do_update_schema(Host, Module, Schema) -> <<" DEFAULT ">>, Default, <<";\n">>]] ++ case Column#sql_column.default of - false -> + false when DBType /= sqlite -> [[<<"ALTER TABLE ">>, TableName, <<" ALTER COLUMN ">>, @@ -779,45 +893,57 @@ do_update_schema(Host, Module, Schema) -> _ -> ok end; - ({create_index, TableName, Columns}) -> - {value, Table1} = - lists:keysearch( - TableName, #sql_table.name, Schema#sql_schema.tables), - {value, Index1} = - lists:keysearch( - Columns, #sql_index.columns, Table1#sql_table.indices), - Table = filter_table_sh(Table1), - Index = + ({create_index, TableName, Columns1}) -> + Columns = case ejabberd_sql:use_new_schema() of true -> - Index1; + Columns1; false -> - Index1#sql_index{ - columns = - lists:delete( - <<"server_host">>, Index1#sql_index.columns) - } + lists:delete( + <<"server_host">>, Columns1) end, - Res = - ejabberd_sql:sql_query( - Host, - fun(DBType, DBVersion) -> - SQL1 = format_create_index( - DBType, DBVersion, Table, Index), - SQL = iolist_to_binary(SQL1), - ?INFO_MSG("Create index ~s/~p:~n~s~n", - [Table#sql_table.name, - Index#sql_index.columns, - SQL]), - ejabberd_sql:sql_query_t(SQL) - end), - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Index} = + lists:keysearch( + Columns, #sql_index.columns, Table#sql_table.indices), + case Index#sql_index.meta of + #{ignore := true} -> ok; _ -> - ok + Res = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + case Index#sql_index.meta of + #{primary_key := true} -> + SQL1 = format_add_primary_key( + DBType, DBVersion, Table, Index), + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Add primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t(SQL); + _ -> + SQL1 = format_create_index( + DBType, DBVersion, Table, Index), + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Create index ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end end; ({drop_index, TableName, Columns1}) -> Columns = @@ -888,7 +1014,9 @@ test() -> #sql_column{name = <<"type">>, type = text}, #sql_column{name = <<"created_at">>, type = timestamp, default = true}], - indices = [#sql_index{ + indices = [#sql_index{columns = [<<"id">>], + unique = true}, + #sql_index{ columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]}, #sql_index{ columns = [<<"server_host">>, <<"username">>, <<"peer">>]}, @@ -905,7 +1033,8 @@ test() -> [<<"server_host">>, <<"origin_id">>]}, {drop_index, <<"archive2">>, [<<"server_host">>, <<"origin_id">>]}, - {drop_column, <<"archive2">>, <<"origin_id">>} + {drop_column, <<"archive2">>, <<"origin_id">>}, + {create_index, <<"archive2">>, [<<"id">>]} ]}, #sql_schema{ version = 1, From efffc3142ae0768f34821b9529a9afd9976a22c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 16 Nov 2023 16:52:18 +0100 Subject: [PATCH 0271/1302] Add implementation for SASL2 and Bind2 --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- src/ejabberd_c2s.erl | 19 ++++++- src/mod_offline.erl | 8 ++- src/mod_stream_mgmt.erl | 115 ++++++++++++++++++++++++++++++---------- 6 files changed, 114 insertions(+), 34 deletions(-) diff --git a/mix.exs b/mix.exs index fb505e8f8..8f996b678 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.7.0"}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 05423248c..6f1517e69 100644 --- a/mix.lock +++ b/mix.lock @@ -30,6 +30,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.7.0", "2c3034ed501c9ccb7a5e73a462a74e082b4cf9f485bb551732e56fb15106dac9", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "821bc8e2c4b288c031aa95aca17f3b215ed9bd634b7448d0f273baf8f6a46243"}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", [ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index d296fa87f..8fd6b49bd 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "307deb6065476bf102d9a8eeb594524eaed36c13"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index d7b3a3340..54c30659a 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -40,7 +40,9 @@ handle_stream_start/2, handle_stream_end/2, handle_unauthenticated_packet/2, handle_authenticated_packet/2, handle_auth_success/4, handle_auth_failure/4, handle_send/3, - handle_recv/3, handle_cdata/2, handle_unbinded_packet/2]). + handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, + inline_stream_features/1, handle_sasl2_inline/2, + handle_sasl2_inline_post/3, handle_bind2_inline/2]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -381,6 +383,9 @@ unauthenticated_stream_features(#{lserver := LServer}) -> authenticated_stream_features(#{lserver := LServer}) -> ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]). +inline_stream_features(#{lserver := LServer}) -> + ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], []}, [LServer]). + sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) -> Type = ejabberd_auth:store_type(LServer), Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer), @@ -533,6 +538,18 @@ handle_cdata(Data, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_cdata, LServer, State, [Data]). +handle_sasl2_inline(Els, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_inline, LServer, + {State, Els, []}, []). + +handle_sasl2_inline_post(Els, Results, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_inline_post, LServer, + State, [Els, Results]). + +handle_bind2_inline(Els, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_bind2_inline, LServer, + State, [Els]). + handle_recv(El, Pkt, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_recv, LServer, State, [El, Pkt]). diff --git a/src/mod_offline.erl b/src/mod_offline.erl index d7de2ccd4..0725e9a85 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -61,7 +61,8 @@ c2s_copy_session/2, webadmin_page/3, webadmin_user/4, - webadmin_user_parse_query/5]). + webadmin_user_parse_query/5, + c2s_handle_bind2_inline/2]). -export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). @@ -130,6 +131,7 @@ start(Host, Opts) -> {hook, disco_info, get_info, 50}, {hook, c2s_handle_info, c2s_handle_info, 50}, {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50}, {hook, webadmin_page_host, webadmin_page, 50}, {hook, webadmin_user, webadmin_user, 50}, {hook, webadmin_user_parse_query, webadmin_user_parse_query, 50}, @@ -297,6 +299,10 @@ c2s_copy_session(State, #{resend_offline := Flag}) -> c2s_copy_session(State, _) -> State. +c2s_handle_bind2_inline(#{jid := #jid{luser = LUser, lserver = LServer}} = State, _Els) -> + delete_all_msgs(LUser, LServer), + State. + -spec handle_offline_query(iq()) -> iq(). handle_offline_query(#iq{from = #jid{luser = U1, lserver = S1}, to = #jid{luser = U2, lserver = S2}, diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index e87109a5f..370a1bb87 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -33,10 +33,15 @@ c2s_authenticated_packet/2, c2s_unauthenticated_packet/2, c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2, c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3, - c2s_handle_recv/3]). + c2s_handle_recv/3, c2s_inline_features/2, + c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3, + c2s_handle_bind2_inline/2]). %% adjust pending session timeout / access queue -export([get_resume_timeout/1, set_resume_timeout/2, queue_find/2]). +%% for sasl2 inline resume +-export([has_resume_data/2, post_resume_tasks/1]). + -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include_lib("p1_utils/include/p1_queue.hrl"). @@ -65,6 +70,7 @@ start(_Host, Opts) -> init_cache(Opts), {ok, [{hook, c2s_stream_started, c2s_stream_started, 50}, {hook, c2s_post_auth_features, c2s_stream_features, 50}, + {hook, c2s_inline_features, c2s_inline_features, 50}, {hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50}, {hook, c2s_unbinded_packet, c2s_unbinded_packet, 50}, {hook, c2s_authenticated_packet, c2s_authenticated_packet, 50}, @@ -72,6 +78,9 @@ start(_Host, Opts) -> {hook, c2s_handle_recv, c2s_handle_recv, 50}, {hook, c2s_handle_info, c2s_handle_info, 50}, {hook, c2s_handle_call, c2s_handle_call, 50}, + {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 50}, + {hook, c2s_handle_sasl2_inline_post, c2s_handle_sasl2_inline_post, 50}, + {hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50}, {hook, c2s_closed, c2s_closed, 50}, {hook, c2s_terminated, c2s_terminated, 50}]}. @@ -112,6 +121,45 @@ c2s_stream_features(Acc, Host) -> Acc end. +c2s_inline_features({Sasl, Bind} = Acc, Host) -> + case gen_mod:is_loaded(Host, ?MODULE) of + true -> + {[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl], + [#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Bind]}; + false -> + Acc + end. + +c2s_handle_sasl2_inline({State, Els, Results} = Acc) -> + case lists:keytake(sm_resume, 1, Els) of + {value, Resume, Rest} -> + case has_resume_data(State, Resume) of + {ok, NewState, Resumed} -> + Rest2 = lists:keydelete(bind2_bind, 1, Rest), + {NewState, Rest2, [Resumed | Results]}; + {error, ResumeError} -> + {State, Els, [ResumeError | Results]} + end; + _ -> + Acc + end. + +c2s_handle_sasl2_inline_post(State, _Els, Results) -> + case lists:keyfind(sm_resumed, 1, Results) of + false -> + State; + _ -> + post_resume_tasks(State) + end. + +c2s_handle_bind2_inline(State, Els) -> + case lists:keyfind(sm_enable, 1, Els) of + #sm_enable{} = Pkt -> + negotiate_stream_mgmt(Pkt, State); + _ -> + State + end. + c2s_unauthenticated_packet(#{lang := Lang} = State, Pkt) when ?is_sm_packet(Pkt) -> %% XEP-0198 says: "For client-to-server connections, the client MUST NOT %% attempt to enable stream management until after it has completed Resource @@ -394,39 +442,48 @@ handle_a(State, #sm_a{h = H}) -> resend_rack(State1). -spec handle_resume(state(), sm_resume()) -> {ok, state()} | {error, state()}. -handle_resume(#{user := User, lserver := LServer, - lang := Lang, socket := Socket} = State, - #sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) -> - R = case inherit_session_state(State, PrevID) of - {ok, InheritedState} -> - {ok, InheritedState, H}; - {error, Err, InH} -> - {error, #sm_failed{reason = 'item-not-found', - text = xmpp:mk_text(format_error(Err), Lang), - h = InH, xmlns = Xmlns}, Err}; - {error, Err} -> - {error, #sm_failed{reason = 'item-not-found', - text = xmpp:mk_text(format_error(Err), Lang), - xmlns = Xmlns}, Err} - end, - case R of - {ok, #{jid := JID} = ResumedState, NumHandled} -> - State1 = check_h_attribute(ResumedState, NumHandled), - #{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1, - State2 = send(State1, #sm_resumed{xmlns = AttrXmlns, - h = AttrH, - previd = PrevID}), - State3 = resend_unacked_stanzas(State2), - State4 = send(State3, #sm_r{xmlns = AttrXmlns}), - State5 = ejabberd_hooks:run_fold(c2s_session_resumed, LServer, State4, []), - ?INFO_MSG("(~ts) Resumed session for ~ts", - [xmpp_socket:pp(Socket), jid:encode(JID)]), - {ok, State5}; +handle_resume(#{user := User, lserver := LServer, lang := Lang} = State, + #sm_resume{} = Resume) -> + case has_resume_data(State, Resume) of + {ok, ResumedState, ResumedEl} -> + State2 = send(ResumedState, ResumedEl), + {ok, post_resume_tasks(State2)}; {error, El, Reason} -> log_resumption_error(User, LServer, Reason), {error, send(State, El)} end. +-spec has_resume_data(state(), sm_resume()) -> + {ok, state(), sm_resumed()} | {error, sm_failed(), error_reason()}. +has_resume_data(#{lang := Lang} = State, + #sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) -> + case inherit_session_state(State, PrevID) of + {ok, InheritedState} -> + State1 = check_h_attribute(InheritedState, H), + #{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1, + {ok, InheritedState, #sm_resumed{xmlns = AttrXmlns, + h = AttrH, + previd = PrevID}}; + {error, Err, InH} -> + {error, #sm_failed{reason = 'item-not-found', + text = xmpp:mk_text(format_error(Err), Lang), + h = InH, xmlns = Xmlns}, Err}; + {error, Err} -> + {error, #sm_failed{reason = 'item-not-found', + text = xmpp:mk_text(format_error(Err), Lang), + xmlns = Xmlns}, Err} + end. + +-spec post_resume_tasks(state()) -> state(). +post_resume_tasks(#{lserver := LServer, socket := Socket, jid := JID, + mgmt_xmlns := AttrXmlns} = State) -> + State3 = resend_unacked_stanzas(State), + State4 = send(State3, #sm_r{xmlns = AttrXmlns}), + State5 = ejabberd_hooks:run_fold(c2s_session_resumed, LServer, State4, []), + ?INFO_MSG("(~ts) Resumed session for ~ts", + [xmpp_socket:pp(Socket), jid:encode(JID)]), + State5. + -spec transition_to_pending(state(), _) -> state(). transition_to_pending(#{mgmt_state := active, mod := Mod, mgmt_timeout := 0} = State, _Reason) -> From ad15659fb202d7962b0fa2c68490f1a1b8766f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 16 Nov 2023 18:45:33 +0100 Subject: [PATCH 0272/1302] Fix warnings --- mix.exs | 2 +- mix.lock | 21 +++++++++++++++++---- rebar.config | 2 +- src/mod_stream_mgmt.erl | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 8f996b678..969066282 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "d39576cf1e1545f1860966b36a659f26a9df9c1a", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 6f1517e69..5d15a6f33 100644 --- a/mix.lock +++ b/mix.lock @@ -1,35 +1,48 @@ %{ + "artificery": {:hex, :artificery, "0.4.3", "0bc4260f988dcb9dda4b23f9fc3c6c8b99a6220a331534fdf5bf2fd0d4333b02", [:mix], [], "hexpm", "12e95333a30e20884e937abdbefa3e7f5e05609c2ba8cf37b33f000b9ffc0504"}, "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"}, + "cheap_counters": {:git, "ssh://git@github.com/processone/cheap_counters.git", "f9a287ee1aad53b7ad29cc5d3883f8887d386021", [tag: "1.0.10"]}, + "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, + "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm", "bbc7008b0161a6f130d8d903b5b3232351fccc9c31a991f8fcbf2a12ace22995"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"}, "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, - "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, + "ejabberd": {:git, "https://github.com/processone/ejabberd", "d55955f7d8f81d491a0707852f119a615a87df46", [ref: "d55955f7d8f81d491a0707852f119a615a87df46"]}, + "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, - "ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"}, + "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, + "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, + "gun": {:hex, :gun, "2.0.1", "160a9a5394800fcba41bc7e6d421295cf9a7894c2252c0678244948e3336ad73", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "a10bc8d6096b9502205022334f719cc9a08d9adcfbfc0dbee9ef31b56274a20b"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, + "lager": {:hex, :lager, "3.6.10", "6172b43ab720ac33914ccd0aeb21fdbdf88213847707d4b91e6af57b2ae5c4d2", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm", "5d10499461826b79c5abee18bb594b3949cbdf76d9d9fd7e66d0a558137c21c9"}, "luerl": {:hex, :luerl, "1.0.0", "1b68c30649323590d5339b967b419260500ffe520cd3abc1987482a82d3b5a6c", [:rebar3], [], "hexpm", "c17bc45cb4b0845ec975387f9a5d8c81ab60456698527a29c96f78992af86bd1"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, + "myproto": {:git, "https://github.com/bosqueviejo/myproto", "a5ddd9f89625bbfe2c19840806f70b9061ca33d3", [ref: "a5ddd9f89625bbfe2c19840806f70b9061ca33d3"]}, + "neotoma": {:git, "https://github.com/seancribbs/neotoma.git", "9e57d8ebd4ebb02c3e2428b08f3a01e2ff834ce2", [ref: "master"]}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, "p1_mysql": {:git, "https://github.com/processone/p1_mysql.git", "f685408b910c425b9905d4ddcdbedba717a5b48c", [ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"]}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.23", "4a8c17b642dcf5265a910d1a0b86ffb2a9dd057c7b51c3def5eacbcc4f27ced9", [:rebar3], [{:xmpp, "1.7.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "819222bcb5a74581263282ff9cdc679adeefc663dcf49ddc778aeaae90be05a6"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, + "p1db": {:git, "ssh://git@github.com/processone/p1db.git", "5aac407d361bba9d14603182455e68a7c0300237", [ref: "5aac407d361bba9d14603182455e68a7c0300237"]}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, + "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", [ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "d39576cf1e1545f1860966b36a659f26a9df9c1a", [ref: "d39576cf1e1545f1860966b36a659f26a9df9c1a"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 8fd6b49bd..d403cdc4c 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "d39576cf1e1545f1860966b36a659f26a9df9c1a"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 370a1bb87..90cb919d0 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -442,7 +442,7 @@ handle_a(State, #sm_a{h = H}) -> resend_rack(State1). -spec handle_resume(state(), sm_resume()) -> {ok, state()} | {error, state()}. -handle_resume(#{user := User, lserver := LServer, lang := Lang} = State, +handle_resume(#{user := User, lserver := LServer} = State, #sm_resume{} = Resume) -> case has_resume_data(State, Resume) of {ok, ResumedState, ResumedEl} -> From c61e56d8a655d2db31fa922826b692eb10d29643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 16 Nov 2023 20:49:28 +0100 Subject: [PATCH 0273/1302] Fix for one more dialyzer warning --- src/mod_stream_mgmt.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 90cb919d0..050e913a4 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -137,7 +137,7 @@ c2s_handle_sasl2_inline({State, Els, Results} = Acc) -> {ok, NewState, Resumed} -> Rest2 = lists:keydelete(bind2_bind, 1, Rest), {NewState, Rest2, [Resumed | Results]}; - {error, ResumeError} -> + {error, ResumeError, _Reason} -> {State, Els, [ResumeError | Results]} end; _ -> From 3300f8bafb81ce4e58fb27a845c18063ce2cc7cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Nov 2023 15:39:07 +0100 Subject: [PATCH 0274/1302] Update fast_tls in mix --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 969066282..541cf2e05 100644 --- a/mix.exs +++ b/mix.exs @@ -102,7 +102,7 @@ defmodule Ejabberd.MixProject do {:cache_tab, "~> 1.0"}, {:eimp, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:fast_tls, "~> 1.1"}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "c98c1a7d190201dc4113babed91fdfedac2bf42a", override: true}, {:fast_xml, "~> 1.1"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, diff --git a/mix.lock b/mix.lock index 5d15a6f33..f462988ef 100644 --- a/mix.lock +++ b/mix.lock @@ -14,7 +14,7 @@ "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:hex, :fast_tls, "1.1.16", "85fa7f3112ea4ff5ccb4f3abadc130a8c855ad74eb00869487399cb0c322d208", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "aa08cca89b4044e74f1f12e399817d8beaeae3ee006c98a893c0bfb1d81fba51"}, + "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "c98c1a7d190201dc4113babed91fdfedac2bf42a", [ref: "c98c1a7d190201dc4113babed91fdfedac2bf42a"]}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, From 59bb6dae147c437c3741c01a164426da9243ca60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Nov 2023 18:47:42 +0100 Subject: [PATCH 0275/1302] Make apps passed to check in dialyzer consult configure optons --- rebar.config | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/rebar.config b/rebar.config index d403cdc4c..3ebb94123 100644 --- a/rebar.config +++ b/rebar.config @@ -207,14 +207,31 @@ {if_version_above, "25", {plt_extra_apps, [asn1, odbc, public_key, stdlib, syntax_tools, - eredis, idna, jiffy, luerl, jose, - cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, - mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - sqlite3, stringprep, stun, xmpp, yconf]}, + idna, jiffy, jose, + cache_tab, eimp, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_oauth2, p1_utils, pkix, + stringprep, xmpp, yconf, + {if_var_true, pam, epam}, + {if_var_true, redis, eredis}, + {if_var_true, sip, esip}, + {if_var_true, zlib, ezlib}, + {if_var_true, lua, luerl}, + {if_var_true, mysql, p1_mysql}, + {if_var_true, pgsql, p1_pgsql}, + {if_var_true, stun, stun}, + {if_var_true, sqlite, sqlite3}]}, {plt_extra_apps, % For Erlang/OTP 25 and older - [cache_tab, eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml, - mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, - sqlite3, stringprep, stun, xmpp, yconf]} + [cache_tab, eimp, fast_tls, fast_xml, fast_yaml, + mqtree, p1_acme, p1_oauth2, p1_utils, pkix, stringprep, xmpp, yconf, + {if_var_true, pam, epam}, + {if_var_true, redis, eredis}, + {if_var_true, sip, esip}, + {if_var_true, zlib, ezlib}, + {if_var_true, lua, luerl}, + {if_var_true, mysql, p1_mysql}, + {if_var_true, pgsql, p1_pgsql}, + {if_var_true, stun, stun}, + {if_var_true, sqlite, sqlite3}]} } ]}. {ct_opts, [{keep_logs, 20}]}. From fdee4efe9879d0b95e50970f1d0f3067beb425ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Nov 2023 18:50:11 +0100 Subject: [PATCH 0276/1302] Fix presenting features and returning results of inline bind2 elements --- mix.exs | 2 +- rebar.config | 2 +- src/ejabberd_c2s.erl | 9 ++++++-- src/mod_offline.erl | 6 ++--- src/mod_stream_mgmt.erl | 50 +++++++++++++++++++++++------------------ 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/mix.exs b/mix.exs index 541cf2e05..ea769a51f 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "d39576cf1e1545f1860966b36a659f26a9df9c1a", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "add2a3dd773afa2dcf5cd5db66fb6ad90669a9d9", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 3ebb94123..18ed4cb93 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "d39576cf1e1545f1860966b36a659f26a9df9c1a"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "add2a3dd773afa2dcf5cd5db66fb6ad90669a9d9"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 54c30659a..5a032205d 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -42,7 +42,8 @@ handle_auth_success/4, handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, inline_stream_features/1, handle_sasl2_inline/2, - handle_sasl2_inline_post/3, handle_bind2_inline/2]). + handle_sasl2_inline_post/3, handle_bind2_inline/2, + handle_bind2_inline_post/3]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -548,7 +549,11 @@ handle_sasl2_inline_post(Els, Results, #{lserver := LServer} = State) -> handle_bind2_inline(Els, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_bind2_inline, LServer, - State, [Els]). + {State, Els, []}, []). + +handle_bind2_inline_post(Els, Results, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_bind2_inline_post, LServer, + State, [Els, Results]). handle_recv(El, Pkt, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_recv, LServer, State, [El, Pkt]). diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 0725e9a85..a00530cb5 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -62,7 +62,7 @@ webadmin_page/3, webadmin_user/4, webadmin_user_parse_query/5, - c2s_handle_bind2_inline/2]). + c2s_handle_bind2_inline/1]). -export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). @@ -299,9 +299,9 @@ c2s_copy_session(State, #{resend_offline := Flag}) -> c2s_copy_session(State, _) -> State. -c2s_handle_bind2_inline(#{jid := #jid{luser = LUser, lserver = LServer}} = State, _Els) -> +c2s_handle_bind2_inline({#{jid := #jid{luser = LUser, lserver = LServer}} = State, Els, Results}) -> delete_all_msgs(LUser, LServer), - State. + {State, Els, Results}. -spec handle_offline_query(iq()) -> iq(). handle_offline_query(#iq{from = #jid{luser = U1, lserver = S1}, diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 050e913a4..4b59245a4 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -35,7 +35,7 @@ c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/2, c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3, - c2s_handle_bind2_inline/2]). + c2s_handle_bind2_inline/1]). %% adjust pending session timeout / access queue -export([get_resume_timeout/1, set_resume_timeout/2, queue_find/2]). @@ -125,7 +125,7 @@ c2s_inline_features({Sasl, Bind} = Acc, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> {[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl], - [#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Bind]}; + [#bind2_feature{var = ?NS_STREAM_MGMT_3} | Bind]}; false -> Acc end. @@ -152,12 +152,13 @@ c2s_handle_sasl2_inline_post(State, _Els, Results) -> post_resume_tasks(State) end. -c2s_handle_bind2_inline(State, Els) -> +c2s_handle_bind2_inline({State, Els, Results}) -> case lists:keyfind(sm_enable, 1, Els) of - #sm_enable{} = Pkt -> - negotiate_stream_mgmt(Pkt, State); + #sm_enable{xmlns = XMLNS} = Pkt -> + {State2, Res} = handle_enable_int(State#{mgmt_xmlns => XMLNS}, Pkt), + {State2, Els, [Res | Results]}; _ -> - State + {State, Els, Results} end. c2s_unauthenticated_packet(#{lang := Lang} = State, Pkt) when ?is_sm_packet(Pkt) -> @@ -400,28 +401,28 @@ perform_stream_mgmt(Pkt, #{mgmt_xmlns := Xmlns, lang := Lang} = State) -> xmlns = Xmlns}) end. --spec handle_enable(state(), sm_enable()) -> state(). -handle_enable(#{mgmt_timeout := DefaultTimeout, - mgmt_queue_type := QueueType, - mgmt_max_timeout := MaxTimeout, - mgmt_xmlns := Xmlns, jid := JID} = State, - #sm_enable{resume = Resume, max = Max}) -> +-spec handle_enable_int(state(), sm_enable()) -> {state(), sm_enabled()}. +handle_enable_int(#{mgmt_timeout := DefaultTimeout, + mgmt_queue_type := QueueType, + mgmt_max_timeout := MaxTimeout, + mgmt_xmlns := Xmlns, jid := JID} = State, + #sm_enable{resume = Resume, max = Max}) -> State1 = State#{mgmt_id => make_id()}, Timeout = if Resume == false -> - 0; - Max /= undefined, Max > 0, Max*1000 =< MaxTimeout -> + 0; + Max /= undefined, Max > 0, Max*1000 =< MaxTimeout -> Max*1000; - true -> + true -> DefaultTimeout end, Res = if Timeout > 0 -> - ?DEBUG("Stream management with resumption enabled for ~ts", - [jid:encode(JID)]), - #sm_enabled{xmlns = Xmlns, - id = encode_id(State1), - resume = true, - max = Timeout div 1000}; - true -> + ?DEBUG("Stream management with resumption enabled for ~ts", + [jid:encode(JID)]), + #sm_enabled{xmlns = Xmlns, + id = encode_id(State1), + resume = true, + max = Timeout div 1000}; + true -> ?DEBUG("Stream management without resumption enabled for ~ts", [jid:encode(JID)]), #sm_enabled{xmlns = Xmlns} @@ -429,6 +430,11 @@ handle_enable(#{mgmt_timeout := DefaultTimeout, State2 = State1#{mgmt_state => active, mgmt_queue => p1_queue:new(QueueType), mgmt_timeout => Timeout}, + {State2, Res}. + +-spec handle_enable(state(), sm_enable()) -> state(). +handle_enable(State, Enable) -> + {State2, Res} = handle_enable_int(State, Enable), send(State2, Res). -spec handle_r(state()) -> state(). From 91e74204b2a120046c63058fbb04899258c4420c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 20 Nov 2023 18:55:07 +0100 Subject: [PATCH 0277/1302] Teach mod_carboncopy how to interact with bind2 inline requests --- src/mod_carboncopy.erl | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 1976e5827..d22f8619b 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -37,7 +37,8 @@ -export([user_send_packet/1, user_receive_packet/1, iq_handler/1, disco_features/5, depends/2, mod_options/1, mod_doc/0]). --export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]). +-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1, + c2s_inline_features/2, c2s_handle_bind2_inline/1]). %% For debugging purposes -export([list/2]). @@ -56,7 +57,9 @@ start(_Host, _Opts) -> {hook, c2s_copy_session, c2s_copy_session, 50}, {hook, c2s_session_resumed, c2s_session_resumed, 50}, {hook, c2s_session_opened, c2s_session_opened, 50}, - {iq_handler, ejabberd_sm, ?NS_CARBONS_2, iq_handler}]}. + {hook, c2s_inline_features, c2s_inline_features, 50}, + {hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50}, + {iq_handler, ejabberd_sm, ?NS_CARBONS_2, iq_handler}]}. stop(_Host) -> ok. @@ -142,6 +145,23 @@ c2s_session_resumed(State) -> c2s_session_opened(State) -> maps:remove(carboncopy, State). +c2s_inline_features({Sasl, Bind} = Acc, Host) -> + case gen_mod:is_loaded(Host, ?MODULE) of + true -> + {Sasl, [#bind2_feature{var = ?NS_CARBONS_2} | Bind]}; + false -> + Acc + end. + +c2s_handle_bind2_inline({#{user := U, server := S, resource := R} = State, Els, Results}) -> + case lists:keyfind(carbons_enable, 1, Els) of + #carbons_enable{} -> + enable(S, U, R, ?NS_CARBONS_2), + {State, Els, Results}; + _ -> + {State, Els, Results} + end. + % Modified from original version: % - registered to the user_send_packet hook, to be called only once even for multicast % - do not support "private" message mode, and do not modify the original packet in any way From 9c7e91a1e9c62c594417144f5ea8a382765383f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 21 Nov 2023 13:55:40 +0100 Subject: [PATCH 0278/1302] Update xmpp and make opening bind2 session close other sessions with same tag --- mix.exs | 2 +- rebar.config | 2 +- src/ejabberd_c2s.erl | 7 ++++++- src/ejabberd_sm.erl | 44 ++++++++++++++++++++++++++++++++--------- src/mod_stream_mgmt.erl | 6 +++--- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/mix.exs b/mix.exs index ea769a51f..0f6b059f6 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "add2a3dd773afa2dcf5cd5db66fb6ad90669a9d9", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "dcb701e6800d827f8806c0d049aaf103cd724e2b", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 18ed4cb93..cbbf1d927 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "add2a3dd773afa2dcf5cd5db66fb6ad90669a9d9"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "dcb701e6800d827f8806c0d049aaf103cd724e2b"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 5a032205d..32ac79305 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -215,7 +215,12 @@ open_session(#{user := U, server := S, resource := R, Pres -> get_priority_from_presence(Pres) end, Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthModule}], - ejabberd_sm:open_session(SID, U, S, R, Prio, Info), + case State of + #{bind2_tag := Tag} -> + ejabberd_sm:open_session(SID, U, S, R, Prio, Info, Tag); + _ -> + ejabberd_sm:open_session(SID, U, S, R, Prio, Info) + end, xmpp_stream_in:establish(State2). %%%=================================================================== diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index c9317b81c..7222a91f5 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -39,6 +39,7 @@ route/2, open_session/5, open_session/6, + open_session/7, close_session/4, check_in_subscription/2, bounce_offline_message/1, @@ -147,15 +148,21 @@ route(Packet) -> ok end. --spec open_session(sid(), binary(), binary(), binary(), prio(), info()) -> ok. -open_session(SID, User, Server, Resource, Priority, Info) -> +-spec open_session(sid(), binary(), binary(), binary(), prio(), info(), binary() | undefined) -> ok. + +open_session(SID, User, Server, Resource, Priority, Info, Bind2Tag) -> set_session(SID, User, Server, Resource, Priority, Info), - check_for_sessions_to_replace(User, Server, Resource), + check_for_sessions_to_replace(User, Server, Resource, Bind2Tag), JID = jid:make(User, Server, Resource), ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver, [SID, JID, Info]). +-spec open_session(sid(), binary(), binary(), binary(), prio(), info()) -> ok. + +open_session(SID, User, Server, Resource, Priority, Info) -> + open_session(SID, User, Server, Resource, Priority, Info, undefined). + -spec open_session(sid(), binary(), binary(), binary(), info()) -> ok. open_session(SID, User, Server, Resource, Info) -> @@ -452,6 +459,13 @@ c2s_handle_info(#{lang := Lang} = State, replaced) -> State1 = State#{replaced => true}, Err = xmpp:serr_conflict(?T("Replaced by new connection"), Lang), {stop, ejabberd_c2s:send(State1, Err)}; +c2s_handle_info(#{lang := Lang, bind2_tag := Tag} = State, + {replaced_with_bind_tag, Bind2Tag}) when Tag == Bind2Tag -> + State1 = State#{replaced => true}, + Err = xmpp:serr_conflict(?T("Replaced by new connection"), Lang), + {stop, ejabberd_c2s:send(State1, Err)}; +c2s_handle_info(State, {replaced_with_bind_tag, _}) -> + State; c2s_handle_info(#{lang := Lang} = State, kick) -> Err = xmpp:serr_policy_violation(?T("has been kicked"), Lang), {stop, ejabberd_c2s:send(State, Err)}; @@ -826,16 +840,17 @@ clean_session_list([S1, S2 | Rest], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% On new session, check if some existing connections need to be replace --spec check_for_sessions_to_replace(binary(), binary(), binary()) -> ok | replaced. -check_for_sessions_to_replace(User, Server, Resource) -> +-spec check_for_sessions_to_replace(binary(), binary(), binary(), binary() | undefined) + -> ok | replaced. +check_for_sessions_to_replace(User, Server, Resource, Bind2Tag) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), - check_existing_resources(LUser, LServer, LResource), + check_existing_resources(LUser, LServer, LResource, Bind2Tag), check_max_sessions(LUser, LServer). --spec check_existing_resources(binary(), binary(), binary()) -> ok. -check_existing_resources(LUser, LServer, LResource) -> +-spec check_existing_resources(binary(), binary(), binary(), binary() | undefined) -> ok. +check_existing_resources(LUser, LServer, LResource, undefined) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer, LResource), if Ss == [] -> ok; @@ -847,7 +862,18 @@ check_existing_resources(LUser, LServer, LResource) -> (_) -> ok end, SIDs) - end. + end; +check_existing_resources(LUser, LServer, _LResource, Bind2Tag) -> + Mod = get_sm_backend(LServer), + Ss = get_sessions(Mod, LUser, LServer), + Len = size(Bind2Tag), + lists:foreach( + fun(#session{sid = {_, Pid}, usr = {_, _, <>}}) + when Pid /= self(), Tag == Bind2Tag -> + ejabberd_c2s:route(Pid, {replaced_with_bind_tag, Bind2Tag}); + (_) -> + ok + end, Ss). -spec is_existing_resource(binary(), binary(), binary()) -> boolean(). diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 4b59245a4..ab0889864 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -467,9 +467,9 @@ has_resume_data(#{lang := Lang} = State, {ok, InheritedState} -> State1 = check_h_attribute(InheritedState, H), #{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1, - {ok, InheritedState, #sm_resumed{xmlns = AttrXmlns, - h = AttrH, - previd = PrevID}}; + {ok, State1, #sm_resumed{xmlns = AttrXmlns, + h = AttrH, + previd = PrevID}}; {error, Err, InH} -> {error, #sm_failed{reason = 'item-not-found', text = xmpp:mk_text(format_error(Err), Lang), From 48f0d9c15ec5a77f5b1800c241ad2a54ee127794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 22 Nov 2023 18:34:18 +0100 Subject: [PATCH 0279/1302] Update xmpp to make us present both sasl1 and sasl2 with from in initial stanza --- mix.exs | 2 +- rebar.config | 2 +- src/ejabberd_c2s.erl | 2 +- src/ejabberd_sm.erl | 25 ++++++++++++++----------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/mix.exs b/mix.exs index 0f6b059f6..135f6778e 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "dcb701e6800d827f8806c0d049aaf103cd724e2b", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "2b0c95c807fc8f5862bc4301e0b7451a0617348e", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index cbbf1d927..594abece8 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "dcb701e6800d827f8806c0d049aaf103cd724e2b"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "2b0c95c807fc8f5862bc4301e0b7451a0617348e"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 32ac79305..de246cbae 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -216,7 +216,7 @@ open_session(#{user := U, server := S, resource := R, end, Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthModule}], case State of - #{bind2_tag := Tag} -> + #{bind2_session_id := Tag} -> ejabberd_sm:open_session(SID, U, S, R, Prio, Info, Tag); _ -> ejabberd_sm:open_session(SID, U, S, R, Prio, Info) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 7222a91f5..37ef9094b 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -149,8 +149,8 @@ route(Packet) -> end. --spec open_session(sid(), binary(), binary(), binary(), prio(), info(), binary() | undefined) -> ok. - +-spec open_session(sid(), binary(), binary(), binary(), prio(), info(), + {binary(), binary()} | undefined) -> ok. open_session(SID, User, Server, Resource, Priority, Info, Bind2Tag) -> set_session(SID, User, Server, Resource, Priority, Info), check_for_sessions_to_replace(User, Server, Resource, Bind2Tag), @@ -459,7 +459,7 @@ c2s_handle_info(#{lang := Lang} = State, replaced) -> State1 = State#{replaced => true}, Err = xmpp:serr_conflict(?T("Replaced by new connection"), Lang), {stop, ejabberd_c2s:send(State1, Err)}; -c2s_handle_info(#{lang := Lang, bind2_tag := Tag} = State, +c2s_handle_info(#{lang := Lang, bind2_session_id := {Tag, _}} = State, {replaced_with_bind_tag, Bind2Tag}) when Tag == Bind2Tag -> State1 = State#{replaced => true}, Err = xmpp:serr_conflict(?T("Replaced by new connection"), Lang), @@ -840,8 +840,8 @@ clean_session_list([S1, S2 | Rest], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% On new session, check if some existing connections need to be replace --spec check_for_sessions_to_replace(binary(), binary(), binary(), binary() | undefined) - -> ok | replaced. +-spec check_for_sessions_to_replace(binary(), binary(), binary(), + {binary(), binary()} | undefined) -> ok | replaced. check_for_sessions_to_replace(User, Server, Resource, Bind2Tag) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), @@ -849,7 +849,8 @@ check_for_sessions_to_replace(User, Server, Resource, Bind2Tag) -> check_existing_resources(LUser, LServer, LResource, Bind2Tag), check_max_sessions(LUser, LServer). --spec check_existing_resources(binary(), binary(), binary(), binary() | undefined) -> ok. +-spec check_existing_resources(binary(), binary(), binary(), + {binary(), binary()} | undefined) -> ok. check_existing_resources(LUser, LServer, LResource, undefined) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer, LResource), @@ -863,14 +864,16 @@ check_existing_resources(LUser, LServer, LResource, undefined) -> end, SIDs) end; -check_existing_resources(LUser, LServer, _LResource, Bind2Tag) -> +check_existing_resources(LUser, LServer, LResource, {Tag, Hash}) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer), - Len = size(Bind2Tag), lists:foreach( - fun(#session{sid = {_, Pid}, usr = {_, _, <>}}) - when Pid /= self(), Tag == Bind2Tag -> - ejabberd_c2s:route(Pid, {replaced_with_bind_tag, Bind2Tag}); + fun(#session{sid = {_, Pid}, usr = {_, _, Res}}) + when Pid /= self(), Res == LResource -> + ejabberd_c2s:route(Pid, replaced); + (#session{sid = {_, Pid}, usr = {_, _, Res}}) + when Pid /= self(), binary_part(Res, size(Res), -size(Hash)) == Hash -> + ejabberd_c2s:route(Pid, {replaced_with_bind_tag, Tag}); (_) -> ok end, Ss). From d2a84c96a4d85eba5491bf12407650b498cf7d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 22 Nov 2023 18:40:10 +0100 Subject: [PATCH 0280/1302] Update xmpp to fix incompatibility with < R21 --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 135f6778e..6dcfd1717 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "2b0c95c807fc8f5862bc4301e0b7451a0617348e", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "d8e862f3b43c7dac4ac53bb4a83f70e440017f92", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 594abece8..08b66093d 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "2b0c95c807fc8f5862bc4301e0b7451a0617348e"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "d8e862f3b43c7dac4ac53bb4a83f70e440017f92"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 225d14cbbec6a7e7158ec3a8067879cde6e7a18a Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 1 Dec 2023 00:07:21 +0100 Subject: [PATCH 0281/1302] Minor improvements in auth_password_format documentation --- src/ejabberd_options_doc.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index fb06ab510..c5ac3eba4 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -373,25 +373,26 @@ doc() -> note => "improved in 20.01", desc => [?T("The option defines in what format the users passwords " - "are stored:"), "", + "are stored, plain text or in http://../authentication/#scram[SCRAM] format:"), "", ?T("* 'plain': The password is stored as plain text " "in the database. This is risky because the passwords " "can be read if your database gets compromised. " "This is the default value. This format allows clients to " "authenticate using: the old Jabber Non-SASL (XEP-0078), " - "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1. "), "", + "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1/256/512(-PLUS). "), "", ?T("* 'scram': The password is not stored, only some information " "that allows to verify the hash provided by the client. " "It is impossible to obtain the original plain password " "from the stored information; for this reason, when this " "value is configured it cannot be changed to plain anymore. " "This format allows clients to authenticate using: " - "SASL PLAIN and SASL SCRAM-SHA-1."), - ?T("The default value is 'plain'.")]}}, + "SASL PLAIN and SASL SCRAM-SHA-1/256/512(-PLUS). The SCRAM variant " + "depends on the _`auth_scram_hash`_ option."), "", + ?T("The default value is 'plain'."), ""]}}, {auth_scram_hash, #{value => "sha | sha256 | sha512", desc => - ?T("Hash algorithm that should be used to store password in SCRAM format. " + ?T("Hash algorithm that should be used to store password in http://../authentication/#scram[SCRAM] format. " "You shouldn't change this if you already have passwords generated with " "a different algorithm - users that have such passwords will not be able " "to authenticate. The default value is 'sha'.")}}, From 7d4330b57a563624c26abcd435443c0d1bd1fc1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 4 Dec 2023 13:24:22 +0100 Subject: [PATCH 0282/1302] Increase default value of negotiation_timeout from 30s to 2m This timeout also covers in-band registration, and if user don't fill registration form in that time leads to disconnect and aborting registration. This will allow for more time to finish that. --- src/ejabberd_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 75a0caa8e..787d03628 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -606,7 +606,7 @@ options() -> {log_modules_fully, []}, {max_fsm_queue, undefined}, {modules, []}, - {negotiation_timeout, timer:seconds(30)}, + {negotiation_timeout, timer:seconds(120)}, {net_ticktime, timer:seconds(60)}, {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, {update_sql_schema, false}, From 6b2b89da78c3fcecb126e29f8eee2e33ae8b1ab8 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 8 Dec 2023 18:52:53 +0100 Subject: [PATCH 0283/1302] mod_push: Fix disabling of notifications Remove the correct field from the c2s state when the client explicitly disables push notifications. This fixes a regression introduced by commit c148ab4430688666d45fb3b3b56d29d67426c1cd. --- src/mod_push.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_push.erl b/src/mod_push.erl index d6cbd66d3..b71449696 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -458,7 +458,7 @@ c2s_handle_cast(State, {push_enable, ID}) -> {stop, State#{push_enabled => true, push_session_id => ID}}; c2s_handle_cast(State, push_disable) -> - State1 = maps:remove(push_disable, State), + State1 = maps:remove(push_enabled, State), State2 = maps:remove(push_session_id, State1), {stop, State2}; c2s_handle_cast(State, _Msg) -> From a5c973f86b6a0bcdc593775d8cfa8f9f69c589f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 12 Dec 2023 11:29:02 +0100 Subject: [PATCH 0284/1302] Mention in docs for sql_prepared_statements that it works with MySQL --- src/ejabberd_options_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index c5ac3eba4..b7d7ed83b 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1348,7 +1348,7 @@ doc() -> note => "added in 20.01", desc => ?T("This option is 'true' by default, and is useful to disable " - "prepared statements. The option is valid for PostgreSQL.")}}, + "prepared statements. The option is valid for PostgreSQL and MySQL.")}}, {sql_query_timeout, #{value => "timeout()", desc => From f87ab9a99f8e3b578d88e798c64c2ce0070ccc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 12 Dec 2023 14:37:01 +0100 Subject: [PATCH 0285/1302] Update xmpp --- mix.exs | 4 ++-- mix.lock | 4 ++-- rebar.config | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 6dcfd1717..9c048f7d3 100644 --- a/mix.exs +++ b/mix.exs @@ -102,7 +102,7 @@ defmodule Ejabberd.MixProject do {:cache_tab, "~> 1.0"}, {:eimp, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "c98c1a7d190201dc4113babed91fdfedac2bf42a", override: true}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d", override: true}, {:fast_xml, "~> 1.1"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "d8e862f3b43c7dac4ac53bb4a83f70e440017f92", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "bdba4eea3da81276c8879fec3e25dbb8884369ca", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index f462988ef..207221c4b 100644 --- a/mix.lock +++ b/mix.lock @@ -14,7 +14,7 @@ "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "c98c1a7d190201dc4113babed91fdfedac2bf42a", [ref: "c98c1a7d190201dc4113babed91fdfedac2bf42a"]}, + "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d", [ref: "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d"]}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, @@ -43,6 +43,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "d39576cf1e1545f1860966b36a659f26a9df9c1a", [ref: "d39576cf1e1545f1860966b36a659f26a9df9c1a"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "bdba4eea3da81276c8879fec3e25dbb8884369ca", [ref: "bdba4eea3da81276c8879fec3e25dbb8884369ca"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 08b66093d..c2b548926 100644 --- a/rebar.config +++ b/rebar.config @@ -37,7 +37,7 @@ {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.50"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "c98c1a7d190201dc4113babed91fdfedac2bf42a"}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d"}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.49"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "d8e862f3b43c7dac4ac53bb4a83f70e440017f92"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "bdba4eea3da81276c8879fec3e25dbb8884369ca"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From b556fae08fc025f5f5228b6e74984678b6c31874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 13 Dec 2023 10:08:31 +0100 Subject: [PATCH 0286/1302] Update xmpp to bring support for XEP-0474: SASL SCRAM Downgrade Protection --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9c048f7d3..ad516fa7d 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "bdba4eea3da81276c8879fec3e25dbb8884369ca", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "01e41061e6adb8569ed595a2b0701a2b91db83b0", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 207221c4b..71c4fc9a7 100644 --- a/mix.lock +++ b/mix.lock @@ -43,6 +43,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "bdba4eea3da81276c8879fec3e25dbb8884369ca", [ref: "bdba4eea3da81276c8879fec3e25dbb8884369ca"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "01e41061e6adb8569ed595a2b0701a2b91db83b0", [ref: "01e41061e6adb8569ed595a2b0701a2b91db83b0"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index c2b548926..b2acf2371 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "bdba4eea3da81276c8879fec3e25dbb8884369ca"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "01e41061e6adb8569ed595a2b0701a2b91db83b0"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From b5ce53c90710e54c3fa749d5d85a878c04043f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 18 Dec 2023 11:58:09 +0100 Subject: [PATCH 0287/1302] Update xmpp to fix issue with scram with missing channel bindings This makes scram downgrade protection hash calculation work properly when using non -plus sasl mechanism or when stream is not encrypted This should fix issue #4123. --- mix.exs | 4 ++-- mix.lock | 4 ++-- rebar.config | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index ad516fa7d..aa00767b4 100644 --- a/mix.exs +++ b/mix.exs @@ -102,7 +102,7 @@ defmodule Ejabberd.MixProject do {:cache_tab, "~> 1.0"}, {:eimp, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d", override: true}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "34e46e8a122e4dd83730eda1aa8b566ab55eea62", override: true}, {:fast_xml, "~> 1.1"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "01e41061e6adb8569ed595a2b0701a2b91db83b0", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "6059f7d98d449675552f4fa8030bfc163ceb6bd2", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 71c4fc9a7..0d9ff4bb5 100644 --- a/mix.lock +++ b/mix.lock @@ -14,7 +14,7 @@ "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d", [ref: "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d"]}, + "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "34e46e8a122e4dd83730eda1aa8b566ab55eea62", [ref: "34e46e8a122e4dd83730eda1aa8b566ab55eea62"]}, "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, @@ -43,6 +43,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "01e41061e6adb8569ed595a2b0701a2b91db83b0", [ref: "01e41061e6adb8569ed595a2b0701a2b91db83b0"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "6059f7d98d449675552f4fa8030bfc163ceb6bd2", [ref: "6059f7d98d449675552f4fa8030bfc163ceb6bd2"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index b2acf2371..0e3c6f6a5 100644 --- a/rebar.config +++ b/rebar.config @@ -37,7 +37,7 @@ {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.50"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "1dc4ac9a0ca4a7e0ef026194a1ce56b45db6fe8d"}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "34e46e8a122e4dd83730eda1aa8b566ab55eea62"}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.49"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "01e41061e6adb8569ed595a2b0701a2b91db83b0"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "6059f7d98d449675552f4fa8030bfc163ceb6bd2"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From bdb513a66066e45cf049a290b44bc26855116e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 19 Dec 2023 10:43:37 +0100 Subject: [PATCH 0288/1302] Update xmpp --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index aa00767b4..03dc9ea32 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "6059f7d98d449675552f4fa8030bfc163ceb6bd2", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ded8be8c169487688b11130eda566b1377ab3301", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 0d9ff4bb5..f3de83b82 100644 --- a/mix.lock +++ b/mix.lock @@ -43,6 +43,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "6059f7d98d449675552f4fa8030bfc163ceb6bd2", [ref: "6059f7d98d449675552f4fa8030bfc163ceb6bd2"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "ded8be8c169487688b11130eda566b1377ab3301", [ref: "ded8be8c169487688b11130eda566b1377ab3301"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 0e3c6f6a5..a71b5ad34 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "6059f7d98d449675552f4fa8030bfc163ceb6bd2"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "ded8be8c169487688b11130eda566b1377ab3301"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From a57bdfffb715abd1334045bf7b33c113c1b46ca0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Dec 2023 23:26:48 +0100 Subject: [PATCH 0289/1302] Document recent change from 7d4330b57 --- src/ejabberd_options_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index b7d7ed83b..ce5499aa6 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -886,7 +886,7 @@ doc() -> desc => ?T("Time to wait for an XMPP stream negotiation to complete. " "When timeout occurs, the corresponding XMPP stream is closed. " - "The default value is '30' seconds.")}}, + "The default value is '120' seconds.")}}, {net_ticktime, #{value => "timeout()", desc => From a4bb695fc3e694622f443fa040c0add89ee806f1 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 27 Dec 2023 08:49:39 +0300 Subject: [PATCH 0290/1302] Support for XEP-0424 "Message Retraction" --- include/mod_mam.hrl | 3 +- mix.exs | 2 +- rebar.config | 2 +- src/mod_mam.erl | 26 ++++++++++-- src/mod_mam_mnesia.erl | 25 ++++++++++-- src/mod_mam_sql.erl | 93 +++++++++++++++++++++++++++++++++++++----- src/mod_muc_room.erl | 2 +- 7 files changed, 131 insertions(+), 22 deletions(-) diff --git a/include/mod_mam.hrl b/include/mod_mam.hrl index 184afb4fb..8b95bf7d4 100644 --- a/include/mod_mam.hrl +++ b/include/mod_mam.hrl @@ -26,7 +26,8 @@ bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid(), packet = #xmlel{} :: xmlel() | message(), nick = <<"">> :: binary(), - type = chat :: chat | groupchat}). + type = chat :: chat | groupchat, + origin_id :: binary()}). -record(archive_prefs, {us = {<<"">>, <<"">>} :: {binary(), binary()}, diff --git a/mix.exs b/mix.exs index 03dc9ea32..d5d7a13f6 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ded8be8c169487688b11130eda566b1377ab3301", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "26dd833dcf66ebb790d9afe212b7a26f3a6c2328", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index a71b5ad34..56b5ccab1 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "ded8be8c169487688b11130eda566b1377ab3301"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "26dd833dcf66ebb790d9afe212b7a26f3a6c2328"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 58e400ade..f81b6a46b 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -69,7 +69,8 @@ all | chat | groupchat) -> any(). -callback extended_fields() -> [mam_query:property() | #xdata_field{}]. -callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat, - jid(), binary(), recv | send, integer()) -> ok | any(). + jid(), binary(), recv | send, integer(), binary(), + {true, binary()} | false) -> ok | any(). -callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any(). -callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error | {error, db_failure}. -callback select(binary(), jid(), jid(), mam_query:result(), @@ -512,6 +513,17 @@ set_stanza_id(Pkt, JID, ID) -> NewEls = [Archived, StanzaID|xmpp:get_els(Pkt)], xmpp:set_els(Pkt, NewEls). +-spec get_origin_id(stanza()) -> binary(). +get_origin_id(#message{type = groupchat} = Pkt) -> + integer_to_binary(get_stanza_id(Pkt)); +get_origin_id(#message{} = Pkt) -> + case xmpp:get_subtag(Pkt, #origin_id{}) of + #origin_id{id = ID} -> + ID; + _ -> + xmpp:get_id(Pkt) + end. + -spec mark_stored_msg(message(), jid()) -> message(). mark_stored_msg(#message{meta = #{stanza_id := ID}} = Pkt, JID) -> Pkt1 = set_stanza_id(Pkt, JID, integer_to_binary(ID)), @@ -585,7 +597,8 @@ disco_sm_features(empty, From, To, Node, Lang) -> disco_sm_features({result, OtherFeatures}, #jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, <<"">>, _Lang) -> - {result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2, ?NS_SID_0 | + {result, [?NS_MAM_TMP, ?NS_MAM_0, ?NS_MAM_1, ?NS_MAM_2, ?NS_SID_0, + ?NS_MESSAGE_RETRACT | OtherFeatures]}; disco_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc. @@ -1038,9 +1051,16 @@ store_mam_message(Pkt, U, S, Peer, Nick, Type, Dir) -> LServer = ejabberd_router:host_of_route(S), US = {U, S}, ID = get_stanza_id(Pkt), + OriginID = get_origin_id(Pkt), + Retract = case xmpp:get_subtag(Pkt, #message_retract{}) of + #message_retract{id = RID} when RID /= <<"">> -> + {true, RID}; + _ -> + false + end, El = xmpp:encode(Pkt), Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:store(El, LServer, US, Type, Peer, Nick, Dir, ID), + Mod:store(El, LServer, US, Type, Peer, Nick, Dir, ID, OriginID, Retract), Pkt. write_prefs(LUser, LServer, Host, Default, Always, Never) -> diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index f9e366860..5cbac5ffb 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -28,8 +28,10 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3, - is_empty_for_user/2, is_empty_for_room/3, delete_old_messages_batch/5]). + extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/6, + remove_from_archive/3, + is_empty_for_user/2, is_empty_for_room/3, delete_old_messages_batch/5, + transform/1]). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -187,7 +189,8 @@ delete_old_messages_batch(LServer, TimeStamp, Type, Batch, LastUS) -> extended_fields() -> []. -store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) -> +store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS, + OriginID, _Retract) -> case {mnesia:table_info(archive_msg, disc_only_copies), mnesia:table_info(archive_msg, memory)} of {[_|_], TableSize} when TableSize > ?TABLE_SIZE_LIMIT -> @@ -205,7 +208,8 @@ store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS) -> bare_peer = {PUser, PServer, <<>>}, type = Type, nick = Nick, - packet = Pkt}) + packet = Pkt, + origin_id = OriginID}) end, case mnesia:transaction(F) of {atomic, ok} -> @@ -330,3 +334,16 @@ filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 -> {lists:sublist(Msgs, Len), length(Msgs) =< Len}; filter_by_max(_Msgs, _Junk) -> {[], true}. + +transform({archive_msg, US, ID, Timestamp, Peer, BarePeer, + Packet, Nick, Type}) -> + #archive_msg{ + us = US, + id = ID, + timestamp = Timestamp, + peer = Peer, + bare_peer = BarePeer, + packet = Packet, + nick = Nick, + type = Type, + origin_id = <<"">>}. diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 4898944ec..ee208b197 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -29,7 +29,7 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/8, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, + extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, is_empty_for_user/2, is_empty_for_room/3, select_with_mucsub/6, delete_old_messages_batch/4, count_messages_to_delete/3]). @@ -49,6 +49,61 @@ init(Host, _Opts) -> schemas() -> [#sql_schema{ + version = 2, + tables = + [#sql_table{ + name = <<"archive">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"timestamp">>, type = bigint}, + #sql_column{name = <<"peer">>, type = text}, + #sql_column{name = <<"bare_peer">>, type = text}, + #sql_column{name = <<"xml">>, type = {text, big}}, + #sql_column{name = <<"txt">>, type = {text, big}}, + #sql_column{name = <<"id">>, type = bigserial}, + #sql_column{name = <<"kind">>, type = {text, 10}}, + #sql_column{name = <<"nick">>, type = text}, + #sql_column{name = <<"origin_id">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"timestamp">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"bare_peer">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"timestamp">>]}, + #sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"origin_id">>]} + ], + post_create = + fun(mysql, _) -> + ejabberd_sql:sql_query_t( + <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); + (_, _) -> + ok + end}, + #sql_table{ + name = <<"archive_prefs">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"def">>, type = text}, + #sql_column{name = <<"always">>, type = text}, + #sql_column{name = <<"never">>, type = text}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>], + unique = true}]}], + update = + [{add_column, <<"archive">>, <<"origin_id">>}, + {create_index, <<"archive">>, + [<<"server_host">>, <<"username">>, <<"origin_id">>]} + ]}, + #sql_schema{ version = 1, tables = [#sql_table{ @@ -200,7 +255,8 @@ delete_old_messages(ServerHost, TimeStamp, Type) -> extended_fields() -> [{withtext, <<"">>}]. -store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) -> +store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS, + OriginID, Retract) -> SUser = case Type of chat -> LUser; groupchat -> jid:encode({LUser, LHost, <<>>}) @@ -223,8 +279,19 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) -> _ -> fxml:element_to_binary(Pkt) end, - case SqlType of - mssql -> case ejabberd_sql:sql_query( + case Retract of + {true, RID} -> + ejabberd_sql:sql_query( + LServer, + ?SQL("delete from archive" + " where username=%(SUser)s" + " and %(LServer)H" + " and bare_peer=%(BarePeer)s" + " and origin_id=%(RID)s")); + false -> ok + end, + case SqlType of + mssql -> case ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "archive", @@ -236,13 +303,14 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) -> "xml=N%(XML)s", "txt=N%(Body)s", "kind=%(SType)s", - "nick=%(Nick)s"])) of + "nick=%(Nick)s", + "origin_id=%(OriginID)s"])) of {updated, _} -> ok; Err -> Err end; - _ -> case ejabberd_sql:sql_query( + _ -> case ejabberd_sql:sql_query( LServer, ?SQL_INSERT( "archive", @@ -254,13 +322,14 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) -> "xml=%(XML)s", "txt=%(Body)s", "kind=%(SType)s", - "nick=%(Nick)s"])) of + "nick=%(Nick)s", + "origin_id=%(OriginID)s"])) of {updated, _} -> ok; Err -> Err end - end. + end. write_prefs(LUser, _LServer, #archive_prefs{default = Default, never = Never, @@ -420,7 +489,7 @@ export(_Server) -> {archive_msg, fun([Host | HostTail], #archive_msg{us ={LUser, LServer}, id = _ID, timestamp = TS, peer = Peer, - type = Type, nick = Nick, packet = Pkt}) + type = Type, nick = Nick, packet = Pkt, origin_id = OriginID}) when (LServer == Host) or ([LServer] == HostTail) -> TStmp = misc:now_to_usec(TS), SUser = case Type of @@ -444,7 +513,8 @@ export(_Server) -> "xml=N%(XML)s", "txt=N%(Body)s", "kind=%(SType)s", - "nick=%(Nick)s"])]; + "nick=%(Nick)s", + "origin_id=%(OriginID)s"])]; _ -> [?SQL_INSERT( "archive", ["username=%(SUser)s", @@ -455,7 +525,8 @@ export(_Server) -> "xml=%(XML)s", "txt=%(Body)s", "kind=%(SType)s", - "nick=%(Nick)s"])] + "nick=%(Nick)s", + "origin_id=%(OriginID)s"])] end; (_Host, _R) -> [] diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 30df7dbaa..5d3099cb4 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5181,7 +5181,7 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, sub_els = [ #fasten_apply_to{id = Id, sub_els = [ #message_moderated{by = By, reason = Reason, - retract = #message_retract{}} + retract = #message_retract{id = Id}} ]}]}, {FromNick, _Role} = get_participant_data(From, StateData), Packet = ejabberd_hooks:run_fold(muc_filter_message, From 4f6730621a21ddbe12324886e5ec8465f6ba91fb Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 27 Dec 2023 09:28:02 +0300 Subject: [PATCH 0291/1302] Add default value to #archive_msg.origin_id --- include/mod_mam.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mod_mam.hrl b/include/mod_mam.hrl index 8b95bf7d4..f76e0780e 100644 --- a/include/mod_mam.hrl +++ b/include/mod_mam.hrl @@ -27,7 +27,7 @@ packet = #xmlel{} :: xmlel() | message(), nick = <<"">> :: binary(), type = chat :: chat | groupchat, - origin_id :: binary()}). + origin_id = <<"">> :: binary()}). -record(archive_prefs, {us = {<<"">>, <<"">>} :: {binary(), binary()}, From 8d2d3a6540f9f578c09cb31f006fd3c45eb2ba77 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 27 Dec 2023 09:42:22 +0300 Subject: [PATCH 0292/1302] Support XEP-0424 in mod_mam_mnesia --- src/mod_mam_mnesia.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 5cbac5ffb..95c5451d4 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -190,7 +190,27 @@ extended_fields() -> []. store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS, - OriginID, _Retract) -> + OriginID, Retract) -> + case Retract of + {true, RID} -> + mnesia:transaction( + fun () -> + {PUser, PServer, _} = jid:tolower(Peer), + Msgs = mnesia:select( + archive_msg, + ets:fun2ms( + fun(#archive_msg{ + us = US1, + bare_peer = Peer1, + origin_id = OriginID1} = Msg) + when US1 == {LUser, LServer}, + Peer1 == {PUser, PServer, <<>>}, + OriginID1 == RID -> Msg + end)), + lists:foreach(fun mnesia:delete_object/1, Msgs) + end); + false -> ok + end, case {mnesia:table_info(archive_msg, disc_only_copies), mnesia:table_info(archive_msg, memory)} of {[_|_], TableSize} when TableSize > ?TABLE_SIZE_LIMIT -> From 97568195d6e6cfad6c8a22917c3dd32132d554cb Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 27 Dec 2023 17:01:57 +0300 Subject: [PATCH 0293/1302] Update SQL schema files --- sql/lite.new.sql | 6 ++++++ sql/lite.sql | 6 ++++++ sql/mssql.new.sql | 4 ++++ sql/mssql.sql | 4 ++++ sql/mysql.new.sql | 7 +++++++ sql/mysql.sql | 7 +++++++ sql/pg.new.sql | 7 +++++++ sql/pg.sql | 7 +++++++ 8 files changed, 48 insertions(+) diff --git a/sql/lite.new.sql b/sql/lite.new.sql index f48a393a0..b5ec2098f 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -106,6 +106,7 @@ CREATE TABLE archive ( id INTEGER PRIMARY KEY AUTOINCREMENT, kind text, nick text, + origin_id text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -113,6 +114,11 @@ CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, username, CREATE INDEX i_archive_sh_username_peer ON archive (server_host, username, peer); CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, username, bare_peer); CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp); +CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, username, origin_id); CREATE TABLE archive_prefs ( username text NOT NULL, diff --git a/sql/lite.sql b/sql/lite.sql index a920a6cd5..67067480d 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -94,6 +94,7 @@ CREATE TABLE archive ( id INTEGER PRIMARY KEY AUTOINCREMENT, kind text, nick text, + origin_id text, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ); @@ -101,6 +102,11 @@ CREATE INDEX i_username_timestamp ON archive(username, timestamp); CREATE INDEX i_archive_username_peer ON archive (username, peer); CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer); CREATE INDEX i_timestamp ON archive(timestamp); +CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id); CREATE TABLE archive_prefs ( username text NOT NULL PRIMARY KEY, diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql index c78e28ea2..2227d0a6b 100644 --- a/sql/mssql.new.sql +++ b/sql/mssql.new.sql @@ -32,6 +32,7 @@ CREATE TABLE [dbo].[archive] ( [id] [bigint] IDENTITY(1,1) NOT NULL, [kind] [varchar] (10) NULL, [nick] [varchar] (250) NULL, + [origin_id] [varchar] (250) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED ( @@ -51,6 +52,9 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE INDEX [archive_sh_timestamp] ON [archive] (server_host, timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE INDEX [archive_sh_username_origin_id] ON [archive] (server_host, username, origin_id) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + CREATE TABLE [dbo].[archive_prefs] ( [username] [varchar] (250) NOT NULL, [server_host] [varchar] (250) NOT NULL, diff --git a/sql/mssql.sql b/sql/mssql.sql index cf2f8b040..93b8c6ed5 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -31,6 +31,7 @@ CREATE TABLE [dbo].[archive] ( [id] [bigint] IDENTITY(1,1) NOT NULL, [kind] [varchar] (10) NULL, [nick] [varchar] (250) NULL, + [origin_id] [varchar] (250) NOT NULL, [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [archive_PK] PRIMARY KEY CLUSTERED ( @@ -50,6 +51,9 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE INDEX [archive_timestamp] ON [archive] (timestamp) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); +CREATE INDEX [archive_username_origin_id] ON [archive] (username, origin_id) +WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); + CREATE TABLE [dbo].[archive_prefs] ( [username] [varchar] (250) NOT NULL, [def] [text] NOT NULL, diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 6254282d5..8df583cf7 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -110,6 +110,7 @@ CREATE TABLE archive ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, kind varchar(10), nick varchar(191), + origin_id varchar(191), created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -118,6 +119,12 @@ CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(server_host( CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_host(191), username(191), peer(191)); CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(server_host(191), username(191), bare_peer(191)); CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), timestamp); +CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); CREATE TABLE archive_prefs ( username varchar(191) NOT NULL, diff --git a/sql/mysql.sql b/sql/mysql.sql index af93302dd..1f97039b4 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -98,6 +98,7 @@ CREATE TABLE archive ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, kind varchar(10), nick varchar(191), + origin_id varchar(191), created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; @@ -106,6 +107,12 @@ CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), timestam CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(191)); CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), bare_peer(191)); CREATE INDEX i_timestamp USING BTREE ON archive(timestamp); +CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); CREATE TABLE archive_prefs ( username varchar(191) NOT NULL PRIMARY KEY, diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 6a13ad6f3..bfb01723e 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -261,6 +261,7 @@ CREATE TABLE archive ( id BIGSERIAL, kind text, nick text, + origin_id text, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -268,6 +269,12 @@ CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (server_host CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_host, username, peer); CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (server_host, username, bare_peer); CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host, timestamp); +CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (server_host, username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (server_host, username, origin_id); CREATE TABLE archive_prefs ( username text NOT NULL, diff --git a/sql/pg.sql b/sql/pg.sql index 813e690dd..3c54466c8 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -98,6 +98,7 @@ CREATE TABLE archive ( id BIGSERIAL, kind text, nick text, + origin_id text, created_at TIMESTAMP NOT NULL DEFAULT now() ); @@ -105,6 +106,12 @@ CREATE INDEX i_username_timestamp ON archive USING btree (username, timestamp); CREATE INDEX i_username_peer ON archive USING btree (username, peer); CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare_peer); CREATE INDEX i_timestamp ON archive USING btree (timestamp); +CREATE INDEX i_archive_username_origin_id ON archive USING btree (username, origin_id); + +-- To update 'archive' from ejabberd <= 23.10: +-- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; +-- CREATE INDEX i_archive_username_origin_id ON archive USING btree (username, origin_id); CREATE TABLE archive_prefs ( username text NOT NULL PRIMARY KEY, From 1326a7764a70afb3a1cc80795cc7228f0b6dab92 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 Nov 2023 19:35:27 +0100 Subject: [PATCH 0294/1302] ejabberd_commands: Update -type and remove obsolete @type --- include/ejabberd_commands.hrl | 58 ++++++++++++----------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index 812aa4d38..8707575f5 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -67,42 +67,24 @@ args_example = none :: none | [any()] | '_', result_example = none :: any()}). -%% TODO Fix me: Type is not up to date --type ejabberd_commands() :: #ejabberd_commands{name :: atom(), - tags :: [atom()], - desc :: string(), - longdesc :: string(), - version :: integer(), - module :: atom(), - function :: atom(), - args :: [aterm()], - policy :: open | restricted | admin | user, - access :: [{atom(),atom(),atom()}|atom()], - result :: rterm()}. +-type ejabberd_commands() :: #ejabberd_commands{name :: atom(), + tags :: [atom()], + desc :: string(), + longdesc :: string(), + version :: integer(), + note :: string(), + weight :: integer(), + module :: atom(), + function :: atom(), + args :: [aterm()], + policy :: open | restricted | admin | user, + access :: [{atom(),atom(),atom()}|atom()], + definer :: atom(), + result :: rterm(), + args_rename :: [{atom(),atom()}], + args_desc :: none | [string()] | '_', + result_desc :: none | string() | '_', + args_example :: none | [any()] | '_', + result_example :: any() + }. -%% @type ejabberd_commands() = #ejabberd_commands{ -%% name = atom(), -%% tags = [atom()], -%% desc = string(), -%% longdesc = string(), -%% module = atom(), -%% function = atom(), -%% args = [aterm()], -%% result = rterm() -%% }. -%% desc: Description of the command -%% args: Describe the accepted arguments. -%% This way the function that calls the command can format the -%% arguments before calling. - -%% @type atype() = integer | string | {tuple, [aterm()]} | {list, aterm()}. -%% Allowed types for arguments are integer, string, tuple and list. - -%% @type rtype() = integer | string | atom | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple. -%% A rtype is either an atom or a tuple with two elements. - -%% @type aterm() = {Name::atom(), Type::atype()}. -%% An argument term is a tuple with the term name and the term type. - -%% @type rterm() = {Name::atom(), Type::rtype()}. -%% A result term is a tuple with the term name and the term type. From 98d75192746c3d7f0b84ac2ea5c2a7ad50678d3f Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 11:17:22 +0100 Subject: [PATCH 0295/1302] ejabberd_commands: Add the command version as a tag "vX" --- src/ejabberd_commands.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index e61569777..416e26edf 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -147,13 +147,25 @@ register_commands(Definer, Commands) -> lists:foreach( fun(Command) -> %% XXX check if command exists - mnesia:dirty_write(Command#ejabberd_commands{definer = Definer}) + mnesia:dirty_write(register_command_prepare(Command, Definer)) %% ?DEBUG("This command is already defined:~n~p", [Command]) end, Commands), ejabberd_access_permissions:invalidate(), ok. + + + +register_command_prepare(Command, Definer) -> + Tags1 = Command#ejabberd_commands.tags, + Tags2 = case Command#ejabberd_commands.version of + 0 -> Tags1; + Version -> Tags1 ++ [list_to_atom("v"++integer_to_list(Version))] + end, + Command#ejabberd_commands{definer = Definer, tags = Tags2}. + + -spec unregister_commands([ejabberd_commands()]) -> ok. unregister_commands(Commands) -> From f18b8d464d7b7c09e639a9698f68de048969312c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Nov 2023 17:12:43 +0100 Subject: [PATCH 0296/1302] Commands: Add a new muc_sub tag to all the relevant commands --- src/mod_muc_admin.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 5c7453e2c..3ef13639c 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -160,7 +160,7 @@ get_commands_spec() -> args_example = ["/home/ejabberd/rooms.txt"], args = [{file, string}], result = {res, rescode}}, - #ejabberd_commands{name = create_room_with_opts, tags = [muc_room], + #ejabberd_commands{name = create_room_with_opts, tags = [muc_room, muc_sub], desc = "Create a MUC room name@service in host with given options", longdesc = "The syntax of `affiliations` is: `Type:JID,Type:JID`. " @@ -246,7 +246,7 @@ get_commands_spec() -> result_example = ["room1@muc.example.com", "room2@muc.example.com"], args = [{user, binary}, {host, binary}], result = {rooms, {list, {room, string}}}}, - #ejabberd_commands{name = get_user_subscriptions, tags = [muc], + #ejabberd_commands{name = get_user_subscriptions, tags = [muc, muc_sub], desc = "Get the list of rooms where this user is subscribed", note = "added in 21.04", module = ?MODULE, function = get_user_subscriptions, @@ -329,7 +329,7 @@ get_commands_spec() -> {value, string} ]}} }}}, - #ejabberd_commands{name = subscribe_room, tags = [muc_room], + #ejabberd_commands{name = subscribe_room, tags = [muc_room, muc_sub], desc = "Subscribe to a MUC conference", module = ?MODULE, function = subscribe_room, args_desc = ["User JID", "a user's nick", @@ -342,7 +342,7 @@ get_commands_spec() -> args = [{user, binary}, {nick, binary}, {room, binary}, {nodes, binary}], result = {nodes, {list, {node, string}}}}, - #ejabberd_commands{name = subscribe_room_many, tags = [muc_room], + #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], desc = "Subscribe several users to a MUC conference", note = "added in 22.05", longdesc = "This command accepts up to 50 users at once " @@ -365,14 +365,14 @@ get_commands_spec() -> {room, binary}, {nodes, binary}], result = {res, rescode}}, - #ejabberd_commands{name = unsubscribe_room, tags = [muc_room], + #ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub], desc = "Unsubscribe from a MUC conference", module = ?MODULE, function = unsubscribe_room, args_desc = ["User JID", "the room to subscribe"], args_example = ["tom@localhost", "room1@conference.localhost"], args = [{user, binary}, {room, binary}], result = {res, rescode}}, - #ejabberd_commands{name = get_subscribers, tags = [muc_room], + #ejabberd_commands{name = get_subscribers, tags = [muc_room, muc_sub], desc = "List subscribers of a MUC conference", module = ?MODULE, function = get_subscribers, args_desc = ["Room name", "MUC service"], From 0961fa183025fbb3992a0ef62d070f43841453b8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 Nov 2023 16:52:52 +0100 Subject: [PATCH 0297/1302] Commands: When result is rescode, result_desc is automatically added --- src/ejabberd_commands_doc.erl | 2 +- src/mod_admin_extra.erl | 21 +++++++-------------- src/mod_admin_update_sql.erl | 3 +-- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 178e75a54..a903610d3 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -386,7 +386,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, ResultText = case Result of {res,rescode} -> [?TAG(dl, [gen_param(res, integer, - "Status code (0 on success, 1 otherwise)", + "Status code (`0` on success, `1` otherwise)", HTMLOutput)])]; {res,restuple} -> [?TAG(dl, [gen_param(res, string, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index fa292ae38..d2dc51058 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -145,8 +145,7 @@ get_commands_spec() -> args_example = ["/home/me/srcs/ejabberd/mod_example.erl"], args_desc = ["Filename of erlang source file to compile"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = get_cookie, tags = [erlang], desc = "Get the Erlang cookie of this node", module = ?MODULE, function = get_cookie, @@ -206,8 +205,7 @@ get_commands_spec() -> args_example = [<<"peter">>, <<"myserver.com">>], args_desc = ["User name to check", "Server to check"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = check_password, tags = [accounts], desc = "Check if a password is correct", module = ?MODULE, function = check_password, @@ -215,8 +213,7 @@ get_commands_spec() -> args_example = [<<"peter">>, <<"myserver.com">>, <<"secret">>], args_desc = ["User name to check", "Server to check", "Password to check"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = check_password_hash, tags = [accounts], desc = "Check if the password hash is correct", longdesc = "Allows hash methods from the Erlang/OTP " @@ -229,8 +226,7 @@ get_commands_spec() -> args_desc = ["User name to check", "Server to check", "Password's hash value", "Name of hash method"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = change_password, tags = [accounts], desc = "Change the password of an account", module = ?MODULE, function = set_password, @@ -239,8 +235,7 @@ get_commands_spec() -> args_desc = ["User name", "Server name", "New password for user"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = ban_account, tags = [accounts], desc = "Ban an account: kick sessions and set random password", module = ?MODULE, function = ban_account, @@ -249,8 +244,7 @@ get_commands_spec() -> args_desc = ["User name to ban", "Server name", "Reason for banning user"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = num_resources, tags = [session], desc = "Get the number of resources of a user", module = ?MODULE, function = num_resources, @@ -278,8 +272,7 @@ get_commands_spec() -> args_desc = ["User name", "Server name", "User's resource", "Reason for closing session"], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"}, + result_example = ok}, #ejabberd_commands{name = status_num_host, tags = [session, statistics], desc = "Number of logged users with this status in host", policy = admin, diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 74d30b3e3..85fb1320d 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -72,8 +72,7 @@ get_commands_spec() -> args_example = [], args_desc = [], result = {res, rescode}, - result_example = ok, - result_desc = "Status code: 0 on success, 1 otherwise"} + result_example = ok} ]. update_sql() -> From c5a5dd859eeaf9c374ed0f7074f9f919ee2018d3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 Nov 2023 11:58:50 +0100 Subject: [PATCH 0298/1302] Commands: Improve syntax of many commands documentation --- src/ejabberd_acme.erl | 6 +-- src/ejabberd_admin.erl | 12 +++-- src/ejabberd_oauth.erl | 14 ++--- src/mod_admin_extra.erl | 116 ++++++++++++++++++++++------------------ src/mod_muc_admin.erl | 10 ++-- 5 files changed, 86 insertions(+), 72 deletions(-) diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 5d8f9bd76..eda2a3c59 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -450,11 +450,11 @@ delete_obsolete_data() -> %%%=================================================================== get_commands_spec() -> [#ejabberd_commands{name = request_certificate, tags = [acme], - desc = "Requests certificates for all or the specified " - "domains: all | domain1,domain2,...", + desc = "Requests certificates for all or some domains", + longdesc = "Domains can be `all`, or a list of domains separared with comma characters", module = ?MODULE, function = request_certificate, args_desc = ["Domains for which to acquire a certificate"], - args_example = ["all | domain.tld,conference.domain.tld,..."], + args_example = ["example.com,domain.tld,conference.domain.tld"], args = [{domains, string}], result = {res, restuple}}, #ejabberd_commands{name = list_certificates, tags = [acme], diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index a000beb54..c6a380e46 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -129,7 +129,7 @@ get_commands_spec() -> desc = "Reopen the log files after being renamed", longdesc = "This can be useful when an external tool is " "used for log rotation. See " - "https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files", + "[Log Files](https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files).", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, @@ -157,9 +157,10 @@ get_commands_spec() -> result = {levelatom, atom}}, #ejabberd_commands{name = set_loglevel, tags = [logs], desc = "Set the loglevel", + longdesc = "Possible loglevels: `none`, `emergency`, `alert`, `critical`, + `error`, `warning`, `notice`, `info`, `debug`.", module = ?MODULE, function = set_loglevel, - args_desc = ["Desired logging level: none | emergency | alert | critical " - "| error | warning | notice | info | debug"], + args_desc = ["Desired logging level"], args_example = ["debug"], args = [{loglevel, string}], result = {res, rescode}}, @@ -171,7 +172,8 @@ get_commands_spec() -> result_example = ["mod_configure", "mod_vcard"], result = {modules, {list, {module, string}}}}, #ejabberd_commands{name = update, tags = [server], - desc = "Update the given module, or use the keyword: all", + desc = "Update the given module", + longdesc = "To update all the possible modules, use `all`.", module = ?MODULE, function = update, args_example = ["mod_vcard"], args = [{module, string}], @@ -373,7 +375,7 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = set_master, tags = [cluster], desc = "Set master node of the clustered Mnesia tables", - longdesc = "If you provide as nodename `self`, this " + longdesc = "If `nodename` is set to `self`, then this " "node will be set as its own master.", module = ?MODULE, function = set_master, args_desc = ["Name of the erlang node that will be considered master of this node"], diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 83eceb1ba..c6801d54e 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -81,7 +81,7 @@ get_commands_spec() -> [ #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an oauth token for the given jid", + desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", module = ?MODULE, function = oauth_issue_token, args = [{jid, string},{ttl, integer}, {scopes, string}], policy = restricted, @@ -92,15 +92,15 @@ get_commands_spec() -> result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], - desc = "List oauth tokens, user, scope, and seconds to expire (only Mnesia)", - longdesc = "List oauth tokens, their user and scope, and how many seconds remain until expirity", + desc = "List [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) tokens, user, scope, and seconds to expire (only Mnesia)", + longdesc = "List OAuth tokens, their user and scope, and how many seconds remain until expirity", module = ?MODULE, function = oauth_list_tokens, args = [], policy = restricted, result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}} }, #ejabberd_commands{name = oauth_revoke_token, tags = [oauth], - desc = "Revoke authorization for a token", + desc = "Revoke authorization for an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token", note = "changed in 22.05", module = ?MODULE, function = oauth_revoke_token, args = [{token, binary}], @@ -109,7 +109,7 @@ get_commands_spec() -> result_desc = "Result code" }, #ejabberd_commands{name = oauth_add_client_password, tags = [oauth], - desc = "Add OAUTH client_id with password grant type", + desc = "Add [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id with password grant type", module = ?MODULE, function = oauth_add_client_password, args = [{client_id, binary}, {client_name, binary}, @@ -118,7 +118,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_add_client_implicit, tags = [oauth], - desc = "Add OAUTH client_id with implicit grant type", + desc = "Add [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id with implicit grant type", module = ?MODULE, function = oauth_add_client_implicit, args = [{client_id, binary}, {client_name, binary}, @@ -127,7 +127,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_remove_client, tags = [oauth], - desc = "Remove OAUTH client_id", + desc = "Remove [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id", module = ?MODULE, function = oauth_remove_client, args = [{client_id, binary}], policy = restricted, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index d2dc51058..152d481b8 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -113,14 +113,14 @@ depends(_Host, _Opts) -> %%% get_commands_spec() -> - Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n\n" + Vcard1FieldsString = "Some vcard field names in `get`/`set_vcard` are:\n\n" "* FN - Full Name\n" "* NICKNAME - Nickname\n" "* BDAY - Birthday\n" "* TITLE - Work: Position\n" "* ROLE - Work: Role\n", - Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n\n" + Vcard2FieldsString = "Some vcard field names and subnames in `get`/`set_vcard2` are:\n\n" "* N FAMILY - Family name\n" "* N GIVEN - Given name\n" "* N MIDDLE - Middle name\n" @@ -134,8 +134,8 @@ get_commands_spec() -> "* ORG ORGNAME - Work: Company\n" "* ORG ORGUNIT - Work: Department\n", - VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at " - "https://xmpp.org/extensions/xep-0054.html", + VcardXEP = "For a full list of vCard fields check [XEP-0054: vcard-temp]" + "(https://xmpp.org/extensions/xep-0054.html)", [ #ejabberd_commands{name = compile, tags = [erlang], @@ -162,9 +162,9 @@ get_commands_spec() -> result = {res, integer}, result_example = 0, result_desc = "Returns integer code:\n" - " - 0: code reloaded, module restarted\n" - " - 1: error: module not loaded\n" - " - 2: code not reloaded, but module restarted"}, + " - `0`: code reloaded, module restarted\n" + " - `1`: error: module not loaded\n" + " - `2`: code not reloaded, but module restarted"}, #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], desc = "Delete users that didn't log in last days, or that never logged", longdesc = "To protect admin accounts, configure this for example:\n" @@ -509,52 +509,56 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = process_rosteritems, tags = [roster], desc = "List/delete rosteritems that match filter", - longdesc = "Explanation of each argument:\n" - " - action: what to do with each rosteritem that " + longdesc = "Explanation of each argument:\n\n" + "* `action`: what to do with each rosteritem that " "matches all the filtering options\n" - " - subs: subscription type\n" - " - asks: pending subscription\n" - " - users: the JIDs of the local user\n" - " - contacts: the JIDs of the contact in the roster\n" + "* `subs`: subscription type\n" + "* `asks`: pending subscription\n" + "* `users`: the JIDs of the local user\n" + "* `contacts`: the JIDs of the contact in the roster\n" "\n" - " *** Mnesia: \n" + "**Mnesia backend:**\n" "\n" - "Allowed values in the arguments:\n" - " ACTION = list | delete\n" - " SUBS = SUB[:SUB]* | any\n" - " SUB = none | from | to | both\n" - " ASKS = ASK[:ASK]* | any\n" - " ASK = none | out | in\n" - " USERS = JID[:JID]* | any\n" - " CONTACTS = JID[:JID]* | any\n" - " JID = characters valid in a JID, and can use the " - "globs: *, ?, ! and [...]\n" + "Allowed values in the arguments:\n\n" + "* `action` = `list` | `delete`\n" + "* `subs` = `any` | SUB[:SUB]*\n" + "* `asks` = `any` | ASK[:ASK]*\n" + "* `users` = `any` | JID[:JID]*\n" + "* `contacts` = `any` | JID[:JID]*\n" + "\nwhere\n\n" + "* SUB = `none` | `from `| `to` | `both`\n" + "* ASK = `none` | `out` | `in`\n" + "* JID = characters valid in a JID, and can use the " + "globs: `*`, `?`, `!` and `[...]`\n" "\n" "This example will list roster items with subscription " - "'none', 'from' or 'to' that have any ask property, of " + "`none`, `from` or `to` that have any ask property, of " "local users which JID is in the virtual host " - "'example.org' and that the contact JID is either a " + "`example.org` and that the contact JID is either a " "bare server name (without user part) or that has a " - "user part and the server part contains the word 'icq'" - ":\n list none:from:to any *@example.org *:*@*icq*" + "user part and the server part contains the word `icq`" + ":\n `list none:from:to any *@example.org *:*@*icq*`" "\n\n" - " *** SQL:\n" + "**SQL backend:**\n" "\n" - "Allowed values in the arguments:\n" - " ACTION = list | delete\n" - " SUBS = any | none | from | to | both\n" - " ASKS = any | none | out | in\n" - " USERS = JID\n" - " CONTACTS = JID\n" - " JID = characters valid in a JID, and can use the " - "globs: _ and %\n" + "Allowed values in the arguments:\n\n" + "* `action` = `list` | `delete`\n" + "* `subs` = `any` | SUB\n" + "* `asks` = `any` | ASK\n" + "* `users` = JID\n" + "* `contacts` = JID\n" + "\nwhere\n\n" + "* SUB = `none` | `from` | `to` | `both`\n" + "* ASK = `none` | `out` | `in`\n" + "* JID = characters valid in a JID, and can use the " + "globs: `_` and `%`\n" "\n" "This example will list roster items with subscription " - "'to' that have any ask property, of " + "`to` that have any ask property, of " "local users which JID is in the virtual host " - "'example.org' and that the contact JID's " - "server part contains the word 'icq'" - ":\n list to any %@example.org %@%icq%", + "`example.org` and that the contact JID's " + "server part contains the word `icq`" + ":\n `list to any %@example.org %@%icq%`", module = mod_roster, function = process_rosteritems, args = [{action, string}, {subs, string}, {asks, string}, {users, string}, @@ -569,8 +573,8 @@ get_commands_spec() -> #ejabberd_commands{name = get_roster, tags = [roster], desc = "Get list of contacts in a local user roster", longdesc = - "Subscription can be: \"none\", \"from\", \"to\", \"both\". " - "Pending can be: \"in\", \"out\", \"none\".", + "`subscription` can be: `none`, `from`, `to`, `both`.\n\n" + "`pending` can be: `in`, `out`, `none`.", note = "improved in 23.10", policy = user, module = ?MODULE, function = get_roster, @@ -586,11 +590,12 @@ get_commands_spec() -> #ejabberd_commands{name = push_roster, tags = [roster], desc = "Push template roster from file to a user", longdesc = "The text file must contain an erlang term: a list " - "of tuples with username, servername, group and nick. Example:\n" - "[{<<\"user1\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 1\">>},\n" - " {<<\"user2\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 2\">>}].\n" - "When using UTF8 character encoding add /utf8 to certain string. Example:\n" - "[{<<\"user2\">>, <<\"localhost\">>, <<\"Workers\"/utf8>>, <<\"User 2\"/utf8>>}].", + "of tuples with username, servername, group and nick. For example:\n" + "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n" + " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`\n\n" + "If there are problems parsing UTF8 character encoding, " + "provide the corresponding string with the `<<\"STRING\"/utf8>>` syntax, for example:\n" + "`[{\"user2\", \"localhost\", \"Workers\", <<\"User 2\"/utf8>>}]`.", module = ?MODULE, function = push_roster, args = [{file, binary}, {user, binary}, {host, binary}], args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>], @@ -600,8 +605,8 @@ get_commands_spec() -> desc = "Push template roster from file to all those users", longdesc = "The text file must contain an erlang term: a list " "of tuples with username, servername, group and nick. Example:\n" - "[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n" - " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].", + "`[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n" + " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].`", module = ?MODULE, function = push_roster_all, args = [{file, binary}], args_example = [<<"/home/ejabberd/roster.txt">>], @@ -617,7 +622,9 @@ get_commands_spec() -> #ejabberd_commands{name = get_last, tags = [last], desc = "Get last activity information", - longdesc = "Timestamp is UTC and XEP-0082 format, for example: " + longdesc = "Timestamp is UTC and " + "[XEP-0082](https://xmpp.org/extensions/xep-0082.html)" + " format, for example: " "`2017-02-23T22:25:28.063062Z ONLINE`", module = ?MODULE, function = get_last, args = [{user, binary}, {host, binary}], @@ -775,7 +782,9 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = stats, tags = [statistics], - desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds processes", + desc = "Get some statistical value for the whole ejabberd server", + longdesc = "Allowed statistics `name` are: `registeredusers`, " + "`onlineusers`, `onlineusersnode`, `uptimeseconds`, `processes`.", policy = admin, module = ?MODULE, function = stats, args = [{name, binary}], @@ -785,7 +794,8 @@ get_commands_spec() -> result_desc = "Integer statistic value", result = {stat, integer}}, #ejabberd_commands{name = stats_host, tags = [statistics], - desc = "Get statistical value for this host: registeredusers onlineusers", + desc = "Get some statistical value for this host", + longdesc = "Allowed statistics `name` are: `registeredusers`, `onlineusers`.", policy = admin, module = ?MODULE, function = stats, args = [{name, binary}, {host, binary}], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 3ef13639c..8b5358ce8 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -93,10 +93,11 @@ depends(_Host, _Opts) -> get_commands_spec() -> [ #ejabberd_commands{name = muc_online_rooms, tags = [muc], - desc = "List existing rooms ('global' to get all vhosts)", + desc = "List existing rooms", + longdesc = "Ask for a specific host, or `global` to use all vhosts.", policy = admin, module = ?MODULE, function = muc_online_rooms, - args_desc = ["MUC service, or 'global' for all"], + args_desc = ["MUC service, or `global` for all"], args_example = ["muc.example.com"], result_desc = "List of rooms", result_example = ["room1@muc.example.com", "room2@muc.example.com"], @@ -104,10 +105,11 @@ get_commands_spec() -> args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = muc_online_rooms_by_regex, tags = [muc], - desc = "List existing rooms ('global' to get all vhosts) by regex", + desc = "List existing rooms filtered by regexp", + longdesc = "Ask for a specific host, or `global` to use all vhosts.", policy = admin, module = ?MODULE, function = muc_online_rooms_by_regex, - args_desc = ["MUC service, or 'global' for all", + args_desc = ["MUC service, or `global` for all", "Regex pattern for room name"], args_example = ["muc.example.com", "^prefix"], result_desc = "List of rooms with summary", From d4113d956985957b122c60e4b8a4b048af4b5538 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 Nov 2023 17:07:21 +0100 Subject: [PATCH 0299/1302] Commands: set_presence: switch priority argument from string to integer --- src/mod_admin_extra.erl | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 152d481b8..70374aa44 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -423,6 +423,22 @@ get_commands_spec() -> "Show: `away`, `chat`, `dnd`, `xa`.", "Status text", "Priority, provide this value as an integer"], result = {res, rescode}}, + #ejabberd_commands{name = set_presence, + tags = [session], + desc = "Set presence of a session", + module = ?MODULE, function = set_presence, + version = 1, + args = [{user, binary}, {host, binary}, + {resource, binary}, {type, binary}, + {show, binary}, {status, binary}, + {priority, integer}], + args_example = [<<"user1">>,<<"myserver.com">>,<<"tka1">>, + <<"available">>,<<"away">>,<<"BB">>, 7], + args_desc = ["User name", "Server name", "Resource", + "Type: `available`, `error`, `probe`...", + "Show: `away`, `chat`, `dnd`, `xa`.", "Status text", + "Priority, provide this value as an integer"], + result = {res, rescode}}, #ejabberd_commands{name = set_nickname, tags = [vcard], desc = "Set nickname in a user's vCard", @@ -1084,14 +1100,10 @@ get_presence(U, S) -> {FullJID, Show, Status} end. -set_presence(User, Host, Resource, Type, Show, Status, Priority) - when is_integer(Priority) -> - BPriority = integer_to_binary(Priority), - set_presence(User, Host, Resource, Type, Show, Status, BPriority); -set_presence(User, Host, Resource, Type, Show, Status, Priority0) -> - Priority = if is_integer(Priority0) -> Priority0; - true -> binary_to_integer(Priority0) - end, +set_presence(User, Host, Resource, Type, Show, Status, Priority) when is_binary(Priority) -> + set_presence(User, Host, Resource, Type, Show, Status, binary_to_integer(Priority)); + +set_presence(User, Host, Resource, Type, Show, Status, Priority) -> Pres = #presence{ from = jid:make(User, Host, Resource), to = jid:make(User, Host), From e26729b4833cb45b22e40317a87492f56dbb5139 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 29 Nov 2023 19:04:57 +0100 Subject: [PATCH 0300/1302] Commands: Use list arguments in many commands that used separators Commands that has some argument change: - add_rosteritem - oauth_issue_token - send_direct_invitation - srg_create - subscribe_room - subscribe_room_many --- src/ejabberd_oauth.erl | 16 +++++++++- src/mod_admin_extra.erl | 46 +++++++++++++++++++++++++--- src/mod_muc_admin.erl | 68 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index c6801d54e..c3d206e3f 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -91,6 +91,18 @@ get_commands_spec() -> "List of scopes to allow, separated by ';'"], result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, + #ejabberd_commands{name = oauth_issue_token, tags = [oauth], + desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", + module = ?MODULE, function = oauth_issue_token, + version = 1, + args = [{jid, string}, {ttl, integer}, {scopes, {list, {scope, binary}}}], + policy = restricted, + args_example = ["user@server.com", 3600, ["connected_users_number", "muc_online_rooms"]], + args_desc = ["Jid for which issue token", + "Time to live of generated token in seconds", + "List of scopes to allow"], + result = {result, {tuple, [{token, string}, {scopes, {list, {scope, string}}}, {expires_in, string}]}} + }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], desc = "List [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) tokens, user, scope, and seconds to expire (only Mnesia)", longdesc = "List OAuth tokens, their user and scope, and how many seconds remain until expirity", @@ -135,8 +147,10 @@ get_commands_spec() -> } ]. -oauth_issue_token(Jid, TTLSeconds, ScopesString) -> +oauth_issue_token(Jid, TTLSeconds, [Head|_] = ScopesString) when is_integer(Head) -> Scopes = [list_to_binary(Scope) || Scope <- string:tokens(ScopesString, ";")], + oauth_issue_token(Jid, TTLSeconds, Scopes); +oauth_issue_token(Jid, TTLSeconds, Scopes) -> try jid:decode(list_to_binary(Jid)) of #jid{luser =Username, lserver = Server} -> Ctx1 = #oauth_ctx{password = admin_generated}, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 70374aa44..cb9deab1d 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -511,6 +511,20 @@ get_commands_spec() -> args_desc = ["User name", "Server name", "Contact user name", "Contact server name", "Nickname", "Group", "Subscription"], result = {res, rescode}}, + #ejabberd_commands{name = add_rosteritem, tags = [roster], + desc = "Add an item to a user's roster (supports ODBC)", + module = ?MODULE, function = add_rosteritem, + version = 1, + args = [{localuser, binary}, {localhost, binary}, + {user, binary}, {host, binary}, + {nick, binary}, {groups, {list, {group, binary}}}, + {subs, binary}], + args_rename = [{localserver, localhost}, {server, host}], + args_example = [<<"user1">>,<<"myserver.com">>,<<"user2">>, <<"myserver.com">>, + <<"User 2">>, [<<"Friends">>, <<"Team 1">>], <<"both">>], + args_desc = ["User name", "Server name", "Contact user name", "Contact server name", + "Nickname", "Groups", "Subscription"], + result = {res, rescode}}, %%{"", "subs= none, from, to or both"}, %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, %%{"", "will add mike@server.com to peter@localhost roster"}, @@ -697,6 +711,18 @@ get_commands_spec() -> args_desc = ["Group identifier", "Group server name", "Group name", "Group description", "Groups to display"], result = {res, rescode}}, + #ejabberd_commands{name = srg_create, tags = [shared_roster_group], + desc = "Create a Shared Roster Group", + module = ?MODULE, function = srg_create, + version = 1, + args = [{group, binary}, {host, binary}, + {label, binary}, {description, binary}, {display, {list, {group, binary}}}], + args_rename = [{name, label}], + args_example = [<<"group3">>, <<"myserver.com">>, <<"Group3">>, + <<"Third group">>, [<<"group1">>, <<"group2">>]], + args_desc = ["Group identifier", "Group server name", "Group name", + "Group description", "List of groups to display"], + result = {res, rescode}}, #ejabberd_commands{name = srg_delete, tags = [shared_roster_group], desc = "Delete a Shared Roster Group", module = ?MODULE, function = srg_delete, @@ -1301,14 +1327,16 @@ update_vcard_els(Data, ContentList, Els1) -> %%% Roster %%% -add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> +add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) when is_binary(Group) -> + add_rosteritem(LocalUser, LocalServer, User, Server, Nick, [Group], Subs); +add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Groups, Subs) -> case {jid:make(LocalUser, LocalServer), jid:make(User, Server)} of {error, _} -> throw({error, "Invalid 'localuser'/'localserver'"}); {_, error} -> throw({error, "Invalid 'user'/'server'"}); {Jid, _Jid2} -> - RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Group}), + RosterItem = build_roster_item(User, Server, {add, Nick, Subs, Groups}), case mod_roster:set_item_and_notify_clients(Jid, RosterItem, true) of ok -> ok; _ -> error @@ -1423,6 +1451,11 @@ push_roster_item(LU, LS, R, U, S, Action) -> ejabberd_router:route( xmpp:set_from_to(ResIQ, jid:remove_resource(LJID), LJID)). +build_roster_item(U, S, {add, Nick, Subs, Groups}) when is_list(Groups) -> + #roster_item{jid = jid:make(U, S), + name = Nick, + subscription = misc:binary_to_atom(Subs), + groups = Groups}; build_roster_item(U, S, {add, Nick, Subs, Group}) -> Groups = binary:split(Group,<<";">>, [global, trim]), #roster_item{jid = jid:make(U, S), @@ -1503,11 +1536,14 @@ private_set2(Username, Host, Xml) -> %%% Shared Roster Groups %%% -srg_create(Group, Host, Label, Description, Display) -> +srg_create(Group, Host, Label, Description, Display) when is_binary(Display) -> DisplayList = case Display of - <<>> -> []; - _ -> ejabberd_regexp:split(Display, <<"\\\\n">>) + <<>> -> []; + _ -> ejabberd_regexp:split(Display, <<"\\\\n">>) end, + srg_create(Group, Host, Label, Description, DisplayList); + +srg_create(Group, Host, Label, Description, DisplayList) -> Opts = [{label, Label}, {displayed_groups, DisplayList}, {description, Description}], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 8b5358ce8..13f196278 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -308,6 +308,22 @@ get_commands_spec() -> args = [{name, binary}, {service, binary}, {password, binary}, {reason, binary}, {users, binary}], result = {res, rescode}}, + #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], + desc = "Send a direct invitation to several destinations", + longdesc = "Since ejabberd 20.12, this command is " + "asynchronous: the API call may return before the " + "server has send all the invitations.\n\n" + "`password` and `message` can be set to `none`.", + module = ?MODULE, function = send_direct_invitation, + version = 1, + args_desc = ["Room name", "MUC service", "Password, or `none`", + "Reason text, or `none`", "List of users JIDs"], + args_example = [<<"room1">>, <<"muc.example.com">>, + <<>>, <<"Check this out!">>, + ["user2@localhost", "user3@example.com"]], + args = [{name, binary}, {service, binary}, {password, binary}, + {reason, binary}, {users, {list, {jid, binary}}}], + result = {res, rescode}}, #ejabberd_commands{name = change_room_option, tags = [muc_room], desc = "Change an option in a MUC room", @@ -344,6 +360,20 @@ get_commands_spec() -> args = [{user, binary}, {nick, binary}, {room, binary}, {nodes, binary}], result = {nodes, {list, {node, string}}}}, + #ejabberd_commands{name = subscribe_room, tags = [muc_room, muc_sub], + desc = "Subscribe to a MUC conference", + module = ?MODULE, function = subscribe_room, + version = 1, + args_desc = ["User JID", "a user's nick", + "the room to subscribe", "list of nodes"], + args_example = ["tom@localhost", "Tom", "room1@conference.localhost", + ["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]], + result_desc = "The list of nodes that has subscribed", + result_example = ["urn:xmpp:mucsub:nodes:messages", + "urn:xmpp:mucsub:nodes:affiliations"], + args = [{user, binary}, {nick, binary}, {room, binary}, + {nodes, {list, {node, binary}}}], + result = {nodes, {list, {node, string}}}}, #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], desc = "Subscribe several users to a MUC conference", note = "added in 22.05", @@ -367,6 +397,30 @@ get_commands_spec() -> {room, binary}, {nodes, binary}], result = {res, rescode}}, + #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], + desc = "Subscribe several users to a MUC conference", + note = "added in 22.05", + longdesc = "This command accepts up to 50 users at once " + "(this is configurable with the *`mod_muc_admin`* option " + "`subscribe_room_many_max_users`)", + module = ?MODULE, function = subscribe_room_many, + version = 1, + args_desc = ["Users JIDs and nicks", + "the room to subscribe", + "nodes separated by commas: `,`"], + args_example = [[{"tom@localhost", "Tom"}, + {"jerry@localhost", "Jerry"}], + "room1@conference.localhost", + ["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]], + args = [{users, {list, + {user, {tuple, + [{jid, binary}, + {nick, binary} + ]}} + }}, + {room, binary}, + {nodes, {list, {node, binary}}}], + result = {res, rescode}}, #ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub], desc = "Unsubscribe from a MUC conference", module = ?MODULE, function = unsubscribe_room, @@ -1074,20 +1128,22 @@ get_room_occupants_number(Room, Host) -> %%---------------------------- %% http://xmpp.org/extensions/xep-0249.html -send_direct_invitation(RoomName, RoomService, Password, Reason, UsersString) -> +send_direct_invitation(RoomName, RoomService, Password, Reason, UsersString) when is_binary(UsersString) -> + UsersStrings = binary:split(UsersString, <<":">>, [global]), + send_direct_invitation(RoomName, RoomService, Password, Reason, UsersStrings); +send_direct_invitation(RoomName, RoomService, Password, Reason, UsersStrings) -> case jid:make(RoomName, RoomService) of error -> throw({error, "Invalid 'roomname' or 'service'"}); RoomJid -> XmlEl = build_invitation(Password, Reason, RoomJid), - Users = get_users_to_invite(RoomJid, UsersString), + Users = get_users_to_invite(RoomJid, UsersStrings), [send_direct_invitation(RoomJid, UserJid, XmlEl) || UserJid <- Users], ok end. -get_users_to_invite(RoomJid, UsersString) -> - UsersStrings = binary:split(UsersString, <<":">>, [global]), +get_users_to_invite(RoomJid, UsersStrings) -> OccupantsTuples = get_room_occupants(RoomJid#jid.luser, RoomJid#jid.lserver), OccupantsJids = [jid:decode(JidString) @@ -1439,8 +1495,10 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> subscribe_room(_User, Nick, _Room, _Nodes) when Nick == <<"">> -> throw({error, "Nickname must be set"}); -subscribe_room(User, Nick, Room, Nodes) -> +subscribe_room(User, Nick, Room, Nodes) when is_binary(Nodes) -> NodeList = re:split(Nodes, "\\h*,\\h*"), + subscribe_room(User, Nick, Room, NodeList); +subscribe_room(User, Nick, Room, NodeList) -> try jid:decode(Room) of #jid{luser = Name, lserver = Host} when Name /= <<"">> -> try jid:decode(User) of From 8671bf70ab057caf69fdc1d1b798ef48ad4ab5b6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 29 Nov 2023 17:39:34 +0100 Subject: [PATCH 0301/1302] mod_http_api: When no specific API version is requested, use the latest --- src/mod_http_api.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 514a8632c..afe658eb0 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -39,7 +39,7 @@ -include("ejabberd_stacktrace.hrl"). -include("translate.hrl"). --define(DEFAULT_API_VERSION, 0). +-define(DEFAULT_API_VERSION, 1000000). -define(CT_PLAIN, {<<"Content-Type">>, <<"text/plain">>}). From d570870be5d27433641cee71c8a79186bcbe7c79 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 Nov 2023 13:13:42 +0100 Subject: [PATCH 0302/1302] mod_http_api: When using API version>0, avoid result names for integers and strings --- src/mod_http_api.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index afe658eb0..801286000 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -412,7 +412,15 @@ format_command_result(Cmd, Auth, Result, Version) -> {_, T} = format_result(Result, ResultFormat), {200, T}; _ -> - {200, {[format_result(Result, ResultFormat)]}} + OtherResult1 = format_result(Result, ResultFormat), + OtherResult2 = case Version of + 0 -> + {[OtherResult1]}; + _ -> + {_, Other3} = OtherResult1, + Other3 + end, + {200, OtherResult2} end. format_result(Atom, {Name, atom}) -> From 9f42f170886cc04af490202418e77240fef1f605 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 12:38:46 +0100 Subject: [PATCH 0303/1302] mod_http_api: Fix to allow the client override the API version When configured like: listen: - request_handlers: /api: mod_http_api /apizero/v0: mod_http_api What API version will be used depending on the URL: - api/commandname use the latest available version - api/commandname/v0 use version 0 - apizero/v0/commandname use version 0 - apizero/v0/commandname/v2 use version 2 --- src/mod_http_api.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 801286000..c6a969091 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -135,7 +135,7 @@ extract_auth(#request{auth = HTTPAuth, ip = {IP, _}, opts = Opts}) -> process(_, #request{method = 'POST', data = <<>>}) -> ?DEBUG("Bad Request: no data", []), badrequest_response(<<"Missing POST data">>); -process([Call], #request{method = 'POST', data = Data, ip = IPPort} = Req) -> +process([Call | _], #request{method = 'POST', data = Data, ip = IPPort} = Req) -> Version = get_api_version(Req), try Args = extract_args(Data), @@ -153,7 +153,7 @@ process([Call], #request{method = 'POST', data = Data, ip = IPPort} = Req) -> ?DEBUG("Bad Request: ~p ~p", [_Error, StackTrace]), badrequest_response() end; -process([Call], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> +process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> Version = get_api_version(Req), try Args = case Data of From c4c0cd1b77c8cb05faa54248ad388aa040126a6c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Nov 2023 17:10:02 +0100 Subject: [PATCH 0304/1302] ejabberd_ctl: Add support for list and tuple arguments Tuple elements are separated with : List elements are separated with , For example: ejabberdctl add_rosteritem user1 localhost testuser7 localhost NickUser77l gr1,gr2,gr3 both ejabberdctl create_room_with_opts room1 conference.localhost localhost public:false,persistent:true ejabberdctl subscribe_room_many user1@localhost:User1,admin@localhost:Admin room1@conference.localhost urn:xmpp:mucsub:nodes:messages,urn:xmpp:mucsub:nodes:affiliations Affected commands: - add_rosteritem - create_room_with_opts - oauth_issue_token - send_direct_invitation - set_vcard2_multi - srg_create - subscribe_room - subscribe_room_many --- src/ejabberd_ctl.erl | 54 ++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index e244fef26..d26c0ed5a 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -23,8 +23,6 @@ %%% %%%---------------------------------------------------------------------- -%%% Does not support commands that have arguments with ctypes: list, tuple - -module(ejabberd_ctl). -behaviour(gen_server). @@ -372,6 +370,13 @@ format_arg(Arg, string) -> NumChars = integer_to_list(length(Arg)), Parse = "~" ++ NumChars ++ "c", format_arg2(Arg, Parse); +format_arg(Arg, {list, {_ArgName, ArgFormat}}) -> + [format_arg(Element, ArgFormat) || Element <- string:tokens(Arg, ",")]; +format_arg(Arg, {list, ArgFormat}) -> + [format_arg(Element, ArgFormat) || Element <- string:tokens(Arg, ",")]; +format_arg(Arg, {tuple, Elements}) -> + Args = string:tokens(Arg, ":"), + list_to_tuple(format_args(Args, Elements)); format_arg(Arg, Format) -> S = unicode:characters_to_binary(Arg, utf8), JSON = jiffy:decode(S), @@ -491,19 +496,24 @@ get_list_commands(Version) -> tuple_command_help({Name, _Args, Desc}) -> {Args, _, _} = ejabberd_commands:get_command_format(Name, admin), Arguments = [atom_to_list(ArgN) || {ArgN, _ArgF} <- Args], - Prepend = case is_supported_args(Args) of - true -> ""; - false -> "*" - end, CallString = atom_to_list(Name), - {CallString, Arguments, Prepend ++ Desc}. + {CallString, Arguments, Desc}. -is_supported_args(Args) -> - lists:all( - fun({_Name, Format}) -> - (Format == integer) - or (Format == string) - or (Format == binary) +has_tuple_args(Args) -> + lists:any( + fun({_Name, tuple}) -> true; + ({_Name, {tuple, _}}) -> true; + ({_Name, {list, SubArg}}) -> + has_tuple_args([SubArg]); + (_) -> false + end, + Args). + +has_list_args(Args) -> + lists:any( + fun({_Name, list}) -> true; + ({_Name, {list, _}}) -> true; + (_) -> false end, Args). @@ -768,9 +778,9 @@ print_usage_help(MaxC, ShCode) -> " ejabberdctl ", ?C("help"), " ", ?C("register"), "\n", " ejabberdctl ", ?C("help"), " ", ?C("regist*"), "\n", "\n", - "Please note that 'ejabberdctl' shows all ejabberd commands,\n", - "even those that cannot be used in the shell with ejabberdctl.\n", - "Those commands can be identified because their description starts with: *\n", + "Some command arguments are lists or tuples, like add_rosteritem and create_room_with_opts.\n", + "Separate the elements in a list with the , character.\n", + "Separate the elements in a tuple with the : character.\n", "\n", "Some commands return lists, like get_roster and get_user_subscriptions.\n", "In those commands, the elements in the list are separated with: ;\n"], @@ -893,9 +903,13 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> _ -> ["", prepare_description(0, MaxC, LongDesc), "\n\n"] end, - NoteEjabberdctl = case is_supported_args(ArgsDef) of - true -> ""; - false -> [" ", ?B("Note:"), " This command cannot be executed using ejabberdctl. Try ejabberd_xmlrpc.\n\n"] + NoteEjabberdctlList = case has_list_args(ArgsDef) of + true -> [" ", ?B("Note:"), " In a list argument, separate the elements using the , character for example: one,two,three\n\n"]; + false -> "" + end, + NoteEjabberdctlTuple = case has_tuple_args(ArgsDef) of + true -> [" ", ?B("Note:"), " In a tuple argument, separate the elements using the : character for example: members_only:true\n\n"]; + false -> "" end, case Cmd of @@ -903,7 +917,7 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> _ -> print([NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", XmlrpcFmt, TagsFmt, "\n\n", ModuleFmt, DescFmt, "\n\n"], []) end, - print([LongDescFmt, NoteEjabberdctl], []). + print([LongDescFmt, NoteEjabberdctlList, NoteEjabberdctlTuple], []). format_usage_ctype(Type, _Indentation) when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> From b34572e7ce70f677a139ab8a89730ba15382f54a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 11:58:07 +0100 Subject: [PATCH 0305/1302] ejabberd_ctl: Show proper command help when version is explicitly set --- src/ejabberd_ctl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index d26c0ed5a..13c565ddf 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -340,7 +340,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) -> {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; {L1, L2} when L1 > L2 -> {L1-L2, "more argument"} end, - process(["help" | [CmdString]]), + process(["help" | [CmdString]], Version), {io_lib:format("Error: the command '~ts' requires ~p ~ts.", [CmdString, NumCompa, TextCompa]), wrong_command_arguments} From d65638efe18d05c28673fd7632ff148084747280 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 Nov 2023 11:48:54 +0100 Subject: [PATCH 0306/1302] ejabberd_ctl: Pass API version to format_result --- src/ejabberd_ctl.erl | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 13c565ddf..27b908036 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -333,7 +333,7 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) -> ArgsFormatted, CI2, Version), - format_result_preliminary(Result, ResultFormat); + format_result_preliminary(Result, ResultFormat, Version); {'EXIT', {function_clause,[{lists,zip,[A1,A2|_], _} | _]}} -> {NumCompa, TextCompa} = case {length(A1), length(A2)} of @@ -390,67 +390,67 @@ format_arg2(Arg, Parse)-> %% Format result %%----------------------------- -format_result_preliminary(Result, {A, {list, B}}) -> - format_result(Result, {A, {top_result_list, B}}); -format_result_preliminary(Result, ResultFormat) -> - format_result(Result, ResultFormat). +format_result_preliminary(Result, {A, {list, B}}, Version) -> + format_result(Result, {A, {top_result_list, B}}, Version); +format_result_preliminary(Result, ResultFormat, Version) -> + format_result(Result, ResultFormat, Version). -format_result({error, ErrorAtom}, _) -> +format_result({error, ErrorAtom}, _, _Version) -> {io_lib:format("Error: ~p", [ErrorAtom]), make_status(error)}; %% An error should always be allowed to return extended error to help with API. %% Extended error is of the form: %% {error, type :: atom(), code :: int(), Desc :: string()} -format_result({error, ErrorAtom, Code, Msg}, _) -> +format_result({error, ErrorAtom, Code, Msg}, _, _Version) -> {io_lib:format("Error: ~p: ~s", [ErrorAtom, Msg]), make_status(Code)}; -format_result(Atom, {_Name, atom}) -> +format_result(Atom, {_Name, atom}, _Version) -> io_lib:format("~p", [Atom]); -format_result(Int, {_Name, integer}) -> +format_result(Int, {_Name, integer}, _Version) -> io_lib:format("~p", [Int]); -format_result([A|_]=String, {_Name, string}) when is_list(String) and is_integer(A) -> +format_result([A|_]=String, {_Name, string}, _Version) when is_list(String) and is_integer(A) -> io_lib:format("~ts", [String]); -format_result(Binary, {_Name, string}) when is_binary(Binary) -> +format_result(Binary, {_Name, string}, _Version) when is_binary(Binary) -> io_lib:format("~ts", [binary_to_list(Binary)]); -format_result(Atom, {_Name, string}) when is_atom(Atom) -> +format_result(Atom, {_Name, string}, _Version) when is_atom(Atom) -> io_lib:format("~ts", [atom_to_list(Atom)]); -format_result(Integer, {_Name, string}) when is_integer(Integer) -> +format_result(Integer, {_Name, string}, _Version) when is_integer(Integer) -> io_lib:format("~ts", [integer_to_list(Integer)]); -format_result(Other, {_Name, string}) -> +format_result(Other, {_Name, string}, _Version) -> io_lib:format("~p", [Other]); -format_result(Code, {_Name, rescode}) -> +format_result(Code, {_Name, rescode}, _Version) -> make_status(Code); -format_result({Code, Text}, {_Name, restuple}) -> +format_result({Code, Text}, {_Name, restuple}, _Version) -> {io_lib:format("~ts", [Text]), make_status(Code)}; -format_result([], {_Name, {top_result_list, _ElementsDef}}) -> +format_result([], {_Name, {top_result_list, _ElementsDef}}, _Version) -> ""; -format_result([FirstElement | Elements], {_Name, {top_result_list, ElementsDef}}) -> - [format_result(FirstElement, ElementsDef) | +format_result([FirstElement | Elements], {_Name, {top_result_list, ElementsDef}}, Version) -> + [format_result(FirstElement, ElementsDef, Version) | lists:map( fun(Element) -> - ["\n" | format_result(Element, ElementsDef)] + ["\n" | format_result(Element, ElementsDef, Version)] end, Elements)]; %% The result is a list of something: [something()] -format_result([], {_Name, {list, _ElementsDef}}) -> +format_result([], {_Name, {list, _ElementsDef}}, _Version) -> ""; -format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> +format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}, Version) -> %% Start formatting the first element - [format_result(FirstElement, ElementsDef) | + [format_result(FirstElement, ElementsDef, Version) | %% If there are more elements, put always first a newline character lists:map( fun(Element) -> - [";" | format_result(Element, ElementsDef)] + [";" | format_result(Element, ElementsDef, Version)] end, Elements)]; @@ -458,17 +458,17 @@ format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> %% NOTE: the elements in the tuple are separated with tabular characters, %% if a string is empty, it will be difficult to notice in the shell, %% maybe a different separation character should be used, like ;;? -format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}) -> +format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}, Version) -> ElementsList = tuple_to_list(ElementsTuple), [{FirstE, FirstD} | ElementsAndDef] = lists:zip(ElementsList, ElementsDef), - [format_result(FirstE, FirstD) | + [format_result(FirstE, FirstD, Version) | lists:map( fun({Element, ElementDef}) -> - ["\t" | format_result(Element, ElementDef)] + ["\t" | format_result(Element, ElementDef, Version)] end, ElementsAndDef)]; -format_result(404, {_Name, _}) -> +format_result(404, {_Name, _}, _Version) -> make_status(not_found). make_status(ok) -> ?STATUS_SUCCESS; From 90766685ae5f9d308e7c305e47337178fe18687c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 17:09:25 +0100 Subject: [PATCH 0307/1302] ejabberd_ctl: When API version>0, update syntax of list results --- src/ejabberd_ctl.erl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 27b908036..df2cce2ce 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -445,12 +445,16 @@ format_result([FirstElement | Elements], {_Name, {top_result_list, ElementsDef}} format_result([], {_Name, {list, _ElementsDef}}, _Version) -> ""; format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}, Version) -> + Separator = case Version of + 0 -> ";"; + _ -> "," + end, %% Start formatting the first element [format_result(FirstElement, ElementsDef, Version) | %% If there are more elements, put always first a newline character lists:map( fun(Element) -> - [";" | format_result(Element, ElementsDef, Version)] + [Separator | format_result(Element, ElementsDef, Version)] end, Elements)]; @@ -782,8 +786,9 @@ print_usage_help(MaxC, ShCode) -> "Separate the elements in a list with the , character.\n", "Separate the elements in a tuple with the : character.\n", "\n", - "Some commands return lists, like get_roster and get_user_subscriptions.\n", - "In those commands, the elements in the list are separated with: ;\n"], + "Some commands results are lists or tuples, like get_roster and get_user_subscriptions.\n", + "The elements in a list are separated with a , character.\n", + "The elements in a tuple are separated with a tabular character.\n"], ArgsDef = [], C = #ejabberd_commands{ name = help, From d140f99b68b80c3126a8de9abb0a109a9a2c595d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 Nov 2023 16:16:14 +0100 Subject: [PATCH 0308/1302] ejabberd_xmlrpc: Fix support for restuple error response --- src/ejabberd_xmlrpc.erl | 74 +++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index 741bf8422..f6d55eba2 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -238,7 +238,7 @@ do_command(Auth, Command, AttrL, ArgsF, ArgsR, ArgsFormatted = format_args(rename_old_args(AttrL, ArgsR), ArgsF), Result = ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth), ResultFormatted = format_result(Result, ResultF), - {command_result, ResultFormatted}. + {command_result, {struct, [ResultFormatted]}}. rename_old_args(Args, []) -> Args; @@ -291,6 +291,14 @@ format_args(Args, ArgsFormat) -> L when is_list(L) -> exit({additional_unused_args, L}) end. +format_arg({array, Elements}, + {list, {_ElementDefName, ElementDefFormat}}) + when is_list(Elements) -> + lists:map(fun (ElementValue) -> + format_arg(ElementValue, ElementDefFormat) + end, + Elements); + format_arg({array, Elements}, {list, {ElementDefName, ElementDefFormat}}) when is_list(Elements) -> @@ -307,11 +315,18 @@ format_arg({array, [{struct, Elements}]}, format_arg(ElementValue, ElementDefFormat) end, Elements); +%% Old ejabberd 23.10 format_arg({array, [{struct, Elements}]}, {tuple, ElementsDef}) when is_list(Elements) -> FormattedList = format_args(Elements, ElementsDef), list_to_tuple(FormattedList); +%% New ejabberd 24.xx +format_arg({struct, Elements}, + {tuple, ElementsDef}) + when is_list(Elements) -> + FormattedList = format_args(Elements, ElementsDef), + list_to_tuple(FormattedList); format_arg({array, Elements}, {list, ElementsDef}) when is_list(Elements) and is_atom(ElementsDef) -> [format_arg(Element, ElementsDef) @@ -336,6 +351,10 @@ process_unicode_codepoints(Str) -> %% Result %% ----------------------------- +format_result(Code, {Name, rescode}) -> + {Name, make_status(Code)}; +format_result({_Code, Text}, {_Name, restuple}) -> + {text, io_lib:format("~s", [Text])}; format_result({error, Error}, _) when is_list(Error) -> throw({error, lists:flatten(Error)}); format_result({error, Error}, _) -> @@ -346,45 +365,36 @@ format_result({error, _Type, _Code, Error}, _) -> throw({error, Error}); format_result(String, string) -> lists:flatten(String); format_result(Atom, {Name, atom}) -> - {struct, - [{Name, iolist_to_binary(atom_to_list(Atom))}]}; + {Name, iolist_to_binary(atom_to_list(Atom))}; format_result(Int, {Name, integer}) -> - {struct, [{Name, Int}]}; + {Name, Int}; format_result([A|_]=String, {Name, string}) when is_list(String) and is_integer(A) -> - {struct, [{Name, lists:flatten(String)}]}; + {Name, lists:flatten(String)}; format_result(Binary, {Name, string}) when is_binary(Binary) -> - {struct, [{Name, binary_to_list(Binary)}]}; + {Name, binary_to_list(Binary)}; format_result(Atom, {Name, string}) when is_atom(Atom) -> - {struct, [{Name, atom_to_list(Atom)}]}; + {Name, atom_to_list(Atom)}; format_result(Integer, {Name, string}) when is_integer(Integer) -> - {struct, [{Name, integer_to_list(Integer)}]}; + {Name, integer_to_list(Integer)}; format_result(Other, {Name, string}) -> - {struct, [{Name, io_lib:format("~p", [Other])}]}; + {Name, io_lib:format("~p", [Other])}; format_result(String, {Name, binary}) when is_list(String) -> - {struct, [{Name, lists:flatten(String)}]}; + {Name, lists:flatten(String)}; format_result(Binary, {Name, binary}) when is_binary(Binary) -> - {struct, [{Name, binary_to_list(Binary)}]}; -format_result(Code, {Name, rescode}) -> - {struct, [{Name, make_status(Code)}]}; -format_result({Code, Text}, {Name, restuple}) -> - {struct, - [{Name, make_status(Code)}, - {text, io_lib:format("~s", [Text])}]}; -format_result(Elements, {Name, {list, ElementsDef}}) -> - FormattedList = lists:map(fun (Element) -> - format_result(Element, ElementsDef) - end, - Elements), - {struct, [{Name, {array, FormattedList}}]}; -format_result(ElementsTuple, - {Name, {tuple, ElementsDef}}) -> - ElementsList = tuple_to_list(ElementsTuple), - ElementsAndDef = lists:zip(ElementsList, ElementsDef), - FormattedList = lists:map(fun ({Element, ElementDef}) -> - format_result(Element, ElementDef) - end, - ElementsAndDef), - {struct, [{Name, {array, FormattedList}}]}; + {Name, binary_to_list(Binary)}; + + +format_result(Els, {Name, {list, Def}}) -> + FormattedList = [element(2, format_result(El, Def)) || El <- Els], + {Name, {array, FormattedList}}; + + +format_result(Tuple, + {Name, {tuple, Def}}) -> + Els = lists:zip(tuple_to_list(Tuple), Def), + FormattedList = [format_result(El, ElDef) || {El, ElDef} <- Els], + {Name, {struct, FormattedList}}; + format_result(404, {Name, _}) -> {struct, [{Name, make_status(not_found)}]}. From 57bd0ef4f5ab4c2de8d715ec7c3e3534408db1fe Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 22:17:54 +0100 Subject: [PATCH 0309/1302] Docs: Optional support to get commands from runtime instead of BEAM files, based in bdeb4a7 --- src/ejabberd_commands.erl | 3 ++- src/ejabberd_commands_doc.erl | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 416e26edf..0114b1720 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -86,7 +86,8 @@ get_commands_spec() -> args_desc = ["Path to file where generated " "documentation should be stored", "Regexp matching names of commands or modules " - "that will be included inside generated document", + "that will be included inside generated document, " + "or `runtime` to get commands registered at runtime", "Comma separated list of languages (chosen from `java`, `perl`, `xmlrpc`, `json`) " "that will have example invocation include in markdown document"], result_desc = "0 if command failed, 1 when succeeded", diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index a903610d3..cb6f29229 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -477,8 +477,16 @@ maybe_add_policy_arguments(#ejabberd_commands{args=Args1, policy=user}=Cmd) -> maybe_add_policy_arguments(Cmd) -> Cmd. +generate_md_output(File, <<"runtime">>, Languages) -> + Cmds = lists:map(fun({N, _, _}) -> + ejabberd_commands:get_command_definition(N) + end, ejabberd_commands:list_commands()), + generate_md_output(File, <<".">>, Languages, Cmds); generate_md_output(File, RegExp, Languages) -> Cmds = find_commands_definitions(), + generate_md_output(File, RegExp, Languages, Cmds). + +generate_md_output(File, RegExp, Languages, Cmds) -> {ok, RE} = re:compile(RegExp), Cmds2 = lists:filter(fun(#ejabberd_commands{name=Name, module=Module}) -> re:run(atom_to_list(Name), RE, [{capture, none}]) == match orelse From d585b1fcb68e4e0414bfc7bf490d8a16aa4466d1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 22:29:34 +0100 Subject: [PATCH 0310/1302] Docs: When definer is unknown, don't show Module section --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index cb6f29229..5204769e6 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -402,7 +402,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, end, TagsText = [?RAW("*`"++atom_to_list(Tag)++"`* ") || Tag <- Tags], IsDefinerMod = case Definer of - unknown -> true; + unknown -> false; _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) end, ModuleText = case IsDefinerMod of From fc13fdceca46043d975c480ab5f58efaaa1918b8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 30 Nov 2023 22:49:21 +0100 Subject: [PATCH 0311/1302] Docs: Separate tags with commas in markdown docs --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 5204769e6..f6fe25616 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -400,7 +400,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] end end, - TagsText = [?RAW("*`"++atom_to_list(Tag)++"`* ") || Tag <- Tags], + TagsText = ?RAW(string:join(["*`"++atom_to_list(Tag)++"`*" || Tag <- Tags], ", ")), IsDefinerMod = case Definer of unknown -> false; _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) From 8e8354caecc5127a27631f68ae497a8f9729fd59 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Jan 2024 19:04:06 +0100 Subject: [PATCH 0312/1302] Fix explanation of --enable-group option (#4135) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ff2772ccb..d1e6d9706 100644 --- a/configure.ac +++ b/configure.ac @@ -112,7 +112,7 @@ esac],[full_xml=false]) ENABLEGROUP="" AC_ARG_ENABLE(group, - [AS_HELP_STRING([--enable-group[[[[=GROUP]]]]], [allow this system group to start ejabberd (default: no)])], + [AS_HELP_STRING([--enable-group[[[[=GROUP]]]]], [specify the group of the account defined in --enable-user (default: no)])], [case "${enableval}" in yes) ENABLEGROUP=`groups |head -n 1` ;; no) ENABLEGROUP="" ;; From 1f60bcb2d0d44e917d2bbcea3c2ea55e4172dc51 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jan 2024 13:31:49 +0100 Subject: [PATCH 0313/1302] Fix syntax of enable-user and enable-group options help --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index d1e6d9706..33913d181 100644 --- a/configure.ac +++ b/configure.ac @@ -112,7 +112,7 @@ esac],[full_xml=false]) ENABLEGROUP="" AC_ARG_ENABLE(group, - [AS_HELP_STRING([--enable-group[[[[=GROUP]]]]], [specify the group of the account defined in --enable-user (default: no)])], + [AS_HELP_STRING([--enable-group[[=GROUP]]], [specify the group of the account defined in --enable-user (default: no)])], [case "${enableval}" in yes) ENABLEGROUP=`groups |head -n 1` ;; no) ENABLEGROUP="" ;; @@ -246,7 +246,7 @@ esac],[if test "x$tools" = "x"; then tools=false; fi]) ENABLEUSER="" AC_ARG_ENABLE(user, - [AS_HELP_STRING([--enable-user[[[[=USER]]]]], [allow this system user to start ejabberd (default: no)])], + [AS_HELP_STRING([--enable-user[[=USER]]], [allow this system user to start ejabberd (default: no)])], [case "${enableval}" in yes) ENABLEUSER=`whoami` ;; no) ENABLEUSER="" ;; From 973ba5874462a9cc44749bc281d91e5d44130617 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Jan 2024 16:39:53 +0100 Subject: [PATCH 0314/1302] mod_avatar implements XEP-0398 0.2.0 since ddc29d4 --- ejabberd.doap | 6 +++--- src/mod_avatar.erl | 3 ++- src/mod_vcard_xupdate.erl | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index bbdb311c1..6eda6e7aa 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -632,10 +632,10 @@ - 0.2.00.2.0 - 17.09 + 0.2.0 + 18.03 - mod_avatar, mod_vcard_xupdate + mod_avatar diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index ae7e74ff0..c4de349c3 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -22,7 +22,8 @@ %%%------------------------------------------------------------------- -module(mod_avatar). -behaviour(gen_mod). --protocol({xep, 398, '0.2.0', '17.09', "", ""}). + +-protocol({xep, 398, '0.2.0', '18.03', "", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]). diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index 3cf67ad09..50a82ed78 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -26,8 +26,6 @@ -module(mod_vcard_xupdate). -behaviour(gen_mod). --protocol({xep, 398, '0.2.0'}). - %% gen_mod callbacks -export([start/2, stop/1, reload/3]). From 5b6329a12e2780e80c43032f90fc894d407335f7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 11 Jan 2024 12:59:29 +0100 Subject: [PATCH 0315/1302] Fix a few spelling errors --- src/ejabberd_options_doc.erl | 4 ++-- src/mod_mqtt_bridge.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index ce5499aa6..55fe3c983 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -401,7 +401,7 @@ doc() -> note => "added in 23.10", desc => ?T("Supplement check for user existence based on 'mod_last' data, for authentication " - "methods that don't have a way to reliable tell if user exists (like is the case for " + "methods that don't have a way to reliably tell if a user exists (like is the case for " "'jwt' and certificate based authentication). This helps with processing offline message " "for those users. The default value is 'true'.")}}, {auth_use_cache, @@ -585,7 +585,7 @@ doc() -> "'destination' - an instance is chosen by the full JID of " "the packet's 'to' attribute; " "'source' - by the full JID of the packet's 'from' attribute; " - "'bare_destination' - by the the bare JID (without resource) " + "'bare_destination' - by the bare JID (without resource) " "of the packet's 'to' attribute; " "'bare_source' - by the bare JID (without resource) of the " "packet's 'from' attribute is used. The default value is 'random'.")}}, diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 74a771d82..1ae08e9ab 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -198,7 +198,7 @@ mod_doc() -> ?T("Declaration of data to share, must contain 'publish' or 'subscribe' or both, and 'authentication' " "section with username/password field or certfile pointing to client certificate. " "Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), " - "ws, wss, ws5, wss5. Certifcate authentication can be only used with mqtts, mqtt5s, wss, wss5.")}}, + "ws, wss, ws5, wss5. Certificate authentication can be only used with mqtts, mqtt5s, wss, wss5.")}}, {replication_user, #{value => "JID", desc => From 74cb2e054f87458d2725ec07539bff171e3da442 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 11 Jan 2024 13:23:45 +0100 Subject: [PATCH 0316/1302] Rephrase sentences to avoid using "allow to + verb" --- src/ejabberd_options_doc.erl | 10 +++++----- src/mod_block_strangers.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_muc_log.erl | 2 +- src/mod_pubsub.erl | 6 +++--- src/mod_register.erl | 2 +- src/mod_sip.erl | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 55fe3c983..c93a771fe 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -381,7 +381,7 @@ doc() -> "authenticate using: the old Jabber Non-SASL (XEP-0078), " "SASL PLAIN, SASL DIGEST-MD5, and SASL SCRAM-SHA-1/256/512(-PLUS). "), "", ?T("* 'scram': The password is not stored, only some information " - "that allows to verify the hash provided by the client. " + "required to verify the hash provided by the client. " "It is impossible to obtain the original plain password " "from the stored information; for this reason, when this " "value is configured it cannot be changed to plain anymore. " @@ -702,7 +702,7 @@ doc() -> #{value => ?T("FieldName"), desc => ?T("By default, the JID is defined in the '\"jid\"' JWT field. " - "This option allows to specify other JWT field name " + "In this option you can specify other JWT field name " "where the JID is defined.")}}, {jwt_key, #{value => ?T("FilePath"), @@ -903,8 +903,8 @@ doc() -> {?T("Whether to use 'new' SQL schema. All schemas are located " "at . " "There are two schemas available. The default legacy schema " - "allows to store one XMPP domain into one ejabberd database. " - "The 'new' schema allows to handle several XMPP domains in a " + "stores one XMPP domain into one ejabberd database. " + "The 'new' schema can handle several XMPP domains in a " "single ejabberd database. Using this 'new' schema is best when " "serving several XMPP domains and/or changing domains from " "time to time. This avoid need to manage several databases and " @@ -1417,7 +1417,7 @@ doc() -> "contains the header 'X-Forwarded-For'. You can specify " "'all' to allow all proxies, or specify a list of IPs, " "possibly with masks. The default value is an empty list. " - "This allows, if enabled, to be able to know the real IP " + "Using this option you can know the real IP " "of the request, for admin purpose, or security configuration " "(for example using 'mod_fail2ban'). IMPORTANT: The proxy MUST " "be configured to set the 'X-Forwarded-For' header if you " diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index 7859e0291..baeef36af 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -256,7 +256,7 @@ mod_options(_) -> mod_doc() -> #{desc => - ?T("This module allows to block/log messages coming from an " + ?T("This module blocks and logs any messages coming from an " "unknown entity. If a writing entity is not in your roster, " "you can let this module drop and/or log the message. " "By default you'll just not receive message from that entity. " diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 0ea78870b..b6f9fc3ff 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1679,7 +1679,7 @@ mod_doc() -> #{value => ?T("Options"), note => "improved in 22.05", desc => - ?T("This option allows to define the desired " + ?T("Define the " "default room options. Note that the creator of a room " "can modify the options of his room at any time using an " "XMPP client with MUC capability. The 'Options' are:")}, diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index d5a8d461f..d9eef97d2 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -1043,7 +1043,7 @@ mod_doc() -> {dirname, #{value => "room_jid | room_name", desc => - ?T("Allows to configure the name of the room directory. " + ?T("Configure the name of the room directory. " "If set to 'room_jid', the room directory name will " "be the full room JID. Otherwise, the room directory " "name will be only the room name, not including the " diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 7cc1d0170..87a73c9be 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4414,7 +4414,7 @@ mod_doc() -> "items. Value is 'true' or 'false'. If not defined, " "pubsub does not cache last items. On systems with not" " so many nodes, caching last items speeds up pubsub " - "and allows to raise user connection rate. The cost " + "and allows you to raise the user connection rate. The cost " "is memory usage, as every item is stored in memory.")}}, {max_item_expire_node, #{value => "timeout() | infinity", @@ -4469,7 +4469,7 @@ mod_doc() -> {pep_mapping, #{value => "List of Key:Value", desc => - ?T("This allows to define a list of key-value to choose " + ?T("In this option you can provide a list of key-value to choose " "defined node plugins on given PEP namespace. " "The following example will use 'node_tune' instead of " "'node_pep' for every PEP node with the tune namespace:"), @@ -4494,7 +4494,7 @@ mod_doc() -> "follows standard XEP-0060 implementation."), ?T("- 'pep' plugin adds extension to handle Personal " "Eventing Protocol (XEP-0163) to the PubSub engine. " - "Adding pep allows to handle PEP automatically.")]}}, + "When enabled, PEP is handled automatically.")]}}, {vcard, #{value => ?T("vCard"), desc => diff --git a/src/mod_register.erl b/src/mod_register.erl index c7edb66df..29624508a 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -652,7 +652,7 @@ mod_doc() -> {access_from, #{value => ?T("AccessName"), desc => - ?T("By default, 'ejabberd' doesn't allow to register new accounts " + ?T("By default, 'ejabberd' doesn't allow the client to register new accounts " "from s2s or existing c2s sessions. You can change it by defining " "access rule in this option. Use with care: allowing registration " "from s2s leads to uncontrolled massive accounts creation by rogue users.")}}, diff --git a/src/mod_sip.erl b/src/mod_sip.erl index 13fcfa6c4..1e649579a 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -390,7 +390,7 @@ mod_doc() -> #{value => "true | false", desc => ?T("Always insert \"Record-Route\" header into " - "SIP messages. This approach allows to bypass " + "SIP messages. With this approach it is possible to bypass " "NATs/firewalls a bit more easily. " "The default value is 'true'.")}}, {flow_timeout_tcp, From 6c691a73bdd678a1aa4f6dec838be6fdcaaebc21 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 15 Jan 2024 21:38:54 +0100 Subject: [PATCH 0317/1302] Support XEP-0198 pings If stream management is enabled, let mod_ping trigger XEP-0198 equests rather than sending XEP-0199 pings. This avoids the overhead of the ping IQ stanzas, which, if stream management is enabled, are accompanied by XEP-0198 elements anyway. Thanks to MoyaApp () for sponsoring this work. --- src/mod_ping.erl | 41 ++++++++++++++++++++++++++--------------- src/mod_stream_mgmt.erl | 12 ++++++++++-- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 9ac6e9bad..1cdb92368 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -49,14 +49,13 @@ -export([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2, code_change/3]). --export([iq_ping/1, user_online/3, user_offline/3, mod_doc/0, - user_send/1, mod_opt_type/1, mod_options/1, depends/2]). +-export([iq_ping/1, user_online/3, user_offline/3, mod_doc/0, user_send/1, + c2s_handle_cast/2, mod_opt_type/1, mod_options/1, depends/2]). -record(state, {host :: binary(), send_pings :: boolean(), ping_interval :: pos_integer(), - ping_ack_timeout :: undefined | non_neg_integer(), timeout_action :: none | kill, timers :: timers()}). @@ -167,13 +166,8 @@ handle_info({timeout, _TRef, {ping, JID}}, State) -> JID#jid.lresource) of none -> del_timer(JID, State#state.timers); - _ -> - Host = State#state.host, - From = jid:make(Host), - IQ = #iq{from = From, to = JID, type = get, sub_els = [#ping{}]}, - ejabberd_router:route_iq(IQ, JID, - gen_mod:get_module_proc(Host, ?MODULE), - State#state.ping_ack_timeout), + Pid -> + ejabberd_c2s:cast(Pid, send_ping), add_timer(JID, State#state.ping_interval, State#state.timers) end, @@ -214,19 +208,29 @@ user_send({Packet, #{jid := JID} = C2SState}) -> start_ping(JID#jid.lserver, JID), {Packet, C2SState}. +-spec c2s_handle_cast(ejabberd_c2s:state(), send_ping | term()) + -> ejabberd_c2s:state() | {stop, ejabberd_c2s:state()}. +c2s_handle_cast(#{lserver := Host, jid := JID} = C2SState, send_ping) -> + From = jid:make(Host), + IQ = #iq{from = From, to = JID, type = get, sub_els = [#ping{}]}, + Proc = gen_mod:get_module_proc(Host, ?MODULE), + PingAckTimeout = mod_ping_opt:ping_ack_timeout(Host), + ejabberd_router:route_iq(IQ, JID, Proc, PingAckTimeout), + {stop, C2SState}; +c2s_handle_cast(C2SState, _Msg) -> + C2SState. + %%==================================================================== %% Internal functions %%==================================================================== init_state(Host, Opts) -> SendPings = mod_ping_opt:send_pings(Opts), PingInterval = mod_ping_opt:ping_interval(Opts), - PingAckTimeout = mod_ping_opt:ping_ack_timeout(Opts), TimeoutAction = mod_ping_opt:timeout_action(Opts), #state{host = Host, send_pings = SendPings, ping_interval = PingInterval, timeout_action = TimeoutAction, - ping_ack_timeout = PingAckTimeout, timers = #{}}. register_hooks(Host) -> @@ -235,7 +239,9 @@ register_hooks(Host) -> ejabberd_hooks:add(sm_remove_connection_hook, Host, ?MODULE, user_offline, 100), ejabberd_hooks:add(user_send_packet, Host, ?MODULE, - user_send, 100). + user_send, 100), + ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE, + c2s_handle_cast, 99). unregister_hooks(Host) -> ejabberd_hooks:delete(sm_remove_connection_hook, Host, @@ -243,7 +249,9 @@ unregister_hooks(Host) -> ejabberd_hooks:delete(sm_register_connection_hook, Host, ?MODULE, user_online, 100), ejabberd_hooks:delete(user_send_packet, Host, ?MODULE, - user_send, 100). + user_send, 100), + ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE, + c2s_handle_cast, 99). register_iq_handlers(Host) -> gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING, @@ -315,7 +323,10 @@ mod_doc() -> #{value => "timeout()", desc => ?T("How long to wait before deeming that a client " - "has not answered a given server ping request. " + "has not answered a given server ping request. NOTE: when " + "_`mod_stream_mgmt`_ is loaded and stream management is " + "enabled by a client, this value is ignored, and the " + "`ack_timeout` applies instead. " "The default value is 'undefined'.")}}, {send_pings, #{value => "true | false", diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index ab0889864..1557091dc 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -32,8 +32,8 @@ -export([c2s_stream_started/2, c2s_stream_features/2, c2s_authenticated_packet/2, c2s_unauthenticated_packet/2, c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2, - c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3, - c2s_handle_recv/3, c2s_inline_features/2, + c2s_handle_send/3, c2s_handle_info/2, c2s_handle_cast/2, + c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/2, c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3, c2s_handle_bind2_inline/1]). %% adjust pending session timeout / access queue @@ -77,6 +77,7 @@ start(_Host, Opts) -> {hook, c2s_handle_send, c2s_handle_send, 50}, {hook, c2s_handle_recv, c2s_handle_recv, 50}, {hook, c2s_handle_info, c2s_handle_info, 50}, + {hook, c2s_handle_cast, c2s_handle_cast, 50}, {hook, c2s_handle_call, c2s_handle_call, 50}, {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 50}, {hook, c2s_handle_sasl2_inline_post, c2s_handle_sasl2_inline_post, 50}, @@ -258,6 +259,13 @@ c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod, c2s_handle_send(State, _Pkt, _Result) -> State. +c2s_handle_cast(#{mgmt_state := active} = State, send_ping) -> + {stop, send_rack(State)}; +c2s_handle_cast(#{mgmt_state := pending} = State, send_ping) -> + {stop, State}; +c2s_handle_cast(State, _Msg) -> + State. + c2s_handle_call(#{mgmt_id := MgmtID, mgmt_queue := Queue, mod := Mod} = State, {resume_session, MgmtID}, From) -> State1 = State#{mgmt_queue => p1_queue:file_to_ram(Queue)}, From 29ec5bff606da07b03cc3c7a9d5f963008996474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jan 2024 12:03:35 +0100 Subject: [PATCH 0318/1302] Add option to disable XEP-0474: SASL SCRAM Downgrade Protection support Looks like clients using strophejs aren't able to authenticate when we add data required by that spec to scram packets, so at least give a way to disable this until clients will be fixed. --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- src/ejabberd_c2s.erl | 8 +++++++- src/ejabberd_option.erl | 8 ++++++++ src/ejabberd_options.erl | 3 +++ src/ejabberd_options_doc.erl | 10 ++++++++++ 7 files changed, 31 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index d5d7a13f6..c1af5a18a 100644 --- a/mix.exs +++ b/mix.exs @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "26dd833dcf66ebb790d9afe212b7a26f3a6c2328", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "db6d730f0e1cd36645c32d7c7e89e19bb27642e3", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index f3de83b82..27029c1fd 100644 --- a/mix.lock +++ b/mix.lock @@ -43,6 +43,6 @@ "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "ded8be8c169487688b11130eda566b1377ab3301", [ref: "ded8be8c169487688b11130eda566b1377ab3301"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "db6d730f0e1cd36645c32d7c7e89e19bb27642e3", [ref: "db6d730f0e1cd36645c32d7c7e89e19bb27642e3"]}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 56b5ccab1..bca63ad41 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "26dd833dcf66ebb790d9afe212b7a26f3a6c2328"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "db6d730f0e1cd36645c32d7c7e89e19bb27642e3"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index de246cbae..1a21cebdb 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -43,7 +43,7 @@ handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, inline_stream_features/1, handle_sasl2_inline/2, handle_sasl2_inline_post/3, handle_bind2_inline/2, - handle_bind2_inline_post/3]). + handle_bind2_inline_post/3, sasl_options/1]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -418,6 +418,12 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St (_) -> false end, Mechs -- Mechs1). +sasl_options(#{lserver := LServer}) -> + case ejabberd_option:disable_sasl_scram_downgrade_protection(LServer) of + true -> [{scram_downgrade_protection, false}]; + _ -> [] + end. + get_password_fun(_Mech, #{lserver := LServer}) -> fun(U) -> ejabberd_auth:get_password_with_authmodule(U, LServer) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index cc706d14e..e7dc04da0 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -40,6 +40,7 @@ -export([default_ram_db/0, default_ram_db/1]). -export([define_macro/0, define_macro/1]). -export([disable_sasl_mechanisms/0, disable_sasl_mechanisms/1]). +-export([disable_sasl_scram_downgrade_protection/0, disable_sasl_scram_downgrade_protection/1]). -export([domain_balancing/0]). -export([ext_api_headers/0, ext_api_headers/1]). -export([ext_api_http_pool_size/0, ext_api_http_pool_size/1]). @@ -384,6 +385,13 @@ disable_sasl_mechanisms() -> disable_sasl_mechanisms(Host) -> ejabberd_config:get_option({disable_sasl_mechanisms, Host}). +-spec disable_sasl_scram_downgrade_protection() -> boolean(). +disable_sasl_scram_downgrade_protection() -> + disable_sasl_scram_downgrade_protection(global). +-spec disable_sasl_scram_downgrade_protection(global | binary()) -> boolean(). +disable_sasl_scram_downgrade_protection(Host) -> + ejabberd_config:get_option({disable_sasl_scram_downgrade_protection, Host}). + -spec domain_balancing() -> #{binary()=>#{'component_number'=>1..1114111, 'type'=>'bare_destination' | 'bare_source' | 'destination' | 'random' | 'source'}}. domain_balancing() -> ejabberd_config:get_option({domain_balancing, global}). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 787d03628..43f334a0d 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -140,6 +140,8 @@ opt_type(default_ram_db) -> econf:enum([mnesia, sql, redis]); opt_type(define_macro) -> econf:any(); +opt_type(disable_sasl_scram_downgrade_protection) -> + econf:bool(); opt_type(disable_sasl_mechanisms) -> econf:list_or_single( econf:and_then( @@ -563,6 +565,7 @@ options() -> {cluster_backend, mnesia}, {cluster_nodes, []}, {define_macro, []}, + {disable_sasl_scram_downgrade_protection, false}, {disable_sasl_mechanisms, []}, {domain_balancing, #{}}, {ext_api_headers, <<>>}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index c93a771fe..25edecf80 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -552,6 +552,16 @@ doc() -> "", "acl:", " admin: USERBOB"]}}, + {disable_sasl_scram_downgrade_protection, + #{value => "true | false", + desc => + ?T("Allows to disable sending data required by " + "'XEP-0474: SASL SCRAM Downgrade Protection'. " + "There are known buggy clients (like those that use strophejs 1.6.2) " + "which will not be able to authenticatate when servers sends data from " + "that specification. This options allows server to disable it to allow " + "even buggy clients connects, but in exchange decrease MITM protection. " + "The default value of this option is 'false' which enables this extension.")}}, {disable_sasl_mechanisms, #{value => "[Mechanism, ...]", desc => From 416253496db1227ad6f9202ac409882b355e9d4d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Dec 2023 21:44:52 +0100 Subject: [PATCH 0319/1302] Mix: Add yecc compiler as suggested by Elixir 1.16 warning over p1_pgsql --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index c1af5a18a..9e7864b3f 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Ejabberd.MixProject do elixir: elixir_required_version(), elixirc_paths: ["lib"], compile_path: ".", - compilers: [:asn1] ++ Mix.compilers(), + compilers: [:asn1, :yecc] ++ Mix.compilers(), erlc_options: erlc_options(), erlc_paths: ["asn1", "src"], # Elixir tests are starting the part of ejabberd they need From d81b3805e349c16ae263233237a19c8d69981621 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Dec 2023 21:48:23 +0100 Subject: [PATCH 0320/1302] Mix: Elixir 1.14 requires mix, and Elixir 1.16 warns about duplicate --- mix.exs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 9e7864b3f..ab41a08ee 100644 --- a/mix.exs +++ b/mix.exs @@ -40,8 +40,7 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, - extra_applications: [:mix], - applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, + applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :base64url, :fast_tls, :fast_xml, :fast_yaml, :jiffy, :jose, :p1_utils, :stringprep, :syntax_tools, :yconf], included_applications: [:mnesia, :os_mon, From e71650e6abc0bc9302b005a125cf46e6745c1e8d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 18 Dec 2023 19:21:40 +0100 Subject: [PATCH 0321/1302] Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release The problem appeared when: ./configure --with-rebar=mix make dev _build/dev/rel/ejabberd/bin/ejabberdctl iexlive --- mix.exs | 2 ++ vars.config.in | 1 + 2 files changed, 3 insertions(+) diff --git a/mix.exs b/mix.exs index ab41a08ee..96c4f5ecd 100644 --- a/mix.exs +++ b/mix.exs @@ -238,6 +238,8 @@ defmodule Ejabberd.MixProject do config_dir: config(:config_dir), logs_dir: config(:logs_dir), spool_dir: config(:spool_dir), + vsn: config(:vsn), + iex: config(:iex), erl: config(:erl), epmd: config(:epmd), bindir: Path.join([config(:release_dir), "releases", version()]), diff --git a/vars.config.in b/vars.config.in index 88cc581eb..1d5decc60 100644 --- a/vars.config.in +++ b/vars.config.in @@ -53,6 +53,7 @@ {sysconfdir, "{{release_dir}}/etc"}. {erts_dir, "{{release_dir}}/erts-${ERTS_VSN#erts-}"}. {installuser, "@INSTALLUSER@"}. +{iex, "{{release_dir}}/releases/{{vsn}}/iex"}. {erl, "{{erts_dir}}/bin/erl"}. {epmd, "{{erts_dir}}/bin/epmd"}. {localstatedir, "{{release_dir}}/var"}. From 6d61e3590de57841cd132701afe1507558f2fe03 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 19 Dec 2023 22:44:25 +0100 Subject: [PATCH 0322/1302] Mix: Print shell commands output to the console when "make dev" --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 96c4f5ecd..cf968a764 100644 --- a/mix.exs +++ b/mix.exs @@ -254,7 +254,7 @@ defmodule Ejabberd.MixProject do execute = fn(command) -> case function_exported?(System, :shell, 1) do true -> - System.shell(command) + System.shell(command, into: IO.stream()) false -> :os.cmd(to_charlist(command)) end From 30df1dbe3f613c9b61185ff3561288227fbc4d88 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 19 Dec 2023 22:45:13 +0100 Subject: [PATCH 0323/1302] Mix: Set the nodename when using the ejabberd script generated by Elixir Also use start instead of console in Elixir script --- mix.exs | 2 +- rebar.config | 2 +- rel/setup-dev.sh | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index cf968a764..11b347df8 100644 --- a/mix.exs +++ b/mix.exs @@ -320,7 +320,7 @@ defmodule Ejabberd.MixProject do end case Mix.env() do - :dev -> execute.("REL_DIR_TEMP=$PWD/rel/overlays/ rel/setup-dev.sh") + :dev -> execute.("REL_DIR_TEMP=$PWD/rel/overlays/ rel/setup-dev.sh mix") _ -> :ok end diff --git a/rebar.config b/rebar.config index bca63ad41..23bfc8b1e 100644 --- a/rebar.config +++ b/rebar.config @@ -268,7 +268,7 @@ {overlay, [{copy, "sql/*", "lib/ejabberd-\{\{release_version\}\}/priv/sql/"}, {copy, "ejabberdctl.cfg.example", "conf/ejabberdctl.cfg"}, {copy, "ejabberd.yml.example", "conf/ejabberd.yml"}]}]}]}, - {dev, [{post_hooks, [{release, "rel/setup-dev.sh"}]}, + {dev, [{post_hooks, [{release, "rel/setup-dev.sh rebar3"}]}, {relx, [{debug_info, keep}, {dev_mode, true}, {include_erts, true}, diff --git a/rel/setup-dev.sh b/rel/setup-dev.sh index d9d94a2d0..af3875cf0 100755 --- a/rel/setup-dev.sh +++ b/rel/setup-dev.sh @@ -23,5 +23,14 @@ sed -i "s|#' POLL|EJABBERD_BYPASS_WARNINGS=true\n\n#' POLL|g" ejabberdctl.cfg.ex echo "" echo "===> Some example ways to start this ejabberd dev:" -echo " _build/dev/rel/ejabberd/bin/ejabberd console" echo " _build/dev/rel/ejabberd/bin/ejabberdctl live" +case "$1" in + "rebar3") + echo " _build/dev/rel/ejabberd/bin/ejabberd console" + ;; + "mix") + echo " RELEASE_NODE=ejabberd@localhost _build/dev/rel/ejabberd/bin/ejabberd start" + ;; + "*") + ;; +esac From fa3c25ab661349a8ce0afe9ec8657c11f82de3a2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 13:11:34 +0100 Subject: [PATCH 0324/1302] Mix: New option vars_config_path to set path to vars.config (4128) Useful when setting ejabberd as dependency in another Elixir project, and you want to enable or disable some ejabberd dependencies, see https://www.process-one.net/blog/how-to-use-ejabberd-as-an-elixir-application-dependency/ --- mix.exs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 11b347df8..9eba55213 100644 --- a/mix.exs +++ b/mix.exs @@ -171,7 +171,13 @@ defmodule Ejabberd.MixProject do end defp vars do - case :file.consult("vars.config") do + filepath = case Application.fetch_env(:ejabberd, :vars_config_path) do + :error -> + "vars.config" + {:ok, path} -> + path + end + case :file.consult(filepath) do {:ok,config} -> config _ -> [zlib: true] end From 8b38aebbc73399e661d38108a46853a3e36fb160 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 13:21:00 +0100 Subject: [PATCH 0325/1302] Mix: Enable stun by default when vars.config not found configure.ac by default enables stun and zlib, in fact ejabberd.yml has ejabberd_stun enabled by default, so for coherence mix.exs should enable stun too when vars.config is not found. --- mix.exs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9eba55213..cc3c1c55b 100644 --- a/mix.exs +++ b/mix.exs @@ -42,11 +42,12 @@ defmodule Ejabberd.MixProject do [mod: {:ejabberd_app, []}, applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :base64url, :fast_tls, :fast_xml, :fast_yaml, :jiffy, :jose, - :p1_utils, :stringprep, :syntax_tools, :yconf], + :p1_utils, :stringprep, :syntax_tools, :yconf] + ++ cond_apps(), included_applications: [:mnesia, :os_mon, :cache_tab, :eimp, :mqtree, :p1_acme, :p1_oauth2, :pkix, :xmpp] - ++ cond_apps()] + ++ cond_included_apps()] end defp if_version_above(ver, okResult) do @@ -145,6 +146,11 @@ defmodule Ejabberd.MixProject do end defp cond_apps do + for {:true, app} <- [{config(:stun), :stun}], do: + app + end + + defp cond_included_apps do for {:true, app} <- [{config(:pam), :epam}, {config(:lua), :luerl}, {config(:redis), :eredis}, @@ -179,7 +185,7 @@ defmodule Ejabberd.MixProject do end case :file.consult(filepath) do {:ok,config} -> config - _ -> [zlib: true] + _ -> [stun: true, zlib: true] end end From 852a540cb0f260dbc75f1d4fb0a272265195f388 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Dec 2023 22:20:45 +0100 Subject: [PATCH 0326/1302] Elixir: Fix Elixir 1.17-dev warnings about single-quoted charlists --- config/runtime.exs | 4 ++-- lib/ejabberd/config/ejabberd_module.ex | 2 +- lib/mod_presence_demo.ex | 8 +++---- mix.exs | 32 +++++++++++++------------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/config/runtime.exs b/config/runtime.exs index b4e6dc5f1..fb2372ea6 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -8,6 +8,6 @@ end rootpath = System.get_env("RELEASE_ROOT", rootdefault) config :ejabberd, file: Path.join(rootpath, "conf/ejabberd.yml"), - log_path: Path.join(rootpath, 'logs/ejabberd.log') + log_path: Path.join(rootpath, "logs/ejabberd.log") config :mnesia, - dir: Path.join(rootpath, 'database/') + dir: Path.join(rootpath, "database/") diff --git a/lib/ejabberd/config/ejabberd_module.ex b/lib/ejabberd/config/ejabberd_module.ex index 6a74fe460..57fd8303c 100644 --- a/lib/ejabberd/config/ejabberd_module.ex +++ b/lib/ejabberd/config/ejabberd_module.ex @@ -60,7 +60,7 @@ defmodule Ejabberd.Config.EjabberdModule do defp fetch_and_store_repo_source_if_not_exists(path, repo) do unless File.exists?(path) do IO.puts "[info] Fetching: #{repo}" - :os.cmd('git clone #{repo} #{path}') + :os.cmd(~c"git clone #{repo} #{path}") end end diff --git a/lib/mod_presence_demo.ex b/lib/mod_presence_demo.ex index f41a53a31..c5be5bba5 100644 --- a/lib/mod_presence_demo.ex +++ b/lib/mod_presence_demo.ex @@ -2,19 +2,19 @@ defmodule ModPresenceDemo do use Ejabberd.Module def start(host, _opts) do - info('Starting ejabberd module Presence Demo') + info("Starting ejabberd module Presence Demo") Ejabberd.Hooks.add(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end def stop(host) do - info('Stopping ejabberd module Presence Demo') + info("Stopping ejabberd module Presence Demo") Ejabberd.Hooks.delete(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end def on_presence(user, _server, _resource, _packet) do - info('Receive presence for #{user}') + info("Receive presence for #{user}") :none end @@ -27,7 +27,7 @@ defmodule ModPresenceDemo do end def mod_doc() do - %{:desc => 'This is just a demonstration.'} + %{:desc => "This is just a demonstration."} end end diff --git a/mix.exs b/mix.exs index cc3c1c55b..faf559446 100644 --- a/mix.exs +++ b/mix.exs @@ -23,11 +23,11 @@ defmodule Ejabberd.MixProject do def version do case config(:vsn) do :false -> "0.0.0" # ./configure wasn't run: vars.config not created - '0.0' -> "0.0.0" # the full git repository wasn't downloaded - 'latest.0' -> "0.0.0" # running 'docker-ejabberd/ecs/build.sh latest' + ~c"0.0" -> "0.0.0" # the full git repository wasn't downloaded + ~c"latest.0" -> "0.0.0" # running 'docker-ejabberd/ecs/build.sh latest' [_, _, ?., _, _] = x -> head = String.replace(:erlang.list_to_binary(x), ~r/\.0+([0-9])/, ".\\1") - <> + "#{head}.0" vsn -> String.replace(:erlang.list_to_binary(vsn), ~r/\.0+([0-9])/, ".\\1") end end @@ -72,16 +72,16 @@ defmodule Ejabberd.MixProject do result = [{:d, :ELIXIR_ENABLED}] ++ cond_options() ++ Enum.map(includes, fn (path) -> {:i, path} end) ++ - if_version_above('20', [{:d, :DEPRECATED_GET_STACKTRACE}]) ++ - if_version_above('20', [{:d, :HAVE_URI_STRING}]) ++ - if_version_above('20', [{:d, :HAVE_ERL_ERROR}]) ++ - if_version_below('21', [{:d, :USE_OLD_HTTP_URI}]) ++ - if_version_below('22', [{:d, :LAGER}]) ++ - if_version_below('21', [{:d, :NO_CUSTOMIZE_HOSTNAME_CHECK}]) ++ - if_version_below('23', [{:d, :USE_OLD_CRYPTO_HMAC}]) ++ - if_version_below('23', [{:d, :USE_OLD_PG2}]) ++ - if_version_below('24', [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ - if_version_below('24', [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) + if_version_above(~c"20", [{:d, :DEPRECATED_GET_STACKTRACE}]) ++ + if_version_above(~c"20", [{:d, :HAVE_URI_STRING}]) ++ + if_version_above(~c"20", [{:d, :HAVE_ERL_ERROR}]) ++ + if_version_below(~c"21", [{:d, :USE_OLD_HTTP_URI}]) ++ + if_version_below(~c"22", [{:d, :LAGER}]) ++ + if_version_below(~c"21", [{:d, :NO_CUSTOMIZE_HOSTNAME_CHECK}]) ++ + if_version_below(~c"23", [{:d, :USE_OLD_CRYPTO_HMAC}]) ++ + if_version_below(~c"23", [{:d, :USE_OLD_PG2}]) ++ + if_version_below(~c"24", [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ + if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] end @@ -136,7 +136,7 @@ defmodule Ejabberd.MixProject do {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, - {if_version_below('22', true), {:lager, "~> 3.9.1"}}, + {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"}}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, @@ -154,7 +154,7 @@ defmodule Ejabberd.MixProject do for {:true, app} <- [{config(:pam), :epam}, {config(:lua), :luerl}, {config(:redis), :eredis}, - {if_version_below('22', true), :lager}, + {if_version_below(~c"22", true), :lager}, {config(:mysql), :p1_mysql}, {config(:sip), :esip}, {config(:odbc), :odbc}, @@ -214,7 +214,7 @@ defmodule Ejabberd.MixProject do _ -> :ok end case Version.match?(System.version(), "< 1.11.4") - and :erlang.system_info(:otp_release) > '23' do + and :erlang.system_info(:otp_release) > ~c"23" do true -> IO.puts("ERROR: To build releases with Elixir lower than 1.11.4, Erlang/OTP lower than 24 is required.") _ -> :ok From 5bb2beb179a2731a443ec75e46fa2c3d9285ce22 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 19 Dec 2023 20:20:48 +0100 Subject: [PATCH 0327/1302] Elixir: Fix: Couldn't find file Elixir.Hex.API Fix for error when starting ejabberd with Elixir 1.15: [critical] Couldn't find file Elixir.Hex.API needed for Erlang application 'hex'. --- src/ejabberd.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index e90c8eb74..578a06d91 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -132,7 +132,7 @@ check_apps() -> fun() -> Apps = [ejabberd | [App || {App, _, _} <- application:which_applications(), - App /= ejabberd]], + App /= ejabberd, App /= hex]], ?DEBUG("Checking consistency of applications: ~ts", [misc:join_atoms(Apps, <<", ">>)]), misc:peach( From 1bf80e861da8d6771cd296e672168ccd14899ed6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 19 Dec 2023 20:21:19 +0100 Subject: [PATCH 0328/1302] Elixir: Fix: FORMATTER ERROR: bad return value (4087) This is required since Elixir 1.15 when starting ejabberd with: ./configure --with-rebar=mix make relive make relive && ejabberdctl iexlive make install && ejabberdctl iexlive Reference: https://elixir-lang.org/blog/2023/06/19/elixir-v1-15-0-released/ https://hexdocs.pm/logger/Logger.Formatter.html#module-formatting --- src/ejabberd_logger.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 736d6c20c..897a4352e 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -340,8 +340,20 @@ progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) progress_filter(Event, _) -> Event. +-ifdef(ELIXIR_ENABLED). +console_template() -> + case (false /= code:is_loaded('Elixir.Logger')) + andalso + lists:keymember(default_formatter, 1, 'Elixir.Logger':module_info(exports)) of + true -> + [date, " ", time, " [", level, "] ", message, "\n"]; + false -> + [time, " [", level, "] " | msg()] + end. +-else. console_template() -> [time, " [", level, "] " | msg()]. +-endif. file_template() -> [time, " [", level, "] ", pid, From b2df22469be038c19c487a81dfc90e9ac6e48366 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Dec 2023 15:27:08 +0100 Subject: [PATCH 0329/1302] Elixir: Fix ejabberdctl start/live when installed The problem appeared when: ./configure --with-rebar=mix make install ejabberdctl live =INFO REPORT==== 19-Dec-2023::21:28:36.006306 === application: ssl exited: stopped type: temporary ... --- Makefile.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index c56070eec..c5be992d9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -100,6 +100,8 @@ REBAR_VER:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print $$2}') REBAR_VER_318:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') endif +REBAR_ENABLE_ELIXIR = @elixir@ + ifeq "$(REBAR_VER)" "6" REBAR=$(MIX) SKIPDEPS= @@ -113,10 +115,16 @@ ifeq "$(REBAR_VER)" "6" EBINDIR=$(DEPSDIR)/ejabberd/ebin XREFOPTIONS=graph CLEANARG=--deps + ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") + ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" REBARREL=MIX_ENV=prod $(REBAR) release --overwrite REBARDEV=MIX_ENV=dev $(REBAR) release --overwrite RELIVECMD=escript rel/relive.escript && MIX_ENV=dev RELIVE=true iex --name ejabberd@localhost -S mix run else +ifeq ($(REBAR_ENABLE_ELIXIR),true) + ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") + ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" +endif ifeq "$(REBAR_VER)" "3" SKIPDEPS= LISTDEPS=tree @@ -299,7 +307,7 @@ ejabberdctl.relive: -e "s*{{spool_dir}}*${SPOOL_DIR}*g" \ -e "s*{{bindir}}/iex*$(iexpath)*g" \ -e "s*{{bindir}}*@bindir@*g" \ - -e "s*{{libdir}}*${relivelibdir}*g" \ + -e "s*{{libdir}}*${relivelibdir}${ELIXIR_LIBDIR}*g" \ -e "s*{{erl}}*@ERL@*g" \ -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ > ejabberdctl.relive @@ -322,7 +330,7 @@ ejabberdctl.example: vars.config -e "s*{{logs_dir}}*${LOGDIR}*g" \ -e "s*{{spool_dir}}*${SPOOLDIR}*g" \ -e "s*{{bindir}}*@bindir@*g" \ - -e "s*{{libdir}}*@libdir@*g" \ + -e "s*{{libdir}}*@libdir@${ELIXIR_LIBDIR}*g" \ -e "s*{{erl}}*@ERL@*g" \ -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ > ejabberdctl.example From 4daeb41f0efece3b5d3349d21b647cbea3b39e5f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Dec 2023 22:18:53 +0100 Subject: [PATCH 0330/1302] Elixir: Fix compiling ejabberd as a dependency (4128) --- mix.exs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index faf559446..bef8d02ba 100644 --- a/mix.exs +++ b/mix.exs @@ -68,7 +68,7 @@ defmodule Ejabberd.MixProject do defp erlc_options do # Use our own includes + includes from all dependencies - includes = ["include"] ++ deps_include(["fast_xml", "xmpp", "p1_utils"]) + includes = ["include", deps_include()] result = [{:d, :ELIXIR_ENABLED}] ++ cond_options() ++ Enum.map(includes, fn (path) -> {:i, path} end) ++ @@ -119,8 +119,8 @@ defmodule Ejabberd.MixProject do ++ cond_deps() end - defp deps_include(deps) do - base = if Mix.Project.umbrella?() do + defp deps_include() do + if Mix.Project.umbrella?() do "../../deps" else case Mix.Project.deps_paths()[:ejabberd] do @@ -128,7 +128,6 @@ defmodule Ejabberd.MixProject do _ -> ".." end end - Enum.map(deps, fn dep -> base<>"/#{dep}/include" end) end defp cond_deps do From 0121adec039a9cbe125964d9f13cfe726c423f3c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Dec 2023 14:01:17 +0100 Subject: [PATCH 0331/1302] ext_mod: Compile *.ex files also in source subfolders --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index ddab4407c..732749d07 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -603,7 +603,7 @@ compile(LibDir) -> Er = [compile_erlang_file(Bin, File, Options) || File <- filelib:wildcard(Src++"/*.erl")], Ex = [compile_elixir_file(Bin, File) - || File <- filelib:wildcard(Lib ++ "/*.ex")], + || File <- filelib:wildcard(Lib ++ "/**/*.ex")], compile_result(Er++Ex). compile_c_files(LibDir) -> From b08001183eb8ff09f282d1d948e3122ac310eda0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Dec 2023 00:44:52 +0100 Subject: [PATCH 0332/1302] ext_mod: Support Elixir modules with several defimpl, like Decimal --- src/ext_mod.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 732749d07..922b4da65 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -604,7 +604,7 @@ compile(LibDir) -> || File <- filelib:wildcard(Src++"/*.erl")], Ex = [compile_elixir_file(Bin, File) || File <- filelib:wildcard(Lib ++ "/**/*.ex")], - compile_result(Er++Ex). + compile_result(lists:flatten([Er, Ex])). compile_c_files(LibDir) -> case file:read_file_info(filename:join(LibDir, "c_src/Makefile")) of @@ -673,7 +673,7 @@ compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) -> compile_elixir_file(Dest, File) -> try 'Elixir.Kernel.ParallelCompiler':files_to_path([File], Dest, []) of - [Module] -> {ok, Module} + Modules when is_list(Modules) -> {ok, Modules} catch _ -> {error, {compilation_failed, File}} end. From 1de28fa566ad24f18ef6cb5a9251c2bee9a0e24e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 2 Jan 2024 17:37:49 +0100 Subject: [PATCH 0333/1302] Rebar/Rebar3: Update binaries to work with Erlang/OTP 23-26 They are compiled from their git repositories, main branches, using erlang:23-slim docker image. To compile ejabberd using rebar/rebar3 and Erlang 22.3 or older, you can download the old binaries from ejabberd 21.12, available at: https://github.com/processone/ejabberd/raw/21.12/rebar https://github.com/processone/ejabberd/raw/21.12/rebar3 --- rebar | Bin 204169 -> 202935 bytes rebar3 | Bin 945778 -> 795852 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/rebar b/rebar index b6f0118468fa9f280bd0c572be5738cb441c5269..0b6871e70eb20c2d9e0ab53c1c5278b243ed8927 100755 GIT binary patch delta 182703 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�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

GK;1@RiDSiPI8KZ~z(n)Qz9QA2W;t6XhM1DBkLh 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 HA`F6io?6 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¨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 diff --git a/rebar3 b/rebar3 index bf1935ce699486b3e078c821b052309aa36745ac..e903655b81c5fb52bbc6ee7ea57a2ba91f49a9a5 100755 GIT binary patch delta 760214 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 z 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

TJ4Ds*lgK^Uy>v08gr3E%2BSz?X^{xYvP z@#M_8#BZ3_AKe}(bF^i37bysEB};T%>-G7q2e>}?N{JVjOb|#a1Bd8%B7WOoS>xOQ z9g{&D=7D>lQ#;8IhFDCs6hcTjIaCJZSrR!FsTQFkYDovUZ z*t2JNUaBZKwHK9?iK+8A=GEMj~XL!*0vr zU0UQqa9WMuMq!|O%y2%_3GndzzVu9h&E9sZN^|xQwZ)b3W_pgPe&eip{zcG38M-+v zR#vQEq_uNoCaj|Y7yc^#b;nvm+eOW?C-V$Iv2d=1b{TQWIw!rM$kjvj(7Q${$g&z# zXihGzIUu>eKTaP-haozcC9&>)2yJeCmOjnuWBA&F3i3CK5#`i}c=Pe7_Spx(P{}ls z1}O@`_gcCeB(T0{{d4dVDBCnnH%@n_TdLv`v1E*M8vVquS5y@9yV`J(-~IGolJ6m8 zZS11G*9jRht+`*Y)o=33&OsG~q6-5!x6)G%ND?wK=0cbNrhAR%{G=M|(mr!n$S{Lu zNP>87rJ0WMe9;cNX(h$HHH;=e*zE7%nDnEm!D=Edhf1}-z`6L@p)fRnAxdDZ?4*o z15-yGXGhT>p8B!a+nOIIE>DM6JKAKG+-;0LpBUs{U$z>B4j&u#Z!o-o!?($z6@J{* z&wa2vRhM_s_bJ)gI#tZWPUc{=?2J@geOp4lX7j~YHY7}$$x9BGhsE2M(A55_mo94C z&zoTczO@%cugkT*2CmHyF9n>ob*hZ~cc1es+AdI)!I$RG6!=HzM=DAOR7ywxwOOr) zPaSZKSE&C5ChGr~m48D{{#o^fOxLePE>*Jp(N|bI1J_qrdw~)VvZ{m!44*cPFSJf= zw1gI?uJfbNaY!`cha7OXU97b`A7Y?tW$ z$8|#*DzZ)6gUP2NQZUKu`whtzT7`T*%^X-kbO4@L!;k5ckGJpLyB$qDzgR)|qo0)w zj+Tij>kvQyhR7Oce+K#Wft-l8jKh-sgt1_^V0M3W8qf^Wo1QSxYVEu&Y)kB z`!XosW5CEod`;%Be~JkqYG^Vhlm-baPvUwTnaAm<1uGk1M&csj!hZ8v^3}$vUxbNZ z2+GbmWx;k!A;#*Y$0>vm_30F5fzcxo?=9Z)ccS`k^$*W$+yxdRJ z`{nZq;K#(e@#A5TR3nO-`!(Ww}p~o%r>;GdG+~V@xssCdbGaz zm}-Obct?6rFoe)$OG&-|rP%Km%0l9{t}aX3JZ&&&J2cZzK572Nj5VVqG+|PQ_2-(| z;A3l%_&hp&mJ&6)orpW7w&~xV+(g>YIW@u<;N?wQzt!dSJ~V+Kb^f;Xf?ou9zFzG} z9eJ1`@iOB+TR)7}fNjk!Z{S{32XSBAg9}9&>fa9OjAyos{Q!|!azFiDBp~0}`nr@_ z_5DKEozmIia2`{#U>k)e+R#c8N!v^Bn|h>3;&9`BIFx#DfKH`pUmu(XQ zG*U~S0vC|%b`UPZ-LRjK5pUiJf6^wLvy8czH=A4url{i!j`axP=&{OveQpE&TnYA}y* z^A^>WQ&sL-1#csLU^BZ2r^@r?gNM)_Al7x>9$yUg1&!K`tAdo7)YV=4Tz{hCPW+af z^tjf6(rEgk!>M!Bf${n9OZWM#lM+$et)+V(h-qZ&VqXSXjy-7bKYDaZ)Jzm2yaY}U zZgwEZk3Dw%`%!KtYP{03`L0HLj@rhDN0U2SNbs|Dd2f$e-r_HLoG*i$01Q)U`smL; zk8jzMRwpt}e_*mZ6e8s+_!!d9ds<<%wf96oIo|LOiO zxc>*+rU{iAYG1fsOO`(Rvg^-O2CU}3AlOa3WF8I7?Gq1t880wV#)rb#>R9SSE3m|Li?(`kH^QZ5{#=I)(RuSyjs%YFv6!n zg}e2}Git(TNebqDB)@Wp#nxg(T`SG8!_w|t%)-)UQKS>j1WS#-6Oodai2tZm%PYAf zr7BSP(>^KKBA=S40Z@g)PtWu<=SheXhMucIFg04`egjhv^DVCD1cSY=4?LG-VhUTK zm2{~QcNTAjvA35=rYKSrQ<`I~n6pn~gVe)lg!4I6Z|8$gX+hl4v!4PtWF`Q}W$l)U zBEXl$7?j40TtVZeW1vJ0L3*gdNeB2w$<1c?V~ z8lzUXa7mX^BXtU?|5@lf7zZ^l`|EtS!-gI@#JIOs5xw<$=3F?qiq>EcY_`z|JX zH*4sgD@j zllPCGw1M}q_AEs2fBh3{Zf=~255gXfS;mWtT1^)&jCQXOH!Nx{+JSUPt&SaZbapj| z7xz{2G)1a}QTQFpLyfIHpjn|=YajhR(}@`K?;B+guQN*4J$!G>uc!OZ8^#5gx@`>& znX7HyGS<&4x&$Am1F0v+AFHO5S+^5y_FkL$KY))I?ye3~7h`45^M!uN#V)xIuZQ@L z_}Op@7sPr3Em!V`LDS9k%8aN^4pz6bVd<`WusT|<*@Ou^&aurTsLC|)p=pXWOpUTT&H6R*lfS26ltQ_KSKdDV2uc-iV|8@ic0D% z4fV17Eon0_1hBYNblAW28C{}+ua!6=T9}Wk&B-npFxRq6ibIt&K4e`!yq571EA$4$ zDb?JAe;&7Ttg^2~Dye@Fubq#SN|68*KH&N;+ZPY(-Oe)=aM|@}qRV;fG;H4)VzvIh zq1hKl&OJHU_{2-V%f{(i(gGUw%g?ar#a5e&RTsvFJqBK@tfkri?CsC(Zkvql*o}2^ z!Thdba9vJ=_uHWL(d0~}rFL5<{+i4F+>xq7gr|!`{f49C_5@GPLzhD!7I{tl( z)0$+H=j0A%Jw=gO=y5cDS9V9#5x*f4J>_xib?5y8_JZt{@^STPgZ6~^-{n^?x8c_D ztF`(E4gx~>r6&xO1V{VNMgtshax`}MAEqWV-qvi+1aB~#Y_Y8{i?yvhEQCh0f;}iX zf%!y-eYTbPPu8N0=D`ESF!^Mr`DNUlc;$H?f_&_8KnqFyQFoC0ltJoxPHk-$VxqHE zEn9D5jXdPf2x45rqwtR9__hoT-6sQITkkGUAbazc+s!p=z2lV=6bSL0$-G!u-AAy3 z0>M(Ft)MEv3or~jNRe0$458^xG7kU2PhUz}?8dOORM`Y8faxCgx8(gtlkPUFA>^r&}-j>9M2eIc7xUfhCWr) z{sn27oU5Kkk7B;3*sM6TK3X*tnjaBHDZhKgpk5)WFb7Qz)2SCD~OR-lh)Eac~eVU8h7<)5vnP`${Vcuo$BQ!r_bXIzY{Cq z8J$i!fu%6O|2wGBWO0<(sE6-w7!?a{NaFy{IzSmSrvmzaw$!S#xUN}TrBtl8hu=Xi zU%SrK-x(~sGF-e2&iI3Vuc$JD&c`V~V86KOdxNfr#})7g_LwNHGjYJT`n9^h)_of_ zyl)?d|5iV9pcV zZ1nHqSGB(%wwwje{%xxKWT}#lm>lQkO5c2*b}Z|9d!^r>tN7Tw1YRyNIv;g7JRk@S zW??U$EcKqvGYm-t_(UAFL>hGf=r8*?o$b3wzpo%Twx1n5*xb$v`Edq^D)hWq1Qr%PUO1Eh}#I2M40ml8l`0*yGzn(Jv;Y8HhCLA`3j}8{b&94u*|MntVB2I+a>laS3KKl0&NVhgMj>oW!ksTSpNtl zbk5i4B>Ho)l*FDRm+nqy3)jtD42V}=tQTwVPbW;^`F;%9D^Ye;VGb6r6`ikNma5aB z&&yG|ds4dS@Wt(@)mfrRs<|hTH`i+*Lk>^|J$S@;{62YTYlH~D`u&cq`5rAg&MvUK z#&{iJC+}Qj)K+?u6XnBvG4qqJ_b?cz8s@}kFx;6QVJ)Ta`Lpe#W;yEjUqiOEU!+-t(>Uf z=5)H|gNrqrq~WE_+I!9XrHAfSXtP&%7V%mtAg9q zBI&XV9IsIexfbkg?XtR==Sz;tgUlys-49@pB-rf9LSFGPAv5W)F9kl-a(hBMJ1kw{yov zi^8g(>T`wDACJH3-Pfa_RW}>wS%s<;Ei+HytBY;sS(~?MS^FjUe1moRe;*O^mU20 zmR3ptZ)X+LdoO*jqpYV8TcZWKCqPoGW}?vK5E8Z$Tu7M3hl|C=AvT8E&d6MwBXD-Q zgI1MAImw|zQJ+96T`NqtS_QnC@c8!#o?7!h4--$-k8Y_cD1b}sj&tP9Wp*d@{L@m& zfI8TNVQ8l;3_Z z-^MvgC~h5$>?mhKC=5W_i?jOkN?98S2r%{-yw}dPqw75<Eafi zE)1udAKw0}b8#}RqoUttWH^lj(mWbgY=43I%bE}rd*18ugQ#H-QS}`Q)h2`fck7eQn&ieU4+6pv_#YmJ8kkR9e2U61 ztEpsuppu7%p^EJpO&N-f+~L+E(I5NS9hyttzE{JNBw`eqf=$k7-72oB@ggHhIOeUF;9`Reuh`*U@(g>+daXP9!+agygT!*SBXru}`( zjO^wLqhPq>_2I?kB1E`v6Cgz~Ui8r{rdb;N(YRXvl$4&~vL-S*xZtE^JyE(nRl0m3 z61jj`l2_+f3sdgj45q z{k&+|_Mnzq#Uuxwe}B1(_N~>X%}Thdp%Mez_|`eI)PG^jK)jGvgHUa8-!AKjoz}myZefkLi~8(cl@@3@4I&NtE4qXV}CS+w)~CHbP^Y*5y8ny{E($~Z)PitJZ*)oDnxYuL9bRqtHqDif;S zWi40fuPc)&JD~1O*9j)=*(iw2RPl$mBwlIbzRJI#J0XPFPtz%$#?Gsqv=zn7{GRvE ze6@o8P26%U2@pUdcU2-+v{W{&IbX>#T^yQqY%Jpy2GzN&C$Ol04GKmR*33N|D2;Ml zsuYQCMc9l|wDUEsU-`qG-h^MZ&6c5(;A(Sd{S*UjpnT@V1-aC;4qgw9GFCc|nyE6L zWlL0N@DHr}IzB6!*q^qEpy9}^=H%Pku1HzE{=Fz|D?pVi^LGJhaq0G{q0|}gA=RIP zx@_HsPJ9~!+oF#_`r8Ibw%SH%3=hr#L7=FC%`noe+Fe3iql}v@|p&btGG_8WZQJ%;g=p#YJBd%BFR}Q1z zP+_PKvFDgXNr*DIYIcs4_$~0XEcl#U_8xE1bgMLiC=o|F#UvySVL2k@9AfVutS*ye zMnV8<$t|_e2II^|Dxns}>~=Q}y1ZqgG3sb41&|~wmU7XMj?gu8U91)h@uI5E@&idZ zfr9VC>EOtSmZR8Vo18b8wq%A-Lz|NtyC+v^;o^xzxSlh-akcm+*lMx+JWa!CMKUdd z>i7pj^!dE>k$wJ>>2s(J2cJTOyt54wE#7g5R}XSR1}K@*4JhpTJOz z#?%e!{g73hn0K+2WUJOnW5RQBEhp2X`{MdDnPZ(--=w5BG_D__3hZ&a6Fn}5dzJ+0_?=-=NOIt|l$ zDwd%Qczn}B5ejQ!Lm;KNoHB6xF&E}Hp)*H9Oj^d4b>nGP02rXwnQkwbUBP1_cXN)isbXa$WFn)lQ3+4xibNqIzbGoZG4;h z{oZ&~!N%$OX?nKI@5jlA@wHq^|K`_^a|xYGrmznhd__(f)@E^&4YrTan+~{9Yjp^k zotg2DLj)TkqtU1kHn_BAGetf2y@8{9Rj8g$)#E6F&hp! zj;Q`ePV4-JRl`|)!?=kddl9qu)-dwoYTH}jFFGH-%qha_YV%#P();3|VRED_f z+~%i;^yJ8 z>?08kJ+?}|wtE?SkQwH6JzUG@s@xOlo1;`GOmFAtfp`4T<0r+92E(MaNE${>g3a0Q zJK@KTzqj9gch!3a;rOE$-q<;iV)9$OnUwgf4hMregEZ9a&6cpjWJ(auSm}#O$4_nJ z0aEwAj5L#O8u`In*F*V>Gyl^=J|Uy z7dBB%7llj z=}0E44Y1SBKtk(1mbWBBOnx1G@Ko)ph)UMwLK_v?+apuTAu=r(vI+mtM$c#orS=rE z`_4qafZ<)UY}&_U5Z}WFNsofqYLkYXzHCFmS~MQf{6|A8?@Ds(;H-KnV}9Wrg{URr zp{-Up&l(+@77!VdlDDET(ZuL%p=zw=At}3}-8CTn85W70g)r&R%>lg<0H+yFVhX>< zOfi}Y38$lY-)|P08Z@lXyTc^Z)_11(7&40Comx_$9H^@kono;d%w7O%5+yS3g`IL9 z=0i%+ASoTF%RyYJU(}Cr_KP;2MY=90Z8^bwPl{EP5wI|c^g$}cu7SJQM4V>}N3jlX zme^KJ94JJn#v6n1gdX@<4ZtEti-?1%oCwzsL<)}08b%10_g~AwE!1F8RCkO>ZK?Tv zT*Gtz=A74VDH{JxVDqKKTI2y=dyAtqqK4dPlY zEyfi-UKA6mp3%w9lMJuOfcv!p-+%|h)x}|4gw;|y+aY#;uqPNB#*)}(YFk6n9cK5= z1f-EBI|F_(G`SL&cbyhHjkb1G$;iWyyA5sF)V5GK?+*CJ{h1RA-xKgnAif3>eO(yQ zc);}mzl4zBU>3#oMlvIf!%}tqX%+Ul6!8`}`?98rh=V6(K!JE~z%#hF4J=9%qKtsr zO96!?3njXLRxwhg>@31*Gs0=|Y2-QIBK6wx`TLr7O!Dg!2 zLRoUVk-UL*D&Uua>nO}~6GDGF%~OiFJ(0(6RP0ZGY!~|y1#w$qOx&6n7q=v?6*nh} zIGYi-EBt0t+fF=gF|`7%x0>1*t+$!lIIa6k?OIy*n_7|9+f8i;t<$D9LF<8lUk=$j zA^Tua%%CdVq41e;aR5HPV?aj&T!B|zhc})UcQP2Ho}uRj{7RlVg-dtJm%J(9SAolJ zAl{6BONj6kNQPm2Q{KEw-pq$LuMThCC2t-I_y}(90pjinaR?E7h_wfh1}sO`3HUV- ze{U%#%MO(xiI^|t@<6B(u?>i(eoimtPyuoM0;7h* zCR5b$_*z`e^UN94sCxoF3MHDpG|Xq&AcD409t`-{@u+#04Kfs#$wD?8-fIMZ zeEfJcAL8?qiILvH17?;a9uD}m;9G{oW@u^%y%gps#`Tv1zTH2wgY}W z$ZrGrgYarQ&DRf#N5xzAiiZw}B}*K8P#irej(k{j9vA*|!h2r03U?uXGa1SQ zAS;;jT7VIB(>>>6@IcP-i~C&!Q02h+hCk` zg!p#`d@uOz2fv4l;++PyoNk^V`@|>FH6Nj_`EFCZOX2ULK8dcGEwOw7B|d73_rhH7 z)@P|NWmN5USp_$QUGEX_pxXa`o`9DjgF5AVC&Z%+>XyN1W>|c%tYJ2I)X)wj^;vU9 zt(z#N;kcw~2NWjfr9m~|H_*s5As(YH{ut@=fxLL1!rvD*ihsQFRQHuBdSy>P>MkFVe;+N1 zkAm076#mh1@g6Yyn8=&vAoVF^-=tAslNzc5(lg*UgZny~qH^8M+6fULE`sH0UPX5g zWOx4ry8Fi!{_#~BsHqiy1@aL6iLNhmYR{E~eGD!jR|XJ4>putl7P$1yFySZh&TFz} zUjYM{5i`Z76#g#(zm-nm@K5a!Pq4kL%yyN2t7_^OFLL7gxe?b;`g2hE9`Ob3Uh#SD zKJg4hJgxAjC&cHBo%m%+Mue3NYEt|5XLwrS&%D9WejKe=_~+l?=yn`kr0_4ue4E~{ z^9>6Bw}2mj2KT^!v|l_eo?|kgGc=(+C*S!jl=zauza-1pLFDMyPm8Z~LknDHFa}{7 z`!dEeS=FAEba#Z%nzSf>US1*5s$H6&6N@*%zgQGMF=$H%Mm9wLBH(kR8T9>SQT)o-BVJPY zug1kMjH38O7QceRUos2j6*eyiyh=Bw6^^f^U*pDa6#naR@iK0_oE;G_L%)~H3ja+= z{@Z|m*Fata`R`7PSG%b)vuj4YDpTbTNR{6!{P#uiD#X27PEJpoWt4R30b|T$H<)Es zpT%(TWHx-MR_ne+GOW`q^;JA9skqp$-<U|>So~RQia#m*PxOhx!niTW=qHhQb#yk036`ZigL+7=KTE>@2LJ&7|8!Y- zU{pn#@0WTFuiLb~PPIl*wBfZ$DwY|481es_*;i_nr0C zYg_S{^0-QQY9Z6k;^E1pigCp}WwT&1@`t$P{HZ#w6tll2yaj}p9)$3v0|{4vaLGXk z_Z>($5rmf=gz)793Fm`w=|KqpJS`P}d97B3mqlI;UmkhIa$c#9C%@$kkMYz@ur50Y z>#NmCL+Qm7PbkhVY4<6jF*O=2mlI2(f+cA=uMNY~a$ZkIe$SN0xaIssJIkwT!pkA= zjlRg6JRYvC4#YFu<=Sbta-r!Vp@-xpp{?HV0^ z1He}hV8#h8?Q1#js#(q?TA1f5%XzOa@_q#eIeq1U9PgJnRvL~E`XV1zaJ-C#jsrP9 zEOB%gj*t2xA6IZt7_2&wH|4GEpeU=(Gt1Q9*&P^+p%Vb@)o53$@PjQ>1!=~ado?!u= zVXi%mO9x>zQU=1ckuNRh%RcGYz?HG%Y#NK~uPugCk-vo-BVUInN4~OuoUhz?pe0e| z0a}Wy7|>m5C+GfJrCBtsovVhs&e44`$l!NKM+9+|U{O1o(9YGM3y^*>PvuxN7R_m= z(@x4s&C;@*ufHRAoskOx;n7}M$a*7$Bp^U@MyqQ`MxPu`d-g@X$$b!paxNpHG{-i8 z)djBKVG>*LZK{}`v$BYl1VGf#RlhY z^yiA)6d-@@CYo~{*UhBcc_!WE_48V|rjX!XE*45wRaIG*%~aY`9TnD#c4+5jQXDw@ zWQcsKoo$`L0FVHG>^9$5x4>7b?5h@D#ah}J&vdvf+C~y?CAX1v>ZJ|q7`t@AF1A~A zDK^NA1y(YqSqUO`PljEGAz0`+9C1ZY2U#-*&k$hIIINx9kaDZymN=wdRA~7+tQ}e}52H0oQ*jvnTzV>bi|Mh z9bq!`$Z)N!u|!Q;a(igDq_^VKpy0aG&qY7DU|m{?Yp1s&yr`zjNx;a51ZTnUYvNyA zkcU%%Qh2AxLFMp9aNLF9KI+m!lZ>&NA{2`myVOA7U}mW91OS&QRfB+dfXB#(eH zN3b~bGek#!zyl_f%hspa+NB=;A9-Myo3#r5?_$HoS0A=N0406j3I62_-LZCagR0$tOCG4_9+*Crp3}#K7q>Va{_*D=Lqbt?HDk=2n!4!Jz z0E;|-Ube`X?^ZysNgl`i98Yj#T}E9?E~^SD)#e2era+f4*?nB)3PhI{k59{yoZ=TG zW8o}HZ~+_9vku0j=QxB3gb)wtMhg86@cf_9#Q(Dkk*l92!rRB_0g0(nm(SV!aG zxXze>B_?X;8Blg7Edl>dWclzBcyRc))Ie}Q#(#jS+erR3UBukIket%a55c+ttm!GT zfm5I8*?#rb&W{kGV@%gUen4qRL&Js!nS!vt>kKBHQ9Si)c zMLRzMz)je=u{s%#njV%VphZfB+Q`6-SsW>NHnrm4R7if-F3O7sQ#O-`r&PB`CJ40w z_D^jk7iAL36o{u~X~k|;p`|N!SVunw#6%>06MWP+AzgQM@1Mxs=L9p8;4xdX!D>&{ZvI>PDvujZE6Md2$ zAbGx$L{)(zP87;e)P+Ft%Y#$Q^eO5<@vEWR43yOOfz-_g#S24~du}Mjd{F#Tb~S{4;Vp3olz@s+vjBx;2h7 z@?Rk7MD6^m?aG;MJLLmwu^Ah@NKnp9)6TykS~eqErdLFZ3U4uyg8lz}c+yFXm(47^ zMLWNSsI!_dpm0LbvrC4Z@oh+d0TKYcSTYTxdS=!`+dZG4ZUNuw;u!r6kx=5;7`vt2 z_99V_!(}I92RPy6KFnq}X)0DCR+GeVhIak~sILd=6RQKRsnmkY52=(9uOHqO&&OwpU&VPgWMkIKDwmP4LMarE& zb7SUQ{~faY_}%pOKX@C9Ll^~_x6E>KoVPH}sQUgME2w^d@GDoYRJn2{rT*9pm)0>W zw1`3fX4zti99}HZVh+;(1uF{WpYW)srW@zQOYr0@?d&s6B*VnZ>+m){Jv#t#e_j`i zI})$l%*UPa+|0kSEXa|6EcSvR$H`;xz6zNWOdFXi@=WeUFO%Dsf?-kl7&WbymFA(f zrknIqROam*bv;SA>oux{rH;?2Yx~O9#Nm*WwDUTQb|)M%OI3Npl5(~sPS(y}XcCs3 z-6&7N?KA}lZyXYIPDt@=xo=A)sH^aDiWd|Sya~_Dr6L>~9N+llYeC<+6_-h$;XN?8%1@>kZuJT7H-WRE=A6NW92N>3-lT=1B~CFN!Ta;D zizQCg&Iho|T?n>+xean27C!X-N+;*MDe^STd_*&GGjp1hd4?$0&c{HBqwC`mHGK&X z`3dq=fAtfrPQs&db-s2!#a278)#(kg6@otlh6$Evogxb)yddQnnEf2HQA?aLLDJRv z3ye~hI8!@+1#=&m&(hA9J|Y}=bSA@q+W88>eaKjOPN>a)u9xSq@R)M%@inldVX%`Z z4`(43zd;pg08^tpo88CQt{6|tvym8Q8?2DQLT#2f+xYx%V4jQs=#&eFtS->be!9EF ziD*A1fMikX0F@m>1;HGO#}v2#ccWyu)(&q8#^rfHe-8R0KTglpA^?^85vS)h%7x~G zNruN$_453GRCr6LJYPAZysVHHgtlibaek+~&`jW&cSfaJ*U1Zu`?9UsvGPJgZ~+O1 z*bC`PO}#8ahO3hP_S_qW-7Qhn!s-kvr_swU4`#dFEXJFIS(Wcf1ybj^0sjQ|N=zxA zX<=HP>wlII-_xQBkRMQ%SVW(*jO{5=1Vdma)e263Z*h6K7Pjwt)R{KkeP9>T22Y#} zuAt-QF}z5NVG#Qug7f0)Vo|jY=AwtoD5{akMKPQ_JYFu2p@etJ#Rz}|7xI!ca%3_5 zZHY^0j|4ekYu?quft#C3->PiCbT6h}HUS@@j3l)P0qXej>80!hom3)BWxK@3Y{Pe{ z7KZ?T;BEk1^8W*@CL7>kq9GmSG-GTLDcj;Qo*wKLO)rf5i(N2i5B=`0m(|Oqq%4dz z8{!Xxu>fG{P%Yi>YPn2{D9}C(%VP6rC#vu$<_Wpn<5*p;Ma?>Ne`s(y^|UB%vz$4Q z&k?axwTKbeMjPm3o2h1Z;o$V+!bjDv#o>^D{wQL31)X?VuU}p8e_KCP_Jpg_U3JE84L(V`h-_v%=SXysStRSKX(;> zaM8WsYN$oePA!gx!jD1Wb$zmv8g#00VierHqDii2rArreOi1$2A!7X?)@!sl2E31h zw+pYOIH;H1I2E-xcJS=QK`i3E@KYU%OSh-R=w5gTgQeXmEynaBWijaJM!tHz!8{E* z_q1w}=tXmm!PY&Cv^cIe*iM6SlU0sx$o{76J z>#T)9UvJR!y5XaNe!M}?>q4P8;7bFa*VTpMz(2v@=XJBYf&T=9pV!UrUa3WYvNza8 zg9UrA*~H%9HX2;C=Qb@S^#*s+U_)r(uIilwbm2+GR5qbn(`r#ptt_aGZR)lT#WyL| zqjtfsli;Z9v`86cH`Tqehr2J5uFr*^>M1GP0d03e$xT|Mq2$w0@>(q#%1UlBN=~j+ za!RF=jYE`d8lvRXN+qXNDmi_BP|46nXxUuS@{gqDjFOgb?AfKoiM_!A8hkwy$OjDa zXPB4Dwk}$^@wQ!*)SIL?`ts^n?H-@=KAsS z22_q2_3}nOV*h?i++cp;5I1Tuk4hkI4I|9+^DBixby874`9P?q&qM5NYmztN^)yr0 zfx?Z)TNo8#I?k@gJ~wH9kuO86T)0__`M~m17-Ab<%jthJHrC?wQu^9xgwG_?A+o#=Tw?0O_~8(Q=A2=#6>|hV%MorRsmH7H4A5pJUJ48sx1Y zJF~p+ttdOCONTfM%w4$epk&<++8!;=2HSIppxaT6d#D=k*eUOSu*4l&EGP$54+82? z6{nwTv;5-ao7%%UPp(VRJhEjSb4leU)4_6CuJ`Tq% z^qC!|PMdEW;sQ)vi%UPCY8WAtVFR#7FAQ;-n#h{vc1!e@mefpttn1}n80RyIBuMW9 z>3lWXU%E?0vFG-GK?BvYbd7?6?p78vFFWvej}{j~?LFvd`Woat$d4j5Na(Zbi%UqTxgYg(+5K(i6MHV5h_%^&jP68jZDZxmXuoPpsZE~3 z#j&52KeZR4ZP9)$7TZeR57f=_UQ6u6C7^ z=~g3_py@igUfxFybw5{@2gZe%zig1~zIwUK4z;RHT=lT%rfARBcBREq$lVC)&p|pH|z!DE+Vnw?9iB-m%%ED%SSEo zaOp>?^6Hf)g;-ZP6t6Ps6t2zErs zSmM!tOu>cO)|<#F7^6_!tap-0bf=GLaSe?AOO%nv)A9*MwUSe$b>SW-Eb&BI4sg=~ z(`2_lIiSUcd2#1hOFXH?MzH@1weuHAAD{6fOEZ2u$7(^_0x z^?v{W0RR7VSqFR+#TviKT;OgExM5EsJkf=Jjk4xgS-Fek#K>?LWGEAZU;$eYGz3vm zjEWMCPoH9kXJafc*u`hTf?!812!aYCRi3dUHZ0hc_n+Cl6h-(YyEF6kHs81DW-POb z`vfB?Mx0K}dy=>drF&UHH)9zi?voYX({u>UjcN84=6vJ%Y|fpI)9?ks9C>abE;2+knC1X_&(#_X6` zgXN2$&r(t5B$!oN*TfPakzi3pVwP!3caEk(Eo{v)P3is{>%pdPYRRMBOvt`}RJzwg z_T?sVUwXi}#H92-YMJMHFH<{yLsP_2Z~nzik(7F2`%h|$JHoMo-zyh7a9q`XT<7cd#@9Bg>-LIn~0OwA)`%cgSV2}7n>18*zJvlev9RVpetv1t7rtC2H{np zYFBzIi2H_g{{sRmLG{gm>T2S?CEaGV3975}RM#k~3{>Z}Q3dFl8Q$A}d8%&*RNo@* zTIt>a0;@pvoi@|W@YVs8DA08c-n)UAjoxN|ffH{^_478TNLSl$^wz5l=JfN{JA{!$ zrMH2&>sfqfg18$(i%9p*&Ss1;8bzqq&1O*tAvxA6j!O5g&VFo2#@j*<#*faBMsK6J zgvJM3eZ7su0^pHw&fSQAUFf$l>1|5HcS`pj7(SMzJ$CM--E2s>N&ns)^6O-J?-BRi zz!__ay9InWCGP*EdoL7S4MpDr;X{>G-Y0I0bnnCV8ZdZ2FyIXD6B?pmoW4VmCjUGb1Z6(b1KK8v2iTh!eMy#Z_%`!@VRMu8_A6Y}-g*2Wj z(eXsS(QXk?{s@$g2BrB>F+$vrTQPfv2M+`XpEP)%1x^^`cw0*t@ji{)-lxRAUI2x#{IeTUeA5Ht|tJLx|9S5tA5UZ@Q7ZMB3mm%Q7|7z9$%mF zeo^lGL%Hu~-}{NUKZT{1vSRnb!;+KSRoKP~nLn9)H1dJeCWC<*zfn-xOQ; z?>B3B#AH5-rf>8-3yJuozEggi>HUuKLQ(2}r-ZFhq4j%MpBHI}KOmG+3_-paif!-- zj|}v7QZr_M`dMpQ3KX{+Hcb)yi~9uy1?r`fFA4}J(tQdj8-Y@gwd_<<(3DXTx(*Q= z_Z@aI7(LzD_p?#c%2~8VIgzWIC6^S@l#wbjYgC#JiydpR9AKYmy-}hpULN2-1N>Z+ zni@9>p~ehKiFiq^u}O3`Hj7TiL86fG!lH<;xXuB8#HQHzI<}7vL89m`4Zb)|t47`^xlc9~^Hr28U_ zRAm%@#)eXz(!8i#7_^fqx)R<+x=VrD0@SWSZJi-@M_tv81K+(t6jKxNZMnj<;!Vux zt+veow^$c2(S`7C(tQb3-Uk(Hpx7f6K)NqSO|e^2B+xbJOnU{H<^w2Ew;(UutB~y< z(p{!bB82ZzF1qFOU6c^sUAnI+$~NI8VZMuhJ;N)4@7W-F1{vB{^gz{@?()B+r6STJ zc=XiSDY_F5hATkz1BkNMAS(7Qsf`~fdc_Cer(EQ?s8FmRYoM~!T7O*@+Ok1yotU>iP)F$ zeZe!@ERs!_C#0^oqPkdChCiorXL9`s@p=|Ux}os`C6m{rTaES_@svL0Yx=`HL!Lr z!BSd1TBTdAwFuvHlnu3g-a##H%!q+hnHp8aQ73~OaS){S^|3z()QFRv-N>ePIV~NsP zHsayEk*v)ZDeX|Gu81Io2PzB|cPUnt!|fv7mXS->mX-uz0)3({k@9Y5Ie(LQP0HA=N(XoXjGT&0`-dy zT<7SW_Ndvv=lW#_)JOGBd(_-6aK7qbp3&I(x&uzt-?Q7^LCJ5h^GyfTp?{D1Z3oo% zvGZLA)WdM~3?h6OLYzzYdqnOR2;$-8g43?HiS#%QwR{H(!te@zQB8+X%c&5foFPua zNsDSn=fDqIF&dpIMno|A(Y|5?og>{J)rjHE(VhMXfc*p_NRFS?d{{w2_2+;4LWBeDGx zWDZqR{OHfGr-&#j-?uOR9;aY(WgA3=~9h|n?guV z)XpKaek*_qf_E9&y7nIQC6ZG$1|LE*yJr~7*DeoC-V|Jfk#?C(ch$PQmj;z!iaPIUcy{`&Ni!>0Oknhr&wZ*$%oE&|L!P zE`UZfj}M8frvvcuG@LsuC%Bf!0}a)TchzGTKQ>(M9xRUwmu9d$Uh-n-vK_A{zz@fl zuo`hvtvHeJ6N@4yYy$q>0!P&;N7X4uP4Go6;kAK(C+gs+N^vsb6I$2sj1_q1q_#kq z*cu20Dhwv(!ypk*pRA})R@6`N#U#Qf1=J>kI^idqzL}?_C|E(~cS&$Gzw3@{kdQMIFAl_3Gk2(&< z)|6hdmXs47v^bP%qx=I*KWG-GE2d^G`c#(J9mz#W)Y1ufy5xK5NtB-fuNH%6&zQ-u zoF;iMDE%#zJ~JuKq6ENaHHztGtREe&#GP(`5&fNmPj|)H!y~}lTbDn)s@=0C-$$(# zG!5VLXAP3up#2?apOX;{%AgI(py$Ffb>bL-+-nU>`+r7nEiyM>dLo z3+-4x%;cD4E+nw7qpK;_9j+VhC?82)8Wd1uKAmMpB;PkMf1@_PVqte+(J1+Tf!!|* zKa}DG`xik?j+kqbye~}jJ?hZK>RWHKxWt)elw}=;5$BSev3L7VvROI}g1Aw$3r~uF zQlrAOBSWJF>XX??R=&$c9PY0>@LJt}fkzX0aa0)mlc_f!ZYNN752VMF{WH)GIJ;`phFW>@tX)D zXz&9v#Z{806j#Ep*7j9h^)-^GRZ+QC@^U!%2SmVi6=Jrk#Hpm1?Zo@T*yvEMBYbwV zxYmNx)yF3k1xpP*NbG|k`SnSEF^3vIhQ3S~`7aNW4-BGejs~rW*|8B={af-3nEi~` z>)SqM@q=-@k4%aisPSt)N{|(Uuse#ud<7}hG%;}#^7IB~{1}AFjglXtXkh@ksY2XF z!+0rWPMTR&L(~^<6)lyJvRU#&K};ZE`Uwen)!0yl!@xq15w~N1X1u+1T)th8 z%eTVVHxqudo{!R&_6Wn(h+LklK3ynjlsV_>lOalE4C6ziX+8T?S?~cofD?G}rlhz9 z%n6qo7MW-4<8PDRAsEePspW-ZCogBm7=g77c@WIh=f-=B$X`FZqbzcAuXF38O@R?>+#^$_)RNd}Kkl zvO*N62hk8Q2lYU8MEc$t1uq?*5s!lR`K@qxtD*;~s6(z(HFjUVMB|iqj z7yw(UK9>w~#49CD@dL%n@d5ZriI?JO@e<)Lp#Uu-{ACtgO0SsP?W|>^->9|jwn|!z zGmVMCf6JKYG@+VR$O)6&vE6I;V zN{u1>HOY^E1C1`AfdE)x88F`Q)n;rg;VW>7gW+TZ-3c(~K6u1%fR5^Q$xj552_LU) z7OSYbIcY6A$W^L#=nh@-+G=|q;j2O&-;jI)P>O-_Ca!o!(tAE+7YWOJS)$+%jVvoH=QKq@59O!I#l~FkcCC!XG9(gl*nyxmIT)B*N0trHbmMff9tioWrsp59 zu7`Dh>S6son#E?79Kp9aJphrEx(sm@NRo5?(z22LJ&7|AbfxSQA&*p2;MTi9`c~q9_`iAiG3^ z3a$fT8LGhrtV^}*8jwK*M5>^;A#QbnqOmA{T1Bn<-dd}ITKC^wOG{A{_XX6tuiv@J zjgCCu^Zh>0<-U36z2}~L?wND%=*Y1ej`8QH5RRcZmND_^wrY9HK*Lq4%y5lTE1ZHU zm6Ve*TnNQULkd-#i619b8!nU?YSB{@pOGt5$zA+mWJaD$T`OgDFoNP7Sx&>qI71D8 zNAg#_!PEs6N35Zov?${(ycu3?=A_C@t5NP$Vil`ne(kogR9w`nP?veaG z6Q7eOi@(lMmxi8C5?uPpm$+6O2h6f~npJSvX{{Y2*Uk6Tq z2a&&BW_Us({L-wRjJitHz*AXp^o9ASWYn#X3IsPYp~`(H$y$-0ad6{oA^9im3**?pKRWG z3LKbh*?JUTj&dwismw8+Vu`VTWo{|upn_TJyBHdzCO$t`M#x>HAj&tdfKTEd1r;lky;SsSh4Uy^@n;-@28OY;9h>>&UH^Z|xv`XGH& zLYd)tlvZ&dI7%Jm4Un``r$cx-23E{)2`m=}6V8AMIVo~q2KK=CGk?Q>bF_nccf$*v z-cCIL4}m4>VZAe@T7iJ504&Ib*t;++pgfsY?(4|1W>PB;V03^AWIBS&mlC?+GKF_O z!JvJC7md@gc&g{#hL;RU@-I}J*0N%jf|b4uFg!*6>&pxuEdHw!+zqdE2_}A4o-AGN zo1oKuUW$(862O^RpZzL-{Nz{RXTJ(R`BnJ;`t=G$%*4-z$!Zg#0L`&vt}ekYKiytx zZ%$l2WLpTwMsrLeM`gJiUME0InO=rBB>$RAgk`)z)xgBhMVSk!QD%6Lk{2bEiJ$i= z3Ff0D2%$o}4R1pjJfBP)+?tBls)64LVPH=kU}gL7RXpan;%WEmIz4Ey7Ni? zy+c0u{Q&2gZGuuG428hQDYg>NSgQ}LB(}k&lDt#s~w>>S6Rz&#Wyg1Nnf=C0eXn*QqZ>sz03|?L<$I#ln`j7lu|Oa zUtxe%n0wj&4=zucC8kbhk!D9klCUxj=CXVVp4 zl)_cvt#FhmT+L1>KnnHK*u=!IK+dR1qg{Z|f(oKoDo|*~b{Cqn>R^`81+yxB51r85 ziSueFH1$#oO46uAuN!4fYdtKi42sPVb75g|?OuuA)=#Se+fD6M<`H_9bA{)tzIYDn zXx4*;DOM_f1__Rep#tO8UT{#f73>vh0z6dNsnAKnDcZB#I2ip?L_nUfkZA2V2g*c5s^mjSquRp42v;=T{6H-q zMjG3jcqKe>gC}f&;0EhrC~rZ-z=>d^5?IoxK?`Casg+Xrtx~H(rIh&qGP?N-?a_=K zOuQX`nCJi|+yewpY{CPZa3_r(Rugt$!d+~_(_iR-On8|*&VmVdFyU>ZQf?uQJ|>T& zz<2=T+e9WGPSN>D*wN%s3A7i`en5YNX|0Lh1hfxSl}>?zKhqr=oUWTuy$*(*3TEJX zA#`S;kZb{N8%0lncdBzRqhj&|owkCZ19a|xf{xTdtr`q*-WsVIKpLUYYy)*IYtLGc zd;~LQJ9YioadhuZ)F_d}px|#UxpzRFW087xoq0unqcDPH%T6bP~7nUDr;&*^l2h#3; zUr2X=H0}#&50iX2NV@>+33K(P6oVK`KNQU!4=aJWdztv1py~z&d)ug$>q%oDliUZG z02s%?t_&xQK_(SVaScC9Zk^WZ^ z-Ydet5ufvLv1sfW(Y$Ag&v|}-4r|c9TojGHDzbS^F2kkxaDSuG+3EL2#@{N>>cv{3a!E3Q%;K`=Yntk?-bP|niOIZHFJ4?EXG@3>j zubuQ1VQ&%k72%GeSUHRMPL2GxVt^);ehA192RzwbIH5V4lNsTKR4k z9n~Ah#>#iW`kpv`R$hmH^=N*SYb547V;;?8E8?}7M|&n0+3~UXL81MoMS3qhuODG~ zr>uGp%%dD?V&(fKXQ49->PfR_Ii-(=)Qqj}s#;ffZ5tt$89>cUe?|Y z(d%S7IyLxaSk#rG*n+8lrLS>p8M!%HzvyMMCRWK_mWH&OcDMc|-E7vx;`2*q7l&*2 zH@mo?;qN|=%gB!#WgkX%{B`F3Q?hHNrK_6tmZcs`_^$1Lz*bRf4@obPFI)b4_FDJW zKQG!fsAxgwEg@Cq+s0hA9a6bxaOqbME|0vGJEEfCb##KAbN(dapR;`)rgNLA4a1zD zy}e@C5&vUtqz!eU^8Ba;q3gNOJ>{jE^xk1-oQHhnRV^W+@|Nb&b0YVCMGYht(qQNyj>?o*WO>fcXyTJbJ_?b6Geeyg*>Zm-Ba)*&i&)u^3O zPkkEKeJ}GtpDl?zSWw}$4c+*=(=nVc_ih#j3))LC#D(QHdeK)xOQd!&zoYB zYyJs;@_yE}wR0EV=0kP{T~pq#`nfv5sZ)LRxJzMPPvcyMRHuy+W*APTR&AXnbW5GT zYh0-F4d$5l=+V-BKe{G=JbJXx-lc!K2mI21B(wTf>)$m?9v?2a9Wi*au(9l9+rE!% zMojaGO`h}TZGU?4$8p)2gQLE!dy3$1hA;7OT6<9@MNowr%oeRqpW9t@p^j+ZDMYdvwa0zDxQB zby~2$`rh)u*0(kM^3?uL(;5;xIsamRQ-6G;&Cc&@LhVMD{@TW;zU`*UJMsn7rp=$e zA$s9&CG*zpKC(Z&W?|cl1Gj{Sx;8&B@${P7$LS+%o@_rCHpP&cY?Qy(=DXaLbNzeq z{dcVB7s@|5FlN^B4cT|gC(c?vUfpEEi{&W^`9r(3ugg}hsEY6TxNT3#jiNVyY5Tta z=j9!$oloYR>hTq?!;ORQsDs{773+?L_Zyo4HOl4)VZLLD>$$jwi|e+yR*UPixF(D1 zu(#exPitDPlmXi2BPQf`%cEA`o(hXyDmU>~E=StgT&to==dR9TCn7QUyQ+~bz!NooKycx)A52dshf zowUb1+|@`G#-{-*vE~F|JB*J3R$zSS^ZiJ&53nsByBn|!<4w?i#2R$({DA#IcT;?4 zmy|*O8uKL>BOcwGUto;x-_NkksnGvxiH{^3`llF=gZ>G|DbPR0_*>|I8!$%SA@$e} z`aXGt`2o;B#5e-_`*>cIcXu&Hxpv!fu1iqv+`>Miv$YOmHS{;I9kjRC@mME}5s&tv z7Gt+qP{xC(pCq^Wi=JVAh)1dp7R-x)$}gUkYh@ou{;2 z*awa(|G^qK(&_lUM|Gh`2tUq0Coc-}BAiY?zG;;ogL{Dv9A&bD8z_AiZr2=b@cuH0 zFN>?eQ987*!JTipq<*%nDajKpobQoo?$=!X=s8w*xLdVwQ!alPYqC1nmC<8@K2~zc#&m(F=blLmL=Ml0nb=gz7%@d;XMut427&>VByRI`M}(kRMz9 z6Q21`FG5pJ1vNBn&~XpWES?xy$=NvdSu{0tiJoG?C>p4CRezq9tkeEgSs0JvCA32B zn3#NzGP7z-W@JbjC57WrPhK8& zmT80_(igyYLEzQMm{={r`cpF{Ju#Yt=Q>V1>9a(JpAjpJ%piC|#Y)TF{opXF-G1bK#m=)CEG&_r}6*erA z(Wqc;n{po9DZTMqITACs0CI1h`jS{i#>~bCD*(LnCovZ%U&pabjP@|uy+m`-_23_? z&en<)NLV%JH*NVQ;{l=q*D-$cO_52Tndr3d*L-dPd;PLZYe8dHYRD}{s{wfl^JF^< zbUYoa4wuC|tL>WDo-egGWl^wN*qa<;%HM2NBxcr+5%}u3huPrS+seoGg!wJcknL| zUXeVqy7^U8z)aGS`3v3)>wBp87ZEt#RUwm6mUFW3OCXVu`H)$`tm^9 z5XyyAd7DIuSS$OuMrKLG_(BUVr_L?zD*Nl{x9JVKZkT3llA~ew-Mq)mc zaNiJmoY+6C`QW`UYVc)xIOTugqCwLLem3@4FG0N$Ai!jQm&n1U%z>=rBU%dpRi&7Fw=Bk$#1B`9WE`OA9hHz10=~o zS-U35exp}Eqz31kAs4rsnxUKY08-;^=dpo|WdDCPAV03jQU5UD_99dF@2W-qQVUUb z#Vosq{Rn7qg{Rv#vh32mMzHKlbNykntKIhZ@!G~SSoRv@!+&zvm#!GPMgE7maOR$H zl_Hk8`k%fkAN_|AFDd%~%Q1Cg_7ZokTvr0M{6V<@F9 z!Nr!;BG1TD6NQBuLE zF@N~5r&dB`6muDWbBJ`AFwH2To}H4j48nNrG%4RNZH5A&s!twjPphA$r`8;Yv8X5m=wo=B`g^RFb`) zX?!Vko_|>3_hOcNEv@ocJrfWf2fLZO5Twz@k%h!kkeqo|>H> z!XbHhB%obx)~Ym{a4)G{V!_pwCl z5Zdbtc@6T#()~hS|F4}iDMIoKW}BgC^X25 zvJ}*sdq^bD_OFH1*f6&5mnuCFqPN0E58wjdggKS(w#HyJ|4H7QGs2KF_< zB(xV-th=jDe~p~P%@lwKY*Y+f@#6}xi<`-WODg&p&i8`R5wa+;|H%rF=%s4*se8IN zR#8FK9MN$Vq$mvw@cWj_C&2M{q)fstr~eAY{;%e;fC|M!Q@ms4=kkuJW{pd=vf)OO zt2pj(C}HoYKauNIK(W1=kOen>2_d|sxDJdYg%0GMy4pd2A~FXj;K|Rm5b|3#iZoY} zz_w@6N1=^!3vs~hSAn&)1D6CeB`RmVQqPszmh{Z0C_6_4i&l_}f}~vUocw9-$LA?M8{_7Ln`-_#;&b44!8=r~J_pWmpJO(r&o_kzEn2KOOx=Z%O}${6 zyoxg)nFKTIupReYfNnG?)V2)~0(aG=v!8L&7IKSy)0)l_H=i>IcbSFAtHEOD@Y4JE zADdzo+Ta=H{rd8PL7{9d3=@tAYOG+(tHY{;M4Ho@Pb^PdKirZfXX@pNhh@bHvzEqU z=7GCR!V}L=qF#^~<$p#hrlLzh2b|JX5-U6osEvEv3e(&qK$UW8q0ydMaZJ$)e8ywD zxoXS#fW&O2hV<@cThpeTwn?qfA#q){V?*odiz?g1B+{xam;PRr#S!*-1xHtJf?1qS zH`S}8GPR<+Te0- zZBaG2?t}#yM*x9F!uVZ)mey`sMjnOAP9%2-Kge=>QNJ%4eT-R80M?EGhT&uPUuE^X z@D54VW5?y~yXWR>$E9}X`dWJhDoE)1(!z~?SVud61kwWoLO!YLrIq!2?Ebh_pk${q zzxY_iq5B%F>L^!Yz)jd+UcQOWq{UVsQcON9ekg=^?swH;uz7e=9*T;CnpPqb8B6(I zUT(PqQbN=~O-xhYK3TQUF9X7=kmnU^X|&yRVo~I|>|%2Tfkq`&CIv!LGmav%=4j&E z6Ke@T{3M+kJ_bn~6<-G>ppVB}PJ(z|iW=v@pau~Scghl4crfjX;esFmPxkw8axxO? z4snao=|TYhPU_fJD!tzI^Y`A!7(seSgBoRs?8>#OR#;p75k=QRs zxQ$>~ruMn`u`b9+;Uk+wTtY+yYN`AQsRy;yV-S?${ZS+#uQOcHHgnpCUCYPG5cT%c zRbxnO1SDn@AB&j}jY(h+l_nN8#)9c5Fn%?`u#{KUejs@kOm)x(MTFqrr{)XFKpw6I z3+6xZLZkjq4`Y&gH>{Q`<(-vUeKlr%b}pVSjKxZF)t2<{OL-&pX_JvvGr;7L4X$$Kps3nuK^0B+9g{#d|AdknZ zZq&i{-#?2ClZTtc3|~cd@Qe%`i*=mS#)2Yu)JjK>N=L2DPziV^-S)k0EjHn>wXvuy{Z~rx1yV?mw>1~&k|M^FLR*He(dlzoEr4)>vvFHx- zT5ym402!Q5QClhcj&fGg50Zl9t^OQ8Gz&Dds=^>V@-p0t_&r-{3a+)sVbRi8Y4r(x zQt+-J7OxlU`w#T_CK}bEDHM|_L$_I~N$LOob+y~oJU+ZveR3b4cO;#0gmfZ^542-`*E?naOac!^o2HE% z+1q1vg#R5$w1(191hap#%!(va+?G*SYR!mx9~k5sQQOUC8(l;bnY z)KG!rA7%XuQyP#&i3@y^VItiEHRe+^V zAHGhoI~+`;eB4hugCc6BooL>UVX^^Vk2Qu2!l&6MUYz$5z;V**{I2qyE~4W1*NP8F_#f`hkSOMWOkFV6Akm_ z;@0ucp7I?FQfTYsR@4RaU$Lr_X03|*p(^S>k}A?0^`PbKS@_QPNq$L|B{%`|^kEAM z3PCH`O^(d9JK8w?smnQz%!(Wpe>yNt7uOOa2f|&VvlnL^L6%5oenK2n83<{FT*2NT zQgOu^=hMsVLj2t)M>RkvwW|tN9sd1|o1z$GjKo%n2i0E<)lMMNHm119Rsyb$5f=m+ zRv7!VUmQI|I2ISaP~HXd!7v+$_v77)ue42hHwBX9avgnl^jUAm_v7w^!r1Iy%Np_T*(+IjCAs!Z)0%Mr?cQ+|u7{gv~U*ri* zfh-Lr9FubWP}Aeomkv!7K)?oO%BOM%ABgge0j&)~MGc#OOn5V**aIYFBPH4o$zdM6 z5AU1Q_dlTSdguiya)ukh$sKki*!@WVGJG>AyW@5NDMs?V7 zc8;78+WqJUzwwZ8=ww<&ws+H8m3(S)0x;kA7rD?M*D%8G*%q9gOxmPD1Yc0KoTOD99o zFWK^u@`J1p;B&S;_{aT{B}tV;^~$^HWN7XDQqS7e{ycn#@3DIo`EaPyZg2avo-%$o z@nOs3;%NWS`>QBawzIQ#fA&;$UUkCi?fpO$z=^8rl*i(cfyaK1iym>beKfzFL?iON z9c9721_O9MK|T@bZW%u0z1chcY(BHr@%cahv#b0Cf$}vUe*dq?jwf47m#}*of6+(=ABL^kl=q`!W5?3Z417;g#07xv+nWu?*+cuIVw#2`s*NUR6#WdR))wGc%Y=lfsk; z(Xv}7fT_@C;MF4U7Q_@v3{#Q@#qJv_<|NY@G8NdDnDtSTk;X2EDtAU~MO+)YQ?u)v zIgBDpAdzbXdQvD(pcQ578A#A5h(Y(}>L?Y#t zTFhv~9O#N$J_Dn&s}b&zh%bT(rbA{-A41*=@6 zj*%gLGQNQ_WM^ecWf)Gq_s8F$A0fF3*!DYoF+hdQ*nYyjd&12?E{kt{#X?R@ST0tm zN(rMi!9De<8olFmK6Ux8$@#+Wi2&XVI;n4x1}T1VNLbBk; z5^M@%sxXawYb zNQn9C@3NENM3fuH)*d95Y;tvMq+J76IpEv?AW+AxP_5vJYJ5r;K?zl9kqU>NQJPT9 zB`c>Ewg2FLmZ~IBCs%99F<$AIvl_PdZcBlOCk&jYhIYw~OqVn>MiU%IYhLlJ(cp^e zQ>kRMmhG<&Jpb~&Kxh^!)g0bhM)>8HE-S}myZw&CvPXkvv9lv!A(xIQx>*)72S#}I zO>2ITFMlT?^MQjyrr{zdV>BzH#8nlrHr1idx2hQTDGc?^JsE4mx8(s`=^9Cms#csq zsP@MWRY#$VP^sks$@$aRNY0*bhYO0%@@;$i^pjc?N7J^W!fD?I`CTslXmS;+=8N>r z>I(N&9i8A+?K4D7n4DO@{-pKJ3+WtGx$vf$Eb6qR4k{fA5?jM8opmQ% z4t&}fVYJc4UqCvzI|E0ZP#?#X;KDlbHH@bj^(az}IF$f091BUc<%k*Qxn{$GsHgQ3lh&E~7+(&Gx_GYX>UfeknWhu3x zoa$?Pt;++ay;cA8UR|etR`!_Mle(WjopH}=5%#A=ffIz6db5k!%V|$z za{U*#b^=t)Ga`1)#T%JquQPUJe#lg=zY6~RSNT&JgmemmVaJR}+d&C)JtLo#fGGb}&h`BFkMpTOPlzHX1PiRI&XU)pG zV7&)`C#~|T0@%|^Fz^wGBm>j)`nAViA6taX=MlMs4&k}Zw#lg8Lp z#5~4@6O*MvkcW0@d;B(hl*ZqD$Y!ZCk7y-5A!k)2!iA0;+k=6PAid*DJx* zfCScgu!@3^SA)(Dygluf@FdF>4=C(er}tRu6|Y<}nQMf143^D>#F{VI`W zHxHZ~8|H_4aQk|i6@kt*+WUGHN&XF)Kz{=Zdl%`-MNNAkzg7>2p#}abj#4aWCq`bZ zSTkJ6~-0@N4V zvzX?C=xaTR^M@S}B3s6V#$qrkV=yw9rxx`vw18Ele12cPV$q)2HlFb+f>i#t-r3MtOHD}TWX=vw}wED7Tf=o0;!A!ii@Edm4f9@=^UxzxJZyem@j04Y15Lj%Qc)60SydycoZ+ntu%+fSOwnGI z;2Yi9mk!j|RN}zbBwo210TReKnv1w<@FcA$ z$%`iNbQFXoxJp+ZJ*>{CZg1!&f?B8;gw`Q&XMd_aP^^|L&;vb?7`E(Z|GR-u55Jt< z7B(X2rdO!`KWEnCRW@54-(yzEaAzWh>ggCmzt?rKlqDQ=SOWj~9o*phYuhV8^S8afv{MW(`jbo7;LNsRZE z0F~@=8)V4hEOIO>?znsvYw`}dzlk~15^ZF0FZn*Vhk}lX;zQjaPQM>RYg_@2u4o(7 zGy$Y23z5wAAYz-v4|hOC0Mo*asACelSKCN|#^8z7A$_n0NCd~(X#urd-F1$_Es}sg zuVOPH;$L5un+8>ka`)v{jj%!+R3=id2eUW{C+rT!*}1Oc3K|=`-%*F0ltdRHWZQo5 z&S{-3SUskbFOqDMC6ULtezEU6c7)Dj4u!Ns>lEXrBM|fpu9QH;Dz5AabN=-p_e-a= zVVyQVm**e@05!<0Vz2=(lV?URa76gM%I6N%a|en?67pm8$qa%gA`2pHwQN&cRP%m{%%M^U@p_2+VHz3`1nNG)5hS*^StcI zf@#MI?1b!uXe}EQMmGzoXnqh}Rg7(6*7dkIiwQYYj3|hC<2S_wegu=)Znz9WU#jS? zIy4D{b>HrZv|xHeXLxWEMjJ_v6iofqg9&ZPFHS+-LRha7@{XacFk`^=V&=t(`H`e< zGdV6D5FAdTFmpm;ni7qDl)te+CY>54b+o_8f5OZ{(rxIBPIk~0yGrFG+K~6HMAGFy ze0>u+Y`x#Xfz-{R>M3db?!|Y+mLOk#%&D=o=u$rZTzI91ZZjF@%Kd@5UMlF>S0sVv zE;TzB=UyuR8ofXiD--pK*^8IrOVif>clg;HaQJNrMZ4_{wTO4f3=Nd0oEYOYClnnK z35mJrzhGL4fx*vwsp2u(sJ202?WrK2@8fpT(D&Hp{$ltJSe<%Z9y{+ps$S2TyYZ-v z-tw_b+9+M8JG-I&mM(QewQ%*G-Df|)dUJw?iaBK6d#+&FS@mpxg7>$nLO!e!?3}d( ze&dR)_B|4F%@t#$G%D}>(~6;w?sL&a;@uL_3pwwDwN$<5GuT|6T_EU~1ed(3!VLyQ zKHsUi5hget<*a9)LESL~7ca$s2h=;`-@KPb1W)5USnPnqF-v=mHAg&NbI4dBqhCE9 zYkBQ_U&iCG7QO4u{zZSb8GZFVb+v>6c*@p%Lww%khr8`H9%Fip3fR0OdXR5}RV8_? z8K5_%R27wf{ypJ$m&gC(!w0f8s@q1w73_Q?qIZ+=)nxdUn760lp0BXBP3(fsu7{$?_4 z#{|~f_#um|ikIz`H-A$%*_Y(*wFo(L@E2<36O-uOA+iqNG(G799Wzro>&kbpXm|PN zCrN0!9Cws?y!qk+h0lb5pT!e;KsI0n^}>eS;Sdo zrYvCY@Q`&x<&bc^%5g|U|5Q>*nZdhj@P8`yU<4#0z36_D-NAi(w}CbRkTm!gKOIEg zLM>jfI1p6(irkyUXFVNXU+U+z>S-vvTgWp}vgn`o=qa~TuTVao#t8Sfk|gS@^}-a0 zU+&5$uC*D@f|wW%pQQJnR6l?GhncmZWYb6Ev=%HoYHrkMXyy|w(>`OYNH}q{?7d5eKlHrxe!h5I_t;#=ZB=jO6`!{IvmaX(9HZoB(Ktae!drHe#S-)9 z7l(XD0)g43NSZ@~%i79nuIFm{GU2iCb0m9RLXwNq=ekIHRfGpn`UY7q4{fz(!=&7h zwy{U*pFOhLrBv|~P9Li9oF`5s0juk-9v_p~Vaf7n0Wp7slP?uqp_<*2Hc3FqjZEy8 z4~E5Lgrjn}^bKw5z`xK3wYz~{dE?s(*c68^>TttEX&Y93AU&*AY)x1^#p!qoTw9^! zYH_9+-aPJRagQ?KMrUY_pC`#E^2GSjE$kS*d^jppdgDDr?HA57jJ`i=>PlSdKcv|q zrmF0XMHogfLoTsZKRPK@EvDC+Kv=r)LUTrlz%zZQb9oeg7` zIK#V6CzOimVS12)BV&p;ud_z>t2^E}kG@JYXdaFP$G`%}QDE(qW+Uq1-naRyWi74W z#f0%Oit%(;R9sHA=Sw=^zk!1Y@g33>L>puq1gQny z4JF&c2N!4S61U3(;!5rA)*BhDHV6cPp9fJ7_KGXOopfv4$JvyR^#}W65yVvqsHX;B znk)ox_0^YJUD3kGzP+{>P`PQDk%Hnuzw+zUkrJ5zZffX6Grvb2#@s%7!oP!OmkUn} zHlVoik|*I`!tK`$J#*2DY`~2O58cZx;2&DlA-157d|R?1a~%_{>@vW;k#im6pICe~ z+w)9QGT?k$r?P{;0H=)J1dAu=gumKJ*9HfgW17G+1tx#-QQQDv5>vMbd<-|2pu$Jb zsB*z?mmmjtX||wpJ|Cz);LoZcoG+aaOVI$#kgzu@{g3CrAU_)2AnAWTzxrSd$$}LU z0SH&bH>6$YAX!E3d!DeGpvK*R_X5#jrjt+R&@2Ao2Rb#$nCS|A<=F7#5k$Giur3fP zSOZ80AB>rktLE^aZ_M8N zru?-qwc!VrvrR3^#tG+b42RKPAHjHFL{^n6_YisbQ;GXh$E8$UUC7TkBqL z&`NYv6b&Yq(_*+X(b+3m2SzPxuG`@azb?lCZ~Y%v+brDHz7YtspI$Au(*UndHEk-> z?p*=at^qPa0@!dZzQE zoD{>!cI-;hgD zJ@|WZAf$Vq@Cwnl4$^6CL_Bwcf-!ZS=uAjGEtm*c=K-Z^w zCkt_%K5)W>COVxo_S!mr-hAKV8tsf@JjsZ5(8k!m@5vw+;pw$$-2#h{jvnEb`4DtG zff!%kzd3=P;;BfI>B2|?iArb&Fd55ZK1en#Hh)&&H7;M(@H*R_2QmUG@;+<9#yShpm|$V zvB)1l@NyusQ#EZ_Klc@iLFAxjr(x%2pD#*mb!EccL|F;Iod6WV28}9gHEtv%waS++ z8k^S&+?=ESrU|D>PjT|m7`LE8Ijm4;&M>-UuB#4)26RwZdsNP6r0NgT81y%y1g|J` zohB)AU9_>9^i*-CFjX~=JzP2)c+a1wl^}kKg$B&AR?Y9<)VriBSi7u9tUGIla?UI0 zRn<4!)5vsm3e~yOI{AxD@^_@7B%jPYRkNz1ZL-QvmlgPXKI_+YgI>D{Js^*pbukB z*Ea6N)v<3eyr1r`0%q&4eQ20W*Ez9D4hDi!11Ki7wSlzuikzLN7`oI7O%>L2(pMaW z3$YXDBaO0xhbMz1>khAfJVrYawU$qqGI}yk@L_LQ;XudN48b0vBtn%k(hH+JKovz9 zmvAgmsjr%>P##Z7yHC+y;hJDyAfR3}M{$~vln@nPH$paYIFdBNzHhPbXD{E9`&RrW zwHQ4i1jxF_xgyI*8UDuLpAo0T5GEfc+#N=b6)Z&lks_Q)%({agm>=&dPNFWB7`wkm zQX5=OhBa(l8`37Rf&Xu4bq{1&@*$>o?>oE522s&4#to<$!Dh70u*)843x8#tMSg-7 zLDDe79$1U;MGU7>Ha~N6&HtTbEoygbt#vusX}`WY!0JO%$DBAJQVX?A&KeY9 zz!Gm%W0}}XT2o@lk=9*Koy4xjL!~d1dJS0h{@+-pTp*4(C=&>XtzOcW(%1dj0XIAX z5U#3i`%^V>;XdJvD(44HLwNXj6dza|x^K7?K0^>|ZLcgCI#w*7*k0pYW1x}KKTUhv z@BTlTQpP5Yxea4e(*AIf5;LWoEubb;RIDUbNJyV^Su2~HKN@8fSEN3cl&m~*H+{}o zg_Rb-xMXE{OgwjZTyR$;(M3H%fyLFC03v_s;1{bhzC$!Z`|@p>Nll`eB&-Tsl@<>d zu)B$bkT=9?sD$1QNHzZfqN7gjw;(D2HoUBnV;!>BiX6>$_+jF8ZtPx51N(;;YWSU9v8JXKS21oCnh z8dc0$ii?@c;t#?HJnPStX1N?A{Evv75p0p)WJ2vrN5+t5FJ!Wp=Wq>VJK zwNuqePD|KG%4LG=_P<6`8^KkiRO8K;`BfaF>#5w8E5`$h5>F(ii`&YgOC;7kyX_lR z3Q#oZR`((6x6#^vyKszZVEkjH0UFfBDTNCbk{{*ht@Wd#qI9@tW(Xkc}5 zaF!2>Cv@Gdp5||f=3?k{x!E`_GzLZK;B>KZkg(nK>us&x-sVp=AOsnw`R4GzaRGNh;U@Q*|XZTfRSw7P(h?g0^z8DOo zw79D5E!?wm72F=TzVt=e)9wT0vSQod9#6Vn6)9hw~=@hOJkMi;UC+{`(oEbgz;s_63Q$O zVw(O-6&#*T<0zABU4_nuWx{BhEaZk|%iv(zr?o^A@ziBq)vq{jAcb1E69B;N#pXEXr8S&9; zLWJx}5}o03zJ*}r<%LV)F!IX$%_H43HC0$-i=$YI z&aOgD@l?3wx2oc1344YFT04gSZLKlHVLVBmfkq&uWS!NCmL}^zrw)De1L7@Cy``|T z&_=q(auKyPIdS_M7H!0FO#5o5=R8f+v^aX!Z(P@C!j-7akFlDOXekNu}hP^LoWtH6RgQA;AYX|F&3k}*?f9p?GE*1PjF$b zW#b!{>&9c=a{#4MP#90~c1Mq$Rx>V##WBbB%_RQS={oQVT6-;$4W5LI*VeC~9JFDlzcj0-7+%-`VB~?g*x=LOVZ6~EbVfXP+JN~sdR%$> zK#cKX9Y3s*kJO+P+lz%B*eh_5hd@J{sL>{m^2GY-Q)gZ!CA znpEUn%*G>!t0icFj9LH-UvLzOo$45<25%O5MaN!$hUt7bMTzwm!OVlXaVjG|0{=@$^s{r`6ep?PzeQay=MN!)I@bFR$?|;NIr-c z|2EdaR>ys>9Je*!=XXZ21cMA;IW0t*654k~klJO%$KdMoT%|YRhY{0dBhT?wuE==5F9_Zrs;Q6xL`5yT1xO!YO%E4zoFs-ci zI<~(Lg&kA!<8Wxy$vB(m^S$T*vJxy*M-Q=#dhv6}rX;DC$I5B1wK^y+4~?nKUU2wn zX2MsgmstyD@Q+Qfw+xzPJV-f=xcB$)Gq8A0emi^^(0+UqpO|$-WL%GpAJAaf=tr`! zhlrA9m9T|Jqv82oi7Fm# zI-{4=W-UG_9_s0sImC|=p@)`>jn9LDILK#`Dc0A=mM)sdrsVaHc4!QEg#@==!pdp< z%4+SLU^sTbkXdj#H$TE^0nZ_Y@>S9cSN_>;VrrlQ0>YG_bBQSWvA++-_vG6|_wNh$ z^s!dT<_nbf)ru$Ei3JY;lRX6C<=-JuCKkmFU@qC)V7{<} z@(Q%)1&eA&pBV2ba}1?HboLYZ3cr(YA~(469L14Lj9c>Dpz|TlvHC>e=iHh7uO6q+QxKSJSLo=#z(h`w!c}P@3M2hUclZr`6Pd6BN4yzfa0eAtG-m46PW$pOkRJYlz4i4y2X#ggXd4;&rsfESsvRD= zuivdEfXsx9n~al_v<0TW_v<&5K|;(EI*QlvLhw)Isvoj}0G%CU(s%t2^r0(J%A4GK zSLmB|iGY0uAzCJTB!%<<6oV=5q-`c2o%!QFD91O@cE6!tVfuWJo5A7#dIp1~ntLpQ zwVVQ|h4(OtlJE}@@m-}u(<#NQ++3x^$Z0}IphM0vy})A@FbolBMw;Ep7L#ln04R#(gD}aEQufHDgXK!jVB{|2 zsrcqeL`d<6lAEJ(rDo0Tai*8iGnOTbUQ6r)6OkzXUEmSq(%b)9W2w=7gE4@U%Kk)* zFE(#vguyH{ud9u5kVP|ObkFoGM2x-<+vq7bG)r8bC8%Cr_wNBZa*yh74 z7q}ve06qZb$!-CiV?-~k7)qaZ5GJS0m_?tvIzIPt$rnGt)&X%{+fePT{LTy1$5!bV zPNn|TW^BYf1I$h^31~Ago+-%ux9>JR$ju#~i`|0)66pOzZf4nCZo_=9-SHIH6yWDj z8C3#aU_^_P-RX;OW**0W=s{G^L8H@fP2pHPK%^cO+7~h*_$a&$8gUX-sP~86wC~au zB3g!481{}d+k-Tk{(2C(8~ysbW) z=>1G{u{YoTZt6q5e!QHd4L0`TX6!Y4fN0~!4n(!zt7*kZ$a`oATgd~u^gY@}ih^8v z39DC(C3!?9vm5N~Al39jCR%s*q2nz$#!bx|BM~t4ZI=eZdnpL?=HCpcrlY>JT|_Kb z*x-{{9GSeme}=E$Qud#J3VlQ({o<i7mHp`*o1g|2RjLGez?9d5oY!5W+6VGS-cG+8~sKcr|Cs7CL%~+ocM;01Otpp{`|n2 zdb@udmD-y-B(NO0h9&Gg%j=W?gk(}WQM~uj`vNGK#O^GFcuWjI<)lDDb-_7lL#wVi zn8$Z{US0>Mm_L0M|BT{Q`Cz7sbYi*Jek9w}SX=*NkHxjCJ_UJb0&#}x`he`>5!%aH z7bo4@Yz70-b@=ARvNhDh=UL4HLx|# zn34Mj9;dx99z>H~U41r!r8dP6&1R zVXo_DD4|aA6OZh@BLfa_-HRu#s;bI+?+M7xD=N>@Avs48J1>%+QuJJ8n9}6Y?D};G zN2pMZ50o1@+tKxt%Fviw)D4b1NYI2?{tp0tK!Lwoms;BFNJP%VcNY@Y+Dhk5hSz-+ z@D_o85mFM*hWs*9CezUcMMH!Bx^fu^Cif&{iYivyE!ec6$WMdi;XsJ?vwxOE1A`V6 zQGH(;C2UEBwX`fSs5EX9w$y!Km+AXu(AQ#0o2sn^gx#lDW_X0K`xU#qOKD(K6Jg7j z25Oopy_e2wnrN-0Eg>MPu-X<8edqA4q*=u#86GG#0gpY&T0xuatj{i~Ym(G28_Q%S zD$|uRRU?z5qz#@obPH~hHGe5NDk*6~R;DG1$Y=&clx2<5Pm7wYraHn_fP+EBCJ&>8 zwR!uESu5@06BN6`=z4|m;|hm6=MJu92xxGC5i%Cb+<}{9)@=kfg$TxF#@2 z`qS0cBZNIHsV$=sSwlnqP>o{O`Qu`qq-nw)k)u*_bW%>xs#I!KqK&dltCA&blcUP@ zsLG>WPh(bxR%NzPWw!BrwnvqR2F4P6bsSFMm{Ji>t~LsLB?%uPvy` z4aW8jMwQK-2YNaxQk}@x@v?=7ED0Xr#n?a+J44ZeUh+e z6`KcEPlu+@6@OaY=30FomD}bLY(wR4F&^KNSFXX@N642|55CA@b(O_za4|rm|rhUkQ7reLF-PWK`P5Kog?LM z1o!eW+M(D2Fm)!F`mY1tV7)+#!$F_p{|ip1I|zGGv44fo@+_!)$!+;EEtYP}mz|a` z684H>&Cv4fFk!FCz@P;DC9z*Sg|gIHI|=)rVs}83e}M-(mj+}V>Yc8eXzil)(}KQC zRmR#?E~iRtqyv78(cTNeM;*gx|s^=lMF<6H?4=*WE!CwqWkZuMcI}N z#sW%p7~Lw{dJmm?7o2*Buy^ud)~>y4Zot9!6l;NZYVC26s)+S@c?+>V4p|@dwLa`` zeK68`pRo7c=As*6A1HPwG_SY&+aO^bJc9Zn!hb$AjeLZCWNre4ee87g17V+d-Huti z_1@zya2|$KTCqh214{LJNTqE@l~1*fyK(Qa8}}Z&BgCWVXO5zuc@*6ZFQJ#!)?0*q z4xqS+_(HL}LEm#g-#vx;?sfJ367}8dI=UD2U2Gg%oYxmhmljE1oiB1)5ez^Uv~gra z?0<>YS9Tp%lqDuZ(N_|$!?cttlgX$|KsLI^9~7tJt^(Jeq|QPq%tA2I;)H$a?Kx)c z)wAwiBT=jIq}3x4L~^g8{erNs6k9S3Xuno$DbSt^wBHoc?sI9sL)v|ApZkz@ndxI$ zo_3P;Ev?g8*5D+fI=SAbldF?ce@64IlYdX{$9(dgx0jf;Pm?V-WGhUsD?DT{VD}la zuL=8Ju{Iz|1JMtKMEhN$A2X0s_d5x6KO|5+hkxu;1;cT_vHCq>KPk2ntd6cD+*e># z(7aXA1$4qYO#lVZsoi+op4X{Ckb<0w9dqJnunIKbUAz`zf@%#`8x7VNi)#usfPXo~ zsqywF!u^V^1#iy-Z;LP_DZfl&NEBOlAeAQbaq(j&b0WgKDz+Y8JRe@{=3q7m=44Rq zEz@0yu7npWwgFm>ftCTc3lZL=xo}k z7a4Il;lEOBBQ(FTiSWZ=&bX_W=zmUIAe%0#S|qxcx7;bpic5uE+*6bmA0tA=$BQ80 zK`?M4;UTzG6J83J69`AEm4uhU<+p@)$0Lg9l~1=>IF0m51bQa>i5@BE?2(1s&^twV zkA9*&eW0pOn(*=p(KCZtg!jx>w`bs{7g+8?cyGlX1h+2&xBC<_WQ4~tBlP$78xsR`M%ZkG+-&^V?0*pw;yu8y{)X^` zVow0$CBRr&$T-kt{5>)bbo&~Jj9ZNDEr#)ThdMSsd8lKfX8fJQ_!Kh!-rH|X4AhKI z8^&jhAJ2FgVSo=bj0wX3pxCp(cquTRP{>&2GX4=6tK7b-knuTV`?);hVMW_)k}~5O zGSVNtFU3Tac5th4aDS^Yven}tOvftY;2#M8lVZ<3=Z3gp4)beq&;YW_;N& zzGD1%#lr{*cZgveO!!|EdleYR0prj@#=p9Z!{FEkcetN`OV$}i!#Y(#p8Tt^`WM27 zEA~IIIv%Y4t$REZ;Dt~N4 z|5HwfX*r7Ue=7D4w7eX)1*cX~aW<}YXNAO>eZ?95 z#p#2^X@s991IJNV6Hb?bWAS>13>=TwGi4x#*Ry1R)Ixf1OCfh9&5?Po%yGa!(TH*zri#7ioY3&H5JGG#-q{@22J92rUhQ-E$Os zf0&=}a~1ml>^FcK>4GDq80&5?FU9TUSa)+c7GwXR*$jSY4jmt1DH(#b`(x)6mUK$6 z=;PkvvVZbTL|hUQ7xfhv_7@in7Uz!?qX{3~AkNczEA|N*NT4ZQvIpyEAt>%MVPQSb z8BCwzU^>4-j7g?~afw9m65(SM`^>S>`MnDi+kNmS7b^C7!IPAJQY$7Beo=$CSVvc} zFN|NhWKZjCkuFMsta7!zalu4J#-Uhg%E?sc};ZYdc` zN;|*ThQ7wSdu?$p;g<#DwaA}@#vc!d-s=_n3Sv1~5K+4LQF~%eFxUljZgqrz60ErG)e%aT1SF?SW1Lo z6km?1MX~R(B4XUfVIzpk4n_OFJ&*l$^ePGy_X%z}!m3o9C-B3oE- z8&phFoD7F)JYDfF;KLNCYHSwMDg4 zigyLst_IoWHH*0*+gy-suC0GY(gBpZm=m{PT@N=NP=dE_$%$K0yB^e3V}C4ebwV;< z@or!{3$|}-7W2XOe6T$qLIS56INP}6!#U>U#O>HaZ>WywPJ1V+g(Cd+oKUpW1Hx5K zEWqY{Ol==%zL4+*IkAv-dO-NXoM?vT?XLUn=zVie+yPD7UFX};={p?XbBZ(YeHtu) z|Bj2CExSaz#>Jh4wID^)L(M57-{6NiesJUAXl?Q5Cp=PlhT6>^oBh=i36R}G~2Bu@pPN=z8@x#C! zoTaVJ;yxH-%cwK^N@{CmLjMjDiAuXt(wdxDs%OtYu>^cA@yAb^QhzO$621h#1#Pd4 z;P;TBb=oqd(51n!Z%9X_{zT%Zvw~7_3kK}-Slu4QF^tvWu;RbMYUzh6I>x6{McJ%i z5I3#-@NU7+(JUJ9DIV+=W5<+)SYeKbXBYXv_6$nKgCz`Zekb~QDVx==b_486~ zR`DKSaV_C1bD~|(Lg%R+b#p*D_DFa;bRsM1n4ko~Y2~cABY$5{uYA2c@~tY6ueU2- z3&k6ceA7Mht#;*W%geV$@jf8mY>;nlv)BOgt*3g*Tk-ILRb@z^yY1xaBf#U1oLCpt zvt8r#fSCIuDBzK(E8**K6h!HmFk;c@tgwS~lo#h{56=1moUn_tiQyX#KVBib`B@ue(-Ti~CNiR=;y6 zmzvMte$V62^W1x0-h1A2&%JZEIJ5rsSWN&+hR3LUHGdj5jFjl6&U)P`wN??M@(Iwe z_54|9(!*Jz{Sm!Lw=3LRt7<^1wdxpE1G!zI1EA?BLGN0m+l^Wq+FKjq)@DdZ(7PAu z_CRZy+*l^JdDN5WKzr*R7MO4{UC)MVAe%{gHLHPKn!Bo>u%rf3n50%nH0`QYs#(?l zke&(+L4WUc(`s3bGfP4k20O;yB+)-wrH7hTFDJeADv?ZPo&DK*8W+9ps+OSlu^NBb z^9flC7=qp}(Lqk0s%d(HJ`hFf%DsP31?mo}nnR(XFi?VY2MPKh>KM8$*Xa%+=@EiH zR1-_7#=#CtbT9;)4#AGlx(ag8IE6w1V9?n2?|da^z3v!k z)}5$vJ_b3R@Ma0!aSz>bfrNANWh>ozlAuq! z>G4br573>J=nz-6UI=KVJ0o}EsfT!H7V9pMdR3Htu2|J<63rI`JDU*@tUE`lqM+8O zzJL2a?dP)mL4Lj_o!K~@3(!j@bbV2xLm{m>;Q4ba-KCmQU5e0MuECdUwEx1|D-x{- z&0Nr2b<^tYnX~@zWm`I{b=TEOHTtw#)jTqWbRw5)0G2w(Xh;&V_2gP5f%lh6^*H$A z*CaX&!p&C`^!0Gv4d`|O>h^}4TFsKq>3^)%8GOTC)iWJdS}RyWp7w9#39YT+Ukx&9 zB`mhqO108ULC`lPIsyX!0Pq$#YEaZ)B)Sc7OMtr#Ts_3yk?6L-Ed}mZ)(J7cNpvJI z%YeCC^R;PF7MSI7fi34cu; zV8Gz|P@+46Y8AjoH7XWVe?;gW+f|QiRI8z>lU?;hqC11i3h*hcxmVufl65c5x)&w7 z=d*Rs2>KZ$RUakw9Dp|hF97%;@DhM80*@Gwjc&C;;(TP_O1_i5PpaTQJssfR9VpT!N!e?)etTU^kk#&*iSO~RIO&DrL zk-Q7q_Izzsb6%yQc}m6bN>vc=N*G*OKjf}tJ0U}{1Tret1DV=vOJv+6+J69{HbL+1 z^7l2=k!TK>%@Dk*|o7m_XpQq&~=lQd?2aTtkRI$e7>1R zOR3cg|0sXHnRlXZ7b73w+bfCp_l-C55cXpa);G(@H}$m$ydPokn*fc<#C?Te6zps? zOHF0UN9kkdN)YESbF{LdoiigYK;~Gb*3Km(uGwl0xdM`^nVH=m#(ywC9z)*TC!!CMqlZZJ zV2HjKqK5|adNNkogjKCY8hwps-v`YQS<_OYhk|B5Xub^Q!|eO43DSgpLi3fZ376<$ zpg91mwH2#6h@4wXbbkU+NSsuqZ9BhiUK9RjK?#MB}#QlgW9I}BVDaPq!rC(+5k z9RaRAa7_`{L84QDtAMOJ2JL2?2l z-GliacsS^MHuU&}C`%Uc5}ggAlOT#)$@lwvC-nWW&imSTLLamf;?Yj%Nf>&5{_~D7$z$pFk1hQr zItNC13Pw5L6MsiduIw~WqDO#JB{&TV<_F6Q8+SeU6RXrLi-t&a9*9nZXlRYonGa4D z+372Z&IhNnu=s{S$Yv;Hf+UeXhW;jsBL;D5eo$P20lP<(lz42%yi5C*~i zs4--y^?Etm%l1c&AycB8fc^^Tvp{e1W>xk1Y?T+EO&GGV$n>!&N1}Z}1n(?EE{M#~ zsRutorG}z`;>nMA#*ZKjBe355u|7|ty+D5r^p-Y!KIyK9=al^9??jT$F)9xYTrDHv z3BD6O^nU};^T$QcTfjX(oG(Bj3(#|&bb|ZoI!wD4Opq^O7%92YlfpTodzW2Of>ZDP|O!LnjL! z|9^RVyHhI0$R!!7a~LN=br$1cP@Ts9L%~*JJP7P5j0b={iE$j*6Bu^~dmLi}*kc%@ zc|3|Sn%@eHJAyre`)v=^VT{oj4q@CDs&b4YpgMpt`j-1~9Q3{SVvN?p9*k+QyD&y; zWv6}4oX|Y&z_=mU?byc`>^6+O!EVJqUVmV>V5|XKhJ8H1ZpOGS*iAS$cc?aEz82UG zc1*knyWSp$_zUbhjNgD=gMI!0YsL5}*i{%m0=oj^`(T&b`y#5KT88mmsFq^9crwD9>9V@zHT(b|HFz<}9T)O6)(0_hM zG0?ufVfS$wrNEOz=9D7tD&}+TWBQxFg88hPc$mM0`Qfv+4#V|x7T7u{ldVB({S{ay`E=_lvzFBo@~X+T|RFJxBdPO_;r)l(NF zEWe#r_Gp$S{Q1O_$ETEP8ja_wm-U`5-L355Rx*0wR;JGQiWI_2JELtyP=5#a&BvZp zdh{_4_qp~HJz;j8?WUu2aP!ix!`RtTjr~d^ANlM&OTW5b^8MW%A@hWIz4L^gqlPW& zmF#(SQQGE%g}>RdC+|IcCbMayZfy=dxcg1nx5=JwGPFYioK2x;H_yr%*JtYY+TT*! z)bmgd@7g#iYU7`IU7o13OMmi$zt5N!(r)bJ7lUsHmh0DSUJ+fEHe~#s?3W8?j2}67 zT6o;pw0n=oULSMg@se6TQPR`O%WqPvT@%!AcR%8(5iT{mYOz258Aw5d)(=b~CPB^y_kq{ZCn@3CX+*Zto1 zKTKt<9~`&lV5v*ooPVOZPLtz$_ZxVrAf)%j*AIvGtMY0R>yZ=Jy+!QC^Q+?L7l~=v zPcj15v_2brdBfdf!%C)RZD|nONx$~Yvgrq&cn)6IZQaXN9n6~_ltn(>^4t2~Uv<4c z;Q8G63%~p#4tu&S@oxI9>KB8%d0%+$zq@MX#T^Tre&6_FY=3ckh5Oi@?7@hKubTWg zgk4^hFgAbUsJi9-Hr@*=3+y^)$h7c{BddhIHn^W<5>Bbx19+AWVd>9aWU)-3-O z7u9rvW;KAexwsq0|p;&TMW zn1eC8uV!J4?vxoAqdQ_c#^_#{hA}$xr(%rG@F^IhvwAYd=!_kY@oKQ+FrEi?EXLn~ zEyj2fKBEvux+42|MdX7m#OL4$uxKve3@1{+j>KnWB7fKdJjQ`wk%j`?53B{_c(8dG z_X2Ci7@eUbFpdSAi!r(vb1+7CVK&C-{>#D`-Q}4WqkAp`V|0fJ7^8b=IL;H@L+Lnw z71%V4UBMbLMrU*?#_0G=v17$sD9IRCLrKE;8I(kf(Y=y@@m0K@(3yQ1%2)P$!>HjXg$PXjNWTJ#^`?RfiZgDb;lU33j@aJJI7*--hbmo^xqKEtpNdT+MGJc<{BF?t^cV}Be8r5VQP4HSSedJFqwtb@`NW2E!L z7`=@eVH^m>8)GfTCkzgE8XKdq{)%Nnk5+Q+ z;qVY83-V0pAHt+oWLS@-Zg-1zd)YmC%-x31vonx`GF3>*Dl9UZWiQ0kHRX&>Q>Wd zp~d)lbM2mMHd-t~T46$Nl4W=jk%11*Xn)QG=OIDMYm!86t_4PO9!N43t`Q-vL)wOf z$!tz8ij#-jTv8RjrksSl+@z7Fgp{1@Tp`P7PRKRqq!{z^kTa2*s?ekXnH>CilLN{wf;|L!T6QnK<>|9_7A z#eemdr>d4clT1ac8sDVc+`JDRhZev>%rP5NL(O3c!;Pa7a+6`Y;9;PV~+2xb3pIroH%dsETUygy>EdJ?5WsaQB-}+J+H)T z#Om~V55um{aL(K|p~D>8=!N>UUw@LiZmL|jP?5-OIzQF;qHgxX>^bEVm;G9?VpnRc z^MXAqIHy-jCo~%{9mf zMbs*49kq$tO6{cfQRUQ8>LhiJx=P)ls;GO^ed+=AhK)_4 z)MAtj#nfgrj4$KPsG0gqIMa%WVA?QknRZNjrUTQF>CALtVi*I%G2IvoQ^1U5ikLA> zF;l{fXC^Q+n1##|TX$wVvwxR4##~@_GIy8<%p>L@bC-F^{KdRy-Z6i0&YT-phjZs# zISto{^XCG%CY+X|xgait3**{w9XTVH#T9cRH=J9jj7tGFfHdTu-S6Ssrg%$0F_ zxeD$mXX6fVXSmDURqhUVg*(sP<6d&r+$-)8_n3PtxCnKGx`I-m1b>a-CHM-B1%H7S zqJ%C2FANkq3apSOj1Y2!3?Wm<6^euj!bIU)p;#CzOcCY?i-g5OsUQi_~-eNDYo7i7W5R=42 zagaDzOcQg&uf@@#MSsi}$B8B4L~)8ZQ=BK3ijufmTrX}AH;Q}3J>o9$Cvm&DP5e>Z zBCZh+h=;{;u|hm9o)AxoC&i=UY4MzRUc4aQw8e=}t*%xdYk;+*wX-$a+S@w7nqW<| zrdyM&nO3tk-&$ZDZJlVHYMp1DVO?NdYF%z!ZQW#DYu#$yZGYWo-ES?o+N>w7m#n9) z*Q|G}Z*7##(^k*sXA80g+gjMZw6(UivW44v+lp=DZ4+%%Y}0KsY_n|tvdy)X+7{Xl z*^b*PZRcz^Z4YdZZO?2kZLe)_s)#D5DrFT_rLOX*@~QHzYE-4I@~>)M6;#!t>fZnW z0RR6b#z6{#Kz|f~;ZD>etcB3BWzZs!KxCTHM}kqqGr~e>VT-_%c(nf(F8WrVuwqTh zmK}Qz9LYG5Q*b7r)HFQkc+vA=;LDG3dQaJ6Bc{PyB0k3Ue`&3hr{1c&X(g7pT00km zi@t39MVD&V8Bj|D1QY-O00;oAp(0q@v(KLW6aWAWC6^GZ1SWrUSqXGg*O}JylkIEA zmpu6i8^R`#!34$O8Lu^kWE^k=2Zdre3^FoKCc&@|Y7*NvwV29BgOofAHtUX~wON~S%88w1kW_wAuX{3YE zP$pE8N+(nCn3*w4+Ko)GClv2CikdCFYZxE2CB>;wUpyI#mT-rmOn2HSGlH4Elo1T~ zWel^(NXJ8o_7c88%smDP$1)ZphvIQ1mgI4QnPe~=ON4*YeZ)YxbS#l^hC_*{98RX? zu$eB68ri7j!)&lN7zt$}9f~E2CE9~+p;#OVl(eHhBb`pB3$4GdJexVeXfl?`m)K<# zRdpw#M%(ykTPm0~LeXGb%!o&W5M}F3BrX5rqV1ytiqeMJ-DM!(xa6zI>TO+6G^xn0 z(iusn`o@1<2FdlW-0X~{L>q*1q9uwOiS|qfLdIiVv5b+mbXm&wbh0}oBx5Xzf6j>I z{JCOTYNc=CO9UGXXNAF@SdNWHOS_D8yVWLB$+1~CJw`g#*2g7kBAE$>f}M%v?nJQ5 zFwIcAQPz`(b#{ePrnA%NGYhSKRos?DkVr>Cva^4nre;i6%rvd}@qU=F9Ns@l7`fP8 zvo|8`>a^9?Xq~B0rlZhOjNwxs*3eYi$Ygke98!`=^8WQ0@!~Z8rcFaeO3t0hSfVJD zN*Rf0S#~3aGRdwozVCVZ7_UjQi$n;uUCCE=v#oVSR+~)ci^p)Wk?8KSm$c2mPR+W! z0(*bS?6%iqY>fqlsw}JGg0i+)BAPX#)*_2@Ta4M)6;8&{DZ4k^I14asgeB(y8|va| z;hguxB1T?4SqTrr*XdNm*4i2HrImq|s#xWUzSa=XP_O7pM!VxiFqUXb zT01yzU0E~Y=+CaLY3okZXf_tR{@z>G0^GwD#mNOqe+Tvq{nNjMzp z4fyU<9QJ=tgMI^ACn-!RD4*t$JlcO)RWI-sX$_LbJP>J;UZ!cf*Qu*+U9Mu9Q=_Kt zR9#Mej?DC8kHfdw9oA)4uaXNr-I~-7Ad4ec#dJWiDj z9Vt|8C)*3Z>UTKH6;-;h;1$b8b+QY^ipDMitDqLA7jAS_*1amb$emmgPA-k&E|qmYdJV1<-oPd zyW}Fz?)>^4W;k~dR0CiG8Xf}uyon0 zyX8WSR=~wrMk-)57Dg3iO`6N;a_ef9F3;_#rDW6m> zDyQX2&Fz%=$J{FZ;VG@KKPnM#98pqD5*f$Hc!U^_5aWGxoE)}WS>&M;BytraS3!9d zQkkHaPVj>Qy5!Z>W?fQ?TInQ-Ok`vNAQM|9GU@5kNv*Q{q#SAOrXD$k_Ly}h^;GJo z=#-LjI$0uV3(|JKwHArkmZh`cw8JP?lUESra^U~VNlM=ngMSXu$>8x6M^+q~VA~SRF zl@hs{ktu+;s{1`#C$mpBhYfj!URlBDE-UH9qGsG8NsO zhVJ_FyE{MI-Fbh!yYsVY&gb2omP>1z=sxI%`Bl*phX*iWW#@F(W=yGy@ea$*<`8Y(+~=sTV7RI)smY z`;0EVOoR=0ea`D*iRg@61FzNax-8#oeb#G$_o+TxK|SwNO)lda(WhRzTt=I68yta` z+da6J_aJ{T(nB?^w|X!;*Mr$PkF!Vg0H>xtm#r?521YbwI}_Qi;3H{v>H$6yMrxfN zx_-H=$htcq=Hlcxm6%8)BXeMQH4InUW%klla*0QA*x9bKLo`ZcH6wl?X92k;i(D(0 z=vm}i8@XB{>lm2}qz^abdixtqmk@aPmSkj}vz32t#MP)c)#b9o%k$e!H>jGo-{I{A zBlkNVS7lAnG=4XGW&S-yHZU@OrBfnJj4XivYmmvt{Oh$PJDQvLXj-z3ZQ-L?m}}%h zF&aj$<6OXn8+4I}7Y=yn&~I+fm7OSdeeH*BPvB(kZOHd_s1q>elA zb60;Br!KSF%M0Z*+9;7`d)ABjtZ(qq&4Fe`(|JWOlE`L8me?LHx7)(V($O_HGID*M zrrFXo(TGI0_R?+EI2h4$d0Ct~aJiRlc3#VPUfUU2ZsXz3&mlSuO5~=I9jvA;*6ka} zZOuTgqXBzsX1RbXz+X)f5vwJ)Pw~xqv!Z`B@%jS*FItW0I~b|QENifbgVTlh4R%F7T?>2m;OdQ;6DASi`4ajPzYi+pN^rTCLsHY;$(3wYd#! zaekMdx4T(vgSIm=-*rt(XjmfcjI2XZvvJTnG}`Xf3;Za3r$l0mtmg`}eP@}b=ibM< z-+p*);16Tk=?v>`>-AN|Ute8t)5(8G(`uZ-I3pY3K!d331dYoebr8hAU->t!SLfUy zO{n|@e7w*rYn;KZPYlu|cEB+?E96hhB%WvZNHMYrUTbl4?9yn;e#%o*B56jNxdP$L zZ@m(4u#(Ln*+$RsTd@aDGCchPuU>1Ka7s}-h6xmzm@z@10vR&XBHJL9w7ZlxkqLwbgqZ6-qw<_}P<1^+! zT}DR^NMsKqH=&R^6mrWvx>tYZ?%{mz0=h5D?-Mqqi-pa8Mp|IA1U3iqx`@y>I~6*h z5Jj<`!b{}_xj;UR(Uo5BY*1zEf$X-Q$vV(G@JvQdH~~SpTc${4U@kp~u*-SagJt-U zBRkubr<}mH7AqI?IuR=NT(atDf-YR_;Xf+E#R~t85GD#KcSz(ABN2bNT?zJ9y|IaVWbU`HIUpPKzB0I4$)eO?y^C5GtvRcI^=wh zohKtP6o3B$dawO0bFZQ~tV-8IwG)*BdX$k)SZx4%pQ71K;swOlD=CQ_W26h(jnLlj zqYq$mjVT`M0ljn_|9pRtkp%Rcpnph+9%dv7Q8T*uh@yL}k>9|7yQD0SjFDYXZ-)B# z2zA<0f0q#x>Mbbigk9F7jAS6W5g`Uei0?7d4bfJJP72W}MtUIH2Dgt1(cdt#8=~zH zoz6zOiAUy@`TcA7k=~b@1;E!=t)Ly0cZz6XGVVk?X^HpF|rS!AObzD z9OZ#9Aq&$w4Bc-*pJC(xpdmn?{j1P^3;KOV1^^8s^m9J?yxiEYhk04gXY+^*TO713 z&N6Zc7ExHd;G-|{VfAy17e}$U)v|brk;AYsVDWMm)HVz{Vu4;^5(CrrJ z2aJ3NpbpIKHNAf{#>1Xt zS^SKVhhUL`#d|*be$L|kQ7j&|EG{te2rRl`@qv$i$Tvdc5I6a7l({m?(T-1n?Y{Qr1`(A6_l1c9ILug z;H5IGD?muJu z9>ISO>J|7;LAwS2F{nrIKL>RS{sT}(@b7_4!T%JL7W{e8F2TPIN(%lBP(s8V0(A-g zbx?m?@IMCa6#NfC9fE%i)GqwI3Ni%$GAJte7eHabKL-klK0ONx3ZDO)><)oB18Ncc z6QJz^a~iZw@Q;DE3d~8+je;KpZ4tSh0Bsid zKx>81yFhCMe+Ou_;BN=568sU+O2OX>ss(?^4tplGA_qQ)*$(@h>=e8#`sxG^rhNRL@BV+s zv)`Y3YrJIY;+La~7Qc4x^CLySJ^#BW|9$4EGcSDhvkTkre0^j1t;F(G|68>Fs&sL~ z$DjZ5H_l_vTw4B^`_4bcUOYYXp7-a50$Dx1Vq&>xd_~P#O5)W7WpyZSBTmN)w&Euy#9@_t3 zZ#+MF|2Gx?_vV@plKvUrJUUb}Bh@we#q#xRX|(69=B@{t&wsh@g}+?=RO4;iUi$M> z<3D}o(iH=}2Nxb#``bVM+vFe3>#1FHzPRES=k8gXjPQQ&*-f~tm-+o$Cis5=&=P^+ z=eRV|DBR+Y!p=(hEKaK z%h5s`3usX!Ahmrp?k=tYuQb?F{E#dtHAWH{W_M<{v%8%cW@fh+@e-&7+ak1SXbcTr znj*1A76WKw6);#z)C7zdV!eOG#2>~qHipn2#ORr2kbfjN$#c#-@4VlcneQCNScEZl zmtcnAaREI2n4sFz^@1u-uN2(r=|=>Wp1xnO*wbOb?VesDxXsfG1oH)jf?EX@0-ra} z_4GVJnU^UQ+~D~o0>7uZr`?QyrQ=v){%(6(j75!w3iD-c-MuN$%h-Rtd!`~0>*Y&d zf4*t!t93u0PP&1aiYBL9+f*x`a-CA$XqGKqif8ngqZzvEm1LzRJ0We*61u9$zU!-W zT1{1owmPbPU6r#;OLZJA-YS{0(IL!9v5y}i%M}#S0vlWSTQyK zOiQsvK}(IwRyZg{Gg?{+ic%#gvX||kZN;u6E3ci@^jJEh{I~9cYo_y!nsNC{QtZAf zS=lu0t1k{_Rb4SGRS8;QsZni_%&27N_b4Emn`Cv-geF?Bcl&>OG5*mq@cb7a&;B%2 zQnb1M^`3&IOGBa1>Ye`gd(RJb{L&Ed9a_guj<-$!ILqEPcgw-VoXPM{yNgoC+E?_Q z**Xx4|0>t#&b~XqHdF4KZuLTG(_~ZMsgA+l#)n3g)kXV08KT0AZ?#uG-SuaGEkF2fgUWAP(SdoYv>7jlAfWB^ek;6nW7Y@1f?iV z207GBFVa5_>ZESkO+C~{d+9aWNBijw8l(gC4h_>G%Fz)zN+UG-|Bj7l{=ZO50|XQR z000O8tf3-UuH^ard>{Y-j60X^zyv6Nlvw+7Q`fcD_L1yu*P9&a2+S))AP18e2N@Yc zVoZ*ac?6OmP=JsSWLY}470c2=M>2K-LEwZuXbVkC+f7qKQfLV6ZAsenNn1#p{M_lx z{k4d#&|->!B@n?mN)y^Yy>6eaGOlgO!7STYSDY zzt88p7AnWnp?0n&pyN$XgYwb=fh=5D4vSzFmKOIE&8D$h{U(YHc%daezBF*9jUFNBt|QOTE$ z+;ZrQ=8Zy5noD~&nvHTHQygo59hs>ZX)FJ#K}Yfxd!b|sg=I$iKKGV-C$3`&yJP-= z8v`zw3m&u^>B@|0r1Ql}d}B+olpf9G3(OT-m2$o~)>19z1=4VKJX6kOD{z&rl+q*l zVg_`;cLrJ&|462oQ$|WTkt!LseFS~gmL$B;RrTrQOZ z_TOOLosH>SDPPg*Bpw^hoz-H_7+sMYHGw9RbCE+NRRVUhkqE($u{<|+KgH&9byK^10YcoPW%n}Ro`iGr3P+Uk()mJJX7dlcGRxhk%vR-W z4vqWV)9U$h&mLTuF84H4$CfL4z$2?+53byOn~mJQ(&{eLBc3olm6yvxu5HpNkJ+SJ zA-R;CH+6Rh=cJx>aFZXM5hATvs-!dNiDKzQF+FKm7LFH9HJI>!Pi9QZKVi&REu$s) z&5k#fCYpMB7ER_Y%kB(8lf|a$U9?Zt^(vdH@6pf*LU;Sr6*WVYDUY$q2}jZHEWeql zj0fz&w@5VI+zB(0i;6%XR9Y(~!Tpp`P!OsAl~TUgoH0$Km}?g;CHg;8DNVMEF)AWN zC0Wedh|;kh5IZM-KlXl!cUmgfix&y8QLIinTQ+K7KzJsnQ>;mxa=udj)pB43XW<}goUq-seIO`!{bSW8?i5& zS?AP=Ky2*@>`>jE1cCkH(c_pBg1vg%ZOD|nUtBL2diI5XRrX)2{$9p)C-j1I_XWEU zuUbe=nzMp33@=@xSg;Oyd#M|esxnC}&=s4Ilt^t=kYd(NkXCy{=Zi*;;ahlakOoFG z1q&Tos+JMuNX9bkc_Xgli!VJ-ZfV{DzQEyYPA1~bmWq{3zBp+VD-@yGD)yl{(Qw#N zpTRo0=4G;fP8ODBStnd&8?_@CnUU0`GnNB>O~Y|RB0ZId8dT=^yZ77@3IagGxkwX) zi^t|(I^xV}cx|3kH0n1dOSx)6jEvEeUBPwxj-G~=-_YALS}pd(dfG*2i!kz#P42)h`8&UOE^x-D))b$1RQ2iMsP0z+F}5y7zUEh(LZ3y* zzojD|2s}s3^0w*5L^?gU14FU{YzWV!deq_K2bCTv!I5hUc>!zT8a%{r2+gb}}( z6to+oLI7nzn!of8(is-a_re6=uE!+|e;L}_o{RRI>kq%E$V!9!rx<5HIszKIo$vkYvH1=Utw{KDz>@I zHW9kXAs4u|5V~1z@`OIlX$*I|5aAZRDXzDzOXwu3Ybl**&BN?TLZ9HY7aETYe-rv7 zGCr&*A1m46DjQHtux9mJY@kE`kZo-Zv3^4PaVbUUR(w21=)esnJHxgqzC~*Ca$uM0 zPh#vP>_*zyqSh`4{E8~3o}JQdxT6#L6sK|cTMvKw&9)xtZ;xUI;jf!LZILS5tC+{J zn}xW>Y2T?p?V_b=_G37f5PcrToZ8) zu%`%p22l5)A)n=R3mA@p;pgfZ-tRG-6b$e8LftPIenN)!gv;;=woe&GKWgpzuxQ^@ zSWn=qq)${=3VKhXkMn9o-J$fw1s|O#EDWQR7)B^8VA_YsR8ISM;~OQ}=&+0J*IH3| zTO~JJWdK_nxIqf{OOVeJe|mt^0f6fTxKus3Lms$;0^A|b*C7FJo3y=6f_vTt7xTcy zJAsVTr&`oodZ&#@X+(s!d)`5$3q%JSy>zidHljg^Xi$0`Y(RtxJtPqwAoMV&+kvPL zh+e2i^rDC8h()HcgR5D5)SR|GGEap1dr?|FOz2CTCSY|FtRAhmf138J9uqvJJp^gN z(~yjK$mMC8y{v#HS?_TCvctg+!NIXc3tcR2bFfo#uuEFp)xZIoC@rIXiO^wAcO%-( zh&CfqlJe^bkrGayMw$I$yP_(3IN|2SFNT+!kr7V!;Ns)Bm~~)Ivz*czQGHIdImg8a zp$4aWp}7T`qn_rNe}XSIS98qKGzcB%^ciSAfti`tn-Z9ralI*JkIk&0!DJJJzQXCV zP(fSiyUz?tSW39IhlJtGG1sUSPKn!VP`Ufs`T2%VAYHwb-|)BR9) z2d^mxugQK&f9Pvc=|e(K%FWLRJ>{%^Oz7*4z`9t?9=rzt4`paMD+lNSyEIXTmgeww z+Gf2b25(Ia-ddFW|M;)LfQWd2l?goq&|(Ci$7pr%f_1`8|S>B&xve2 zFN1vE&Bh7#hGIG+*c4ML8`e{udZN!BR0qYNQsR1_fBl8tnX+e9R?I5YC#LTkPM;nU zefnmjuP%1Z?$g7Ppu^Jl;Rb?`$8!?jSwi39^abD=0Is*|ah>;Yy(@5?_k5ifxQf%lSt_im%hE_U9AcT~bVDnmHh01q{L zUc!5ef6#Ao`ZC~c1H5n5!@JwA9LYhH5t%-ru~wR;Nh%^0TI=z z?4o80K^PTxQS+&h+72s~k~NK~^6f_JUF?F5BrTDorMGkg613t4iR7DvUPKV$asC}n z@l&Na(f0(ROPNYleSTjwq!%vk|J$C(vo)CQZra0ZNocK zfAP4RHsB@nhmF>|*kv2Zm`&gD6sP0T+qm=18pRvRxyx@TuzOhypvz(aU5*kw_App3 z;{Xto1MK^R{s_^De7?_V9#997&mY(4^8+uRS7z~8_`rD?KMTjTZFSS^-=8H~aq11fWH6^CTV&ZpS)Op|klGEyL zKcTlcor3*cc)WaC|0C_T_nP~oc+K7R-eR{!f=<>ZqyVdNsz3exGf9%)$*=K})HqCxz6U6DO!a!V@vRB%m(+3IXdH5-z zzj8AEn#lOCyV>W7lp50oB3vT$b52h>7Utjk4X39TKKU)Duh*TV5>DIwmC)Z!v)|k4 z;PkYN%U)?`=3Du_6IV^d^#@MRIQmh0f@#e<`wO9eY-F&D-L{_*XXR`5f2?HmtQ__B zDiKVGf1t~{zHZB>?JYw8q{fm0e*(%gd*J=goX#Sb_}xhVf~wL*E)#l{-BpsvWvZLq zNuh=BI{kWAWb%zu0sU$;sJR6dPw84*0=rYM+r{qMP4%X@fttiL|kINfB8y`n(q>~ByKV8 zMiZAPMvYO7iA!7({jYk8FdFmE$>*Hvx^wHkSFc{bsxDl!3KMiX19PVf)i9ZSs z4?7d5fcRQLiw0^LbVNag;Cm2IN6YymEHqsOG+%vjNSSM-;3pAO!5?FN^;M*Yk*dBP zLJWzbuTBbq`HS52yUSd)tN=r-m!Md(st?h_n(REBMpk>jVouh)(F9i{b<;vD$F{ z41-Qr@ZWe7;x8lLRPbl~UmRi3nF{_a&^6%rEEv}4JiR^rpL1TrdYK^NvlaZeM8G~z ze_O$S=l@swe>+L*0Ou6+9xOCk&!F=r{+v#7@oQk>FEZ$Yf)si(Ws^8T_6aO4!kLD!ZBhG zNtsU_ z;RF^kfBBR_KPspk7C90Yd83^F85Vh)B_+d~^!0luVz16oyvsm`w`jYZ-KlnAK3TnRRBh zBpv>2iXcJ8SpNdOBQ2HBr)7n6T5da~rK&D1e}ydh;hdKE=hE_XU0Nz>TJ9)lC8T8( z#PExO|CJp*Q=`!U7(L_Jvu7-s45s?k$lqn_EUT&I-8y17CGII`6}TD=uI`8O57==2 zHwHcE%s*in^qYr?20PK8f25$*Zh{H>cjh0eQ>c63;d1_W*5n@Gh*xqSQMcH~upIINcA-pU`lCcZBmh%#sFE|Dm8Y5Y|{wK6Tnckf;x|f>9$Nfuu_B zK8}rGUD#D{Zn%%vM(9|Uu-@v^-3;orNXUW=K2y+#;BJDz7&M)X48$J?DKan~P|2(| zMgO4|wEuDwW;(sMb#8hjsJS`@gCD^!e^1g0$O}3oAf1LWxG3n~;5Qi{D@9%+KA~>b z1C;`ls}y;MPz@Bc0jP;Uxk)aBK?5`K7y~TfQy9&Je^IY6 z*60JLkkQ*}1YfXDB#>#n0eBMNBe@5VzKMeV12)9DFQu9)Xe&^Y^o*gIAnDbrwX(3& zwyCo;SI~ACw!v_V=Z1Hv!>=glGZ@Z>rM7e|wUvT)0+Rz%yh^8lqh}XTxj?n1R2v2D z25K_6^`jJ==X-#f0#rcVOnGFcf4%BVZ58x63{M3o?Su)AWK45Zhd<=x3sq!L(3c>Z z4kGa-qJ65Uy@K|GC?6&Y6bi{idRC`u-mI4%P^GUa=paaEfYkW6(nG4WgMz*S=}ee9 zXpJ$16?7lySbdJMqfXBWg2pe*&)CsdY8De`3~n|e$!KB>ra3T(Or|{Oe>9Sne$ubsct!M)lYm_ojBxgM>;9!YY1r;gwz=-q${bAP^+;EW9R~f;YGZM z0?-lA^(9`8s9w4$=qPxZ4PLs}iRRRcj;W$B1sw;`+aT)k6441&6t19?Aesxs(o=GO zhfLc`L8pK!07{bF?S$&Bf1uMq%>$|rMCVPoz6$yVxcR{K1I~wV5ehm3+ydbG1J{Ib zkqSBs+(HOrK%h}(Batr*H6=rGU0CndhxM%*)<6Y)2S$s)Xi%UrN;Pss7xfaGbE?f? z1)T?*La>PrG!9X1)bbzl5|az6NsNLnf=LmWydG#AT5mG+B_`jief!!h+`IL1+iqe&T#Vhp3^z@S=&W1!|>3NpcH zl_Z7eM2`9UQoN#0{>I;qj8)JN5Y}P{YurmV=2cZRUP0GD^gf8pFDatys%V0Oegsho zh~nx*rS+m4swiGTe?Ng}3DiacMBj`=pQxalz$w5b0oR;x$%+Q3GN4i zOmwg?11290bwH1N7(*(2N?@cMM$!O+x(Q=QhkF5#6|kc-V3%e3PS|U-)EF%%j26aV zaeToInS#lr#<<+^bu?tbodm2Z!D>=hqm_kgkjZh=>&>#-e~23LU&<}G1pPwpZ~o*< z<_b3$P+L10ZN#IE++$c5_~5R9WiN*SeHlYGT#&$P73gxh8gpOtdV+YZRd0b#UUNX5 z+sQbYc%A&5*9wQ%DR3bKuPZ^92P>TBD^(e%78|EBhN*OU(_UQObhw~`$tp0(?`nMW z#Xzb^Ah#iqe;dw$p3my)E> z$QfC{GyX8v6q**AD>N5q0yIx(48>j)w}AGF6W_{7|Ed$#JK+GTZx1cd$+m-&CWzvW z&_bLvQIvlj+E6Dy!<=k~Q%v}HC!FAf6PgzDpn!w+d)5tYDi8eQ|t$QF4eS#K8IpbPuVoKmhiAqJ*mM-6gPuD zi|T!$&!m{tYX-&S9i~ytL!U~0HiQSJn1jbeirwImLNVD7$rQs*(vZB9{vySsUd}@s zsB!F{LlhIu0cfo$4x*UojkIT}jZ0nYpJO5Ke>;kft#tHEo@0KEOM_Db$bPG!JYC

y|A`{Bri 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 z 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 delta 911237 zcmV)AK*Yby$uRQVIgleEF*q(T3MC~WAS-iXbafyrK`{zYO9KQH00008070gjRIx-J z0S-Z?npAj+bDj(y002xdlW+kge{@)DbQ{NY#&Qd zS1Vkb5&zp{xP%8V^LO_L}|^JvmIO4~f! zq)vMJ-+%g}|J33&AQnnKZ*2Cpe!)vtHigmB#m8z8vyfxgB(Nd*YpN<5B z@lx64^iwHLx@}IWPR}sdc&_f%r(B)Z+Usu7QKT)^qwVeq&#g4TkB=4`YTpW48mUyh zk@4zO#c6fxWjg1{C){A2xY%H^0yQq>fe_gSxjy^(0aO?Wx z)l#LSd8m=nWUZ_n*7mg5>eX6c?g_Vfly;cH9WPZ}=f--m1Y1`tBV$#Z>4Y~jS!y&m zQI1xtrN&58N%IT?kr;**xA0U0Ms5Yt zyy8Tx;f@i+M!>IB8LQUo7#mA(O((UgjoZR_9Xp~Wv~Hc)wiLa#N>$AT1lw7$TweJJ zKNcr~rBoBP^<#d5Xcwj6Pyb+_gsM4a;U;@0CwU^e}zJKgA5 z45tx{ZU~k~2sJHUc@ievC&9e_LnOLsM=OF&G;Tb{oLxmPTe+mx}~#@cHy zD^#{Gp}93cf6Wa|@DSh$ABaYeH1wuxZfnD<0~Q>^5gO|hD*BW+-qY38AKcs%z&C-1 z7FR6K6YM()%TCcN0ytw+^+xHK%fPJb>ghl^AkI+>`ed=z&}rBTpB|m6=@pnb3ezv| zAi5hStIiY!TxGnvlDJ+mzOg$v+SYB|< zS&2XjAT(AkPBq-lZt@?o0m(Kzw{@FKQ_Gyq?j`tO3?7(U-~B0|ZB1VxXk+*F0&r`p z$zWCYl{p2wX?pv$r?*{u`Z`Qf*Pc#y8{I*jSkj@bnS5F&-N{k69tsU0Lxw^({}<|u z5eoj^e-DMUEey zaEeQo_Aj&<2AI4P>nfaQIU{GaEXXwTFSQxg{QofP&%|BESTyEeCV8YbmOpGWlKzLf zAudCCJ@IcWmn5~)ESmN|YO|zq*?5O_Sxg^fEleq=Gbq>h@7jk(Efh?Uw%SLT81#)R ze|@F)u^~f)@fJ3i2D6C4{EyqLyv7Zm!^#m@hews2g{kpQpGRpjsc~~&bh1LH2+bu2 z&6ZS#Spyj+hu%p+Ov`{hgc$?Iw0+*GOM!>kW)$$tp^L_2K97OXDnsKpc8gW4BLsOj zwiz?{<;EN+R^1`Gn9o-oFfM4k>rHVJf9n&gHGWgCSi>~F+OP~96AG^dQJ2zq&2A>v zb;-y&u{N?^tkw8h*}|aox};bi2)L=uxP)JBT!w)4Si0HgtD(jQ9P%?hU-N9ZeQ(H` zF+=8~bD_ZT31P9Elscqu(70ttV^D7~2bsyDy3g0P86RqVgU{D-D5UX?K3@-hf40=P z-6M9fem1~*#m<2{2gJ^3 zi{4bBv!gZ>)#X@BHV8B_m6i6eG@VS!GUXUfeRG@jkz~+t6N_xlWRqr8kH%%>HY|On z&H5PDxD#9x;?@PRTjRINY)TH>f6^hugyE5z#y1%jx!%lVC4r4wZ5B6V)|AYahrnh- zTarh~((zy;OTiE_J$z0ku}E$uNOWe}s34Es?Dv zq4C7L=pz#rYJmw^T&B{}&PsE5!pNkP>WGmfRJsLc?38I`8>WM`v=QnOfW4KJMUKGR zeq)R1CLCZv(Kf?#iH5)cDuv3d-6ytce9OGpb3GNTPpGg-sSq<4wTMY`S&Pj|Ld+y- zoA(36o7;@bpqWT6@z@3)f43Hld zS9UFEJV|yljX69ybubBD&$%6tyYw}X_wQuEJVOW|$TQFv#M=w(5~XHs%#i|8F{@%i>s z;R25hi~&A!NS6bjf56Ws(W8yY0cf?o&EPjCL=StLvUn`Z26bkC+M7KT=CSNm^Sy?Z z9~`jlRtz=1D=jtxc9b!98-);+l86ygkDZW|f}~X%zZ1!29};k%%;i<|sM$TiXj3q{ zD;RAFMhNLNa$*mT+K&p6>JhnAM@=Fq)LA5R`cvt6#9Zni1S?TOKRBQ^aUIH+RCbCfz8h`{v2e{ezU)%ak5!KOTwQ+F@r)N2U3Gl&!=C$=cz?kj+7DIR@_EY@sbh$fd7 zG6h4xd3%8bcr6(*(nv~s0w>%9^~i1eU?zI*hn4C1IiKGJ=iCVv_pY!kRj;L<6o-1n zL0IyjvSf@b*((mj=EcES;d};I^m#uR-$b2I%rG;tf4r@l8IzhBf;c;8AXQ~h@{OeG zN_5s~K)C}6u!!sdcOBgId`3qW$ra2XCk?3e`sm?-NChKa8d)-6tN^_#-m1J+1La2{{)0 ze|s<3zo()kJ&~d1dfIklqgM>Joh24kEJ~5*4 z!^AH;3>%CXc}sz7D?nn}l#U%`ar8t5rTxs+$`+5)AtU$9gwj#fkMo0&@l&7YPXRD7 z2509_hb_L=jGCLosHs6g*m?A#7y}rOf77oy6EbJ zEK`jv9y95U6kmg;Hsd6(xj;wOA!$hY<&v@^#OEEBj+;u4J*=|^(~c`iF> z1$I|ybMdGil$uy{;-U-hUvy#6TiqYH@W`SIOBz42i;B-5hQQAOf2Y+Ib9rfd(wv1XnSwzdxq^X1`9EkAz=Bfmqm+8* zMV-N(NKx5dQP1wee?F5nSVDY(d6{g8q1R8NWw%w;drUDLP7Z@<988hglDHGpW+U5u zepvb2Vd@Par45G_NXge(>x!XGSqY^v9nD6a=qwgg+GDdSnFBi`l+0_Cf144j4)dsW z3>r#0H@cph>fwX!YDa-~sBE5$#2t3khA*hhpN$S{^NN1L|1VU#xD z(Tu=q*o+!D)97T_ELgb385e?x30U20E^!Lo#wp3>}1RXLO4y^C4vCDVdv7)Jotn zpN~V}K?pqFAtiy(js;%@2nM+e(0+odRYFW7r_03j(pqawV?K9+0rW&$S<4*iq~Y*%Jt{ z4mI5~veesZxF2G3e;wX?H#3`@rLK7vnm4e)Lb>V@&$5gc^7@$$%grharZMMpPlXEQ zEKhX>Y*5T{uC zX&GgG;&2<4^m1dWq>7#WZL#rUQM>u=bNO>S+9#E#gd8!;hoE zGv}-tl!J{n(wXVzh9L9#6J*AqNkxaF2oM@41N%?~JlS+o7Wo&RY_bUYbccKeT~7u~ z93qJ~nNVice^i^82aHY;1tN*QTOqvY4#PSvB>6K`%e)xtoWo$2{IvM3%45Og*#wXn z$tk*)VCq?TdtXfB&jBWTJ&m6OZmHqNNIuU~)ZjrW;3jnxo($Y3ESgO#t$i?5{%=czSS z$Jc_i_;iqY-t_r1u<^slJa1hT z^C-XXG25CFUuQ`iDs=`gGYRo^Gt~;*Yy9i)e=~a;WTC)Y^JES@FZlcnxI6+bZ}*A| z$P5=$w~s5It+upcH9Mv>ku#^DklXFi!vNwD=B+iGuHUIzkAsFGJve_lWPRQsMI#fx%l%lNwNy z(Kz@j`&|(3ZPEB8pT7iF1<3i{Meze%e~#ZXnKdiEZ_ZpkL%&FZt&pto@4s)dcd-UR zKfwRr_2oArth>|OY`GIl7Hu?_pQ_#kj*LVRFS%Lc^#T;m@Eck5sH z{B^K!!R_)6@k^Te)qi5)AARnFW&*FjLJIml z?iw4#@7nJazr~&7H@GzYTI0W#f4Pr|`EO*78}#y9yhQ2ccX;Wfm*4yRtCL|PWQ5Fz z!gTctH2f!@e+}G^;E?}J7kZ{=%p~HpWXs6o(DP0Da00P^Lgp)p42_XrS1QJAfg*;H$1A^L~NFX8z0YMb?DD2`Yz84h` zP#yt6c@q$#5Tfp_nTp%TU!I+FzSFnA{_3k+w{BG;^Z3gmyPCcY*%X<_U#TXfIUjBz}vOeJMyn zHV;@GaKXF9)+8P_<=9e89@%PAEd<=xMk!bRhQvd$YO{FI;u62%@i(eHJm{dBo+GPq z$ZEI7>A2aXh+Cu_X&s7HEM+M47zz%}Q4;RS<3r#@(>tuD`$N7VfA(8QiL&46_+?0X zriaHzeBwzo3MGwpiAR&tbG+iw(M{ra4oJ)4&+v&yGbB$=b4JL2AA&57hQCJp{dB3L z?W!YAmAxuTRb`lGok_vgBgEr8KBih+ULGHB z5?j5>J~|-6VU4q;Jlj}W7X#9FQUNsqp!=|0|ir*p0ql8u?!|3mIya5Ukgm0%B#nZBmUmd65 z0X#lERQyqn<`f-oj8d>GS^NPF6VD(;@yWvIyX+j19^No z%z{aqisbMI1-p5CMaM1hWe%+1>U!}CQXG}yPY%xli9^wfM|GG~*$xuemp$;9YnE^V zf@QYUnPth&f5{Wg7HvLJsOv2qf7gSc?y?86M(%-@>b&03@N08KOPntR0+ir#C37xp+(1M-3OWZ0rLUxjMi~HFpGf^!9cA3M`fKE@Eo8sc6oz75dZ zf56|#;=TaIFxTBI?hlA(bJ2K!A%}s6*v)e3(j$f>_M{pw^BfEqV#vW}m@C<^t`P=( zieawNhB%eQRFlTW(fD2i?>FS|@SpguIKz-nrosOaLpMZ~><1veQgVxy7A>3P8LUa3&>FdLKK8EGR@n^pO3?7RZFEGC@elg=k z=GVp7Fuog`vx~0{&(HVh%7gKBVI6Pl%AfHI!@hhJTSJ%6{ID*R3jJoH{Mf<@e^?6- zXD1?}s`1GOKOgsjN1C*0$XEW1V;?T>yYUiUJ77bnZ+h&? z`(M4`S$*~L-YWh?Pj1QUpZW&Q-K}^F>pe%>_79GB*HxG8QYr`iwzB`o=$bGNE_VElj!f4;h%1OGAX%ZxMM)}AQJT;Bhsw`;yEoaGk9eHA&6 zUGPSBZ#ka%*_`*YcE8xW?f05KKkJ>X@g4VV8<{%ulcK>hN){3(WQ}?{}u<)ZPc+z(?dFA7t>4!_R#A=&H;jq zKLwb{dg=(El=abmz%-`$e*!R-joky7!r~7A0T%BD6fpfRKpx}Y1>~}L2Ox*VuLB-u z`YnLTjBf!76%<#nf*&^Lzl{V?6!tV4jXq6&19$&PCLPf1egnz@b^?C83uR zi7SbFC%5JLFYRB)7O%T6t8;i!(P*s%>dC7iJ)-)kS%F}AVQI;9E^27P-~?|%Qiv@r zs{pc`%FG4aZ6&4I0EOS1DzXOtBd2ZGsU!P5Ldc{vsH1qGZ`k~<>+lKenHc13kr zApD_lI!6+NfxO&ce^O$0US(l%L1IB*N^WIwMPhMbUSeKe*Prmj@?d_~2=`wbQCN~+ zTv-t48rAPFjVhw@$nw(4V18ga=ljdK8D0|t!Q!ISys3pyn$?T^v;nFbm+pP9`@cQW zi*w6wU!HTC@72p+Q>D0;P3~RxtTR?BeXwQ8u50r~p1Xc~f9;5FJEA(iN}Jo-YuSnz z_o^4Sw4A+r%)h1_uI-h|=X_jdv5wi^8{wdENH{8-6fO&G z!WH4V&@S8*ZVA5=Gl?Wo1QQz>N|MP)@^>uSfkkpbI zQcoJlGh{hgK~|FIh)$Zw8uBvPL|VvZ@*3GjUMJhh4)QkHNp_Kcl0D=j@(KBrd`A95 zj+0h$ikv3r$a!*sTqJGe3b{&tBG*Ve=^(dAr-n4FiJDcrO}kTz*PL2{mZ&9ZUTvs0 zTpOW{VA4{xG)>dSX^&{*wFz35Hd!msrfSo*GOa?Z(yFy5wE0?twnSU1Ez?$MFKUh2 z8g0F{LEEV9)QZUu@bm65a792-Qr#mJYhmcrL z{Fy&yh8|iB5j-eXo&S$-G6z;&!5h#lM1Q4GX53(gETyxm*N_0~w-^0#G2>1P!Gu3M zeLd}I)?uz`JkkTkOD`#->ml0tf!DTMZ&HBQ!n6-dOQ#I4!8sibzD_LXaYzyUZ1 z@Cj%dY*YJLZuiHMcZ%Lq)}rrkyYk~yrr_s^Z+-z#O9KQH00008070gjR7Z1-90@_1 z0RRx10RS13j~*C*Vr5}%Oppao989pS*##DN4esvl?(UETXOYES0>RzgJ-B-a!QC}D z!7aGEyyV{hRo$ujx=)?1Iny;Vsv7K2fJ}G^X>nz7S8YBhC@AN@pum}#JG(+GAc$rr z|C9y9)*Qyn0c;NM=nQc;0z;f(&7GYcoMAnzAZAwZ|Hk%zCU)j6(p3==wo4DN4-hRNfGCey%x`RA~~p`-sRWEo3-uK>YtW=zx4N zXsrJ=$<&}VU7fZ5EhTT~_^&Jd1@sr_fBXFf@Sg_#r{Vqr|9>>XUx0r>{`a^4`Tt)7 zr9J;Alk2}Mr2poK|7ocIvJn5%p#QS|9iS4#RSOD#kj4!KA{?!hvr@gxHpzOgb zXNv$?%0m5fb{4Rt4EnDy#nQJ&x}hP?V0!4tLS}2&(Vj-R0ec+k<+ zGP|LF=(LTYb~SLhu&J=ILo-~S6u)~Jj!|DF30619s*_<70S)SQ=}3{exTXn$k)?uz zbb!hGSq7`^xDAHDo0Hf2D*T-V) zDbItj@9kXFM#OgA=$HlKi{+|S3gAywCh-!tmvO%&Y67%vZEQ0Mq{JD1c86?#lREf+ zt^m%|$ln5cL64)S(GLaVhb-c2xk{f!pR!A2vbV;DEXVxl6%!|WdVP?qBPTyMHV&A+ z_w%PhL$PA>WaER5(|W15H?g>&QCKfp;kM|YXd;Fw6eg1KJ4AD-o!`uJgSP_#xB`Ht zH9GoCV_`Zte&np@I+#LSj>a!UDD&Tc;Otma@V+lbVOblog?!TH7$!@1ZzeR(LWC_! zlM6Y|I=s6lkW!&S|B~ztlo~H9%@?2Ohj>;bQX*Myy-o%a$vpicf%NlO88fQ&Szjl zW25t{g%KLXfnVd-R|pqCZC7*yRZb875Q@$<{MntV42n)JJpAlb;lf6*u^*r{y6?o5 zbUeeY01PEF;w>h}SBj;1RmDXO`9P=@E^lFg`P;1ClSsx#pN%vHcs5FN%%qc!%H}d4 z^7-TCsJm3>IHmHC?fPDuPi$y^V!Mnu2f2e#$8RdCFXq~iW;vdDLc)q9JY3$bE}W6- z3~n284gK7zD#*`ZS95NQH#=d~JlS#j55DIgl6M#seC;fx+2twaPu*GM*IN=O_BbunLu)ULiAFd_$Oe2HE zrN|eGnI{f-ap>=IRt_7NdQit}a2MGb6{B0QWNTF;syI#@Df9di3PvanuD&(x5AO&A z2T6XK#knEWrTJ3#6qVtTPx(jNQ2DR)XT$6DhT!w;o)lgyyz;GURMNfi=Q9zzI!V`q zP{<*9Q4jslx&xc(ir=$;ZKg`bPenyTT%gwU9HW;2al(-H-Wi-WSKx)D-|6&2`bMe za*1L-JTh!7AbPnCBG>wN1h>edDZvmi8D`Qt;L;_6U)wz{FmAtxfJYHCT z8(!^PWxXu*V(9IoD%ZoSB;2d9{tJ5|b&E`^R!J@$X99-FAD6t{Ews5or`$h`Eg7_f zyIaPo>8oT7E=UxAEV*PHb$i~{u<|oB{9Qq`DRuk=)H*&5xjnRH6pm+EhmkXkxuU4? z>Ov+5)O_lV*k7msmTyfs7(G^78JUjq8KY4Rx<^Du#e{VU9nw+YAGmf5hr%9XLB}Gp z4Fx@z+fqa5rM~t2V-&pzwE!a;8SA?bT5Z~E#W=>8!5ZIx%0IU86vGR4lDNrYlRE`j z9ks{s;qRgj>k;~_+4^92feR;0Wa2PxYBV=Q_=A;633pD2@AY@9dyyozBJ%8pj3f?Z zDP3hxSVNtu()bTt?VSF20jlkUG*5x6zt>XLNFuT{@Xx<4crQX9gg(k4UO%D?I*ST=sZS5DC~JS-j(535K`1!m^fP_QfhCzyBmzFY%iN+M7N z90m;R+t)}qy8-twOWBheiay0|(6PrvaFX{XUp&zT=Q`YSxX;Tj6(ESo*SUc1-A^WR zCQrP)0GwP66K$bKot20^isQSfaC*G!eQkFn3$J1VL}nL>1hIFHUPBL86`TBCen`43Y6)Bv6-c;;AbQ_ zJhpK3@OR<_{0f$qidYtv$*$hZfLStoGD0M{_^6zuoSlGKThWMjEXfB!t}fz?Hv1s; zkK%xf@nDXgQTQAX=poz2+yab$&c~eFgTokqKW{*ha&>)vb43;vDppTW+iqM{0 z!e)VY{GrDSP6t|E<8sv{K{^jQM~jPcakaxW5ib@lz4}BD{EOIN`L#OHlkv!<@9HCe zY1UY(1rQ;^P>G6a0%boiA?^oD0fWxu_#g;IrSAh(xWxtLp3MDQz=jbh>6=XAJM}>Ff6zvC&9)R=Z>Xv{|hS6r5IH zh2slKDnpDd&HS?bf+|7wM=L|%mr3`dh2Jz-{vckfw%E+{y|G3bY5nKU{vkaojX1rh zR`oo5Gz0xo^G`iM#vo2v8Im8t?=ADJQA<9^dgfV~rEmDePb>MRVC9&9XFN|yYdylR z>(#<=c(?smFBZfdQzXkva?{D(Rs@r6?(xsDeV=hN%Y5X&WSr4XufTS})U17(NPN=a z?hM1+9ku-;-!WR*pk8>?%eWr;TnYGzRILFAmK^A@FVx` z*CvIBO@++4H6@fj8;;C>#jN*C(LbPfhxk%g8OhmKJ06m+yD!WNNg)tMk-0?B2KGcG zX)&!Y=7)5DJ|Oe;n7IsblY#HBeE2iUQV9=c0tx?-YFvj$GLdqn>b8q*m-Xa_Kj%)F zP|3**a!AoVO%Bq!PIo4W?&dJkv7xpBVNR1-(;KiBlua0~4o}U0i=tCI z`r&Zs*s5%=E0snelNa;6XYqlM5V0* zZC*7myJ)&7c!@d^${=PhV6--rs}|nU3<^sON0e*C*kEoqy@T*C6R zz#WNzpWa@4RnDgZF<4Ba?{x3^1b=seU!~m=gXec)o0K zLpe=zI;~+#aIupdBDkTeS@wR@_&a~y<@r%{S}Pe=_-z2)B)$$II6J$0&>c!$(o)ht zGAxO6X%Hd5m#QD85TqgODn3Z_)wnEbf>e!RciYzfz-sG%1VHh;NA4dmIQTqsiI*Cv z>1n_7_z2rIB#tffH6I<-A3Y-@`#akcOJ^xQ$hTPK z5LkTh^g8tWd`7&mV(Kpp?`xjwrsN5#7O~@(?npI%AbF4`h+2!mUx36+i??2POAB$! zTnCwBf9>Q$r-`fs`E_)(4(PrzQs3xLm=Wl5q2^Z&MXOm`t7Ca1AxgVvCb12 zZ3uJ?D-7FBt(PNqF+IfUei=4cMSTBY{r(yDQL8OmmB4w|X1g4CSZ|xWZhf3k%U2rS zNjA%Wsf5~IB^g4;{jE47Oo%!dLI&~ZaQk)Y#m@JK_J*W|&VzWoUy7#N-Ocwe(BPdwkV>1Q>bZcoif6!3GpmxwcQ)+XRFUBvI(Uu zy(MKvMZaOM$9Kdrw=XMmpW+cn9(@?oC!_NtHz|mTxYm_v{$TijPd>V68*g|5wb0U(`;rj$Pd0egQY*Pq+GPKx zW*l~lMzGxAe8<4oUNW!y|*GflyJQdwQ8T$ergHCQuN<)&oE7UKRxJhUssL4ul7f!7>kPJQbIARTdk7~^Y zxIEp5x?ydvbXCXmeydItpP)&$tR~UOD~#!`rQvs-{2;Fk=JUw6%kYm)<0DV&P2DXL ziY~tG`1SP5I}S8Y?xgxydZGF%cDz%C&skTYQJ5XdHFV9z1=ZE9Pwx5B8dKbvHW23R zA1e8ApK5_sn*gj)a5;@y?nsG$Vw>~I;f{E1L+*BXHwr2XH-Zz>r^w6@EuESulkHDr zznXTOXw_FtB9znrkmI6kviN2(d4}4_EnF5>)wC1w0LbHi!TOB`@ERsaFdom%>CL<9 zhOd8D)>lN^Oa3W>=%h3k4Je`Ad~%1b`@VpXVs;L5aWr_2|_D}$=wH5t~JH4QA< z4}@?wV#O|?tR=nv-r9{t)tKQz267&^Tx3L8-{!ma(&f6F_0Hminqm<#f+m{l+10|z zf5(j4cd?Rv`}0`%)UsTEsb1{U^?a^(fjRjrV>ac0@8o5|HY=F-nlRdu!_Ty5YLDLq zo(vxmzIsrvPI`LBjW6G7RXQdYR|6t=4Ihxt9NTitM!xVhF-(tn-0B*G0umK5ue;a= zp+lF-aa0>M_~SmPZHw8w?wo=;ZK9PIHS8QJlzjZis|(8j6+6QCt~=B+c;&g zlB(pL-#*p6hMqez<#|T9kN=~1slVsV43#w*`&Ug+L>zt;K;We&t31bJ z1rlAj=D_mobJWX7ngAT>`!l}@dT%1Ry z_dcxE#&Le5rV^LPXH8(Iw28jlc90E%4~mNvVK`Z!KT4*lNN)}f=zoY@>T(Y9#0tA% z3`3{FZv3vfRdG>wVvk?({R6KUEU|knYpy4xV_M|nr-;E;7z+G}?IqO%GNSpANhliH>+YA$*6`btb_V>~;a9?QD3!+ziL zQ8?1wWNg4CSPpfH$gZ$!|9E6vPd>TU$< zYZl{&>zSy^B;mEJhc`2Q%;(Mmo=mWVrA579W<8G(5GjGT%OcUA;CmSUNn00Hmn zU=}qMsh~uuxE-_j!tEf$4ljE6?cN5%RAEQahumkt{o41J$**9$44zZQ5XT&c%?Z&$ zqcd6`#PUEo|G2K5B=~VFTT3&4Xm>6UwC#y4GYOxy4Jf{jJ@I8kS#g*F9%-b4p|ZS8 zWZW6bFT({OqU7P@Ck%roMm8kc|9-RghB|c#m^~`U=})FH%;S6^yYE{eaN#%)74jzXWEAZcOOn7bC`h7MO_)#vVrG z$bXj|Y0rHsP3tcUNE3^hQ>QhN^wftJv_@8*1hGW3`zh{Ovc7Hw!VeJCxRCGk%m_wV zxisk#W%W%Yga>gj=BM|2^nrPgB5W1a`@NA*S<|C+6(Wt4e@}PzzFo2K`vzdBQh+$w z!oq~YWGF4)TR0PGD!aXZvsBV~yQlaz(xV-hDhHd&yhlUl>?hzISeMER>g?|d6+Z&7=1fLjO}dY?Q23$j%6hGSl1UdF$YBR z?s1r;Kjb^IN&H=+Ni;ti4f+A%4OL2VBfm749hGycLd~@@OWLx?rRn-iVSywK zHsW@{5h2>99xQthFily@z|wyZ3$}%sU7mZlkOT+ECqFWtuqoC zS0jlUuHjgJleJ~N0MD1DZ^)E>4zgJeH;$UBtcONjH^bq8Hus#Mh)tQ}{S;x*1P z+EVJe8z3H|V;tEy9dL2?+Q(vByaLl(xrL|ePOH;%<%p$QRy{H)%xWhZUN#BkKx1}y z!E9ggb9QRmHkueC?np8<)t{@o#xe&5^o9?pwr(naqKNggYgBGTxfb09;wx@O4sI%G zbnMM;(_%XH0j!(R!J3fVs$0-L<)G5%9sEV4&W-c-vzZ(5fgU9F;uh_VIyCDBi=Hb4 zsEDSiYESbt-SJdl9cse5sgweB11sgvLtVyky1D`r;HH68zq<=N-k7sy8=+9yVkX)~ zbx!?%oQTKU!Z9I~jh26trQr@az|y-&J$P)z&Ru74;fx$}~QIvVg0z`iJ*(@_t8|IMs+CMVI1Zzu)$^ zx~1Af$u`-%=A49r=E#r?=I(`*nGtj;WU40>>61LvSIol5dZ!oo;*TGFx7mhNxBX58 z-l>=sB`Zm6)<)FaT}+-+Sq=3jC5AQNjFg1arz1Hcf@iw$by9*&<6_`HaCgzxFB_eBeaI&ENaKXKQveRMSezoq>wDh@zs~m&S->nVQ#Z<%jmhJsE z=<jc%CN>n3g>Y{+fU!|H}B^0Gay%ka35&sOiW`XcV0Ewfa}AY zU2qc&Wa|%e`SXqt4OburP6S7?;r{udvXS8%;c|0W*+y*$*B6CHd8S`~{@k0YW(yPW zaD{M$hXS}AieU5^I_-qw^;MNXWDJGK(qf>Tqv5&WB0dnYrRA`LvP~;L(_K%pGR`{N zX5R*{ZmHlY*%q*k_Evzsw)SAY(s}nKK?@aLAwom3(s!y0BDbGoIZq}EMh$$%jq{PQ zL7sIM%lLk5Z+zLgGOI~{f6>!n?{+yW*vi*OD{}aqf=J-*MFK6a%lNe5$T~+w-#Dt} zI6y zQIj+8fX(|n_oUQ+Xi_R95Gx1BTDSaA-PfAz%Gs>jC!{ z>Sb|dcr2gF&DCy`Tc+4nPX-L%q6MGH1Q~or^{i*n^oUhs@E56dCVO5&l|eF>9nm}@ z%mkBX8YmaW-e6tCaUUrSsIByLt#?~$g zdRtgCadKhwnjOyT6XwrYyR^I5BD`L&#Pe)p=hz!&Rz~m7_qtrLnoc{L@o>7OZLj2^ zyNZ~GiyUQMp$NMhPpqNac}{2ZIVZhnl;jqg1zeBw)PCEsf9nZvao}u^NUC-RDb#K^ zKx2=prdLpD4g0b`LpT={1G&>Ze{j2w_o%VGce}?w>a0d*EY_Pn>*l(Tj8@W^(tihk zp!@GjNSMDEAq)}kgAvpqY?`j z3kML$CJkZ$f6{$lWxcA9RBIh+NW6xCiWkMV zSQ759uHhj-tJ90DHPy>zyX0VyS~?cLt`)Tq#WuZ}f9t)e zQnrJHmXC_~>w4=ykJTZYnGZWKgAI-p#)WK4oyfK)ZKZza*exJ{ruUA$LVBHE@U1iE z(W`PiidA_OqAk6AN_{I)&ugwVg`qStd`wv!>L+U4F4EA$*fE)L_aFHtZEXj~DTl_^xul4)WVcG+F< zq|^q1OY!k1Sa}vOmjyXO-uGMXKGD|-cU#|)OatM4a=i*j7cl=C>%b2gpb zjy+m`tZ9|l#M)TxmD?C6nk%4q{U8E-zk}m|=@eViS>y?U^v7{NZLtQ6LfR{e(a?FU zGziI}0`ywmxR(xS&b<-uWG_!%M>RdQfB0m_tH`YRsi$h3_>m>b+8fc5`4Jqw1b4#@ zG}}eGH_Rx=4h6M%0%=>9!k~A5 zpPqiK>N={z16dF!bO4hZjhZuqI5<0$`t?vmud_5?XiQhd-hLpofN|Q&&`I%0f3P9i zvq#0QvmEeVUyimL{P<&X{~ETEamufEcZcIYXBi(+hQovRS~%ZeuU`O~iT zhyt1Dhw6_-(|HQ<4HI)5rtA}OhKHQmbn=4J~AGKSXo4fF{;~S{47~$ zg`c9@9wNP7tPdl*k71)ad-!$df5-@L$j|B%-=|YDV19+n0QRSFNXUUECCyG#o@!H> zo*597(XMk4%95No<8KkdZx8!qX6XCu5r(#dQ(j2Ocvk9~eS<$ww)|H$(qAJ4mO;PT zbUszMmB~q_FL{uUkmkN^4LlG3zt?Q~*LzNUfie#T-1ZMPFx0>RZ?6e$hC2B``sTkr0=3!JaYZ|B4{;x5$iB- z4I}Bf`znf3Qqg0~5A_P0)R885B2c|^7!DEBcrlepYeXKXlmtPL^zoug0O<+o7^xR{-Seb|j%}27Oyd&4@0@U*LBv650E&DEy}cG_-w0`R z7|)(_!<`nMJ30oXf9Snl!Lt$AgQK_*&msR5f3}jkMHfjpb);CZ62`=G<9vX(MdGu`%CFl+tf+_w!F(R>RI6A0mY8 zd(@=g|4@EMH3~x8izMq5>O4Pl3EtmD2mY}?QBmz5P8b0je?iLH+QQb(!U=@&ixtAa zW5bJ>-=ogV?x$JNUzVV_8M=Z2l6KZ67Pe+i3;-!xVr6dV4nU+qLd)6JdEs1k0|2jWb zqoR+~u@wqvMuk+3qW`(rIL>Y}2Km;Md{E*h9CRLd`@93Aoq(P+`izz|LF+K;G3Ss{ za=xw1gO!@xTd2YP5Bv`+MYVi}AV8zbuk`A@-cN&q`d`yH3p>ZZWTouiH~6spB>tZq zvlbBhe>W@retV%I;h}{gV0^Buw@DxyLB`t#EZplS~v1$LA%% zaSfv48udj%(t*=U-m}Fr`FCxw#({cn5P1p&%KvRc%oTzwi?NxI-gn8D#z?8H&W?~i z|5@kI5)eR9C;LOZ}c1;ClW=L?;Nf7K+B7yv;~>P|mu^IB!)YI#Js%?1)%(bG@ji0x{$Xga=V-x;Qpe)#Of5Bk zS}qX!*Lp+wv))ktq&Jj*t~U^n`Fk8m^C!LiD<;MUV)@y`9>o7kL;bsmw^>h#M&>DJ zf80dCXd6k}$VyY`7!v$QWmzspVknP4w-pRhW+*pK3jJUlea%Kx!Oya0dOHhAe+9B7Dk;|0_RaN-6~~rf=&UF6GuRyl8_eVF zn)WzR*~z55+et@%5%{!SO#agdA}wz+bIlbP2!_OCky@W*Lg$4vrv-v`p?)@xg%hK0 zk>*j=#OWaD>8>{9B%GfNkpqUepLmaorf2Z8ESyS7O{DK&J`PSvCZ;0$#!0^192f|`cuI3l1 z9DB|ns>wat*1fchq|;#S_(r#8C5%)0DD51v-j(^r)FH>(?=)`VVL0HAuEHru3VE|j z=Y|~($ao52+L$#WB+G7M5>^QNEnmgJPG2Yvwr`a8cPGr@rElR!S8`nae@kl*Q$7%y zp&Bi@)-Lij>FnGHPlC`w{*^`K$xilbRa2k14`dA%im`(6NfXC+RB3ok7#|m0q^o0F zO7bc6^V2TbXa=hCg?LlIPHzw|?{I;GdtC~(m)K$3+*Vs@IR#kS=}N%WOu;@4Wu6jPwAf;+C2F@VCZraF(=+ljN3l=K#k20!Ld35DPe zQ69eyPU3==y`*H!@gO4!6Vj5;f*owtY0>f@kKz=_qK-3?R*wrjEW;cWWT^2aZx2os z(G{e;hsD<0o(uL0e=QrTQ^Q$oItuwuH9&@CG4nWj-0}cBgbw=k*16m?U3ePk6v9~--d$RhX4kS?@?s1g!uBp)=F>=n+bSRpz3U@H%+ zS=||fN0-NkA=n;mWE@zW7U0bYwHpZZ8loP!AvJ}lVw!r)e;mb%XPx{BqX>R>IDTwt z!h(2XaNt8j+U}@>!>Y9aUY^GT;$`j;FL94}=I@9{#DrjLkniJQZdV?XRX{-sq}K|%FlC6xrr4=%F+Ie^?OoUH8Mx%`{> zpSZ-|@wBtoe}ypCTh!S(^8F9C@+!)kyrP6Wk6K$oPM;%{?ucw z8*WZvV7ua81A1}jJQJhE#k-qb)6EPnF&1LFjujd|{Ef zk@kglO82r4k1B)WEuf1JU4Tol>NY@>t3;b9+{##!s}6Un0n@^hRP}Qt5qX(ok^qI) z>jCnhE_j7u-!+XvEc@01@Uex9aoek_M@b_aMU`h0@uJY z(x6tMf7K5vdAj4g1u*+w;i!GYOu3ms%FcjVH+r_uF2m5|xWmt86ug$}8oe1$ zHEyB`S_s+(9Chj367=L5lkpPO~ZhG4a2zY!)` z&FW!Kb?d6WVR6cGKmBS$I+U4TkV(Egp_CEeVPZgw&7XUt;AmO8Hoe(Ism35G6CNs> ze>NC2MaTPJ@bsV869XPIn)c0QeZbuxl|^*xG00H0h5jp^8mtmEZ66r~%s{jH55a|h zC70UW*^Bk5->240WlAdvd2eqCHe4 z0Ka)!wet@`*KkknnmJBIaG*`yKVnud0-lLOxI;Y7CxxIfbhj&lgp5vSOT9bcin(f< zsC9%JaM<$i_4tHb;BkbFoOZ5<=1Cb}C%F{-Y*jJXeOqt~R21WCZ&;be$$(iOf6!P` zLCL1$h+=^{qLq2(P??52fy8wQM;@p`g>tRbv(>)F{R)|~ymI2zM&g{aiIsyq)YFm| z$ADZBZdV;^#oN?098xe&IBr{RfS9(Fy5(eAwK2=4;Rh9Rv_d4Qy&)g6qBSGGGYm21 zWH^G={1Il~lIdN31jhpkXsD+Je!z)$pv=Y z3h8^}O!i_tj)Taiz!Sc(=#e^n{kd9vAq+igCxaInQsnRtxtZuaY6O7+kefe3W=K`Z z!^byCTBxTmNWaU?9DahGq}s1BC_D;+u#1VIJ=n<^ga^d=9&w_|Sr|LoIf0D)c&8#gD26iEC%{=@|{syw$av$gwaxA{GJe~pV-KrBE` zAjhw9@vq|l#*qGbW|g?i7d~I*Y1c>HE*eu^tB&g#B|_;<6r}ap*u17sKV%$NlC&tM zyiFx%ag`^gz~GrVOMS4=`w$%l&)!?F=ZpXD0-q2}T!e{|E_ambf6CFmftbS%AWY}) z7Wn*Sho3VqDF~oA)MJQxjg`4@HDr#D!J^-$7tCKaEmMStqfRq>?>QZG$XOTNcNxmB z!}_*ezcc0&e|^jzZ6~koRrf}|=Gw7DN9%HX8T8(Y<4i`E_^KR1)H|`MO#O zd(x72U$NRh&KQ7yf1lI*xbaBXxZgEpXX^Y*VcyR1mlOoV0c8804m19C{I{=aJ`zpG%~h_a!QJ&izbulCVEq*N=8j@-LDGdWqT z#PZWy_oAebxf^|e-*UDL`PFn-dCRCol7yOPwAec{nxI$2=D@>4sSoU)CfmtX35#+s zc=^?OXeu?1Gq9J$sWVA)^hE>n@&{+IYh?s_*1NZYe+~1Lfz8hW3U=sRDnqO&*!0n} zscm%c7^{iBWt0XbCM2mH^+NV%#I_{C>6OYcEjl>_U=6HH^X6$>tArj4^5hfN51%fT zqJ$I&*c4o2%C5P(=0AEiudgS36t@^cLsGQDZYe|O)NaJ8kMSo}uh1Y5UKp61m{VFW zh1EZMVYN}`FD?18^f7U4M=RAXnTDzNNKyFBv$h-KL+wqRB}dPk8H zhNL=;(@a-1cW_xcLZ!*#5gntG%-w?k*Hz|=#s`uA-OsB6mH(Z!1c*agzQHy2(L`^# z*E<(>J6h#xD1aDni1cufFpvS9xR4KLfcUGqe}l40e!XtY#IUH`?YP+_+$QXt0%0bo z5jOsIxOVbX~fm~6NdIeadIvV1&@`&1q3b&1sYy_%~7n5j5AV&1DN`T*61)hFe zOo6R+yx5>Cn;>5{=kl<^(dN0i%pjAv_E*{Eh$^frpON;Ym7?s_N?Ym^#493w=W`UP zf4~NQ;!CVB71s61&Z<<)@f#QGYPft&HgA#gu(~W>*uFRUs*>_i+69pQ3VmaC8TzXh z!Hz^&jfQqYL-9O5J-*bY>rj_^3J+)XodrW|Z2ZUfvbmhDa@m;|Hl!p|-wv)$#7MAh z#?G}i$*CRbgO4>@fuAVlmc7+mb89cye>}pvJ{&Dj z*cuaEU!FdX%2^-LwZK=x{UQI3DuCe=;WC zOs2*>x85jvS0M5^uRi*AWK*BlA?Jmy?I`m0UXScv*0RJqiQ?Jp zoSc7n#1AGI zN{QLB$lnMVJbX5oPHHD@s+0r0kRJQ30k1mJcGYWS$)kXIv~|>hc{9J1f9Aj##x&*P z+fh6Q>_>kZ;q^ZAldVTV2GU`bpFGfcN57tlo=I1;cn z%wNFeJQ`<)0zstg;?<#je^Zy7&I97bx-Y3lf+54V6-~QAjod9wMC9=D*H)@{fIIs7 zZCMn#0tKgg3xiVt2Eg6t>oBa}EesQ4p};(){;?ncoA$>i{>!xh_a~BI`^A3Qf$S`t zpobU7FKZL-qmFx#?Lqwi|9|lB;+X0K2I0?Z6TrLtIEgjJw=E_~HiPG=^;%uELq+C< z^OZq1Kzlu(W2VV_f56|8=`ToD>S4cJx-({#n}UFP({nKahmhc*8IHS3TYGcUqp(x5 zmc{Ea(qSdSva;Ze(R_YlKHWMVGE*WGPG?64Q(sfipY?o8vUGAnYk1a&Vk^n|i}2>M zk_@iUCn@QHRg9!r*RI3-dJdc<_IY=5cI88nwk7H%L-hc*f90W20~BQKO$Nw5)ylb& zE)35}SnVdkjNrnzF~Lyk%81X?CaD8dq`}ITr%$(wj?yY4+NRK^r}b?^GA#MTNG9|i zo$Ml&8Khi=)!ijCjCqmlLY2Uzk~S@e+EUe8o1Q<7Io9Z^Wj?hzx*E*BpiLK#sI@%X zGJ|htFhimvf77nv*XWsg{MZ<*9Q&v`CCbBRYyTz0f_BNC(@tIhltkjKPL!%)?mn{& zuquMW#nJ2W(|2cIou`RCb79p!x61Ldw5Ph6W|UdPki(N1@+7uzrYa1>KBdu`>OxU1 z(lTs&=P0|goEhTVmL|!e|0)-)nTt~G+QoDqY8`=%e^$D0ot~LBYcV%CA1mp`kA4wH zcs;I-|42{Y%$bQeRDzc6*&0hx8Wm?`OEN@Kgj6Ew4OMvA^5VRnv#n{>o#^{)g1&Z1 zX3hlni&xSDzS#PDS~t$YBF0Wq|4;RbOs*bvinrW5Sq|1as2*mU`AGzk-4n?&S2u)?-eldACt(?$Z<|e6AbqFau4vjERF?*+IlBeHP%B#n z%Z{!zgYRbh^fOa2Qe<2#Z!LF|02>>w&-=zHe@kLU05L5RCRe`jwJPrQNJd9aBBMg| z{+3KH@Dfy}t)4lOmnty|&p@Q5bgZ@}@M_$oo(2!8^Xh~`ZhFzkKwnA)de!)Pt$&Kj z_BBw6QcG84!$NON^0W?-7TrFzIq>8BqDg2gZSMMoEN)}3K5oiT2?M#jaN$}1vt_be zf8EGzpVy5UQ|fl6`tXT>Psq+!rt%S(xV!#RK6X{pioWD?&%jWq1t%{^=PTf3qzflO zrrj5#n+OGYP>G`E8a*?tV}#5&Jqg5~c?(}M(W?E{CNeNhibFAZTmv?Zy8Y(!0zbtc zdNIdTvB)OknoBBAryz7v?hvon=08S@f9s4P8v{HWRbE`AgL=Lht6 z*scwo+Z#m($hU9JLfd@Sy6)C^e@||~G_jaRg}B9{9#a=pq>cQSEMZbnr*Mc(4_J9^SZTQSnGVO^m{Yli$?!As}4b6v>*T zxk9!v7hAKU&kN8rJ+*)oe-8Gs=o)|<+ z<^X%L;mXo=^6959vy3YraSw7|GcyeE({EVt-Z@fLa|G-3`&vb)J(BJP81hvNDSkxY zX|IYl7E%EsXgy6PaceP-eLjexXwdOx?#-)^=%n7;BumJITrZs$ezjh-U4xwp1v?dk?;-jF_iwLee}dI%lbMqXJYw zUl#8i=!Eg*f9KiCtG4kk37*`jm9<5`o*b*(4rUl4L$u4aD-N1=>X6HJek4y}a5Rio z;+vNyOI1{3%FA`PkN`Y$3~D!5!$9wIV!*SAhrg{B`_!7FW?8jZFAYUS{bH`MF<2BY z%-WBhKdCrv#z#%IZ&eI~cyE_C6I$i-=jV`6c<$3Xf8FikuA$fh85_G3x~zK}$jIi!97vWR#Cp8>5MxWN96BLxu^L`ugM{8)d#{ z5&eBOTK%2tNS+Q20qRXyUTHE&jD=ROLlGx)o#L+@ty=>;z-few9e)|qy=xM8(U0|-|l~I*{7?i!Y zWUuu4F1aeXTzsoAN~K#GMS=I6A?7H!|HAk7^r-j`Yg4bZCCB{$f=5NW5bzd+ZiQQK ze^18#er@VsY(cA=XNC&9X^=H)5s}9Za;&ddAzq!WUsjLS6x3#11;~;I4#pSxe8v*& zpAAvCc>e+lNQ`5$;guMOnN~HSZ75TKE;R*loqfHLf9G312ORHr^KJKsR|6^na85P2 zkcvP`_cjCVb{_{g-P?`Mj~(I=6=M)Be?4O?7b1UJ?W6wxth?X#i2dmLliomoYcu~{ z*A0ODvqOh`aOjZt4jmE_Vr;*-`D+U7@I1|9>0>80r66^T(<}uBto3i$snp|RmmWmU z&qzGDuXQf$qdvqbpKnlIss=8QOuf^7A)w#i0RNf4b2PQ`|KskheiJgZEt2r(cV3O-&6dv8Wz{VK}@db1J!SDx38*{jyLf0V!+^6CO@ zn$ZES=*3d+|0&PwZ{yYTkNZ(v`4)x1jXY;>3+L_&O&3Q3za$qKb(c9hig!5`{0#9nSEjX$lo8t;S(ICQPTfSwwnV`bLC;Wm7f- z%9PGAxiW|htgP$Hj{se{e{bN)%Cag$-m|*OXSb4l#wXL=m5L)q+Jpkx<1V4?dj%=p zqXV)f#_T!K)prTsUcA?&sCA)7B_ks{`Pk@|9LCd5+X6&2(Z#nJ0?vS&I!v03bce8? zLI{I^(W`~!BTL_xfM~fh!0#2!l_OHG=#8}qFqjgx-`Ob(rVB&ge~uzr_)g7zt(-7t z9m*B{R<}f6a_wxTzs(O;;45CsKleN7XLFnQj@R5*;6=vPaPy)5f52Ie?E-aR7RoRpeq{sAGOk@O~|`y%n(qoZv{KmoVDFK_0;qo%FAYF!mA}{{PZS7z^R6$jOlETDYToYP6^y=Yq z-}>oj1c57VSeg-af{2gOsiuM(klOqxnL2b%+9av^e>#%ClF?ab8dkgWHis_;M-y$^ znwMoKr~aevdPyF;riL(Xeo{Q>J+dV@TZ8&a%6lG2>{; zTP&D-*u3G?M;t2xtA70UE4f1-1K)@mZju0J&%S2XkNVcr%qIu{}Se_J)MVLzV)!-$<4&l7@Pkxk!Hr)np&iD zu#8G=&y%t3FhA2&*dlT`o(HF(tN21;{c;;T;_7H|_Ddk;j?U1A_|9@cMKimRVo zY+Va*c(f_M^shf~)QdD8c&>mDRote5f$!bv#d`C-?=gpBARmUw9H}8<@#YUV?_zWF zq4Un>6A0LwV-MfWRu}^OV6v+Yb}j@Rbz8pnA^p5ug*(5zTKRLR0pH#OgAes*4#?SK zf3w2c0vwe5q-#K**#M6VIZd0O<)%SAZ%^^B>-pOBzF->kz96rDtmpUdVFjNl0mJ5Q z^ILRn6s-NZkN(VbvokQWGSRbtQ%ht0 zN$>jGsMLL(11`^9w- z@$Y*|G9Nt%!Ssi~tv4n*CYrJywvaP`!w z9p4<{;DM;>(PN?}L&;{{yc-2|eK+@{+g#BM`1tZd^v|Y;w7^0Yr}kechg@HewF#0k zQky`@%>!cZ02qIS1r$_BB zUY&+2;9GINx&(1ILGo`I8!H4G0lYSDf2Y#SsZ$0L7Q0b)f4U%Op|&WdR$iKt%u3Ji z=Ey|&HuqyppyiGAarxV~@mO81tqd2F<6Fs3ZK8(`m(Hj-1=by2+*sK$e|IB&+|%8$ zP?48PoR0CsYDpoG@lK2>eofRMoQbWIbZs-{sX6@~7Nnn)rbaqlwM zq$HPGw{M^C3R_rN&qh-leq z_V(6xoOEID!Jf61Z>r1)CuxsaZ% z`5(&s_}})qH|XimKHsKi$a*dkNcB?z{U5!IR?o`fS4DoY4iwjrA#=JB4?(yyWXyk! ztu*Bhuuj7n{MwkEPL2>-JbYlU`898|CQlXlcJ zynTI@gyz@tJ6&Nif0FvbIGS%a)RqOtQr~$N1TRHmf1>w2;z}E8-@{lnodt4WE4gMS zCyzb{fs$d_ctZ+!JBko?gJ61G!P6-_5vk4O!X5a|PADo2zs}YUji?RWV9PlMwh6HP zAPn2^LusHBU>t1P_8vz#UV&U#G7N{)T5akrwvmdJscA$Jf0d+)i>nIZ$dI<4aJZ%# zXW$n`5#3xPSoG+{y8^RIQ5`!XwFscX7Qy|`V(Dj!syA;c=F#)$$b}YNvw{L&UvTX_ zQLuR5>%WTI-oUnMD}$rdOQ!nd8E|E@3kDqjxEv{ptv(sEOLe@%u+Riz`Zj5&Tl>VL zknkFCR_Z2re-thSR4p$fY0iqO-?~lzqf+kYy^78lip3yQ;$-e{(xf_#`oZCPHt+}1 zxugK%Cp9AAl%w?Ycbi)5!!z=*R^u-%szA-ehC`x-?LJ@8oxZbkf-@-14mXrH@su!z z>BVIu^UDwG1=>U&5(BKXt9j^&;1X2r<{425FHM!avt%p)?diVypS2EioDeCW6F?W zRy8EDe)H!cM&u`$eHd%L4uPb0aYfi>KEg_LUpyPiq3N2eRsRT z4&ZtiY>E#b+FhB-1gsv|qZ&BX5zs8tlg202-*#-0&`{<>pW+?^dH_n5B0AdO$eYlI z-#lJ<7CBogB>Yrt^rqIy(-e;zQ{EMX2b?qFXwY)dYZCWFeIv-Z59%nOfftathpLQB2p3NJm5z9u zFIdp9&f%)e^C)xe?jiAT88H+PcIIlRX&q^^bC*3$gCKX$!9%`O553q~x8sX#h8TOf ze|8TYS3`2*UNAFOk@=L6avANG1no5GxeGq7`#zpytn zBH*vUtly5`#v)L-ib@^C^;%0+7){Ale|h5r0@k`h6hwJRubzPOzpXIM*9yHGk*!Wq ztmq7GQl+`1PpV?I?iNzVg&jkxHk4w{L}oN~Q}{8!`3 zFVE0n4Nn{tK+&gQOC_bAzAHyDp*F+Byw$=!}SxM8*Rgq~8 zieQ>aA_GWay|D$ewy*BKDAwG8e@p3ei7H_m-Iv4ASUi89j(72z4+!aA8XK&?202O* z4FeJL3~vU;ME%Of`E#5{G<3valww4SRkmcwy}kZ}P0AO3@a&=JP41M{R7MWF63Qu) zKIdps29Nb#`m=jvXD38Bq|ZyJaUUd%+^iJ>`YW3+*rlkWIND)9zBb!if8Ux43ZJI_ zP=eL=p8PE$n!V+{$9?4JThdCbyGHYp^$Z@A4lY_FAl2y1lP>~z)b+-f4rL?nubvP* zKAsZimrdMeaTndbUd+!r5zK3w-rd?nN5cOI>rRxS6n1u?PMotBy zkaPLvW?wC^-Nz16?`CKLyfWAdQrr2#wwhc&RaoC{JYl(wH1xsn)bK;G0|(iuFmkni zd*=BtXo3>leXOnC2Nd3RwBkD=OH$#1+nXv|Gm{ZQe`sC!(m4!(f4Ce+?n#=IBajRl zFx?ky^4I~kSY%s+Jq(9zhk9zM&^V5Ro{T9$@er)?&C8D3j$&eEUabcB)t@@39MK&! zm8-r&toSp;3O_?E?RSWMy;3!=o_qAVP2B+B066O8&k)P=m&k`jkMSGwA$~=^?;(UD zjK2dbEB)64@qSm+fBYF({|_JkS8&?BO=abNLJCTj>q9(mH2`>%oKnR2k?^e!e>B%X zr!f{-w`37jhY=bf4%N{OMR076b8C!QRxWIgXMb!>!B`l}l*0|$)re5{mg}KYY*r*u zIUFXiSKH+15v^N^Fp8J2YS1#hU8RK;Ze{L`);W|Zxf_yqcMM_mz zK%lc*6geaF_S=~{EUz);AUaIWaIR)wgjErMQTewecY&Vp#AbZy%tkHYy)=PVF1!&% z+-MSBmB|~GHg6W8FZDZzU$t1+G>$KmX@u;~_LQ%wd5n20?!?#^K8QPRs+;M`u>aY0FVwH0&ree>2jiJ6F072-xUwGg$pYI5uk>>8t)gnXEln#_`=Y&_E+90D7R9u6CN{_8e zZx$&wV6eFmr3I%8r7KDMF6J;3qmB1Z!iB-cm4#GOxMAS^8bC=pIYzkhn1kzG5a~p^ zcZw2Cf9?yMjNY%fow*vIZ0aZ*{5_SfUrhUd$6KGSu))hs`loyt2<k;dG^{~a&cE*;rYUBVLV|JfGT{$}s3l?t+ zR~^V`%Z=D&>UBUjYu!N0mp+cEO>OX26vagQfBya>nWiRv$4s9Fe<3l+aI2hys==|& zxOu^k+w1%2yE%AdpiMJd(?vr|Cyq(s0B}QCx4`ug-l}k9FXivza{9FlK27`2H?r7V zkhaS>ZTpaoJ8`8TTe*C`8eN-+YZ_INYb67P?Re-zq(8O_N1%d8pPc;QErgsBBsfpM ze_va;aeSiTJH0AW)Q)GR4wl9_Rbx>I5b{vW9miI@bd1Gl&mW759#D;fE+^hX+qynRAGHwpmP_==49%-QRNbp zspmU1U1+SG2Z%9bOO zA~PpxLR(8U&;UN$-vDfEG4xZfU0;|f-Bwr07*5DP+he^Yrw{C(#8`8ue`fFB^LCq_ z#^HqhLh!;=R^A?>%X%?G)}CND+?AkHCczSAjF3f#<`#nO7{j7$p?c5*ocZz%2K9U9 zIV*f1dSIKXrrbBsfk!D2rt!v^>ose!`tDk>1uHHz;QwK!2A&MlTPBK-Z2#qfUIJDA z;C);jNM9PShTsPrcX{Y(>AE9bsd!?DD=*ge1nSlz{pvKpTO(UkK9i%n{+PV%Lvff0N((fJevpuy{>4Xhx`5 z>`I5msHN5^Ej_L8{cTGv8kFc(P-=Io*>YunE%535=con){cBChvRzL@t>l@8kGy@O zNa#gTa_M?Z&c$A9=X3c_C157rI!WCuWXJNbcoTWRM zgm&KJ^w%T;y@Xs`h-C@1J)Mr=#rX9sVk&zvV!Mv0f3^%pR0*CTxxI19gda>LJOK%+ zi5OQlQv@y4G+ti(@~U%3QRT&7KzsEwsB_8DHnT$Hnem9gI2szW8f zaH-x7e};C@@1H7Zf$TufewtLw4&Z)3CI+s4JCRraFHy1Pr~U|{Oqt~Vg>~bCd=xS? z+&Q>kg*4TfoEA7eW*kHNl&!TF_1>`H@cEW(gVe4JT;ss|oq8fkg@+w)FcEfg-1Dti z@=FLPw4nx_jk9ZY87E^W$;XE67$0Go+PQ-Ae@S^6a80Y84f~0_*?3AUN_*pkuJqm$ zB6ei-irWYnMF;Z6$K~=+Ausb&Bw(mrmy_e7U(O|r80I_2<(O3cFE!NNWE0@+5Z-x7 zF&lshK*d1EkzCzl7Eny|cFv75il`r;!L#!;w+B9H`bWaAqBv1|l6uZ}Nh}niI)PUX ze>v>h3QWiQrw96-Z$=G4icE$AThFWa01JFRV#_(jl2w#|oAqDDAW!xYDTl;*DLU3^ zwO;uK?edriD1RP6!y>BWIA;%wYp++3Oen%E^D@uB#p%W@v6NR%CBMeQY;E+f5(kzlqD>G+@TT>VnBaD4z|KW@Shs>#1NdZqjp;0v;RAx>y) zSdNsi7g>)@D)I8>d|)W7rx|`nd&Zi# ziy*W&k4Bo76(w52m>*G)p~ZIKx-}!6JmF5?j#n4;o7Z%m6&XUHo&G@e57ZFyGQLZ> zr;~w@Kz|2fyCp{aV*mO8?m7gZP~44dr&O-R4{^Np@r8Wc z*$=)q9*khVHdj|+UU$0-EvB@Ie~Gza5;s&=RX4-_I8`^@iOVMd00960001EW-;L+F zdo75;T7|{(C0~fh2J))X^cH54Jx`&u4vY&v_o$1&O1)s(kgh_??L#-0IJ}39098P$ zzgkJxrywB$&#oQkp?$MDG7kB+c}VnB>f=Y(ppfRK5jBkIipWav$_$1x-;VYb+J6EI za}2omv!0}wLclzgE_rd}W$<34Tkjwq6Lz_c;}nklID@EA5+JKrUBhS{rqd1zmGvVN zWQ@ys5fF?B+`^;27;qN!^Oj5wfmXwewBP~JFiL#&2u9?f_-i!^Pi?7I)^1z z-_O=W2#q+lAMsH=D8N@1PI19jh2#J)Gn5TF?OVC=POn50B5t?c&NAM=(0@agvt52Y zjT+lZVa#;GX?O@8Lb`%9qhjDpwyY(F43GohmGX|DuBa-$GQSZP6=A;DT$Gz{)e}~I zNSS*q5wvkY;)p{ct?T8u5|Slx<+RQ1=Z>bqBdp6uAO2Z@Bp%Mba@Qm_bkO*J31T-Z zs7P%vsYJ;F=;RHlPfml8Y=3xv1+nZ2P{%yA3AoxP>1ohRnN+L&591L~c>MPH?#2F- zTj#jjZ1Rk0&|^9{AILlE1Swb?oRX^D=UVMcLyR|y7$7MC-`H)4xGTJi5+^&6n8}RY z@ns!OF^2Ko8JT>6YNZXAta*iJ<8gB0M(4TDI-O_@jdDDrscg!Wzkead4%6Uq7aovT zrKa`SbZsg7N?)*iTh5Qbum^$nVp!xjjU10;{;AY3eu)KM^8}w-_aVBeXz*S=E!M}6 zB2nC3-L{rvwBBjJ<-vqAhV96b-9XrL?@g;qP@*}bD zAwl(7W6;jd^+e4}Lp4oAZ*{cIht9xZ-KNW?YqO4qf44pVrGG1i&z!{5tp;+j8m-J* z@YFazGS1=2UVZl+kUI?IdXWTlRsgha<4HC{%-zYA97wSFZjpUh-sc&U#XQ2Nh77iE* z@3%bP%l<*2SLD?XmjE_;7J3eP4rX>H=HJ3Dz9m`Mzkdo*|A(vdccw{_d$R34@V4kJ zvu8~;!Rs#Kxu}I^io^?YPy3%Ci94YQGY3q`e0^ZY+bbA0?R_o#N#?>F($5w~Q*tB- zJW&hvqQ*HUtKK?mqbI=2VzIEnoAxZuAxY&$M6qqRYA{6qB8v1M_I}Z~$fX;d8BuRI zXZnMWCx6PqYGn=oTT!H>y(SuqQ#;i=eK28Rol7oZSb9^RtG)e;NZKXU zEyO6^I3ulliXP}c&zlyQ@fjhWQpAAjf|ijhUYjS|gk!!8omtIc4a5fb<#bed_?UBa z;#TP`qY5vf!0~NI@a^-U?)N;XoBd{Sa6;S7z<+*BodE`-+fPDre;(2$f4JsBBcc8I zP={}hRPwApT(qE&pnlKs<0A0S7r$8)GsDj(K{5TD{5QYtuLk#c@Ljup!<$3oemCxG zSctP)A!usfTG5^Z4eoNk2L2c(L=u3Ib?!E@g*$vp6R#8 zQh%H%U`P6f&@tZqg3ESs(iernUNX*l5PgWU76OKJQjIR;Qu3G~nBMgmK5(90Aualn z1lX^PXLz_nx~mPRqEhM^z`EPkEf~QHaBmt9igO!2wE61Ej-H|iuWZ7*T<+)KQI1>F zy6v>YHd`pHi@Jxs0B3(Ptaj4zT-^`jE`P4gySj)MM+CQc2&)br8O_ph>Ql9^oFwAB ze$CmRvycwC{92GE>da?ICFa(%MHWPldBrRiz0HiZnq#w+n6>`&qz)TNCAMz4`pbGi zy=TYC3DSm14-5t+FL;P`8|bAWGbEZZ~^Es0r8wYZa0Y?`WGr}3liVzVW{{~#I&x_2-{fb{`oWJqtx`?y~ zYt=xzXk_^*6yd!;atHv1wVz9LV)P^?*#|aUM(|l=J#KQzir=Gj{)z5gZVs9oi?p7z zvp$18-FxcQXQYtz2bYpxX86VE??-a9JN6JSKa|l9k`-;TtvTwyyjYeavwth%JZ+Fv zr16gXtvgW2m}Nbx-L`hSV$zg-TgT8kLUb7sI?vLYtzbfJyjHtgM6xsLDfioJc=^5`+;nJbxeGXcY#)v;b-%c^#h3Ah)C_j4id0oN$7P4We0qa^k$| z^7wjzw^&l?AqAz#YOUaI#hQMv^H@Om=wmEs@U4fQ+sjcKA9sM=q;fcC=1!8k3aCtc zvn4L1NW`#peoRqUyDTS@-a1V$-#*+&RV;F!A$Cwp&&)o-X6Fizj4(sHhFat-79&BzGQx)Kogd{rgd)1 zJEGXvYIK+fFDdji(MV4OE*bgaLtw&OvU+W@3U=OLGfJ>R6E!IOW>*GA6*}{jWO~!l zHC_dAJc#o<;O1kWt$%rK_fYZdD0`B^8eWM$YUS46z7zI!&6rb{>oor+dm0@;d{57@ za~W_o?NFLm@(02&ach@`A}&r-iW+!j#B2jX>e5+)qJ6WK$=xXakXO@UV(kVE+n6?% zS?|t#cZGP(r-5dmjQl{=KjppnI{GI)W6@XlT+Huhu~Ji{X@8&|fyVzqDaz}Qlb!E} zJ3zpIDjGQJ{47-}YV|zpv9kPe+yfL0yn=${9|Rnj=^209`J;Ni^VO3GUr6~MN2v?F zdi8xa#`sl7f`Ntco3J#)Ps#uN3jQ5tP>d)Nm4tP7(#Z$zhcA6b@0$ZQb{-R`bACl& z+z-&zyZ;J6F@JFJ0dEDMfnQg1(HfO;883Nqup7J&0#64OT#m;>6I@|~g*|L#D07Y$ zR=f^_R^3`1?5OQDyRv^2lHwb5{EiG7{aRbg<37bh?`ZVp>gH@y6SrmlE0X*S6~8R; zXRm{wC8oZI1BGn!1+yLOO(D(oPnr>5RPwwPR>eH5T7R*+Q*N?0>t!F@tqPNYzdkc^ zg!3E4m5T(T=eZ&xCzb4Z^EQ?=wVDODY5JOb414&Ngu4LK2Swuo5#IdRAmr2AcQ@dx zx+!6rU)@u`9uf?C@JtlGha^BK*HH{Hw>V$@7nKR$e_#7Q)6L)7pZ34o{@>K1erQXw zFflwEC4Vy$+jp+YME^bce@7+%ijYLI8t|=6OSR9`?g`JE?K6oCN#1yPw6)UkoGv)8 z1?JB+L8=%ZsqkivLh$?Tkn{4~iG#Q!d>b)aOy!VMBH zN76QGo(kfAgj;dg2>#v3>jVtwt_|CRvM~=FvXhvD7aueZxGF}xmpaB1Kr~!E^NP6H z9rJV}vN*K%okm^*s6`r5^MdE(f|4SbB<=^)!zVyX^zp69HqnL*T*Idj?s%oJAq|~N zA%B?>jI|FEo@WbR37Vw!>#%?WSWW9dAnZ3aTcJI&#)~zPhX%z~Gh@%@DOuKIk1i)GaS?qcT)m+%Bt68Jby^UIj%?ck zdSlJ6HCZmpL5NH`P|am$uOtHQ;=}Qd9!YQuB&0k6i9`UtQK2jKerOYWnftb68h=b2 z&!`}Y9(##;VG%uE#jxi|GgSG~=^um*{|p;9Z<;4JrkJl9F2TG>3hfqzP2OevghRi) z{on9qQm%;@fJzsO_>k8X-E4>kDwpJNw+@l5ZFF^v+)*m-zm-bf3#7M}A?eipuU zB}jMuwQb=tA#5uXiEx;H4Cw|htzN=>Y8cNKbjTUtL61P+)qW6gIoX+~m?@Z(`M}KN znS=TY<8=(uodzE|_?20uH^?$~F^F5Jr z+|_r{i`YA<>1*|{wvp?3T}93}2gs#8uRuQ)uPozF@#(rrFiU-*J-MQN^2(5nSL6m=vS^2b_>K8{l z_HrerKDq*w%b+*QylO}nu`>#5gJhVw#mdRC>P6=@C}#U~g}tsQnp2EPln(ZCytln# zBszBI1@P#HyjKo^3h1s-w2;`Wnb8lvA%qfRaIz#z1Hdq7pZowIq<>kYLPC#Df#M9Q z1-%C1$r8}YAn8l%Y|t!2(XOx|Cuz8n@@*y*k>=i9TuKR$#G0W^9)FHDkU*&aa?v#cXAq($$A-8mLaY%4OC&+HgeJc4C^7P+d6l4hrp9>V4+{z@^%#i(c`r=L9f#j-?>|OD?ugmdc!hp8RpjH zHs?=;MnX~-y!Ko_1&1)U7Qt z9o?lOVutQy=6L=PyU7Fp5j6wauv0lM1I;gBSo!1}^#MTlk`PjH*8okLzj0MYII$S3 zufbAR%cM$sffC5`-n{>UfA(l}_x`1h=usswGR_M)34e^$X6eH~gD2JtEh8Z4^gtz@ z2Uqr^$8`*VdaG<%T?c#atlK`4m|9C$!rm{q*&EeM(F`S2I3Q2?;vj5$0eMVR59`xP z%Nwz?8Z_1(wMmMZRr@-K6w|Si z5IWh@Jb!rt&GX^;0^NZ=YXZrT50u+v!gKa9JtB~>?B6$azu}p4N1v)Lns9Iiwk7Q6 z?8G~v-l8qK&7iK66Mm=wp5A|BydQwdY8J4E+g`KOMZD-u>BDak$J@91$%h{xL+gWA zH$+knI<){_&5ad1nS`|WXC5Ds)gbqM?BVwgntyV%*i>Gaw&Xhi@5k(WW;Qd+z+>wa zSXl1aHao9gqqY&)EyqB8$?5<0zjX4!eT_3DlXa6}f#VUD-2rAk zY=0La5`LUAlr*)ubk)O18e~%r)6v`CxySD?vIGJNo^$EyA+LZK6MxL^O#)k>_K#uY zmnHri+yfsSA&%mXG#wik*IMT+-Zs{TxXd_KHG;7MtHv7mo`lLiAd1J?^mAiqej?X< z^yp^X!W%w~=o_Wv3S5LyO`Jl*jjA|s7=K2Nf_rLLQJyb$(RF98Gc zMGAR@t6R1Rjj!F(W~q6h2>NK4N)hbC!O z2!^lmmR%M)7W68KbJkCqxFTPAJ8NxVe+SoybWjMdQ^c4sxZHfCCyCx%#CM&gXn%4{Zkm=i&gMWARo(6OKV?dQX{9T^`P@X)X$dVXg z$A{Sivr^CHLp>&xdLoF(9{6y6^`(gJ#>g$9m(B}K<5+xEk2X>-SABAtv}zAgR%4eS z@I2R)lA#HfV>6)cwsXZ=S?M2loPVmb91`Et_ynOD;9t*20c3uXA7t!jalhn&pQW0) zBsKp8#+-n@&O=;8uh7vu#mFUs@5d^Sgrc1*h7R&^p6DgIHgMjzkL^Fwz{gz8|m+~P%3%?N^PHBpq#b?==hh^ilWpOLE)3{`?D-I2Xih_WsM z!Brt^d`5W?VtQW?{AZMh_MKhpf*?)IU76NWp$MBk_ zAHm(&C-F_Z1oj5=<=d@VFO)09@gp@WS-wS!rkd?3#jVP~0GvuJ*oUp}Qon6Skqb2jG|QXjSiSDfugim zM>?+;ewg=IDTk`DQ4{{6N%MZ#PytBrhVMQU}OjOLcRj9qgJaT z09(5&MURKceM(NarXow7A|Aw9>fl^W60GdnB6yfwY|ct1JWuGG zTHo+u2x(oR)f?#X)m3hdWlbA0l8@WKKA|rZHhPtEYJW3lw!g+rGtv8k(LI9&+IKog zlD*Q;G@u6bUvMJt|Avl+_sve8I~wZWGJYj%Uo+!BcR0pB;>EY+)vudk-|&L=AUVilq zho$iT)qm7OW5zs_7~?Q#A2{VKUd$zG5@cVx#0h=j&x%EhoQgCIRLQ>QbCfeDHUJF_ zd2O!5;GF{a=OL3ZAXo;WM24Yv2k9@_**m5`KBzS(O{e)U&Gv+sHy)EZh+QGbk5NC=&~+XkA>=@MiSeqS48-l(TwXEnVzdvVbrdJ4EN)j~rWdfEJ{ zFk0!z>tEsK+#>;>Wn4x}Do>h65i!|Kh$&ce9-30t-T5~qTe-o(OMthBOE-Hl zi3^p&0v8jy#qo~}M#v_rQBcLu$81EsvU0VTo*5f%*PDX^j5J~}15aWwskqD!mX--2 zF2HqG3y=d3+6KsMYwVX7I;g6f?|&^|p+T@-%F=k%^txiAdcXM+g13i7*>lqbh-va+ zT(wtG1tM~dP6BfUP1nSZUn7nz)XONEyRKL$`2!9f5nSm>b0?AbtM;b@Uma^e#ed1+07wBl>AL$X~y+?s-5 zLff=USf*OlTzamhNmIqxHh)a1i`cm9dw=Mc{`i{Az=GZLWi3!0KqI(YadQ_D8ncq8 z>4!RK&B%<3FhCTsxG{lOUMD*-pm1tS$q=-(w`lPJNXZEmEPvpYy8Y^3DBFU8=U-G| z0)y(qrK^Wb2U=qhFF%rpP>q^a-n-^3R=dE38o>VwIFHT5xLseuZd5_mhxCE~d8WrH)pKvQg;?U&>pzK61p>h-WapV7>W zK8X&kUpz9*8Hq6DYRTjTS|wXTO=^#ygv#6WNpXl{NsH1BVgoBvi-)QlGn&BY58h!^ zXT|3AeWxDV)-$RfT{kjUhd#34Ic|_!&=rBk&~fMKxPLGau%TM*E6u$c%oGm| zpU*hWt?ojN@pb(%jNTVC=ovVyzY|Um$i&A}k$u-cF++Z)Zd5mHI|y?<$0*TrRhZXS~eIV9qN-hR=X0Y4pD4qm4Y zKMV~cQnWVXI~VVWZ)7|I+qyE12WgtJK4@o?N9Td) zU9UhdtRvfqu*jSjzQf>oasC-!Pr!hJThZ zy0km^QK};>$Hq+#r7vHyaTd9RFU5XIJp%2y8KFVVf_)G$Sd^=GuouxB^0BRlSKZ-|)5=bTB(P78aXG_fFY$QN}! zWT`T)3+Jv9TUBT=l0sFu^?w={WQ1)6_nl!? z*fzfD$-StweUd;0g%z!7@14h(Ap_xHAb_W#AB@1~R_2&a%zc@DPg^Ny>#zdR=7MbG zt2#iX#P*pb zNjTU#SU5aCLs3aUOp#xLfrg&yH>Xu%37zK~3cv9nGaWk@D}PITDFa(G16Px;ul#mX z>ZfCLzCYl{9sIBRJQ7xBKRw}VIfbw9!)N#|(Z;~YLeI{~!txFJnHcE*mi+(cm;Nhu zSm8j=RNQPfkxY%q=06wimgqfUx$~4N8_ydRuPiWw;ZEuw9&-zb!wJ8x&t4zavKw?0 zN?byld&#!R7k`0H;^OCk$Mt0XuDeJ1OYKpJU~Y-I+T$$M?X8D#I?pL;Eg-(BFyz+;$n>)V^cPy&@S*A-x+VCwWlox&Fl zN>N^LG-T}dD?1PK(uf;UD1<0$Wg#=46lDjw;D7s3z5Pe!lshAoy@Cmu=(Yo( zTJzswu`W+6Qd@BKBDJa!QEzYMUC%-v?MLwmzKd^;of`FKt`l~K!vlyFUOV2qIg~;B z71kQO>c_a@bqSE&-sWei)TsvcoC|^s`l+YS6!!?B*CoDqj)l%`W zQ`YzPcYlrDglv2;1+lXf5W}&vq1@N2Dle zo)8Kls^Oy&Z3&NoW+ZWv?mBPT+}l^M7n7P>_6kbPlZ;FBdySm=7Cr77NF# z2v<4#NqX^|fl+y1|7QPvI;x?7?S23!;)Z!!Kob@DP@yG+t=nkkw&# zvhA4(;Ujeq0O{(;DIb6YFFQVF5^CkZXMYVo2fTVVwe#nX^KV##1ZJK&)6x7wlH%2t zjenaPbsuI2+#iB8IR3cJ`+d>{g#^ZLq2p?0`8ORzq!+(k_?vh8@5ddn{v_@4Q}X}h z(SOG;VZ0=SO(s_X_OToa8y5P#EIHw9%=W;uNEDc1$-@>$@pq^QKbrPciJ z99nD96eCmap!)fKAWMUibXb_`T_;>=RiHUvfdG%D4-mYbj-eEhxDR`CK-O%-%B@O80EH14k;pU)+ z@=G1{BWDyc>P(m(u_*Qmy~mU>*Vk#A(DSs7|CuFF{N8-y2`qt`Q1DxU&VOGtNSj00 z{v7V|FaOMp|8p()*GubV37Ir$;*#P%E^^6-Ap>S_`1jBe!$|mR%;Nx4S&bA6W zm|h?$@3{Xey&hyt_DX4WYJa>yEMSjquivv=GhRwGd!d1(?T9fP*lPjhk6WT zr~90~r|-Gl@7+7z{leG`=3cPY+SygVT{UObf9hBoen1{#{i+WQMS-#mNW-wmBXO0% z@g0>f?j++Qc;H>DL7_V1;gF+X{H>*#JWC(B&4UuKChmP^i+{+$o|s5HlynFIgxLC( z9?n@CO{l%KkJ_^e$E_1XuC&hK5t;sil*9S3FRJd*$iebb!C;RUP|LX@xZ-0qH3Su^ zqIlAT;)a|F_~enbmksYEwXYhv@lZ5-JOYj*>C?(V`G0wru-`jl1@x&T-6e=Bkmw`h zj(oX9z?QN6pMUkK6c_qL!Mf(I=S+|6UM0D6eggomNQ8}TRxkWc6>c9?RQcfT@MvJ? znjfS#Mz;Y2UxlP@<#%Pi>yw!U0%f`_V$o;5R{Q9}V84TQ2XUDdOQkg#?)1UgC>k2r zt&X11pw!hLBG5Eo@cDL(BqhdeZ$zSJPp<%V=%NTQHh6++C@;T3Y)?nq>t;u=OJ~&0<}YAB-RW0104PQNV?au;mBoGRv5?_Dz3XR6bnYt ze!P}I<$q;}9Ha>>Z|1Obd<=?G&vqKC-@mPp9;n21)M5KB_bv5~DhFY@h?*2bsIt6D z+76~&Re076bVy<7%aaBF2)~!7B;0#@H+QuWZE@I=)S@H@xA)bT!wvOd&{d9_^?f$M z+0g};#kXxH9G4i#`Z#r&t=yI5gLI)qd@Aje*2$B@6 z=YMRrZ)*J>Lf7(DM*fAQz^^4_za!~ba$7hg`{t=@sNik_i^kwTW?=s|?&$x7q>OBI z^z^L%$0YshY@*85$8OMdX3MTz#7EZA(|Ud`*!eQq^jZ~P_tr50khTPfI&dmW&|SJJ zZ=9tOur*>yv>yzWMe_1En6QuMfc2^d{eMQxv|@Fx!I=$PFC*{Ck0pOcTzOk)^>x^zS>n4XF)N=-5vUVNZ0 zB3NpPYdcMCv*&N|KtpVyWwLBGOZU|Jm_2ose0bh?KHWZ&Je))% zk#EW@Q~HZx{wAaIvb@YalRub_TVczX3{?z*q5T(*7P8^+3 z<{_@m??ljyEF<_MIlWVcE^7*8=Rl}f_kkr!zv^WHSzn!>_Ea5d)Ggn|X}V+}<$KGQ z#-HEBa;}DHY7DB&Yv`80y_Pu1z0c%}x1v{k5SZ!{#w9FWfD^S%S#WZ-n}4tuWOA}r zQR@WKB+(@wBSc4u3>M6v-W~$KCqxk37bLgW&o0u^2A_^DtDhfz+c~au9{9aGR?x** zuf1WPe=gBg7uZ_dzAS&jvk^Wc&=NDWL5=a|pgX$ttl>|(Z{f6JBj`+6`a!WV`V<4q z!2Z#TIDcpALJGeVa}<`AQh%hD7nJ(V*rA}|`33Rh`Nh=nemnH)N3ZJZ4D3JVhJWmy z`YP!DH>Jq`qm%ztOwwrQ<1QI!?cI-L)!yrg1&|m;1x?rlpTbv!Q5O_^ld5cfzv~eh z786-Otfhvq#6nXihl*PSj{63cNVS+%9BplfeOl#lu7m0$;KxmZX@B5$)dv@m!zdm{ z!NwA%2q`valgNsG8#+c+JI(4V3+rh>*?J@|OYvN>2|V=U=5}&4qk`3^lNHEl{5=el z7y8Ny@S2OmLz+7qgrC8gjK zIAiY8yy249S_zqjaDRX_pK1zIlZh9#F9^EWueK>@rh0L^EsJ}w%mG2fX(xL+3Ykc> zjPoaf@Nd!TK(C~eZ3;h2%I%NIqV>4+vqMmc#V{lnHld=rRjBvu=60(Yi7@+X7wt1c zB{yx4;Ps}8gR<9{J`gk>D}_@^f4ivMdZ(MDK zl92!1`9m&Q)K?#CUxj=toqEyR&RdpGG-A*737z3!Us8ir9}Y0FR;g__6JzMIL$;cg z3Q1=rj-pG$?hJ_4s0*1_1``>ZcSicsCk{`vVa20CNeoIG7MQ8Q8l8=(N>rjeFTvvG ze1Q_Qa>2xA+<(VaZZY>1gq?~j+ysV9?ZCh4wy>LxUY2($^YxX==Rj=R1d4>>9*~i0 z`Qw$p8L(7N2@}5hNfKXwI$xiEMX~+bv*)pUKLyjpu;2PfK z<1ayWN;>l239@7VQcC|#j-AHX+RBl}$br_-*1&=G&wpl-!J@_2E(oPhyVB&0Qqu@> zLZLrgIFMUNJppbOK^p+Jy)ns8Ptth=M7ldn(f=CwRI5K}Eg5&;?j-cy2>Paqb*uz# zEW+j%{W|Ip=iD~qbg=%U?%A2uS>wBP@I|>k13@edhk4{%V`ypxM-?NSnW_~+U(S4E zLQ)fmsDBNYV!;t3Iq)Mn)u&zd+aZyFJmfRyro7`k&TgHPD#io^w7LPKR<$kx&{a+tLV$-RmD*xw@)4_15jQ z6)Z_@M~v_e(OAHU96SJ|J^&a&9H?pQjIjjd#r}8tG#M_{>{5rtCM&S(Tbg zu;c5@^v(r8I__poK@tgPgO|@P3ZW-VD`0B`xSh6Ni=2c-^?;V8OX{aT3%|LU8mNUQ z=5u;NDIO5l4Ww;6p>x?*+^i2sR`n@H3V)O1kP-bJR{Q0Z#WpW2ws>K&!f*R;_`#1= zO!951y)#&EGVlun_(vMc{4ww#rz8Ctc;JayI~v(r8@);n894ncxi@k!(z7=(#S^i& zak8aHe#yaqge1^n3i2X90}|EWA{`bwCVF~0CPubbLjQjFfB%AinMv*wIb6PFy?;o} zpj08l3Tj=hTVsn3x8~Ya$#T7R>dbk%GPnK?AVapaY8q(yF1EJP;PEY@MH}wqH~Lh3 zuL(x3M$xMkx3w?z&xmLEDH0u_fp%u%#>SRWUR<_qrQ`&l7u)T-K4qH{QFX~)aln~V zB8l&lMF|!5*~(-fZTx8_#8kq49Di#UX~)?jJb-QSF`droxbC9LRu&CDAE3x|Mj}Xf zjX~`{*SOGJR(Xi8OCl0O+eW-(_!|mDhe!*@=xy*{#KU<*a`og^OM<}SE)DDsX*O*% zl6*HpG@0BioliKU_1$)t@=|C?ddA^5U{@F5BT??_t_!dGc7VW=qFBJ=_kT876JS`J zGVWpRE3s3Xfp5h?=6zQ{swrmqzm@$>_Hjb94 zX`7~d+s}sdfbx^`sPU|jToDZ*6sYG)^5F_+C)X5k$XN$+W_a2cAcDB69r4JyB0~X3 zMU2yeRPlI-V@WuGAU^bdTYr~DdM_C3_+`*>FA^6Je4K7t!;YmItg(ZRPE)kbk2C3$ zr*3FYF1g=uV~hVA%p8AU{_I(hGtAD~)573t=3+E2MJP&&`HcjDj-%mVe%nTeaG*NA1ow zpo0&1a6g9X_phnef5u!TT-BoHszTXu=*oEvGdxmq@De>I%~0zbza2B$neWOH4fPK2 zYE$@$UVD;u_f?a+rR;@J-%#U0IuB*iP@R7O98hei zCm9Y6W(<*}4&d@zJ5EpmX{2hp?yl>T=Jac&?KL~D~AHRj1 zeVunSK&6v^|6D=_MAJIw(X?gf5sfkhrY)@So)t_We5yMkZ6>*fVm%(iN3=|m9c*-1 zBu_q`va$ZYbdpiQa|c_>Y9_m~PrzoEkCa(6m#hvfVlRR*dXF&kUN+6mQh25s`AdSe zS1NP@N4-2X?|-bu#yxmvz!OnnZpdPZ`34$AWuc&+#uV-{XN2=P^?roY8Y-hDx5B;0 z5Wmjza?ZFO+eSitiB=fvoYM?rdon^jWhY3{Q;@<&JME>A%VfAe`3n?^bGXBGvyz{|2;3{ zui~4|n<8G!kOwZVH(K1$EEOL)+_;R@PpsGh#W1St#=NBn73!h^wvZ^uKGC0j1lS@w zEW_s?#a(>#MAQKEhij3{$~uz$XGO`Y?5 z2*67gL$5^?uDlmgLMh5MDoF!_`)09>O9N_u7#NJWO}(T8Lc4tmJVnB|=hHbNkd4X& z^zoP@X6Exb|zoYA*J8L4$w zPIEpLQbr<8Spn#)%y$^wZ&CtE`|mOF@ux63hF#9A1~z zWcu9_o2T=e7dA|jcMb;OCnyz# zdBPMo2Z)FE)h9QUUg7fXVF8M_v^-l%h0Dg33mZD~U&7Ux)bc*dST^CJEjfLjABq`? zW7C5L1I3jPhCf4Yjeq!{$th0h>jqbCmMJEiD0L01tkoWG-pSI1X*zI4u`H6(S~c23 zynhGWuy~7N{W0JQi>&{FWOxoMTDd^ShT~21h-(N`BphvSu}lsoA~y;+AKaO+T)<>1$YetZSC{9(>{7go9)nFX7PbG}l3cYoBLS#jo&I{umWf!<0| z{^I!#c?^06kCcHBG_?hBYmUK7Z~u2<+fVq%Njj20O5o5})n(qFkTN z&z3rIPwySIuCr0e3m1tZraK23fp%L=0{@v}YJ9>Z0#mqEgl>k$exk=11mWE3?vGRhJg&nlf^-YCTwxj(7ngnJ~_0~Ibxs7wRF42yI2O>~~ z$dehIfas6ON%}2-c5C#7yjhXe2~9($c~+%KD}PS;pqr&ohoo}8;EaQ+uY8XkQ*lbZ zx(mcGasASk@Wiy-vL~;+^JT}$V~+nL$g-B%Ry>Sg!-IbUd%TCGkGc$!eeawnx%<0D z`8cU|h2P6PNK)%0sgaZHo0RR60 z03iS0wH3at0swh8vrlw1eUrl9cOso0&xaevgEWRg;dPt*nd`JoRz(hzMbGlGk)KM# z^&{DpWY|#}Dh<+~>;4n8{~hJE*^2ByU0J-5q0N7H(>GY3COBXp{{#8|=%PQ+ZkG=` zy8X3{mu)6Qy&FjHUb5#cDs`BQf(1P4gHb>qyjb+=QXffdFo})rERZ&P)^*h9gqnOL zP2aaxu)cl>n4!|WGWjd|IPcaV)Gjq#pjf|#;q~4?O4F!f9WaiM1?mng-uuvINa_Z- za^rs+&ffFmqQUJAIN7zdXs$8^@$gr`es7rNg+NMpOS_tat=^zK%FI!rV=JY-MSey| zX0KNJxij3D1j~hr#F;satiUYmmh*~mbyn9Fv(Bvp4GW}u?^>I=(j&{xfiNHEggU9VGNy(I=C3*1=&c-vCVGl^kvriQfz+^ zbyL;myjcSBGwEPo>T;CeX2H27j}11}D13*Ge_F8b3q!RZ%Tmo+HCTa3;+{f`o08&_ zAwj`=pPTaJz+l&z(QCgEYD#*x4OiVQnXeanlL!w+K}THF&~I0NVmeIib+zsV=U0Dd zQ`>0iFX%E78iSghq2Y?cEwssKzp`OGm^?MmL-Gh?`bM~{b|E#q6}`2x_Z)po6KIfX z-bNg3VSss&&*{9o0No}-06xiSH$F_a`KSkRE_8GzNmDrixaZgfl=*pV`&!r-;tX$%#i;FYrL9@9G2?j@$(@;rJ;*+|iz z1jCr$t(51YnVh*^AXYbH-15>$gdtixQGYh1ad#OXjfUf!dy4@*^DbibU|*2iR2A6M@i?m>Vuuh;!Hur9|rd}=2(o-E-PY!=rSJ1O?(r;!!7E z-T;OB{e=`EW-=pF7R^-=6z{aw##lh+CVHn%=qw)2mP6gE>ovT%Ud@Z^rT>bmoGbC< z9Zx?534ggE#Q5IX{*P|=Kc`GB@S9LJ6a8xt#Y{)X_&O8B$o_LkPsd3AKPi;`<97ba zUFT8ju*8WdW)pua2RCuPTPPthEJ{Iv6(92}GKG zpLWLfN-wBa36(RkpPMeMk5}Y#0RH~%^5BU~z=b!R5VNY5+vhEqB#q4V3?vFni?JZ9 z7onhA7hm9&B>7@wN-0eKo1kEv4_jXqW>WjZfDK#^*3reSn`kW<4OL@H&Dx?cPy9st ze!8n1oeX~+)DQ3cRnYLCgbmDa{VWFBo4JR#oW{tba`IarQ@H;`)B8`nmrwFOs4Cev z+AF=MqJm^3*n^TG!nz*cPBqc9jP-3f&)#*)rssFNq~tfnf{w1aTVi@V4&hR9>$i5@ zwJ5L(uDG)~L9{^ZzT{lx$VvEgPh`;m;S(D}ciMmbk2mnboH?!nA5k5ZAXjBgq>LvD zo*x(5YYKBiM~$!G1V;)eRXhxY&5LQ|=P!>otV+qFT}2x=2#a$=4r@Lqv{y^yU?*m{ zpIh_Ut(l$~NsYb-SyM17%0I14d}E6&44gDQ*``>XrRphfv#3m>KRv$`LT`h0O3*o_ zkHvpHB58)Kh!LH%dVr%d=+ZY7$t`G>mZCaN#JPsam*B-*>f0x6*BFfSniG9|$%!If zJlN@{_r5_BAD;b3k5&F-+Zr7m&CgFQQbAKQYdt*Smv7bvW-oUB&ytAW7QCRL1%+R? z3s(MnyBq@@+shXt9V;FE-+E+!4gcG&`&WP2%AA(P2s|$?h}sN@6l*2yVG?B|GWK1p z1gamt9lZRUoS`jBr;?deB(haz8#HF;dalVVbjcV&S-FcMg@k0Ng1mdzG0o~1JK~P7 zTHMFkw7J(5TmyvI=AW^%3(hVMvu56nA&=S9VjCpxC*MBC<|+C}%1_IGrV(3<9;tuV zY2{tb9+bkHCf*MqSE#ED#(ln9jLw8CqUPtVJVPTtMp2_;62mA2ohuBU1h#i)*!*TH zWlfb}GD%h++eU|M~ zf0;PUwe4o#t6p;;UPh;Yx&hB(%wpvV=F+e3vdWkSp6q*~x<>*^6d*darL{M^DyX49 zpuc~YRLlxPZ%`iVSCr9WYSVH^Un36Nj?_{qE5$(yYKae+Uwn3Lg@&ilj_h5l?cMMWeS?oo?s&?R7M%`F za~8^@UJMO^(>KGFdz9FP!*s`X3l6*0u0`oTp=>@-Td1f&0HKRlAxt;z%aCVg;ef&O@Kg|SXdk+R&!&vM=G2ok^^&pr#qnMZmDKCg*$^F~)5 zfs73J`9#4OAYYsb#rXXrL|$2j35E?nlm#eKYE+;dNHuLlh%qC%vtkOu4e4DK@5l8~ z7|aVgjM6p&EzGGlVi13+jTj8?!2v4himVwOHZb8k4h8048@m2dJ_JiEhS5O5WS zaH>#iO*j!nII@3hnNAgK&qe18`jOz9 zTInJ>4MAr_LwL}-akB%V4!)s*kLXnv7aDh5zbHR8U!(TZlQMgD|Cn|Lx}h-qBI?IF z5TBB;=&A%%t}wNaVJabyL+`L%T|yw_SBqo2oVtc@ILEscLn^em|zjqI%)UXzudzez`C zK>j*&&w%%bQ{hqL8Qa+7>HVavyOANDlYta4M3)*NW+Gelot|gs zKs}jR-!*?`!a&gjWfb78z=%k*vuB;7 zcN{qea=2Gp`qM=sU_8Tk|7xi~oxvW)O2k+2PfGrI>*lijMhF68f3mHpp4CsEhEbmEH-^)qQ`>+VGR~^Hb4o0T7)=^4tej&>9c$ z#{<$Z@wnTKyMjm(@cs8OrdAH~HY6qZK=sLWB>Wo0x@F^{7{{QmU8DkZes6eQ;u4Zy zaf!G>IN5(#!fXBSwhTJ<|FUKLYUT$D%r&yO*%s3)yzKlb$9&#lct4-_(=bpDIZ zl#n!*{j4;@xqT?ywLki}xsMNk5KcwkUP>FH3Mp2uDJ^7Bi@61?w$TNa%gpqL2wVK> zFARHOIWvLmK$Z2ABBR>_V%?kttM`hYDy@H~`_`RqRUA-Co3cqL3`m-^uF4&qabM2W zw@FhniRO9_$5V;@wuU>qg?hph-9arV5HgD(twlL)mZlNV1+JsO+O8CI(s(XWsdmju zq!}djUB1=r?=&$rqlcBH$?es7)hJXl66{!w*>YjrZKj&k`d>!mm+MPPt*e$8G9iBg z-_%CL84%^!{aYnH=Jq7+<2Watdz@x+)0PpL(TK*M|17<6%fwFTt##uihzObr0)C@w zZjnNx7UF;ID^>;(k)|Cy1Co@FpYRW!D$sg>{KBS)|B=(bB_n?e*?9zkYZR1jEd}=mG8SLrA8t997=0sEl->v07bf=QO_fLNqh2}B~ zQgq03=!|`qgD*i=1e|{WsSL^XcJxmXn_HIl2o<#n6Z-y|-k86nH^wg*k^VI~0SPGJ z0{{RhL^vk={^jHJKgiNa{n6nNU&#H#;UL5ejjVqxeb7_Tk^S@o{J%YiC+nndX=d(F^5OTx|J#@Q%N-L5;vVuu%lAbrAn!??x6gS)Io-vn z=qbV@DvG-3Eec(n14wF1d<9^@50q4!+;5tVJkPVN!U{#c>O12g9D~1|sCtKT`EC3T zhgN%O$brAC8K6R(lCP>{gHxiDI(<7k^$>#t=M?nTo3Vb_8b1=L{I=b4W&Z0(QtmnhOP&hIc}b0YfZ!zG~DdHddmd zdV`W{3KQk>&F?Zl(^~l3pE&s}f-+)i^)DLqN{y5l(~nH7@&v)NY&7J#pXka~4Gn%$ zUS}=;%!I^Fyw7gDo#cOBjF^dhdrHv56lZG*w7Q5 zVJt%RZkxX!J3rb|u8?!xK;mc!V1+5uDq&(})712#?A{b}#YBIa)dF_Z1g3|;L9xXt z?tfd(;3jTXAUT@(%wF8XC#m?ks(NAJX3l;ItJ$i>NarM&nZlU%`1=>5Q^tt*N&IHQ zaDrH7cd0ewdgdd85(|>i!k>}VvPBfhY5d7_usU--+oJ-2MhYEo?g+3w?BWf(zzA|)_Clp$olR;7yyq6xc6`2X3K6pW4=IOVae{JMO^GbSMJ2y z+&{}(MDA7~D-b@il}c${@4zN1qWg&kDTWstCeN!s1q1e|<|kJYDiYS2W8>_`AmCDV)z#n-Ev_~Dcr-0A! z&uYTrn_2tl!6LWUkM)Ql$`7}43{TUrfvhq=;@Z^vm?m*cWexHfO**{43R{4T=bh&z zw8EI6AVOdG@CEh6)w)P^f^e!2>ByegTdUH+cHA2@5h8%xM-Tf>``Mu+Yb&H4%MQ7B zmEIe1dBcAywR~?s)5%rxvoL4MXd4KcmJ~729J9WK>3WW?>pdh}im z>rM?Ag}$@)s9$=3>6sB+U31pI(uI5btf{VXHIgQ5`Y>5-B~2NUmWXk;34 zfHu18W}wYa0@=QSGC@J<^{ZzC7UV0NltrWfzz z2BodE%Z3$Kq{f)hZ|CUdEf-STZ(dK=Z_d_bow31+@(n~^QUg)ssG$0bs&b#$g*T!p z1ijs<)s3{s{p^S19#uZ{JnVv72|?CnQ+1iKgHA)o)oSF0OGQNtTCBN5JH_b81-x_x zO7DNqQJdfBnq^r}&O~ZF-&4IgG)=D-dk24MFM(ya_poaby<+BR_6VSyUsqCFS~ti8 zc#`&tTf-OJ>b~Gs^dE8C|E}iGa4Y`T{QI?14UB~Icm4_5SXmj_|CsvuGuE=v|0dP> zyW#)UYyG93rY!m@Z!-5ZqwtxHoSt7*kxYM-Nr+a!g2D_~{I#fXHc)eT7eoo)q4D|3BD zq^0@*GrLJlY(7&1=puBRD0*98My8`Y4Av2~aKb*=+#OJCxLVk)7qxOR2?tE=&I$j6 z(Mq)P=>6w>-SqDCTltExmY~}xN&u85{e%->q#ou z$ij`WdhXu>?a@2*_Ni0Qu;YI`k5+*_62<;V7Uw?$ElgQ7!}m`T@83D2>*0`+jc6;L z5I1juN8ggqm6?KX!~v+D(9n1($cNWzf_)2aOb(NcoAdKo(}MtrErofzJKAy18s}P} zllCRYp*{*EmTe@0r=tG8eXR2QkeV8E_^>$TOR=sWCN*bQ0JlnL0#1LLQ~ea(h%UH) z^i-tcnUrz6mLo7;YW6Ia9B0-okfUIO$xMrKopMj9<4b#D@*dLFa} zX`tdM%EgK+07D&W4Kg0-KMM8HYAmZ}`LOTa%{?K#{dB|~CobSP-Q}X@FcKx)s>F73 zxBrphTcYY4f>pMz;byA=Mhl}{ZW9TlB9nHvyuWKSm5mIF`&wv2UC3=XQhxO!Em%kv zlfKFKSt5qRGSCklU^KDdg7@z&T`e3ziHtY}W3|gX|NOvz)e&|Gz^VnYBkgl?SEHyUF`JneYG)^hmxQsoA%F~BX9*sVb7*MoS&5+46}>q{yPu3vDwe5;PuuN__9*FfB09birh4 ze~#M$n8lvt&1P4AM(2$-TCJVDBw1+WmG!$cZ#L8Ms!#3(6Z9=!p{k^UUm6)dP8B2} zYo_c@!8t8F@Fykm6caB+#n4oEKI+=e-l93QE&YUCK3Mp-^IcT$9;af&W zGOG_v5gtsnx4M=Hkf|Ey3tQO&&B5PFC_ZQfiU*B(4+@nasMp}hMbHL|B(k@UHMR=? z@+FJnLs=9|J|@gevX1qjoNUf=^#)-9aVWdW7ys63|I>eGfas(QqKBekjL>`1`ZhZ2Le4(}@0bgf zwQ{TUm)Zb;xX&U3Fq6)GftlD~Q?=Di;I8FnlTc!-j6Ef#oxwn!pmoeZ9l8T;-TQH? z(^ExA#OhDhIc1(8%bpjOr(T~Tju+-eRZ;hg$jcl=gZ5Gft;w?ubXd}jPT4yB%%g`XOVrtZd4_(9Kwg`V29!EyAZZ&ek_#-T*`Oty1!ME97TLOzY2T$m zxfZT5x&tk28B+zBE2$`ehKz{TviN_{=xF`iQJZBrU!w_VjB|J#T@GyLzPCbnJGMseC|*<=2ZOTP1sjTc*#HVl^Xp z`r91tCI+7qmaWPX4<9m}m4j)F$24jO@+#&)8aGI3W1C1Qa$+kmFCts9`Eq|7SfY6R zFfg=(zscUHZTbt7Lv6j&+=#C=i%xlKlUU|EW_psG(ps~(;eB(5B!VJ2Yz5A@7v^ds z>-SEU*BCVPvtWR-I2;~zAneuT(xiK+#7O?W^qDpZF`yb@me=HWFMO3b(q?28$vokX zb$=XaNhw?zQ1s2)!b=Xn>?(gZLATjb8+X!Z;Jj}{LA(ns3t!K5WW}(K59fZ&%oDtz z28YfMFz;7iqOO|QItfja(k-M*sNJpjyyYKe=VgDZpM*lf{N0|(zcqOL+Bcc`U)-gc z;f1q|f7D-Kr(=8xep%K38?N|IX0JtrU=!{=wc5V2PvGBVLeeExV zyOTX(6li{_9ZuX6npVA=aIq51X%8}Dg2Z)SIpAWObI>m7IYd(sez&H{Vf^S;)h>i9^Hy7xcon3QoPz>EtUKmHSzN%C0yqL(w%OmKQ znNYChVI@GmSrw=v%ZJ)$N&@;rYt?`4O=J2WH-$#50j}JDnLqR)i2~3G0pSBI$7E+AeHsI<*zLwojJ|uTLJfR) z%r#{_SZL|ooU?!01cXOg_<@u;Rk=6!Tx7D&8WRWrwb-ACu5u|C8WIkkUpjc;ZI(4n}T&Q~pMR z`uW69QzJYj1tEDwc_jtK-|DdGY3QhazA1miKaTy#U0>hMe=?r2Eyw2QfV36|HX89lW z@mNf|(nSN@Gwsx3qd%A(&+x^sVoeS^(8geQtpr3UmCC$v<|(Oxrz z&6f;e^(8~-{biSck5+UKxr1_rn9YW;%)r$DsIK-u{C>agy8FMxH2y)D;^U?L1IHx&3*k++%g$;&eB86_R9MJ|&0Q-N{6y~z^3apiQX zD{#9k$Nuo?a&v~{Q#nWiGp>qy-Sg-zcO8FK?RUkM{yjtvsWQpXI?a^oIy~+{Zvv(= zFHTcqz;}Cq(!vUeb8$g3}wTyoN})5 zCSk|i%1_YaW^kBr7sMnMj8@myB6kjgJ6^E)#+Bsn83GKHLb_o(VioDokxHgKSqpy- zv^Xr8HJAA35EqNYpw{MnLLp-HBWsMuh=WH0>MMEk*x?p{G7b{tE9Ssf*sZ<@Vi;BSc8>tQL2&LtDEz<6QguZ~V={owvg0an4S zO}&^>d`!`2V?MP#{k#g;c<5hJfc;JVO>_D^%-A)cCx5aLv_p@4=FrVpGN^wy=As~S zW(JUwO2XSSc`nkRII<&L4nWo%=LZGZ_JGu03+cvie3_g`CH;>VX2|o+!CT$MP>&~t zUnP_%K=G`UT*=Xob?7uDh!B*XiBhb!DJYMjq%ys)0omf-#tVW$-0Kz{V|_*p&q~oQ zNHSNm3gZR8@1GDVM?*G@MudNJAfxHoB$9qmJv_Y>dLsC8KpQ!^AfyM^-md7HZsNK$F??l`tQ1rOefs^dTGuQZgX_e^fYmD)U}-J@ zyyMF=huqV5O}xV&W^KHSsMi{RuCEePYM2+hOe%uJi^wtvBcC-%EjB7-F2IffXr|SI zhUEF#V9UJm1y^nuT1BzeKPw#9vRwr`|$WN_{?t=zfy9!}(WSAxEsHlKiPm}Dz zntj+Zs}T|QrOomCJZ13CwmagPnz6Ao%A>|MxQqvVjE`*op+CAwE1BR*epj^zH<(`V zI2e1bHH*)sq|LO#gN;y72K8^UE)opMSx8`!CE(7TM~}{P?$RPm#bB^el~S><#HL=+J)h z6G2c$O6Hx+YoI7;W@YARWJv!i`}2dH1iy~`HRE3+(tpDE|3~iguK@pXUomD9Qn|7O zhG?Z{?45~YG9`a&t|y@_fL*h~TK63VVw8k8ymCvXt$m%I_kaW(Y616F#T&ijXN1JQ z%RnB;J08v3*j*;~g|`-aXCNx_+7sBx^3|OI< zB522=>lpXk#tRg-sk=_LVP$9{`bA-*UO_rWE-1bFou|Q-eK;h2--&F1e#C`Ya zj4OX2vh2QI8VfxsWFDfX2u=aF3jg~O8tM6 zJP(MENhaZM0Y^+h%?*es`Kf&S3RR@(;tlOC_z}-R=c~=(C#Y+6Vo($3)^t!9u+q*)2oG0Zm8(44 z`m7*h!}G`6Yq487q~mhttSzBBL*L)wU372njKCBQeT=Dm|KYIat(h^b-`;=7Xg~EQ zIdA+9W`z%+;#!%dO_(~4uYiwx0etie;D1S+!-J$^s$@jsGi2W@7eZV<$0dgTILfBEi2_iy6Kg`XS9`U}Hs(|D)>p1&?Td`-|Utet-6{ z{M15cq6>au0uh1?INhXb02Prfn^8E;<4_GseTt0%Dp0eD3qmd9}_B z@&azv&(0|$WgeITFx_;x7npf$CvHX@t?aHbM`HtqkC>lk&7%SF48HQ?f&XaxS;>!| zVQ%S-1BLZR!1wr))6IWnn7~e2uEq--^zTv_`P#te)Wz~TcZ;%2R`N)jvt`(85L8jG zi8O_y46i%Pf}N9`xz)4;oP$CV&D=##*K-gtjZ+*4S0!-vU=7N4r%z&HI5N1G0k(yR zLuymrp;LOb$5l`2SsgGg^sGs|0T}kgitmcL#;>YEsB7{*p9z1rrQ+8ut~5Y<7mwkr z@AciM;(p)o6XQe(*hWj;u3#Qo{U;Z|4Y)XHeC#Ska^eun9awoe5yw`Nl3ISJRreB| z2;PC_DSiY*jU9N|1NxppdlUkQwfB-vyatE(I^B437B-ci1H)uGXN7dq@gA~qdfKCM zD!m{{lCQSSXp?`isjn7_s<3x3|1GuD!?`WLNjoh=$u$}i-o3`8GAuZmKLc}<-hyY5 zWh-j;N6fD59Ds-|LsK~AF_$vi+s72~5SYL;6 zR1sf$nD(~}Cv<8wW$Sk5%G>6NHZihR2@$65DOD_Q-9gWZ8Xyp4=fErLL)-%h^#sUw zaz}FT_~(Bn(hL{Akz|Lxos&eblI6GK=9RJ~41^Y6_MIGNhzo!Lhe*VdX+O5WbEKPG zXJ>-{fR79azOTfvYA3cphAJzy+bT8TR5Ctz&tET?Up&e!NnZJgk4 z!CiuE+%32}1a}MW!QCB#yF+jb5InfMySoGnL4SWpPR?Y`oIB@!bLY0|Y_F2cA!(2nfO zP3(WTG_0(K)XJ1fAiR@N1ew?7eb7zE0yi8sdA(9(dCd30noa(CxD4Z&uN04xRoWMB z5J#};LdCHofj^SRDeQg>woq>}nIzr~mCIKCcAD>%IgMA{gn*#nK>1-G#c6Xn%$u+j z5>W)HG>{u|%%N%I+{okE@F(zu8eV`8aMyoNFe@oHooVGVGk4@7Oce{+{+;1Z_kT)6 z0s!6*B4Yf0#NQ1rWc*7g{|7GNzcAD-70fk;)Wh7ucoL4EwzSxtGittUV%lHsPH7sA zyE+{lh54kbE+_1fPLpvQp%0632x)ye;z!Hpo4U)(Z^2nQk&CLz@-7Wj;206b+;)Gt zET|yMtX5#yc#-%K6enk$=l@j8s~1Fbzh!mJma*^;BI%pzQ8r0*|Jkd8(xx+WUS#nx z8ukuos06!+MV>Yok5>(gZy z3#Y`gk9roz#5^&8>w5ztrNe?=6()Zdg>;PpHD4>PCvT~{Rg+^pkDrZsdbmeC)g{N@ z>Jcc%m+bj|~6+SEax99B*VDVp?=H>#H(}DiwEl6#hgA z{UZDlOR?`ZXyLr)ytjZa@<@=e{f%m?wWrz3z(o3$?r5uwjSaRC96_cnbQGzUrjH|98 zZj`hTI=w2YiUZW#ODctti>rU2y7{CH*~Ltti$lPM$&N+hLA79HZ4>t2`)(mN`Q|}) z8dF@^p`vmNJ9Y%31`{$#l_Lnj=8I*0YE12_Wb~!<|Cb(b(nN{`m~q=1mfHw7Hx0-~ z5Ezl{f95%^V@&bh3SLzIfPQJ;7;`GvovSNU+;gvG;T6V8g|?j&6$5_?ySMRcKjJeX zW9Gx4*6YX$U09~vG6G4z?uBdSHOIM?{MU^=f-eF*S?o#F=FQGHEJ~0P78D_9s0Xi*Q6J)MPbs0qMDvk<1oJ~ALth=cjlnqi_Y2E|Ma@|vFREPjU)e&(z zljM|UndQu!lKp@Gw6+h7HW#fWg3Dz|v1a=^4~OGYZTkJmcqYoW)2Ww+qdk!V}pbS=afRpkcX9>g8Cy*Fem?o*pg0qh_EHx`9cz?PmqN7DMo9L^M` z8@zeM7#po$~(&y;^pexiK*QqVG|H!TUYtsLRF5|CgKet0cC(p}bD;5;XbbkF7k6zI$qg0WLL=;dntQ6t{WbF=)C6N-nVM6l+#yFnd z`NF=rY#4v9A8hnh&LoDoz*Ysv=(_ZE7vBl6#Q{$|99H= z=P8A4^zvJpk{382op1%PSdFK3zhlESjn9rTrDZcuaR-|W&a0$n4e28sEH)ns#YvuS z6&-(PFlrBJJE!9~Y1^2L4j7Z&09~+ivJz)kI9ev_sdX=Z^?J-fZ$tbQNRO<XP@6so9_A zfNeF-rP`O^D{dND;SW_P!^jZqxtmXY+s3U=TaN&@EOI48;Y%5?w#D@E#af*IKcBp<7mXXxbQa*d< zS0KDhk2eIS?i0tlm!GP40HdtL0k*nAl%lF8R!;e}-Yt+)R^`K;LMYLw))0S@_!-xM zrj_s~9l=-!`#8FHF6JSN)1WZvWS@|iCKcI>$#NL?9lYmhh7e>nLB~$e60pspxSjUkFGX@v;RC%2$6EF0*xZdjU#lSXQ@&lUGTVO}}ge_O;D^*Jk;VTXis= z3^~+4PUU5>nFEX z&GHdHEh;#O=jsE&0rL)03DK|$_i`V=0uj`I=*wdmdTo0ZPmsBeXOw?T+}A6bQebvS zup0+2_f|34qm#Z)H=F}6kA;t;ODGS06R9&*2}S>s|717DXj9=nh`<>(M3UyKD1wHW z)ISE>`ffF0(w$STMGjPwTFPOj7jDdjv~AMX)!WUxg<-Aa`dEEKndS*{ncG(wTjyd! zVH#<-CLc9F)xxn==lFkC*^6*Rb9;{?eT(NR+q8!Yx&l*Vk-*wrK8Huut^HG9FB+e2 zzwNYj$#~r<&Sn7R>F|k({%9>h2%2uPM?hjt-)G0$!A}76!5rOgdz3A+lN_{t&#O?7k8PBwp7-a3t^Z}MtWVZKHZ zDUr8H09$Y-!A;_(=o{0G9>et;HNBU~FFm?D)-O@vH6$IW3Wywi@mTMOYPq~cepvjj zR-xe|?=Hc&@xm5e;r_rdzw>WjGEGv5@Vv=V5W%8t+92>3Gbb993pNsIJblwv!d|5^ zL@>NR1?D26$`F6}Oxuyj4$0yH|D1u$+1KxC>$F`CxeW#nj2KoiPm)foOML93 zr4F3gomr?qi9ua3d@-qrI@gGYSaZ)FbocyM4|t3h6QT*DHZi9N>^w|waolImh{_sG zFBog`MU}=7;K_CmKf=%y_%jdPtM+D$4e_W^GBt4f4Of2}PO3&nD?1RjJEebfS<^|I zCy41Wh&dNyYEUqJht_-`!f zuRxnsMp}ObjJc>1#4CL8t$h7}*k(O>OxK7|`$IXo+?FLa`Xr6JY=weT^E(cmbpwzT zw}6!Cml36p_-LL}ad^lB+o*nE?JF$R=ouj!eG7?9!?TPS6vB4}eW&9!Q&Ld2V&BdJ zH;U%nv)s+9I7o{uPNYX@ zHc2KPcfB-mJygiwLuJlc8S=`26}`o|_|hs~e=`+kBi@5w`oN!D?W=j_r+kv9A}~x@ z8diU-)Y^Hg4!lH6nDquIqGR+e$`Q&2@++@J_@DL^BZqv*9 z024X|t%~svgzbsjfDL1%o)pUxtU{%HA&608j)en5O)4a$Q%;$FiwmYso6SccwJVJ> zVjX|GFPn&!q-9evjVSG~tL*bN>?*az%pZSj2|0b4cj#AK2rcUwUby2jM5Br8*^X>D zWi?G9VLJjFzgXpm={P{Myw1g%%PuZoCzn@#1rkDyO7gAsHApL2Z+GOXC7Zhf&DK$3 zb)3G5=Qrkym7p#i=9o4&Qykqm^wEQ5m~tL4L?OdBvTWs|XR063&$!^CK)S7IflGh6 z&}RN6&>rnrIjXhl9Ez!T=Q%anqT8f&a{n1Aa0P?HvZy`xbh1Ye}^TA;m?B z5R4x3Mp6`E*H3q!%QoL>^L1HJj=+B#XOFV}JO#KSZtG?OEZtmlwmSiawRVxm>%%Wd zL=5q%pRd$S<6Q}01^AQIuT-&umX(57Gm~a7sSzm{O zXrnUDcgR0~i4I}MSl+Uc&fo1)+-|3fr>&U~8Jzd74pa<8y_a-*U6Jk5IWP=Yp-S4{ z6@smY`ue)UHk@?JN&W&9M>JnnJBR>adFCTf%$?x3qi{>uE`van98iC+N_8c|jTJ<* z>%W$M=>Otvr8!d{?xE-_&UZ&foe|EazRR9re+JrjZ?tJksj(zBSRt92P$+8SCk8EU}a+Z8)$!s|HhL33bdImvWO9TXXbo0V5$BM^!x$1Ywg@CKHGmXr6Pgmhw5VO;!};0hk+%YKFI-x+@MMFz~jv6CG)2WeMVf9 zB_P1_Bomy4Aj0LjI2+e?Zyf8c;G^JbPe%rCzI97(bp!yQ!!bOV54u(w;Zxhf12=-$ z{EcsTdWu(3Qo%73M`AB4V`}b*Cq||^$;DRFxl&%_glbX|uHJvKe^8ir_ZWEzrO>1a z4mOe{9vSNB1DAC@-h=9-9>oIbox%fgBd?|3l@IigwVGxYRT(T0rq8vq5!Z@^GPp1d zxQ>CXQwRSHw3s)4>JE22Eq+8xN9e43LCAL`%EOrfC(ClcpvN2y~bNp6(B z&hepV2^!kF?F5S#4F&EA2Od#pgoqb5vd5J7^eL`;b!id@LFgmjG}%JL0Fg&u4zMnqUeaSq87rF8Ss8v?;65Zg)VjC` z?bk6>9J4WhxcHWzuOqA zV}Lk?74312^H3CHN44Y4<*9iZaMVqD)K#kOh{${g{w6bl!kOaOimq=y$T}(8vOfcj zmR*#*=IhR3Zi%&TSbn%u*|LNDxBRFb&C`8qIr>I_AYRSj9GoJlhd^YKmqBu^g~Xni zkQFdTp=4PG6T0FD8X3#g<;I}Z_)8X=rY5GvS{#RNrcFJZ{?h4a4K-*o9dRyiX8O$c z%p1-r^|flKPieT1)C^aP)cHP(CV5+=<3mt!A=iW=q-2wv=7&$atV(<|h}Vzv`z$_$ zLrW5W$f&c`@od?1eS)mh6K=GBfcAw3?XB#7dNh2e+4mst zaNlEIfaCQ-XS7AcXcEL6%~Rp=O~x|oiD)>*-|^z&HR+wOk=P}%9Vh(+u6 zB0a!^Qhj|(un2+%r-k4=ltvW&b!)Mgx$i-f8m#?mw?+5EW$)&e2dyCCmtbtuya4Esal91p z_gxM$3e)z@z+`-$ipRu1RU~o=gZu#zcYy0Jj3~&1 zsHDz^(m5VW*t0Hc;>0pvApA$h{2D=j@K29;_EW?q07C*i=WT)c9<;QA1{4e!_#Z5L ze`2~Lb7ER}FB(9_pGM6C5v9`^Rsy3~kKf=r0m;63!{b`TEq#r>I~fc?kmuO#R0s1o&t(2lO?UdhV;y5f@#?%4wrQpIVY zYeCwx{`EZR-*eWg;@>j;0RKanFrUAx>_7cKp<#&>X%w8Fl*GdBIS7`at+lPGogsmO zp{=8-zM&m~h=ragJ^uGQ4h92@{$LSmoD=n8=3uqcTGCwJ8jeTY|?Ol3C01v1ZR;Xb1RL{TEmS|NaU;%Q@%p@`kn>y1S zucsdH5&tt?rU%W|g~7MqwPoqH_$HNpLsYottvoW+n6Y0Rjm1{}!3|C7{9{gd-bKWf zU{S#?VO%K>kREz}&?(5LJ6+;!#uR{y8H=84DHyTe8^iU5S0Ov33aokx+D-GodX(Au z}!{xN+75;*d-$`;TZ|Tc2iJX4M_sz+EUsJopOfQJjYOIBVoYTxgLtM39!# zj7CGJ6pe0tmG<2}QwC%2Syd0OgZuT-@x1fbv+UDuf2VJM|E5h86E=zcAb0R(t+QG< zTIwt++?5NYN)M4QI1N_{DH%6rb&-HuZ*Q?dBz#V5Ds~!RVSW^g_5;_KTShO(&7o2( z8F2Cllq}27&sn=<06q{B03R^M-=3v!O?Xn}QU(wblKZ ztqK~JRftA^(V;WLR+m8NwFtp$Q*+a<3{wjNMMJaageY%K%?)i$2^Ek{>#S`+NrDrxPgra1l4twR5(zB#<(+H8XTE{XO(IEdI~^{+Y`=BY+X` z{Bn(*={cb&!?VcvoB03ckNT_b*ZL)IEVvw=zn-FhmJ7@!CZunfN4eYA$q=(VeF63c ziLV{q2Ep9#X%)TX+1Vi_B$oUk9Rmkj>Ea{@-3z#zL=Y0KufzQD>?2|u4_@EwR?=LXnRhAW`URJ%YHGrP%B-opErD!s| z9GUQc>XSMj2&@4{TW##n+7}d~VOZyrv1p27?M!5OfE`vuw`YvjXNt|m{Pu8H~I?cM$57-i^IErAVAX98X)3a?TqtP?E>Y{{F--N6+iCy zHBUonA%nkIg7AX`eWcnzsl5pPUUc0G1sGB0B!cF;v&_D^e?W6#{_y5a$69sSGMC)( z0E!#1rysng-1~GC*pIY%lHb^^S*+swX<-GGy83T4L2_264Z_r_fuW0Pp+ztrPonvM zs3Nk2Ip`C&knRQ>#gs$t8b)=Ktqje#I|g_h#2z9G)`Po@XVQdGr0E?wNvhM4348Eg8W(_DukTj4&0Kpkq3gIk4JpxYnq1}Q;uI@{ftEosnuvn@*qBLsFD)5=cs;D~ zArTvV6-Zdz$19I9lw$FSZDM7MXYb)wbxGGxW?V5IMHhu9(#y0ZR-~3$U|g+{TjQkq zwa5^s5(-2G)WW^~6-N+sl9k8WFt);5K_gc_7QLNWSJ@2A` z&}c(T<(L&93(ZW*zDaXIk`29o0Jk1w=XGq(n=mE=4#-y+*Wq*%@Kg$EihJ2k!7hm} zFdltjs~Xave8*dpHgBEks1_z5dmI6g^rc$CZIaC}fD8l@(2@u*G~vdr=Y#t_3+Uu&;G={;?K#Fz5*i^Lmb ze1h4w0$Bg9s0&V-c(O|B7Rlm+%JGoCg?A_jcu;8;kw6(B^X)hIE-p}=;@d$~2^qnm z_pI959K&p?0TSglFg3Gh)-o9kC>s?8DqHjO0t0?bXB3_L78g_}?PiYiw;qlDT;mF# zrCMy(gvRcPjp0xnZbuA%jfv9ADxk)o9&wpkABYoJ$JuYVpBm3K3!FK{RvNe;J$9>o zn2sPcNS+O0TmT<%wx>2C!Ou+6aV_YXUulKExU&1$-h>Xt|5|atpB^r(KgW(j!@@i@ zmP~aOtSk+m@cUaU3-qa#^|!UZ8>1Y*Mrz6G*0?_*Y*p}bp&5@3)2BZ@BsuNK8@F{sw76ic+uC+>-Vdww2`v-- zSOT+7d5qMp)_nJKshhcSG%Jme+>V(-f`S(o*tt>Acd&XQR|w`ryOSq_Sw zCsd)K(RhA;QK7+6l_gDJi(r+TLC2p+uC9l%aS4qs_gy}utG6%&!2 zNYP)iX#CnJJ&jal1FF7^Opb@0I=FHDfU%2${0BYw4$ziO4Lxa+3)mNrrxUOyo3Ua< zyn^IbjTXzkM#F`JaCeu<5L-^D(FbY#u1QpWTaR~y?V6{X_$qY{MOoS)WREjFU!|92 zuD{}c2)DV&@KF;npJG*xni*496t1@Mu`*xzX=rSezkc^n&<7{G59jB2C3oVh(CzXF zuPjCz=`szwO|G~h1+8PNHxvyyo!8o%WOmevwOuTU#MWg+ziuhk%j@Vx21-V`%5rr@ zps;~q&4iJrZ-Qrb*dY~Aj{>066owNKn8N;lpbFoh5e^|*Y9FZGb4RbpkFwrF2%o-+ z2e;PdD&Qa074oLr5c-|USvN5P@En1}GQw?)zXO$qUAToSX>|XwP#J^-(BMR+s_Z;R z5GSR8uvWq=#bE`jaPgh^n^qh6*P(9r6q%!<{qvpPl8xDRv1Gjmu+wBgAKOSq8k^OB z4;8%uxC5ws<2z&iSTN&kgiG$@M1K@q)l_~P0W)qwprr~Z>QUk^D>Yp1fpjlSXN zISxGfrCGomDa^@DxQM(o_2@-M5?S7VZmQe4y42d~?a{eitWY!?(bBs8S=FG)s8;mq zxxErqUBzCUPyOxvYYhaFksPtXd6|Q)6ITwo4)GSs&j1yA0x16zKq-F!6a=>ApJmcg zRQV;eq9VVT3coas0RPV+nlLDkzj2Ko2k`QTVIM_N$80>y|zC{!l3{ow8D65SVUdD^TMmCw`UPXoyV_$A!8=1M?qF(uawG-;0IPkbm(3& zurteZQ21WEqIYA~>rIQ0Mh~BVVj)YF>R(RqI%rd#W&v=hG86OBnvd_XSWefY1tM?y zrxD4;Bu|#wzxZ3H^zRp{3EWu(SlR8d3KgBQ^b7_$;5C{xdxuyO0g&HgI3 zhgQoqouzw;^cCC+_a3Qb6ae{|vI0t)Y;hyIF#0Y2=V+lIjjhCbn>;jstUfmfq(N(Y z8JZN_Xu*4Bip+<`fVaIj8Zd7byCocZi;3p!5b`e6K!SN?(usVIv+WhAxE07vam^zb zs11O~^mSRJbA;=7-K%LzETXdGvWG|JeRd$3u;TaX2|rMJLzP>K^8IpbG%)Iu3A zPftP$pHt0m6S3+Hcz;BH$VAxdjq_|(iUxNX;YMq-T84TL>^sARJ;;}LTAyM(l^D%g z;P)<$ELZKt^(*@tATX|#den&Bm7;KS;}}NVwz~1dnBEqsR%%LcD-g5vlcrXxCiOPi zNPcUZz$&Raup+-4hVrswGmOxx$_MkcsIZ{I1kvGMseyBd5$(5s(61};@SNx(GPh&k zA@0D5#>v*`<=hz-e;dnB3?TeJ8q4{!DXPun?RXN8JGE6i2cqwX-d>hzEm!bWQiQn| zW`=@0*kHIyCuEHd8S}V&@uVNFJ8ajnu5JJzJn%ddIwJ<%r4=wG$W1h+OJ=tRG$@9lA9$`A< zrFek-q7oq8r}PzPayHK$!o_N81#!h&n$WQF;~Iu@oDjsY_TZv$u)c|4cdFW@kK$d80eD>NZWvC&AfjZ#PEg4o~SG@e-_zP zQYQ!G_|@U0ehiFO3!S4`&PTGQNYGj6Gf4yCW_G=;_yZ<=B#ObfFQq3Zi(YR;_ay4~ z4NLa!ylQKIdxgH0v}epU?CS+ZbbgWWMK0YA^_x!reBg_j@wO40M2FFO@bD|cxyGEc z`$>B4LmzPm$uU|?C3mz8SwORq9SO>QZkc|2sku6 z`X|poVWId11eE3Z#nkA3=NT-dtc2L}>R(Oz&*I;RD5UV)3j^k-4?80(BkS(~{Z0J0 zl<`-8#7rqFZq*e}D5py>O19S?or}L8S>H4|!N(V2{4AKWlDv#xtGI;bX3C6Y)z}Lg z@fH(rHgXQV2~m_q<5a&oN=<`=btY$e0LrG}3$U$Uh5Le(*U;UBRp0geM|hucA^$>b zlS1y@3qkYdtj`0WEnz$;Kz=LYqHP;Q(7StotOb~ol~8cR%AR0Sa~OV@dA;*|@9hqT z;Z?Xmy*bjLvs463OH?eJquy=R)^N@?NrqEo7336r0+A*iqbfTp10||ABR~L9D8_lN z9uGqsrH%2?SQ3?|y=(`^8nGqJa~=Aff{_8-^P*LS2f zw4*aJH8<2XurRfx&Y;{dtp7*7v)wi_=;QxL$-~g20&Ss@= zXZ`bZzchZw18X)`Sjl6Ru9WwGy|-8f!|-^U|GE9)Pmk~cw)^q!#s~QAlVJ2T=|O-! zt#~$=zj-#M|GXp+kS7Th&j%P0h#$xS$R5ZF$QH;H$N)(9X(D)<44=LQp1y6LwwVGM zJx%&g+w`C2=RlTE+vK05BOsUG_cec#37*a+@bpoBTB3MbHh$V-`TZDw-QV|9eEQaZ zT5I)mgw@j?N8h(O|3zs+PkTQfXaBUv^O_&qslOj31|J>jEFONz z?@GQtb?V@!8A+%8WM7*TAmdU^wKSbsd*@o~w1C&9q=N~6w&)U$M@qE5%kKVDN>Yug`z^V`Iz%XZ6KRw9Ks9&8KL;F}+e<5gN^?y2`{snHyA z@ddU$4&2tpEvxnKCR^EVPu{}=5y_w`v?x*^ITUj8Oe9Zc(4^Y%JMKy}qKhZMU7GD6 zpQ-rvFy}shG@20!gZf4;gD$j#jb?c_aymRP_flB^J&F1vSGk6%UYE7+^BXVh%!I5v z)$l!}t1$!#B_?62&;ZWoUXsPrbKUf*mn8S&*$xF1Qy;WY5z?;z$Ig=cFV9c~iJ!sC zAW@)S+v?gs*L6qTU&1a5n%bEf6X?>~(f)Lj0!2Z8eCoUWSpU82BG2$!Z;6$j9l%b{ z{M|c%=~wZ8rOdy2_-E&I0zpmtYtmStUVZMcOK1LUqvYXzTP!TQ+U_RyZoAm|4cGjm z8&1Y-0?0caGk&l={^k}?YN0DSgUMpe91(7Vls%K=?m=wcPc2oUOdzz0N?3PcRv)zI zC5NSdKnZhZ$C+Z4;|C;RfzY|MUk>&5nCF*H&=)lAZmk(jkKcmU@Ei6amcfJTcYD1x z1>BDxN1ectuVlp8U96GLo1-uDvoY<5-i_^oq3Y7`dzADzNe{>=-fG2QVuPufboX`M z)S+Z1)*-#Bn^qZgOUD=+AbkjsCZSb^eJ7QF4vd`tssPx~(oGN7&u?YF&8#+$D$ch< z0SVR)7p6)$YV0+~FrQ^y>0|f%Qp*It2zRUW5HC0}3%hmK7`%@2_VWW;aH~1&5mraA z-Ws({jDmqBNBmJN$6adh>giOb+4x1>z-aJI!556}^9?TyC(Ei>;0`{qeS+OeNk0gG zH(E*>nGWW^0*eWuq_i;Oz}azd2eE-aZ`)}ez*!x6#x_|Qevf5P7Hx5A~iA@GYw@vfZ zYItsp>?$VYX^*NxvBohM3ci+Xbo`ZnO;&s)yW1#eLxF5Kw`&MsB}VrOxcZCBvb@(S zZY)S)u3DX-X<^Q3|I*9S+NA69iuacr1k=!^!_vyRw3e@r{;W4e2BBrWW60R+V>y?K z!GY~!R0VwXj?+|&ZvRpG8%0MVm9AtB337qo|_#bP)oDB+W^u6 z%%8?tec2S)SU5+__r96IkKTgq$~es!0>2A@b>Sh@`{Jo@1KXD&xKK!*?a*TB%`?+> zfSrh7t-(|kGT7#id0^>M{Z%asvbE@S%()Ix!X|1KWQw$7TZH?4E@_X*(IdWH$zVs` zo)vUNis5Hj|Js~P1>`}0r9jv5HjZb5x#opvUbC_NoCm=EHitVe*F zU|M3l)EiqH!rD83#*~qKQ?`yb4Xci(Hp@}piD`hh!B|xr>?94_I``fTCBX_g=OL*G zY!K~G$AsnRg=^hqYwVE`y>D2z4O5^r#(sHdbU2POe;E<07V8=A+!zfUovB(-scgh7tA3w+}Uy!UWm3c%0TJv5D?scJXI_T_^t5rIciMcwjs@nX?E3 z^1ga*$Ds;wQAH}*`?}z>-=sRpZ!AlXzRk#eHOu?0lL1Wf^p6nyv$(($??WW@P^hqI z2g_|k+*xLlaTbmL0Y%QMcY&bXu)ze-!~NHq{WFCSEZ%>u*?$BHBQrDGFCh6Y z{=19&D~e21iI2F5oH(Qq-*C`P%L)ZEb99?6JS+g26t03I;j+rU$dD2@dCy#}F!o-* zfIzu?S?=8-75BNQs4Nfkim*~BxkU$}6kN2tz--{ErZh_}VZSyzADFhf$6!wPt0F#s zyg3%XY0>i-JSJfRlJ;D=2=SLEvb?66Se`;1J;0H2eZ;%AWi?I8P@c#GIF;p;QP6!# zg9sBfFONi+)x?id?Z5~}KQzOl)B^RBnRlAOzE_KTKQEj7S@BaAZh@)x-a6><)?d^LN#9D2~ zR=NsAxUQcRdHM8@p#L+9+|Q$SW&oB6oAF+=-xcc-d~`$vjQ{6>)hfOjvz+bh!} z(=EW|-@}l92_FKP{F%G~-2W0j{uw(w6BYoyE4iuegf3pz!>GunL8x&$g1{wzReJ!L z6xq*4vmW7qT5I~fb<>`6>jeU|fKj;9mJ(nGzhRFIsn$9j&eZdM+;uIJ*_^b5Ek2!b z7Q%2R;g5`J0kNQsH5rhSlv34HRW9#!{7TO*>dR z!kW1J^Ua{i;8tT3b;>-tNfJVIwWA}9VxR(9hC)+lc&_9oWhYfBntjg(DO?o-EN01q ztY`M4hj<$L+oCMhy0z>LXEYvi*8g8|KxO>Z0hNKBndSdR2UNztq*SwiL+q&@krheV zs*7WZd-EL0RDIpu(CO>dgLgs7S}DnjxKgkoR}&-F7xJ~Uv0xj*0XF>NuTH6`l|ZLO zJ674*j?Of=cm|S(*Ff{Z#Y3UDcbGvZ&HT@`MNA$_ja)dZ|rpLVIkFwqq?* zD$;U|Y25j7=f3j<%GH3tWiVBQFRGIU{t`*$q+ob`!bJ(oK2_M(8L3z0HuOub;Ca*& z4gDfl{byG@r46EgyiMqJ?G_jNQ00f`wL9SH`FJ4;a?`QigZ8mk4fFY=;P%dH9(+7L z5@bjK%Qas99uXkssmqD4iag%j^I~t4t4|QQw-2nwdWl2^21kJDW9Be7h+3fVUh=3_ zZJNOb;>O>)h8NJ;)C4X;2W6iVE-5IiRN&$1>n3xqB zs8q#)a#zPg&Kg0qQ7A)oqSMG-$KC-CERv*&EVCegaHEoS^ewFgNrX%tC9zl&J*0va zME@7M5;iDBTfsy-W5Jg41;JRayNtunnus>~ps*vp*_)+;>_%RA zvoOx7geU@zW~}Fdsh=;*3k9#ppw$Ye=R$se;72?DB1ZPp4rLAQd0${t03JMih~M z`k}`ytK;jcLpo{rZ=@9wTxK2r9d}G10%Cq`dO9cT%#t&_CF@L4^;uSqH|XfO zqO8<=QJ>GauAwt#y|)_G=Sit^3zTd~7_NF@&YIp{$Cgw36 ze#Wb@v3E8b-autP`|@)UJ=!?I+rvA5B4CPyqCuoQt~2y%k+i#UFT&1&Q4wqY ztbSjt7t|JKnog-9s3?;xI5DSN5Uh*&+fgZOks4;$!T|!WwRwOc6%;iM-M@y|WhE=B z;Zq8rl7IF`s10&`$@#txSEryhb%)YaMJ*-PMF4;eI-}Rim;d3;U{ic1S^>C!kRZr1 zRa>xOK*yWu&}zbcUR3*_CaQ_12nVJFXDZu-zzb~pt}>FYQJfx5JOJllorRL@a(=e+ z%t3d=qs&oX+=b6DHt-tHD=c6uU^){1PJvoy7hU&dHjjYo5W9}z+sm_HGGG&5(QL-u zKCtL2Bqk)D{ZRode9+h^*he{kZj}pu%e8^bmV-Y1A&bGyYnBftX-CbP3D$yeAK{r7 z*I+dpGv(e7c%UC3(dEPl^-9{zHT5OQs|eG{jx;lyUT~ugL*!8-@LYL+df(X5*lPH0 zJiz%;jPs>0?CJ8m!`?F~$(PGMl-KQQ^GC69rZ#S+v8yBT! z49?_Qwm%fB?a?1YG}*PH=s{=nG$i~73Cr+jgk@v{F#bZ=@8Z9`yuTvsOpn&rcF0Mm zTLHq0i%ZkZV+P=boB;eyRk%+(4!-iwk!jFG8*w4~!^$cavZ5AF_!PU!v}{S9?Jpm?L-B z@*y z(icj8-5mY_VwS1)FATDX(w-Lqv4?&W5ls@U8o<(!+>59o@1b*l_w8m0% zyc$zPbLU$7T7&i4SWnAoHCyTMk$6}7rVF{-mcz`40n(inGw1~bsaWa4 z%pq6WGgaWXb#V2N-X5WB*lgR-DoNbOgu6MHu;@#nhfEQvR5(Cr7~v+q2zeJUewvg*i~)$uPkQ#94g$Uaq~T3;JE%<;Pnz zY~JGhHd{@9#7djvCYg=W5>N|T8`|>DjnbTESfqlQNAW6GL`3`o75=KWvI}a}X%WPr zb3!>vK)+5vxo|P=5r=xk`(?om{ukNphq%-CG9RZ!Mp0qxK=9`H|F@=FFva{yyR%Z$O9| zzcWaGq09+>F4?gwf*LpH)6^kDS=)z_uVXG!2a}NNqF;$3DsnU44o6f~>0IOxjS?Tc zS9394LTKevDKS%(oy$pDvwR2#b|xoATaHO{g_2U+kq`x_VQxcPY)7t+H*T-ESrYoR zo3KmOB)&1y8x)Vy>bM~Xt8_-QPa13~C0HhZ)4@4VmdnrC-^}4Hc+_3|q9$d|KrC=v zQh0nUjts@6Y4f?o`Q~$=2`+4jORRDUDuHu-O|>`cbQ`#vMbo3u zqv@?P-cVoqddT2P648}2p9$Og>|yjo*qq<17LrD z3^xLoGBww=q$dUt{s7@ietmmgb5lD41zKxbep)&JBO@~#GXsE~h4mNv)6borhXy_M z`F@TR`jx`;0Con3A7RbDyCeM~{%0@dFVlB-cji&%QlD&Lc2&scSjll_K&$nA7G;C} zjFXA!8C@ILz{k6UYl8p6cZvotDDyghQaREh2_z;8eM@WliT*$G-U6zsZSMo6 zJEgn3_NKeLkrI$*(&~c&$-^~yIAEjtE&Pd97gH$&oO&-@&>~R zmI2gPxfvw>B~m0%I|V!mFga;|1b0LO)E!7d8AMo4OfU zjRt!G@~_F~8UDh5i+lO@Ks!=v1){<;VMGyANlW4sNsm_l#io$N@bL4047XX0clKnX)-MYNe!4ljAUx<%;r0+^iM`k^(FX&e}2k7OrxWOMv0 zGy)@&ESnF>xt&R{n#hnZopiS({x$2L_KpSw1^|Rig95#geQ_l+f^p zPb{(d@rVM?(&<2L$3p3|pO3Ud$)5Nr41D0u5PEs>0iZA(G7dt+!yQe9sAg~wHLu8R zRn(rtZMS;rKXRSkriUdxgpSqRLHD?BGwh2XXH}!oHL=m6y&D>#w9eeCLhcr`J#|4n z&7}!QVR`?;F(u=F5_nN~%qy_cH-3&v%pJnB=$_Ks9uOVlJG9``kDOk2IFj2WCSUV- zzocJ14OnGN)mJ2mPrA5aESPb@Z&Ik?R?4^_k~VOsUOBoh9ZBD`kCpM5T5m-_nP)nR zZr@og^d9I*ezG}N;MM)+%qaniAf%#@TvfQC;zr(O_%exqwSyzaMaXtN&NjE0I=_jr zZImJSy2QM<1)QPU+KH&iF?nOZv4`q2m^@gGPXl5k;ZcWjHdXTnxJ6FWTdtzfmue=A zeKhrQ2|l3Pai-;F#aF(CB4lwQuEjhg+u5r*Bc^uHyJapu?N->iJM}$R32H11=rmMZ zZt^OYFOCL(AL(KqSW08vzJYD1jz*1ntSnh8&<_4osYXjpN|4>t%|;`U*>T&H)@_Ig zvpqMKg}I42<`6StaI4U(e#Nhf*X1OP;_nwA(+u~O`e;>IIWMk*y_)6!2Un>E98~&et?%!D+OI5*EnTtD3g6nu83J9x25NM6QV)IfqB9taWmYMT9B16E{9s5Ob4_NccD{J&9V# zODE2M(FA*HPNY7>t@1gF5*YkEu+MwT_4yvYXBN(RZ5FpOYO%I^yx()Ks^W3rWj!@~ z7QglL*2cxTz&v_03@@S!`Cm6GXN$4iWy{o z^6lHs!3+TH|Lv^*x3m7=&-#Bm`}a7!yO%`r$z5dG(^jY^Duhc^R*{g4ING#@(!WpX z0V-JsWNHYNjqW%(-81Wak?CY}YzyUcio}timdB0{yfO10QZP2VID&kT;sz64ZMGL9)RvCv9==+rvVCK*80p zRX-W3-qiZ6^fZMR{AaRY872!kJaVHn(@po%pZ1;fr*TI;1iu%W-e(ZlnIK!ld}dSu zb+ph0{ttxY=r1wdKt2G^H$bwAn1O7KEeuJ;4DW;~Lt8^q6VRV&;Kmkqq_m1MlB6n3 zU&}{xaWcu!{m2_f#2~r%4}k7}Brs)XBWnv|$G_hTZhh-b|j#|N2y zphayFUA2w*f=QETu^*38ppR0$W18=v^Z+9S$8CaYRY$iZX!Nf7xjL=cN8IQq8osjIMcCqTdVAF=yF=CE2`{1k|bz_ z1lBfVg`!BmD>yb7kdF&u!*I<`*Tu(}>6{nnP4(Ef-z)j(HUDIoDamNbX6l5yO{gds z0k7%V47{_beb@G<0hC_$XAj7qA~j(oHOTFL9*Nta`A!5^0sbFq0`~h{F@u=SL8KUS|&jt&VeO)m3(smz8@PfF8bvZX7^PkVffxERwiou`&iS)PJ&cg(Jn(S;L*F=9&?dz|C*}Yb+`xD`iRsP}lM8F_al@L=>`0f?` z;Ug7OVrGDCJ9(tTLPD+cnD>p4t$HP4wkRcsRVOz-n50^ygXQ~|lF zQ{7#%dXe&>W*?WFwsS#F_FTT#r$BTGr$hlwzwYyY@U;H*a?6SOIgvQD*HcrX;cwcH zxgR9Ut3iL^T$2d&!~{*fM^YGF^g$};>%wZ|)hO1sbFp|>>e1Z0W0|h|aD)lLiUS)S zEoj8DpBDyh9*i6FydnNY@QG}E-Ujw$Og_(2 zQjOtisfQ zM3+pq3HPd6%(-D_)IHhq%@5MQ_*u8Z5r`+R8uU>_g3w+oM0#&a zUZpCT^PX%mBZxIT*L-Yb?$vl*5D}DHVbsO|AIkpO68vQbod*967}qd`^?_npgOlWc z1MB#G_Q~D!_zm&4IH1F%48$qK`Z39l*KCS<>FsXBd79KMhc-kR)Ifwz=o7m5*1|du z6ES#F7Rs|03O}GB!PbTCWmsnjNsJXs&zw`QG904ucTJzY4av zmuK{UofwSti^O20KN5rgjK}<%82s&j@INo(bI04cS-J0|1@>QLCjYNL!0#~l!pEVh z5Hjgw$s#;=yeztKXo;7W38rT~I@-cb`((#Ujg?@kV-5$@Gu081WpVH~b%dQjQEaJT zHgcC51UuAqDrKeMkhb=x%xGrBZRpS;&~aT_BVKN3FZ>=iF844v4|wHN)sJR>QE(ZT zycf-9x>LZFUwbwQ67*?6MY251s4UgkdVO12`Y068;Qu96;tEfbkHKzD>Nri}hUnld zwIGa$i|}#m2N$Yz<+EJ1Tk$v-iZRx=o#im?SC{QamEH+P5IS_@90iH#VkQQ`#zda_dk>FGgw~DtV{{;^D5saTK@wiU;yfx(duKg|~zv!Kqo2DkrqP5D{pmHVH&v z*%a1A(g17|ULPK=iTm`pHExlb_q(!EUw8i4_ev|9aJ8P24 zz!Y3>lzISRK%T!$o}dHTEvABOPht-T?Z8@hyH9mjBM3#0lq8ofZQQjEgUBCNp$=Yp z@oiZ4OBr^0oQ)?yku;G8Epw`2fA|bW+I@~^ zL{kAGpC0FbyB<@cIw0SG zkPzUo^dWz1Y}v^fua!RA6H5(>U|Ni)Qk}MY1^?x7$66J)4BMbfa1M{eSYh_zgD90V zuQev$lM4ME?3`+*(U!q@QCE4YzBq2X`OkxwF~q4_f|CY{AE9aYe`MS-V$`Z;GMgR=u9}eBu&brdUz!ki?D38{mRMbC29j|A<7b^=Q976lMK#f zXaM$>^0a$s?`7Ppl^`xj=P&=t!1@Ej#m_3q-+>Dh7u*_+NM}XrIlOQaUwu#b9R8X@ z`l6M-t63eY9<3mne`XOez<6YT5sAxPM2EcQMY_o2V*3vhBd!A!<6s}GHCUD{kdbH0 z+R6NtXJLw--6nI5l-;b4-EC9CX#f z$s6aJnd)J9l2eWBCqD0Z(r393RK6%N-nTcq&q%$w`viY?5;>((Mf3=HsQsm_nZ#ea zKmA#~NW|3Cf5O4$>%RWUBmUD-Qe_8_jm6#Gm-wb^e~mQc0kX3KSlPdM;P1isZTLU6 z%s**Mh78anz{-Lg$#dsu#!UjuEN)#&^T()#=JM(ee^$vvinyZ5Zi8I>@hcf12D-mV zd+E>VJUY-hIH*+sV4apX!jn}~{J+-*|FJC@4*XGQn6mcs-VV`rDg6^`%a9LUG)`;W zs?*TE1njGCS!(;?`MI*DH<~05uJcx&KmbIwLd7OjmRiQ)S+(DV`DhAc>Wf;Pz%tc; z4oE!Lf3mAGxp=f5NxW*@R_{M#2NC6dZ!MAq1VBsPk#pwn+rTCYQ?j}kVgK)4rA7Xo zSM;O23KGUIUeV9H00>~?{Am|_9sW-&@jJg>*Mp@}{Tq>mg_E3!)zgY)ez){jGoO7u za@pwLN0aJQG1+Y-&T`sYnh(dQ6a?#-LFJ-Rf7?3ri!>;?CtxF**6Syb<=^a?UI4H0o~U@%oj=9%GV9$*@a$Lh(}mn|j>Mv+U1uBESE>(iGSf8aOkEAhfUwL|GFiBX`$r5@-gg+e^~Mj zz(>^T(qeV3ePv*w!E;>)!a3V2y%$8{Q$!w1-z0h*&^VzQ=cdSnf9-E4KFtU2 zlp^{8Y=zlsyL;cE;${2IcGc^@| zU^Q+&dB|x@(M?1#`CTur4MqTkF}+YmrCoa*iyok&amOE*xndFHt+lq>MbFq6ss z%akcQUZ;uBFZUd`m`0s>rZ*_(XB73tVzWmudD_+OrPDNlb(#CJPa#tPBnB}11Qa*^ z1{C;GM!`jccuZ|U1o&${f5}H}@4&W~bEK<> zyuvN6f2LO#Xl-R7Jf8FZpwq&tdD#a3_@GSL1;NAH11C>CMKlcvzhHnK4Gr-57$Z5F zg*n%Qvj&|K)kRrPFpLRXBYaknEfVqyYQ!p~m)?*4y8RPO!NQ~NU;)9{n{2ud4NdUo zc!aoO_mZ*l9VwN+eNL>klI#bfb*ht~aB7Vu|U_g#pzur>RcqTcDF z?0;&a--iD~tNcz>tqU4t!(p~cvbHjOn7dOj?4BgQp5&J{pl%`#iZwG?fmwabC3DJ> z5fntsbZC$TP#g>jUSS+2&c5L+XLIEcpW#Ts7vS9VK$)48f6BA8h@8E8$y?>FJ$XF! zd{avUKfv0qkz%v@JoIc zFlZN29(zwMf1;9&$t4O^V6XX%AasxE!gD_gVYSpW9B)A6d_kPkaCpU4ve*0cIKiLu zdY=8j!RH7GyR{DMS40JtWNagI?u~7}aNP?JWi(}{uHIi%82L9u)o{%kY>#JeuBT=* zGT7f_*0EwLloT%Qww1J(H2gv@=BXyE(?`?eO@B)G@4Ns|fBP~YD3Jvm6}lH4?KzxSX5 z&MJ_Wv{HavFX%v%XkCjLLCAV$^kf~r&RPu&;RBXJsIyJuqZI80n=!E5;|%GxPOich z&JSLpf5EBvSow?0`8uu7anIwFhwQb^Xw0MrdU9cTy+;{!t?_tG>%Fhz?Edt9HJJWE_UwFnF_IFu1(-_r(D3ns+oOh9^kQC!(R|0__ry28*TZy9VVp+8?tA`Yjoe^xGxFtxD0*Ta2Qw2Z;`@y$DGr2&fA*Nw_bBebUvlcix2lnP{m+GRh*8(ml3!7t zCdK|op!#7)eN=G_g)-EEe2Z{B(Sof{*Xn7W_DE)|j)+Uam&RKXMpx`@2(zGL4YjvY zVrfGceR?@rDrWU!)OdL~){GV9!~u5IOyT1RUzM0WT=42d}bE7JoBu%3aE)A$?#&z!r@hC1k*rHQsOI!|sEezwzeXuKBLf3c-7-ZuEtyj}F?iYq5=c&Yj^VXD<6LIcA&q!>1IyuGO2{9mkGxQ%By z4W4D_JqGiuVbStE4Lzq43)!$Bc$gly{A6{830cwZv{z=f;Rs)&ilM;?b7%OHdKJHg zD23UQ?@>~?l*TPpZlb+vf8bn@6K1DbHk3KDgg7+MeveuL~%OU;XMARCBb#`xmmjm2PG881`C0$!Wa`h9lh7G%gKwk4^Z zYO83f*^GDoqY{#=DfXd@5GXQY!41}qFHN=$MJoCx)Rsb7ddO?2e+P54)Xa0P+UQxj z)Crx14RC@X)OjL(q5n$>ZkhUEOJ(5G00!=`{3aXO%tQ3}z+d5Bjgwc{tT!8&AsBOq{7{xHgqS){Xdo5HJ3EgD-nO^VY>(>1Q zJ65N|_yCsH08x)hfBHsJZ-rML>JQ#vZMS~0OlG!r${8lCJM0E82p(||taL0#%y34s zk!Xxbsz~9g&mD=yl0sSC3|xr4{v-nKI!IG$6wG$W1^4O6uq(`BFCI~t@UXWe(JgV$ z^1EA#`L`F*>WZZh8&c?!g2&gbo^;eNOAU>?A^Ed=ZvXx zuFV?(yY`pP5sfwmW;MjU&090i>>wh|kQJykpUjb7y_QvuuBL6d$Nlmh?icQG-~CT< zAN#)v9##P08+hpcyzIXX_y2QG{M$6vz1NUer?FQM)pj~L(z99aEBTF(h8nn?QVrAn z0vycYVqfwse=-&$@UJmQGwWnrS?M&cn|GrY$S)k;(-@}ipK1r~+Tt~bGrkYfnRL3e z((ev0LVFl4seat#O!(1vO|44qOx-~f0`CmxiPjD=Ev!>_mUNqS-ErZIBNHz7gpOTF zJ>O*;Re7W5LnMgCFL|>ruiU|&8afSA2M8M<)i_wnVr_&2%FCSEvmAFx|yt>r|7P^)}o9DU1Vn@lR3 z0KVH4Vde>3F=LKOhNNZ|%aR42qSPb3Y+T{;C2s*MK=FKdY4wt?YusH$aVYNC3>L4K zU(y2^+`IeHhRw%d08Tn(7upA?FCPwlC(00Re?^J7_LA03v>uSUa*wkLa{D`{I9`~? zRu8-*>E6A_^cnW4K7eIvxN7e&Y#&L5mVLF2OA8U#s^$hy_GEn2n`jpMMX8~{2m%=S zRgdQYyom%NpRoTIZ!gx9r~Qv&yy^Gjv>Z*&Ii2*V@=i>lnSFV%O6f0^BV&Rs?z`+l zfA-y>y!#I2mH!mwf&V4U;D2pbb%z;rDLZjP6BBo4V>_GgKmh_+z5#_+%8vBw$^SmG z|4%&S@6g=-#0c)-LW}(iK_70x7eARtTjYTl#oeT0;Cw*`AA@CbXtqy-2o!Vc=Ix*M zu!2`Z3&JYyA73c3XaUGK%JDR;R{UVEe~z%7{diP7*Uvc`oD@r=12EPagi0X;FY?)}x_rT)T9{yDp+W_1h%fFR#aJqqLa|iA>z|H7*G*WurAyq9oeeU-If*7Xc(c{EiIp zv4v#q2dV!Ont9WMVbRQJ8$O-3LW|gvzD}A$UHlcA)8=*EC(l#0%peU9eE491&P;iv zF-oA&x-Z3jxqPNx?!22{~}A|yvsFB+K{ z(97dq(D{2S=R>RWC_etD!IpVOzA`I_yv}yLon5&9nZw|KsgHUIZ)-6 z=Imydn?Fd?d>i|K7rwAn{2B&}>vhH&Yr!7(=8VY^qYvg4#CFcmEvz}te>`~{H+Wsyj z!D*_{JDtF;5raokI4!ZNOA7+^dxWj5h_^ldvQ2n$Z=^o~1VA6$A$se;GBA1bHYno|5Hw=CXQ-2wXQh{!1m_SOe-;YTu;%>pLytXe zsa?Y#RZi_7)rfdF+S<%}xQT4Rg+1^IWi~}OwpP33&vNK^#eDmwaRJ$7m3R6}oq1dS z$*RBYYe_rZ=no2S)zriB*?w@g1@_KDQ&vy*Zq(&oM!Lr;{A=o>RAg{{9MX@^$e*OVo zTtUI4V;NaHl>AjCLBS1mJ=RglRS@3RZeCcOxJ6LlQTV76Oz9xv)SE?`U}frloTfgH zPz^?WaANB=j7qeMddd^-_`E5(xr3Kui&FImul!H7tcLMaf48Es24n|H?Znp!M243` zO8Q4Eb6&|u!?0=R2d~F*}}?cN?ae=Ij#hW$Eutx`Th5GQ`3+Gsss-4Ch|(kHro&Nm-5RS9Vv`Eq)6-NA>j z*Y#E>9ksCvT5|p18TPJJA}iH z1}FuP__p^1f7*L@1Y1l-l2il$WMkuEFg6v2o?(Q|RFaa5o0@?58 z2i(UAlm2bS|Ag`p5rR;j9a4GL#SgDlSAT3(cd~~5RT_|6F%q_Hj(iwz#{fzZws%gm zv_5Vuf3gwh=@6b(Mfv1uym!@;BcWOpx8RKALK-qDQCTl(-V+J0TBH~iw~Azp#kbeq zP55#0}bJ_m%%3)b+JhrrhX zip?+2+uG@ag*t=_c(tyy%*CW@=64(^H{yNO3LE?c zvkLg-9o}emLG#AhH`C3c6Hm%pfT8tW; z!`MJQ9Fo_0rbTvE?RJ0BZ-!}zxr`HmNVqrg0OqRibsH8C{#dlPVPSfO#%0L@<5J1>&Jf^{%dUD|XvbbJdf&FA%`f1+!Vs2`VfiXjtNPSDqkrdgkG&q5(RDHl-jL4{p1 zhJL)&o@Do$E})}sPUXdG{+E?pvXA=@h0Bc3E;6X3}6#1#hrj7E#V2E3UX8QvBV;cLfjCj zPbrQ>^FLQAJi2*KKMtMce-4*7aPK|qjjJ&`#>GzLy^@+4bxQ$~v@js)>!wv2hig-k zy38zHmn#;Nmu2aFq>1o0rz_ZqXqrQRiNlnZg+VF#rF&&2C&srY*2zY@X4B!MVxJPN zk_B__mMLBGiBy~D(o699W8ZPwSfG~9cppS@-NSy{)Jw5$IBL6yf8&?eIkaWSlM+W5 zyhJk{Zm}X}{KQEnRi235o3r3yJou?0CL)u3p-8qDsBc@PG23|*CbC5uVpE8WQxa#B zVcdY`*<3Jue&G^^JQO_y;ssdigl7qWm*08BCxHL%SHa^O!G#0B+?BGM)9o!7jC*( z4>oj6QaD+0cZ*nEr&q-rI?(C;nguLvy)wotuR5m+KAO;ZjV^N&2Qb{yfSzAsV|6h< z30kkey(pAwHDP^uN)?n(BYHym;td8G`Vbmci)bAEnD}xE^$t+_!8QF{AIz{5ajD#7 zDDAmBdx9XdfB5EUtWqq85LGSDdt>@ID^mTghYu7}8R;vsQWvEU3ry$Vj0Gkao0eo# zp>83v#e)#EJ7?7%EIjX2L?NNpLw;Wyy*ue|zc{8u34LU(cp@)j8a;YDhDaqMeWMce zR`j_a)bNOpiOkyJQvPnWd1zw6b()8jIGe^}$Xra3THYYWl>5|G$ z=x?-|e|v%sv53cbOPRK2tSBo|ABjsIo%yR4w_YoR|9B ztcwE7m!M!e;e_e#F;9b?eHBaTwvf0zRz6xiJ#aI#0LaQT1N2;_f+Pa$I9 zd@H-AYGL+AKKz|j^N{VQ`rm(E@rNw`^TF?|fepwF{Gqn9{yh92UhQ}4_aK!^oI8vg zIrd3MU_($pZT2;^3wn2Dn8TGMgji2aL>81juo&drz%pJt4d)k z=}`!vA)a1S>ZeNAMw4`pcKz2FpE8Utk*z z`;1wFvPwbu!Z3TnI$=T4`lOaK4? z|Nj60AphUnGBcBxL1d?h3I;{iyDzL(N#M`YHW=ARc=@QtLWJM(F^0JH8M0uu?9Z#) zEd}jE!z;AX&N#dqH&O->?EGvKqW}V+5bkhx_@lD_e;Xe$C`BnD zynk8eQCb~{{`Cz&!6LzlI-3~UgB+cJ*Z|CLS_`s*g|UO3BgmLk-pPddr_vRF90xI} zdfe@QyFYMqUx)LnXZgpX@Zi{JUzH;eljGM*na!QQgOd};3A|I=f!seLp8?;8|2H1; zcbMKfi#;-~oC}#L`5<~Ze;mJ9OnsJCWNcrVQPs~T>}```n};I4zl@QUB>FJD)j8OS z@fiwHVgqht6yvku%g0Ze=XP&4ePY*Gh-#iWht4$St@911$x4m{;(2%EJs?cBkv*R2 zDNd+=1bN5rft*UwO`*jka~Llz3`R$mL-#!ZJhe=Q?~UkTnx_Iq_z z$W%{Q>D#nRt_aF5twCip8Hi0jnWgS>P6z{>!F<`=2QE--9jGP$>i z^%wQuB^^J2&wrv>e-5C&T@n==6d0(NrpUwhD5ihOZvK~w6u-QH;G43I33%ZDk1y&M zB%yBf2d(rw-C}5;+{Te$;ZT8 z1#6Vj}J+0+j%1y`XD5@hJ6WI0xBkD)%%(A1_C2{L;=n|(L>g055tdR^8&-YrF|Li z=O2kr+i^ele{~x#sKj?}A$-m06-#VG*y$`;#Q|M>`dEN$U*mIM06CL~TDg6vH#sQ4 z{=Pf|MTBwCe|!V-8Elc-m=c8>aoD3vnqy>W zg*ews6;mW7Yx7=Tc9)tU=aL>>>pFNv3F(8IIbo%&Lhy&qR}Fy2yg4#`8!T}Q@s_vV zsOI@aW_#hs2|vKoBjkp}*wTp^&chd15&evLSZv9_xekKEeWSDxW1;qiMEbyIF3lJu zjqemie}iMmH<%|pa^GI7u4;2x-l`Bu6ktLKQpIp-ls$N3PFinGqWC<>Y z`Cb6iIc$3)VaCDY50l_P^407xU>@NspL-y+h9{>sOIT|^J{1n>9-?^=Iq3PW5uM-2 z0G2|KLD)9Q58kGs*3Q+A15Wu8BD7mD{8uq6e>!IhN;O3x6TD*w#_73j@TZ!Mi~mvX z-ygZRdED5h%q1EmWSKA*M0?HT-Q3hOib}f$zV3^XWO<>kv9NIIHHF>JQCXiNdYRHS zWO!3Olo2bJXJ)YVuz!w?vI=2+Df$sNOJYX7cav{nWBHgFk&ykd^&{EKs4!*yWWx5n ze{%AAP;GEaHfmGlx(7joGA~gp@+(g@WyEID=jyq*{*|D#Pi8*S=6Y`pCe_*0weF|(OEk;HZUbF~}jPU;AM+$MC_j%+~ zEs%%zsAIoF9pDaixZfL*LT>&Yc2Ym{Hn~3-83wYnovoSG-G@}!?x$P=44l6l|AUDE zzt;!g0CL=Y*nsz;m8{=}>>RADJlg+r3;hmM6}Q|CTc!{)tNC@yap%IZ?&mA5f9^LB zt^1S-MC7M)!44KT(?akQ3-R?3nIqBK%j%uVA4=f;QN&O_qnKVlH)RHyG zwMb}}NJs^2@;Tcma;t6Yt7(LIRN=SFEjOKN-hdv*w0m{B1r23mU!5>3kA|+x8W~%tPrSkSc@0o7l|194OG+s%3o;wkbgtc+g||(e?@YVziK6P zR?j~Y;;9H+l>Do?`F$FKfDKEDUF=Au;M}7c`&R9gmVR5CeaT4M4SMF3T0FL$x}0Y< zT>DR-r8Y1(n9)oW%UlyCf?TpiS>BUdrz;(5WARi!nPyPgv>|-jF-w1(7vMSBCz4MA z?!>JgiQ3F;mQM=BHJ6Dve~v@;AvV>W-7ZKPf|QNmBfEG`y#2ACUXa)ux-shqm}`d7 z6=-qULCP;br`Sp4L>%>7D_x-TVqr;k4v_@vFo`_aQ#KR95Fjg0iYQa`dR={4GF4B* zl3H&2foNrvkdZ`u<*F?{FY|JWPSr6aM|DOFMR&?L;0ckRQ~&7-e}vVn_Pqp)cPGJO z-2vzN8*pG`L>Sk=(EdSuE%~iv_-7XaMUa!cov{_EoT(EtkPE=^Roz2kRx`BiY;o+Y zbaLs;vvs$!Ff{3`wIZecHP@m0QMdvUiSfsLA`XVO?sqqQEvhMHXKg}9D%l z8GD_Q)w9(#|e^GiqMYWN~bq#-|2Rq0mRfpvz9#-&~Mes>lN$DYbNohkdS2LS{m*t5t234quy;1BgLMB%{;v&7F0O(R7e z#7C;vQL0OU(<#3aX3Kr|39(Bv`BlJjJJ8{vOVU2tfrs6_`h#ZNO4-UAe_@IwmP=(P^Yw5=6@I6BY47YpxzB|YYZ0(b@ zB`rQTRB0h&@oX!?GR~Vw4n4?jJ_w*nJD=l%29rsG3=-dH7XuDEM^cR* z7n__Uf1H*>?+9!aA#xhc4Z`SiMmFdHo9Hj~PM<7^Q)hR5M2@dxc24$Fv6KT8zc9*6 zJjj@N*JbQ1q$O2-&sO>G*y_FUo%B0(AccX(wuksTYvub1QV*4WKnei^?!I=2s*IH4 zk6v+z7-&BZkv>#XQu~pP%L(8Dumid76h!uKf8}=m9RBwv{~d-J*_wTNsbpj7=j&(* z$~5CPN6E)cTE-JMCn`b=g)I#<&DDYwURkO^K;JH9q|;aF!p6#W()xb7WVmoY=&AgS zrb-zXY1;QJto*rl0oYbsi{!qJqbDOyfWMv*V;%&kwzZo9%%|%Ll1&qNZ{^~huEZ4g ze-KAQ&Fhg&f7!CN8t}(NvHEFU)s^#MWIJ(i0e(k-0GJ87HrD)tJH?i_M z8Is!~CokZfI!D6T8oZ{Rmfi=AilI33i^;p@<@j%7Luf3k)?&FfQPnO>}5tNm96XaCMW{{+y7sEm0k znc4%dkf|EcP{a|d+}X8Xu~)${!f}19h4{4hc8GIA@Vaie<>0b zvDIC2?cHsL$Vi4+m|o$nh5^sC@crR?Q;VH?47zj%HDhX=uL&UF=e6t{5-q*}MW({I z+e2$wvE9$h*4o0T77S@92TLx{J$~Ml3Z}OPGxjR$?@-eF zYlVnEYnzIhJKLI>yBd<-x5zNIvmq6;V`ltjd-+}<0uhM}q%0@%`wA88fA@7&0r#;~ zUt2%_Jp5l@|95`8Wh&JD==m@+xTg$lH+HlSKdzQ=P)$A5ar! z_s>4aPv)Q7GHZv+HCde!e{GcblzM!6GF>l=1@XxH=_5IVS3}dt=@@LPqlV`mlhbas z>1Nc#lf2SjvRp5sbFZOy$ zM<25#r#PBYN?5DifxCv&n(H=gR2M}Hq1@t2M-7yOhZ#aG;83oIIk_AOt;BhCOz`@L1(KL{bFQ|J2eb%F6e80N*rrcS!6s9 zUqV7IDne|R79lu&e=a2K;r^lsJYg(L01m4N4VcC9=@XWEe|?T1zPx&?)b5tPu{LCj#vd{#YO}Sr21-+pVE_qZz?VH-+eT zX6tLOV;U(;SF+-q*`JnjGDM^&HD~P(MpU0sg+JDAV2L8Je{rWQd1&4l#Ia%;ZHz*t zFo{?5(VeS=JWK6+a)%|r{3m)tjP#|!>~J}O8eXD9orl>~NK`ieYYku`j32~`2L{TF z9u^D=2w(v)-N!V-!$32jKtkZ(K?)WI+Ke9ZPSnMec=4tOLBnfb#|6bI-^@pf@X~5`wYNaO{jQmA<6&*K~sE&)lZnc&Jz3zLT8 zZGWQa5UArprSddac2dto((=|85J{qX2=_sX5etYnf7uSJ{`TdA`qX3nz~GiAuV=0O z(OnP_UbF|Ke%ThdxY%VXrA^|~SQt}buIgxo!-{f!$euElQ87*wV~EP(nNh`KfoNnP zOv)Mi{E~B@2~L)%sXgQI*>({@u4D1cne7KQD1GxXn$W%3*KwJ9t*e$C&yAPLac5gT zZpm&de@CDiCK9hO;jI9YI1+SG8epH}`Nc}bsmL)aECjENAv{FDNx#B>p$xGOUSP~c zCNJKVp^}D<;+P2&tHxYHt0&GAs=>t78Z4f)j3mdH*_jZ`Za9G2lp!nj@P?h&yJ?H= zy-R5?;0*JL-&5F`!$A}hd~BH|s+hac9p$@E>H2Cn`y<;61N+Z2 z-@6-qC6wR9pF4f!PCfbIFa!QH{O?WvJMrhN_f;#VvMI*x=W+L4io-_yo|xc+2ad&f zWoYx6`5-54S3wnTjnnrO4HJ9%kE*#8ephr^cejXx^^ea^^e*u1D4k>zlMF~EOL-^95LsAa>M5f9|GGfFjIu8lr z-Tu`^Y?GDgk*_#dyD1eW!vY$B2sTAX;nb=rw$T%*hCd%gjn>^I03Ko8s;(7XYSqv0 z2r1wUpC#e7Kp6?|l5pF*K7e^r0>IU!qWRt*(!U7cpZ}kD$Qs~0kv#@0NaaH`SoMa> zZG)NYH332}U)#EI-wCtqxvaT>e-P$BqXSr(?Y~xa{N7jXYiNLjHH)#+-M{;$YF}HZ z{cMf@U}iC;%9kc$Wf^vX>q}2PYSjQfP(<$~`%sG> z51Owl4Oevj0OJET6lLu%X)AKcCRRdCck>A%}b@8q68WgE$dKULonH@C2Hw{x;^ z{GnYyBEfwXV9aa{zs0=*I9Yjs96&a}eXtYj&%^)XtiMBMulYgWf7DbhyEAnF@ort) z_Ty#hJ#;C<<$Nz}H(|0hgKX_yj8Vuw6TPS}I$5L0n@TDj`8}QtVUe zUaBf+*}4s5#-k`~RiDR~==gB2C5>{(n-1PYTfD#EeFU@ge;t{x0RGMY3W@l8H~&v{ zZylD^wyqCLw{&+mymUx+N_TfigQRqKOGr0@bT@*ubazWhBO&pDv6imo-kyEVKKnbr zKj6CNJI8#-gfZ^>dB&6c98pyyYAry=@)N?J)H&u60ft|Jqy^CM`sIL3_8iv#-m>}p zu(|%q7l`0cf4Tu;Kmiv5KtK;4J%acZVEsYxVr)MNl&)m!rR|=y!5`<2&&F>~wwSVV zdsyL1g&d5)6IyEL${P$KT^vx&TJN&mA_I5#Kj9&IHmgVHIc~?j09e73;=>GspGq0= z1-K=u&AWHi!%|9RGG*^E}#f4JcmCk}EJKP>=ZX*P& z-(P}j%AfyQWlP~T#3=Zu2^4UcXprYL7IxKo@yT$Z^>81~mK5JFZca}^K_(b%w9}Q< z3X!#uwUsIX(w;`f4HS4JikkbZ+odqztbo()wn)G_fTIb{6VbV|G|HwyqsN&21M?T8`k@x%u zm5x9}-RPh9`k?xMe!d=!HeRHkbjBkO13t+A$TMLDnuH4%2y~x&G#n}ccKP}G)n}&= zsxuH>N!Cy_Vzf6+vOZnd)WM|mAx(I|N(&0Z4ln@aG?q&|pUE{!M6}Yt9r@MMT5Ak8 zFNNC$f@PLu){$AT`+o5-zL#-Ru(tSCNM4FgicXM@0RW);x`yO`AM(rmqVI~~ z4+&6T%hY|}aQ}&S_??tmZ}Nx%SawZEP{TBd3@($uTwJ`oLK@>@vabZCZ?X;Be@L$( z(&f?78icy@fyGXilf@znUYO9+!kkBfr-jZE)IuL-jv?5wA7ZBgCxddeJHL`;ym%Ws zW|l=f$X`CNK7r3V62f^AX0VE)X7&+o%=6h+9ms}^`9`9FnHX3L-5pcSc7mGOg$ZU! ztlmo@)G@m<-X?SRJu%Plj-uDLf7A+Q{0hv~W%cjCp*zcj&-k^hf`#3-n0t)?U=#ynQ_vcl`s`_NkhYQ?;vxdN?WTvTFhzdz*@`Zz8yFdcC8omF#ju5Y4sl z07h9-yxikG+`6Eg~DfUVc zU!KgnS-0f}9V<9h>S~viP|UK>SThTck*zH5X~>X2LwnII1d8EPe;Mq8j$q5}nuYfC z4CsB1g^_8dxTB_E?{pKZiA8Q)_4q)2vpI`0;o@}1?&tm-U8ypqj0`uGiU*$;A;1R| z_a5?K-{Ah#n>NJ9Xc-20pN9olWP|%}b@09a*SP(A+!Qk2_j~_D{`@mK_`mRuzpVhx z7aD4}7L9!_*N&|Sf0{T}*xJ4sqV**3b7RmT+!{SE_{+mAO4+26xhI(_We#$-?>`De zOY`3r_Dq`xI0-eX87IhUSB=xy2o24$#S55m0Xl6fA|NdxnjkQWar=L zA_CwY`U73Sf2eMRc1fYK-8$>7T13}F9(F78Rf z+R)lS?+aUeU9_Lp?6)kzlbH7E6ZZBLN3C-St{sN>Sm3&#&n|E8>$8ID1Em*)@bHi{>X z&M=kpf7Cxm;grViZs3fYBxr#?Zh`U5L-(x*AZ5H`LItab``iuYxHc^je$9-Y-9<>z z0uW}v>vnEY)nhMyvI=DMpcJ&-Gepz9QjqhFB_NcEa<;}pf~8N9dk!yla{tN_5dHHcZxAE0N5Bfm>C(_|Ey8V!NT}Z zY3ScM*Y8kU%0f3?Y@uWZZo7eON>V8^JwPaWaiq&091ODEyrV6m05z|9roMG9Oa;8l zpf^RyBsJLm0-DVOzJW1u1vjBIvU|Rl{pHCV*0tP6)2?BwoEIb`B?3J!Wew<-Z=c^L zf9C6m)5WiGFd0VR<>*9>ns8Oj-165eJU642!qn>K=}u)5Pz7%59Iy ze)l#XQWs7a6yc>y8XEIvVa*k(Fh>ub(Piw}>m&L3q(tuA04!J*rYp%@P;gWn2CswH z7|-6iP0bEoG7(IiHJdZhdV_v0(;QF9e_qwZT`LrLuI)DNbikq5&a4>RcAO{#Pde%zKRbx?fqbvO8gPyi_AKfcPH3Aa(a3&iDpm*uw}-NVMEH0-lGv*V zm&8Yc&JoRIyxp?mTdJ=aJaSEViW`yTs$t$nmn?2cWgqd-*)Y-s$q0a$>s2JH&;=2lD6wd3YoiVs&^ zu=HO9u;RZ0V8BNii4|2&d4D^N>bz_tjJ!sOB`+t+mE*PVa)i%mfO-Y5kvVStWkyJWM#iQqd zli}4E%CqX|n#`oU19O1+U_v|!c^$M}66(1EIA!y=>0+FvO%nnDEcPK5N13lj@Ay7R zo*IjQE9M~VU zz3501Z0bI5V*Bvawm~v7*B)2XDzOAarG%YLkRF{Izh}@Cp?}@E1QUcB;flt^_tRFr zX}!`hs8Bw~uRDU1l)}10Oqh~QTy?m z$M1diW-=xAK2=C=2>WkZI>&<2rTAE~7Irci;s^M`P@S?sJNGIL@t28i1JaXP^a{ZJ z>M2}x*k`Z{*~@%A?b|z^h*9~#6TYe&C}8RE!}hJ@X@ALyHv{4fe&h@~(|ekPS)}Zt z5gNdO8l8hc1I6g2TN&F%Rf7SO>Jct}j3D88IUz!i8IzrqPgdL*#&8JhCVPngSzEWJFz+KnfYgBQzZgLG_l{Ec z)MjU7V{QLX=D>x{{$YFD-*4|P{Prtvx_y`TGWjSi{Bk^CHJRU2mPSWMU#~F0+zbQO zL3~onZ--wV!&>!?tTKUlS2Kl~YV>?GB1<7BZWC6nZrU`yr*O2{j7OuhT{}2D)nKH* zjelNo#(S7L4oYP|#-{;|+?`Z2Q}}lKw{n?8{N=X523S62`bF9*NccGF_Kanu>GS-jjAWg*U>bb zpW`ODTB;RNMY=6^1O_7{iQ7B(a5u}bI8aR-d6;2SH9l!3txHztw$qC;4%~;Glz$#a zbbv9xg#o$FR#FMRmFO?gKnLqN4ku<%U>~J5=k=)%hcrLe=6A5_K=gnprqqq*W$#l# zfNKWOcjs>MXUvv>1tw?Y7+FX`!3HOr$y4xbSMy7KyMHw(ncY{9fryXN8(+-NTOcNoRvcFupp1f3nS}YC z4emfpC!6E#twT3bN$o=Eat!wAPt#dFuz1Toi#Ob}c-D6xT_Yck;F-2e*%SZ{P-b^_Ln3MU}E`^#2+sIUDN!I!b{ipJwizyBUu&7(xM4HiHXtdtAEq-ixEJk zYBEys;p0&uc`qAEN>yupeULTWh7U|4f&4O(7I35CF1S~Ux(tcNdbMiDNkI9{2p0y&<+c~GxP4jGGu&Gno zUC!!9#l-jFr)*bpQTLw!A%C~354BfA>ozY`CmAZ#WxTAkUL|vi4qMho08VFy&zXo} zl3Q#EZ}4-~&LlEVDhy} z-9%c9<_QS~$aB4OF05|;oD%QkgD*)wq&gM)+$rJY+L}?10FxY7l7Edqn*~+cXov2$ z1h7R>t*42akMx)lyRCT>ftECOFwX5D<7sb^LgHXZ_YvI~23Wxf>vl zog>I71P&o!^a&75IDbU03PA=y*9)Cne<->Ex!HjJH&CEQa^E z;i;l!;_^xsR+f|OP^Ur6o+xJO?&h<>_Wve@YmmCV%rTPlsDI-)X>&H#-s1dlxc?J{ z|8l^uC>;3TL*e8NYjH1*O4=6DH#epy&J@dhPaEHBgSpA1KQ}Jr+EVtowsU3&nW5Il zepG+4t^0mpP%flfmceR8!UKceOs$67(3^VA*Kd<3Fv5L#A2>;`4)3&%ItU5P?=3?t z`%8wr`X>s!9e*UT${J=dd+*vC*&nU#p%>Q8cfSDgO=lEskSAisWpss7&VND5TF5Oq z+vZ~p-H6l^C`pcZiAUQV%bPZ*L-tI5hA!j?rAsIrwXn9ec`*-bzl8R)m=pSK<6bGo z8I{~2hhIXxg%x%Ob*F=_3uzfN`O=d1D94bnyMW|UC4UXxyDqIhCC-c#v*gIxgNib) z^gs(FY9kv|c?K+!A!`O=S^O}WNa79$eghn(0+a=wWjWcQBW!^3LQxMW}kFg#S5 z`F|fz@Vj&KQt5VrETpeW)quXyrzoGj?lGEYfIf}AnGW}EzIF_h(B&;AM{%k(UPAHM z;-`;GdBpmS_OHhX;f+SAW*8&GHqM>Okld={+k&$T8$R08xx#`o`eu2Apa9+@W~@-L z*SkP7+{iY287UrMs`UXRj?{yN`IBL z4enRzhrn9@t>S&1`QbR+{e%A=V|oA2&Bmg6h5ksh5O1Htn2od5 zx0_e811ck)Kx=JAO=y${j;A?9GQ$CX5Wy$K0Snh75o@6zS+bHqXgzv+;MzTR&V1u` z^Je+!LhU2YY8&y=ax%zyqkPHnMH3_Mj#9Xw$5L)fG8H-+?{S($i$N6O=?w(ZcQ9* zo#)`Plu?j5NErOH3V`nqfGZ2K5KX{PQIB>F4Rl|jH3jt|ZH~G}hhB0shJPDXZNDg2 zaol-VL&4FCYJ}7C$?2szbhs1DbTkZT1jHO=4fKAh4+{&zFG_JD02gbLnZxkCiiB}F zE)gMh`YWU?el{8XskJ<8{|5vAm#Dx$(;*(}(iq;~`qIMnp4)$jB>3YAF*6fW0)Bh@ z=XQEl21W$J%HI+4-GKGY;eYhC5AqL}|KYd!9ay^tHtaV)k2h}6(lX{PZFuwcz9$v0 zv8C#mh+{U%g2pM+G0WpC@2ste98SzB!krllU-KB zi2*yjvYSLWIltKQ5sEr!Z3FVI9CRW-KhaYoy+Ch%**q7`;6+Rcl?l2{EgS_JkHq&_ z;AlCf_QTAe$eNk9W9l!I=Clo0*a88{pc{x)lwL?jE*wEwz=$u%jcbEyosavV3)qXG z4d1pJYsLGuXOiLtYJXwL!Vn-0@Hbk)$g$T+_J0Md0YL9B*!-skt#Y0YGmwi`QK0ce zGAu~o0|P_qj-M2bUk>;MnsKne6XvH<(z$|+!68)$=LkhFs7zkBVlX=LA*i&6oVd;) z8bz>NrCkeGr_D$)NY+Jf3DQJVIuFjRDY)29ZV<)+%_;>ZlYba~3Nap7OksOpz+7(j zinu4unTXm(a7{^+K@aml^_@TcQ^2PT1~lTH535FKO#Q7`*csdxecGW6p}LU4w6Nfs z^0l3p(1|0cI_XF)FIWxOn}u>b*U6}%I}^;{w!>E25LD`V9Fd}xj>XKTo2Y;_;DyEs z80JnJY{;~tbbr&DB+;ShtNQT-O?x58+ebCecL?iEky5x)TGOt*G+!CK-xGaJN^X+D zcJj!_U2&@LVw(;*+i445;sMQ-_h>G?M{~kAG($dFR*n)7DbIXv-m4bfE%8@0>-|sh z^JfO}A5Z)c{QF;l@PFVHeuvxDCo70{iv>Aj*($GPvwtFCoT7TOa`%x2UupnB5exvu z=0640Dx3>;nmtlUT@5E%2n6SQtbHJer^gw|`IPyRCn zLI|pig@4a6CPuPaL|4hpPm`H0#36M<$91YW{aQ0$OGvr%49VzNIBI7|vPGxO6Kz@! zqF_as_usy+L@`9~)}TydY(3t;8iNrSFRJBdoP`}fxbU863|Hl?#@-UA$g-Jb09`n* zKs%O#ehN|WQG#2$+A}+acbp{BQ&m@~)!O)oKhje82=llo#*ig0dx;gW9UM-&=%OtaZ#{l*RV;+gPA@*Y zBFsZo&#ExQDGPp1x7Dd4ZlOnrF#-CskJ8DJshLbZ~n;yTxj>MaZ3Dy%VVl zpbeWOdu4umN%Tc^6$vC5V2`#Lrd7CAwtv3xWH%8A=}k{iS0b}cI5bz+_C$QV<>CWw z8}D&jdym`n?=GSb%Nyv^BDawny4To;0FJ-n*64oWc~EAxFPJFJA-^jKb^-KwR8I$BO0 zWl6$O-@(|;rs)nxM6q-^D+Ll{4u5S}LidQjU`S-LAleQbW>n8=K=wiW$bp`k#A%ng zd6hFWfzI%}#NVB$gcnF8;1h*QPx zzTAb>!EloKZ{b&XH>>nCrr(EwRxUI@pD^{rvIqVr{Qh#lFYuf7xM^win@)%7uMUvXj81Qs)_0?{4iE|(&t zX%lb1B~K`>xXn5;8iHo-N{Zlfojan{1V8+3ql3WZrFIkXO0C@rD5_nukIpUbywoSP zWGt3C!9X?Q8tFh_uZp-y+kXdYJl`H!2XtK-n(lhj#_bB!UWMT;=pVJnn$%h}pB&*o zgJp$4^JIHbsA1`Pge0QtJ4FuKA~~4Q(qTenin!$U0hYckV@00HkR9GleA zA~LsD52$=Jn=BK-HucSF$7M&7TrD*OZ=-_ehk|G_u$0V4Q2Y~dbY3;8wi znX%_~+cpo>{|zjn;nJY=K@SKTXRf^T!;Z|^@o6h+g{aRYIXr%cXrKRCjN7DL+o7zXlD=9={CEW!9M!jP_xjG3F|-tM|D{;p9Q+d2#@5WbFD16NCHC zR*nlmr_;jiWM82LZqY%koJ{Rc7K4)yT)EB*R|@IkQ^(4^lYcVCQrTqIcQ%l^bz>Ng z?!{!KEuodvix*gxiq$q@h0H>%z4S_l`(X0i@$C*zqca?ifXOq^>5Iu(>(FDN44@)l zAdqbDcL49VKcKQzr*>p;0YBhWL#cRv3gPYJ!k`ZT@0hQ!^bP@d2YUT-Or}MS`G0O{ zeeO5-&&iL!;C}_MRNkP}kzOpN%wn^X6Sdh_K!I2?DutOYP*ij0^c8pm-P-2UzJZwu z2jVO|PT>5i`mGVFb#`wL8S9Sy3Qhd|irA}$Su8mpUXUymkGl+br*ECep zsGj-yT6Cmik6;L}a7{1K>rR_PKT@bINTY?K`}R~Ab$`>k=Du!H2(VmG$?3Zw;D<3i zJpJs@qO9fWa(0R9zeXG1a%LMPI1rAX{0KUba$AW=^%3#T8j>KThA8Ah7{9p@c)O)Vs{0o$D z{~PcFC49O3&%DI%*kCbrYDWb_JX2$LN=GbUr)A-8{lEn0)X20_#Wq9Rsqy10@ogI~ zZe3fMl;ul9aK08FI!jF;T?ajoj$hyJA7*$EP)naIirFSVhr_TA72A_hks)Oe}K1- z*DuFpn&cS$M^^Q}L(bq^_nST&r5l$IQ;3%3=co^r9dEC&Lmw{f0R%Yuj z(E)eN(FS(x`vW*Y*|>2OISij%I~AusZ7`^UWDN}J&&3JbSGNlHl2lLl0KXx2F$=G& zksch9>SH3_!U=kv8p>~l)=RJvoc8|7tj)Pc5*Y!CB59G8z5nJb!c=GrpT`9kZ+~I0 z5b!Ks9t6U5!s~^;+4rT6zlezOv*Xw$*zx`7`#!-6vT64zS&cpQ`q(F_TQb{KJwykI zI~A-z_~&ADLvm*58Nf(-@e?C_6$n!Moy7=(dAVuO6Y=`Va6>^p?-*g%sndlw;nFEm z%Im4rRo<|{m*RTPHK#ui0rNc(0Dtb4{BO0v5M<*l_AquPQluvm4xUG5y!l(Wx6jY3 zhX=s!>Y z0P~*~YG42`Ft9N&e=Uo`@}<833lkdyhx-4Gm;Bw~Adv*G8MUfmsXHu}ynlur#_Xi# zX#ou#A+Dy-5}}p{vBRylN0mfv-W&XqWpK3BR~j=W4$ri_A}Lb?uxC=>5-lAOC{)*t z$~+|!UDB637(BeFtHav(o*)Jc_h$vL>gG%Ud@N_IhT)MwnG$ql^wba6FQbb-;67>E z8ajPO%lu+hOb;jGL{DhII)6i=Fs@8`_Cq1Fs~vj*{{UAUqWf-7URZ5Fe;?D68E&@F z&#pWHG#~7zpjCrlP5M#Tm+-p1_r-)h&(-le+OeB)qi_Ddbbxot|9>psA0%T9bAOv9?H5pFuW(f!Rv@Mf zz2Mti_#o(>HO*OF7=Fy0)H#l@0)7MKhwx;-Q|fe&{Dr_-8wR8-jmuxlsEVUnUctZK&>~5cG zd3(D-!;mOB*_wuv%uEg}iEMrX$K19n*&Q!+;e|z4n2Cu5&xLInCV3$gY+ih;WEkv~ zQsR^8Euu_GTXvV?gc!5RV2>R_Q}9HTmw}^ttWB2W%71Ap`)zH?H;h9$GZAlzr@{+P zrb&wv)@|5NYdEV?pCph8W@k2utJz5r0|J3mKRs>1F;zA$eb2AeQz=cQBvOPwi?_kh5(@jT7=G}o{|Uzod+{XGd4CO~wO17MD(THkO%P%~V)?&T zx6_obu%K~WM$4rVtQx&i7kRfUMr(Ow%>4Fq%PY&)ZZJzVf=-;n+F3tUV1p|$v;&!3 zm~)B5W8O6EH=@rB1|dav<$0Y`N~KFKCS-<^6Mv}?_&MY|7iL~PK8OV>si5mK6(A9? zD68b;dA~g?)`{A`eF*^{BP$-Vz>uKRyUq6ciJDPD9Aj@~IuED5>O1i_`j2gM8DG

vrd=JT_3EC=cJ26Zp7A`m62?UYG;Rz|b8DwaMc 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

i&P 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

t6i%Yp>7qaHTiya(%uGaPt4zw(9H} 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

;rs69gk0e7rtRPf#o+7x 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

54^} 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 m3y_Tsq>dpFD+ho#BK{8yc^$U^ From f3302673612829286c23b849b27f3a4118cd7bfa Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Dec 2023 12:46:09 +0100 Subject: [PATCH 0334/1302] Rebar/Rebar3: Remove Elixir as a rebar dependency Elixir is standalone since years ago. When Elixir support is required, better simply install Elixir and use Mix as build tool. Or install Elixir and use the experimental Rebar3 support to compile Elixir files and dependencies. --- configure.ac | 2 +- rebar.config | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/configure.ac b/configure.ac index 33913d181..434f0e9f3 100644 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ AC_ARG_ENABLE(debug, esac],[if test "x$debug" = "x"; then debug=true; fi]) AC_ARG_ENABLE(elixir, -[AS_HELP_STRING([--enable-elixir],[enable Elixir support (default: no)])], +[AS_HELP_STRING([--enable-elixir],[enable Elixir support in Rebar3 (default: no)])], [case "${enableval}" in yes) elixir=true ;; no) elixir=false ;; diff --git a/rebar.config b/rebar.config index 23bfc8b1e..b2157c774 100644 --- a/rebar.config +++ b/rebar.config @@ -27,8 +27,6 @@ {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, {if_var_true, tools, {ejabberd_po, ".*", {git, "https://github.com/processone/ejabberd-po", {branch, "main"}}}}, - {if_var_true, elixir, - {elixir, ".*", {git, "https://github.com/elixir-lang/elixir", {tag, "v1.4.4"}}}}, {if_var_true, pam, {epam, ".*", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, @@ -69,9 +67,6 @@ {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.23"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, - {if_not_rebar3, %% Needed because modules are not fully migrated to new structure and mix - {if_var_true, elixir, - {rebar_elixir_plugin, ".*", {git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}}, {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, @@ -146,24 +141,14 @@ {if_var_true, stun, {d, 'STUN'}}, {src_dirs, [src, {if_rebar3, sql}, - {if_var_true, tools, tools}, - {if_var_true, elixir, include}]}]}. + {if_var_true, tools, tools}]}]}. {if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}]}}. {if_rebar3, {project_plugins, [configure_deps]}}. {if_not_rebar3, {plugins, [ - deps_erl_opts, override_deps_versions2, override_opts, configure_deps, - {if_var_true, elixir, rebar_elixir_compiler}, - {if_var_true, elixir, rebar_exunit} + deps_erl_opts, override_deps_versions2, override_opts, configure_deps ]}}. -{if_rebar3, {if_var_true, elixir, - {project_app_dirs, [".", "elixir/lib"]}}}. -{if_not_rebar3, {if_var_true, elixir, - {lib_dirs, ["deps/elixir/lib"]}}}. -{if_var_true, elixir, - {src_dirs, ["include"]}}. - {sub_dirs, ["rel"]}. {keep_build_info, true}. From 65ff45e202983cf1fa455bb8719f396aece0358b Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 30 Dec 2023 14:02:26 +0100 Subject: [PATCH 0335/1302] Rebar/Rebar3: Remove elixir as a gitonly_deps, and add ejabberd_po Adding ejabberd_po to gitonly_deps isn't strictly needed, because we request a branch in the ejabberd-po git repository, and rebar.config.script understands that this means ejabberd_po should be downloaded from git, not hex.pm. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index b2157c774..eefba14cf 100644 --- a/rebar.config +++ b/rebar.config @@ -76,7 +76,7 @@ {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. -{gitonly_deps, [elixir]}. +{gitonly_deps, [ejabberd_po]}. {if_var_true, latest_deps, {floating_deps, [cache_tab, From e1f14aca9bcfb9af165e253d709b1bda9fe5465d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Dec 2023 15:32:37 +0100 Subject: [PATCH 0336/1302] Rebar3: Provide proper path to iex We can't use iex variable, because rebar3/relx_overlay.erl already uses all existing app names as variables. --- Makefile.in | 4 ++-- configure.ac | 1 + ejabberdctl.template | 2 +- mix.exs | 2 +- vars.config.in | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index c5be992d9..f7195f9a1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -295,7 +295,6 @@ relive: relivelibdir=$(shell pwd)/$(DEPSDIR) relivedir=$(shell pwd)/_build/relive -iexpath=$(shell which iex) CONFIG_DIR = ${relivedir}/conf SPOOL_DIR = ${relivedir}/database LOGS_DIR = ${relivedir}/logs @@ -305,9 +304,9 @@ ejabberdctl.relive: -e "s*{{config_dir}}*${CONFIG_DIR}*g" \ -e "s*{{logs_dir}}*${LOGS_DIR}*g" \ -e "s*{{spool_dir}}*${SPOOL_DIR}*g" \ - -e "s*{{bindir}}/iex*$(iexpath)*g" \ -e "s*{{bindir}}*@bindir@*g" \ -e "s*{{libdir}}*${relivelibdir}${ELIXIR_LIBDIR}*g" \ + -e "s*{{iexpath}}*@IEX@*g" \ -e "s*{{erl}}*@ERL@*g" \ -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ > ejabberdctl.relive @@ -331,6 +330,7 @@ ejabberdctl.example: vars.config -e "s*{{spool_dir}}*${SPOOLDIR}*g" \ -e "s*{{bindir}}*@bindir@*g" \ -e "s*{{libdir}}*@libdir@${ELIXIR_LIBDIR}*g" \ + -e "s*{{iexpath}}*@IEX@*g" \ -e "s*{{erl}}*@ERL@*g" \ -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ > ejabberdctl.example diff --git a/configure.ac b/configure.ac index 434f0e9f3..5549b5ffb 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,7 @@ fi AC_PATH_TOOL(ERL, erl, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(EPMD, epmd, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(IEX, iex, , [${extra_erl_path}$PATH]) AC_ERLANG_NEED_ERL AC_ERLANG_NEED_ERLC diff --git a/ejabberdctl.template b/ejabberdctl.template index 0e221f473..8b3d90fc5 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -15,8 +15,8 @@ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" # shellcheck disable=SC2034 ERTS_VSN="{{erts_vsn}}" ERL="{{erl}}" -IEX="{{bindir}}/iex" EPMD="{{epmd}}" +IEX="{{iexpath}}" INSTALLUSER="{{installuser}}" # check the proper system user is used diff --git a/mix.exs b/mix.exs index bef8d02ba..d11a6854a 100644 --- a/mix.exs +++ b/mix.exs @@ -250,7 +250,7 @@ defmodule Ejabberd.MixProject do logs_dir: config(:logs_dir), spool_dir: config(:spool_dir), vsn: config(:vsn), - iex: config(:iex), + iexpath: config(:iexpath), erl: config(:erl), epmd: config(:epmd), bindir: Path.join([config(:release_dir), "releases", version()]), diff --git a/vars.config.in b/vars.config.in index 1d5decc60..d0b4a392a 100644 --- a/vars.config.in +++ b/vars.config.in @@ -53,9 +53,9 @@ {sysconfdir, "{{release_dir}}/etc"}. {erts_dir, "{{release_dir}}/erts-${ERTS_VSN#erts-}"}. {installuser, "@INSTALLUSER@"}. -{iex, "{{release_dir}}/releases/{{vsn}}/iex"}. {erl, "{{erts_dir}}/bin/erl"}. {epmd, "{{erts_dir}}/bin/epmd"}. +{iexpath, "{{release_dir}}/releases/{{vsn}}/iex"}. {localstatedir, "{{release_dir}}/var"}. {libdir, "{{release_dir}}/lib"}. {docdir, "{{release_dir}}/doc"}. From ff783d15e8964ae1bd7be8d932e1645a03884bb2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Dec 2023 15:29:33 +0100 Subject: [PATCH 0337/1302] Rebar3: Compile explicitly our Elixir files when --enable-elixir --- Makefile.in | 2 ++ rebar.config | 1 + 2 files changed, 3 insertions(+) diff --git a/Makefile.in b/Makefile.in index f7195f9a1..e805645db 100644 --- a/Makefile.in +++ b/Makefile.in @@ -124,6 +124,7 @@ else ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" + EXPLICIT_ELIXIR_COMPILE=MIX_ENV=default mix compile.elixir endif ifeq "$(REBAR_VER)" "3" SKIPDEPS= @@ -179,6 +180,7 @@ $(DEPSDIR)/.built: $(DEPSDIR)/.got src: $(DEPSDIR)/.built $(REBAR) $(SKIPDEPS) compile + $(EXPLICIT_ELIXIR_COMPILE) update: rm -rf $(DEPSDIR)/.got diff --git a/rebar.config b/rebar.config index eefba14cf..fe2dfe6ec 100644 --- a/rebar.config +++ b/rebar.config @@ -240,6 +240,7 @@ {mkdir, "conf"}, {copy, "rel/files/erl", "erts-\{\{erts_vsn\}\}/bin/erl"}, {template, "ejabberdctl.template", "bin/ejabberdctl"}, + {copy, "_build/default/lib/ejabberd/ebin/Elixir.*", "lib/ejabberd-{{release_version}}/ebin/"}, {copy, "inetrc", "conf/inetrc"}, {copy, "tools/captcha*.sh", "lib/ejabberd-\{\{release_version\}\}/priv/bin/"}, {copy, "rel/files/install_upgrade.escript", "bin/install_upgrade.escript"}]} From a6c1e42c0a18ac0b55a6a4aa5e345fbd5913a391 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Dec 2023 13:38:40 +0100 Subject: [PATCH 0338/1302] Rebar3: Add support to compile Elixir dependencies As described in https://rebar3.org/docs/configuration/plugins/#elixir-dependencies --- rebar.config | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index fe2dfe6ec..95dec9b93 100644 --- a/rebar.config +++ b/rebar.config @@ -143,12 +143,18 @@ {if_rebar3, sql}, {if_var_true, tools, tools}]}]}. -{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}]}}. +{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}, + {if_var_true, elixir, rebar_mix}]}}. {if_rebar3, {project_plugins, [configure_deps]}}. {if_not_rebar3, {plugins, [ deps_erl_opts, override_deps_versions2, override_opts, configure_deps ]}}. +{if_rebar3, {if_var_true, elixir, + {provider_hooks, [ + {post, [{compile, {mix, consolidate_protocols}}]} + ]}}}. + {sub_dirs, ["rel"]}. {keep_build_info, true}. @@ -241,6 +247,7 @@ {copy, "rel/files/erl", "erts-\{\{erts_vsn\}\}/bin/erl"}, {template, "ejabberdctl.template", "bin/ejabberdctl"}, {copy, "_build/default/lib/ejabberd/ebin/Elixir.*", "lib/ejabberd-{{release_version}}/ebin/"}, + {copy, "{{base_dir}}/consolidated/*", "lib/ejabberd-{{release_version}}/ebin/"}, {copy, "inetrc", "conf/inetrc"}, {copy, "tools/captcha*.sh", "lib/ejabberd-\{\{release_version\}\}/priv/bin/"}, {copy, "rel/files/install_upgrade.escript", "bin/install_upgrade.escript"}]} From 2d067f749832b2bc8f7dfb6efa45d9816157208c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Dec 2023 13:28:44 +0100 Subject: [PATCH 0339/1302] Rebar3: Workaround to fix protocol consolidation --- rebar.config | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 95dec9b93..81849430f 100644 --- a/rebar.config +++ b/rebar.config @@ -144,7 +144,14 @@ {if_var_true, tools, tools}]}]}. {if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}, - {if_var_true, elixir, rebar_mix}]}}. + %% Protocol consolidation doesn't work correctly in upstream rebar_mix, see + %% https://github.com/Supersonido/rebar_mix/issues/27#issuecomment-894873335 + %% Let's use this fixed rebar_mix fork, see its PR: + %% https://github.com/Supersonido/rebar_mix/pull/31 + {if_var_true, elixir, {rebar_mix, ".*", + {git, "https://github.com/bsanyi/rebar_mix.git", + {branch, "consolidation_fix"}}} + }]}}. {if_rebar3, {project_plugins, [configure_deps]}}. {if_not_rebar3, {plugins, [ deps_erl_opts, override_deps_versions2, override_opts, configure_deps From dd5a1982220041c7640a230282fe39033ed5af0d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Dec 2023 01:46:51 +0100 Subject: [PATCH 0340/1302] Rebar3: Include Elixir files when making a release --- Makefile.in | 3 +++ rebar.config | 4 ++++ src/ejabberd.app.src.script | 14 +++++++++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index e805645db..40c14d894 100644 --- a/Makefile.in +++ b/Makefile.in @@ -125,6 +125,7 @@ ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" EXPLICIT_ELIXIR_COMPILE=MIX_ENV=default mix compile.elixir + PREPARE_ELIXIR_SCRIPTS=mkdir rel/overlays; cp $(ELIXIR_LIBDIR_RAW)/../bin/iex rel/overlays/; cp $(ELIXIR_LIBDIR_RAW)/../bin/elixir rel/overlays/; sed -i 's|ERTS_BIN=$$|ERTS_BIN=$$SCRIPT_PATH/../../erts-{{erts_vsn}}/bin/|' rel/overlays/elixir endif ifeq "$(REBAR_VER)" "3" SKIPDEPS= @@ -438,11 +439,13 @@ distclean: clean clean-rel rm -f vars.config rel: + $(PREPARE_ELIXIR_SCRIPTS) $(REBARREL) DEV_CONFIG = _build/dev/rel/ejabberd/conf/ejabberd.yml dev $(DEV_CONFIG): + $(PREPARE_ELIXIR_SCRIPTS) $(REBARDEV) TAGS: diff --git a/rebar.config b/rebar.config index 81849430f..6a715b586 100644 --- a/rebar.config +++ b/rebar.config @@ -255,6 +255,10 @@ {template, "ejabberdctl.template", "bin/ejabberdctl"}, {copy, "_build/default/lib/ejabberd/ebin/Elixir.*", "lib/ejabberd-{{release_version}}/ebin/"}, {copy, "{{base_dir}}/consolidated/*", "lib/ejabberd-{{release_version}}/ebin/"}, + {copy, "rel/overlays/iex", "releases/{{release_version}}/"}, + {if_var_true, elixir, + {template, "rel/overlays/elixir", "releases/{{release_version}}/elixir"} + }, {copy, "inetrc", "conf/inetrc"}, {copy, "tools/captcha*.sh", "lib/ejabberd-\{\{release_version\}\}/priv/bin/"}, {copy, "rel/files/install_upgrade.escript", "bin/install_upgrade.escript"}]} diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index 4c8745146..82048c4e0 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -1,13 +1,17 @@ -Vars = case file:consult(filename:join([filename:dirname(SCRIPT), "..", "vars.config"])) of +{Vars, ElixirApps} = case file:consult(filename:join([filename:dirname(SCRIPT), "..", "vars.config"])) of {ok, Terms} -> Backends = [mssql, mysql, odbc, pgsql, redis, sqlite], EBs = lists:filter(fun(Backend) -> lists:member({Backend, true}, Terms) end, Backends), - [lists:keyfind(description, 1, Terms), + Elixirs = case lists:keyfind(elixir, 1, Terms) of + {elixir, true} -> [elixir, iex, logger, mix]; + _ -> [] + end, + {[lists:keyfind(description, 1, Terms), lists:keyfind(vsn, 1, Terms), {env, [{enabled_backends, EBs}]} - ]; + ], Elixirs}; _Err -> - [] + {[], []} end, {application, ejabberd, @@ -27,7 +31,7 @@ Vars = case file:consult(filename:join([filename:dirname(SCRIPT), "..", "vars.co pkix, stringprep, yconf, - xmpp]}, + xmpp | ElixirApps]}, {mod, {ejabberd_app, []}}]}. %% Local Variables: From 0d852474be062b14f8da51999e52f4f96b9e4015 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 19:23:21 +0100 Subject: [PATCH 0341/1302] Use Mix or Rebar3 by default instead of Rebar2 to compile ejabberd Use the program specified in option: --with-rebar=/path/to/bin When none is specified, use the system mix When Elixir not found, use the system rebar3 When Rebar3 not found, use the rebar3 binary included with ejabberd --- COMPILE.md | 2 +- configure.ac | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/COMPILE.md b/COMPILE.md index c6b10800a..c0ebef4b8 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -89,7 +89,7 @@ Build an OTP Release Instead of installing ejabberd in the system, you can build an OTP release that includes all necessary to run ejabberd in a subdirectory: - ./configure --with-rebar=rebar3 + ./configure make rel Or, if you have Elixir available and plan to develop Elixir code: diff --git a/configure.ac b/configure.ac index 5549b5ffb..fcc0ee71d 100644 --- a/configure.ac +++ b/configure.ac @@ -28,18 +28,34 @@ fi ]) AC_ARG_WITH(rebar, - AS_HELP_STRING([--with-rebar=bin],[use the rebar/rebar3/mix binary specified]), + AS_HELP_STRING([--with-rebar=bin],[use as build tool the rebar/rebar3/mix binary specified]), [if test "$withval" = "yes" -o "$withval" = "no" -o "X$with_rebar" = "X"; then - rebar="rebar" + rebar="rebar3" else rebar="$with_rebar" fi -], [rebar="rebar"]) +], [rebar="unconfigured"]) AC_PATH_TOOL(ERL, erl, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(EPMD, epmd, , [${extra_erl_path}$PATH]) + +AC_PATH_TOOL(REBAR3, rebar3, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(ELIXIR, elixir, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(IEX, iex, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(MIX, mix, , [${extra_erl_path}$PATH]) + +if test "$rebar" = unconfigured; then + if test "x$ELIXIR" = "x" -o "x$IEX" = "x" -o "x$MIX" = "x"; then + if test "x$REBAR3" = "x"; then + rebar="rebar3" + else + rebar=$REBAR3 + fi + else + rebar=$MIX + fi +fi AC_ERLANG_NEED_ERL AC_ERLANG_NEED_ERLC @@ -281,6 +297,8 @@ case "`uname`" in ;; esac +AC_MSG_RESULT([build tool to use (change using --with-rebar): $rebar]) + AC_SUBST(roster_gateway_workaround) AC_SUBST(new_sql_schema) AC_SUBST(full_xml) From f838319455ccde53f2bc1ed47f55c2a22bfe9fa7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 2 Jan 2024 20:21:52 +0100 Subject: [PATCH 0342/1302] Runtime: Rebars: Get old rebar binaries when testing old erlang versions --- .github/workflows/runtime.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 4b62858f8..8583eb02b 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -44,6 +44,16 @@ jobs: - uses: actions/checkout@v4 + - name: Get compatible Rebar binaries + if: matrix.otp < 23 + run: | + rm rebar + rm rebar3 + wget https://github.com/processone/ejabberd/raw/21.12/rebar + wget https://github.com/processone/ejabberd/raw/21.12/rebar3 + chmod +x rebar + chmod +x rebar3 + - name: Prepare libraries run: | apt-get -qq update From b18fa2ebc2fa7e010889c30d2812669c29f4d060 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 2 Jan 2024 20:09:55 +0100 Subject: [PATCH 0343/1302] Runtime: Rebars: Use the rebar binaries included in the repository --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 8583eb02b..cc5dc1df7 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -64,7 +64,7 @@ jobs: - name: Compile run: | ./autogen.sh - ./configure --with-rebar=`which ${{ matrix.rebar }}` \ + ./configure --with-rebar=${{ matrix.rebar }} \ --prefix=/tmp/ejabberd \ --enable-all \ --disable-elixir \ From c69d88c45aa81b5083c98590c6ff7514527d3920 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Dec 2023 15:12:05 +0100 Subject: [PATCH 0344/1302] Runtime: Rebars: Add log tests similar to the Mix job --- .github/workflows/runtime.yml | 43 +++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index cc5dc1df7..6202a97cf 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -81,33 +81,62 @@ jobs: rel/ejabberd/bin/ejabberdctl start \ && rel/ejabberd/bin/ejabberdctl started rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass - rel/ejabberd/bin/ejabberdctl registered_users localhost + rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log cat rel/ejabberd/logs/* - - name: Test rel + - name: Run rel if: matrix.rebar != 'rebar' run: | make rel _build/prod/rel/ejabberd/bin/ejabberdctl start \ && _build/prod/rel/ejabberd/bin/ejabberdctl started _build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass - _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost + _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log _build/prod/rel/ejabberd/bin/ejabberdctl stop \ && _build/prod/rel/ejabberd/bin/ejabberdctl stopped - cat _build/prod/rel/ejabberd/logs/* - - name: Test dev + - name: Run dev if: matrix.rebar != 'rebar' run: | make dev _build/dev/rel/ejabberd/bin/ejabberdctl start \ && _build/dev/rel/ejabberd/bin/ejabberdctl started - _build/dev/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass - _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost + _build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log _build/dev/rel/ejabberd/bin/ejabberdctl stop \ && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + + - name: View logs + if: matrix.rebar != 'rebar' + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" cat _build/dev/rel/ejabberd/logs/* + - name: Check rel + if: matrix.rebar != 'rebar' + run: | + grep -q '^user1$' registered.log + grep -q '^user2$' registered.log + grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + test $(find _build/prod/ -empty -name error.log) + grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + test $(find _build/dev/ -empty -name error.log) + + - name: View logs failures + if: matrix.rebar != 'rebar' + run: | + cat _build/prod/rel/ejabberd/logs/ejabberd.log + cat _build/prod/rel/ejabberd/logs/error.log + cat _build/dev/rel/ejabberd/logs/ejabberd.log + cat _build/dev/rel/ejabberd/logs/error.log + mix: name: Mix strategy: From 8bdd811a16af8a12cfc4326c25146b3c57cffdd3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 13:28:51 +0100 Subject: [PATCH 0345/1302] Runtime: Mix: Update Erlang and Elixir versions to test --- .github/workflows/runtime.yml | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 6202a97cf..7f6e8ef26 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -142,29 +142,13 @@ jobs: strategy: fail-fast: false matrix: - otp: ['21.3', '25.0', '26'] - elixir: ['1.10.3', '1.11.4', '1.12.3', '1.13.4', '1.14.5', '1.15'] + otp: ['23.0', '25.3', '26'] + elixir: ['1.13.4', '1.15.7', '1.16'] exclude: - - otp: '21.3' - elixir: '1.12.3' - - otp: '21.3' - elixir: '1.13.4' - - otp: '21.3' - elixir: '1.14.5' - - otp: '21.3' - elixir: '1.15' - - otp: '25.0' - elixir: '1.10.3' - - otp: '25.0' - elixir: '1.11.4' - - otp: '25.0' - elixir: '1.12.3' - - otp: '26' - elixir: '1.10.3' - - otp: '26' - elixir: '1.11.4' - - otp: '26' - elixir: '1.12.3' + - otp: '23.0' + elixir: '1.15.7' + - otp: '23.0' + elixir: '1.16' - otp: '26' elixir: '1.13.4' runs-on: ubuntu-20.04 From 07be61585c08e02dfae37895587258c3d2689668 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Dec 2023 13:31:04 +0100 Subject: [PATCH 0346/1302] Runtime: Mix: No need to specify --disable-elixir --- .github/workflows/runtime.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 7f6e8ef26..7e185ccd5 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -184,7 +184,6 @@ jobs: ./configure --with-rebar=mix \ --prefix=/tmp/ejabberd \ --enable-all \ - --disable-elixir \ --disable-odbc mix deps.get make From db8583b1c647e7f4c84d721f279d73c85f55517c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Dec 2023 17:44:47 +0100 Subject: [PATCH 0347/1302] Runtime: Mix: Add step to view logs --- .github/workflows/runtime.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 7e185ccd5..921c89f73 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -210,6 +210,16 @@ jobs: _build/dev/rel/ejabberd/bin/ejabberdctl stop \ && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + - name: View logs + if: always() + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" + cat _build/dev/rel/ejabberd/logs/* + - name: Check rel if: always() run: | From 7186cd09bf3fd1ea5a0da09b8dfebbfc44dd8435 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Dec 2023 22:19:25 +0100 Subject: [PATCH 0348/1302] Runtime: Test also "make install" --- .github/workflows/runtime.yml | 41 +++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 921c89f73..cad231014 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -107,6 +107,17 @@ jobs: _build/dev/rel/ejabberd/bin/ejabberdctl stop \ && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + - name: Run install + if: matrix.rebar != 'rebar' + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + - name: View logs if: matrix.rebar != 'rebar' run: | @@ -116,18 +127,24 @@ jobs: cat _build/prod/rel/ejabberd/logs/* echo "===> Dev:" cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* - - name: Check rel + - name: Check logs if: matrix.rebar != 'rebar' run: | grep -q '^user1$' registered.log grep -q '^user2$' registered.log + grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log test $(find _build/prod/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log test $(find _build/dev/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures if: matrix.rebar != 'rebar' @@ -136,6 +153,8 @@ jobs: cat _build/prod/rel/ejabberd/logs/error.log cat _build/dev/rel/ejabberd/logs/ejabberd.log cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log mix: name: Mix @@ -210,6 +229,16 @@ jobs: _build/dev/rel/ejabberd/bin/ejabberdctl stop \ && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + - name: Run install + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + - name: View logs if: always() run: | @@ -219,18 +248,24 @@ jobs: cat _build/prod/rel/ejabberd/logs/* echo "===> Dev:" cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* - - name: Check rel + - name: Check logs if: always() run: | grep -q '^user1$' registered.log grep -q '^user2$' registered.log + grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log test $(find _build/prod/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log test $(find _build/dev/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures if: failure() @@ -239,3 +274,5 @@ jobs: cat _build/prod/rel/ejabberd/logs/error.log cat _build/dev/rel/ejabberd/logs/ejabberd.log cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log From c0a909296ef4a0c80821d5adacfd538bb53bfcc6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 13:40:24 +0100 Subject: [PATCH 0349/1302] Runtime: Mix: Enable ModPresenceDemo and Elixir dep --- .github/workflows/runtime.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index cad231014..9f5306409 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -197,6 +197,13 @@ jobs: echo "::remove-matcher owner=elixir-mixTestFailure::" echo "::remove-matcher owner=elixir-dialyzerOutputDefault::" + - name: Enable ModPresenceDemo and an Elixir dependency + run: | + sed -i "s|^modules:|modules:\n 'ModPresenceDemo': {}|g" ejabberd.yml.example + cat ejabberd.yml.example + sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config + cat rebar.config + - name: Compile run: | ./autogen.sh @@ -259,12 +266,15 @@ jobs: grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'module Presence Demo' _build/prod/rel/ejabberd/logs/ejabberd.log test $(find _build/prod/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'module Presence Demo' _build/dev/rel/ejabberd/logs/ejabberd.log test $(find _build/dev/ -empty -name error.log) grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'module Presence Demo' /tmp/ejabberd/var/log/ejabberd/ejabberd.log test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures From ad7538f3949fc57be6dc9c0749ab9ecc947c5d6a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 13:37:16 +0100 Subject: [PATCH 0350/1302] Runtime: Rebar3+Elixir: Add new job to test Rebar3 compiling Elixir --- .github/workflows/runtime.yml | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 9f5306409..cc0e20c54 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -156,6 +156,129 @@ jobs: cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log cat /tmp/ejabberd/var/log/ejabberd/error.log + rebar3-elixir: + name: Rebar3+Elixir + strategy: + fail-fast: false + matrix: + otp: ['23.0', '25.3', '26'] + elixir: ['1.13.4', '1.15.7', '1.16'] + exclude: + - otp: '23.0' + elixir: '1.15.7' + - otp: '23.0' + elixir: '1.16' + - otp: '26' + elixir: '1.13.4' + runs-on: ubuntu-20.04 + + steps: + + - uses: actions/checkout@v4 + + - name: Get specific Erlang/OTP + uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp}} + elixir-version: ${{matrix.elixir}} + + - name: Prepare libraries + run: | + sudo apt-get -qq update + sudo apt-get -y purge libgd3 nginx + sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev + + - name: Enable ModPresenceDemo and an Elixir dependency + run: | + sed -i "s|^modules:|modules:\n 'ModPresenceDemo': {}|g" ejabberd.yml.example + cat ejabberd.yml.example + sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config + cat rebar.config + + - name: Compile + run: | + ./autogen.sh + ./configure --with-rebar=rebar3 \ + --prefix=/tmp/ejabberd \ + --enable-all \ + --disable-odbc + make update + make + + - run: make xref + + - name: Run rel + run: | + make rel + _build/prod/rel/ejabberd/bin/ejabberdctl start \ + && _build/prod/rel/ejabberd/bin/ejabberdctl started + _build/prod/rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass + _build/prod/rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log + _build/prod/rel/ejabberd/bin/ejabberdctl stop \ + && _build/prod/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run dev + run: | + make dev + _build/dev/rel/ejabberd/bin/ejabberdctl start \ + && _build/dev/rel/ejabberd/bin/ejabberdctl started + _build/dev/rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass + _build/dev/rel/ejabberd/bin/ejabberdctl registered_users localhost >> registered.log + _build/dev/rel/ejabberd/bin/ejabberdctl stop \ + && _build/dev/rel/ejabberd/bin/ejabberdctl stopped + + - name: Run install + run: | + make install + /tmp/ejabberd/sbin/ejabberdctl start \ + && /tmp/ejabberd/sbin/ejabberdctl started + /tmp/ejabberd/sbin/ejabberdctl register user3 localhost s0mePass + /tmp/ejabberd/sbin/ejabberdctl registered_users localhost >> registered.log + /tmp/ejabberd/sbin/ejabberdctl stop \ + && /tmp/ejabberd/sbin/ejabberdctl stopped + + - name: View logs + if: always() + run: | + echo "===> Registered:" + cat registered.log + echo "===> Prod:" + cat _build/prod/rel/ejabberd/logs/* + echo "===> Dev:" + cat _build/dev/rel/ejabberd/logs/* + echo "===> Install:" + cat /tmp/ejabberd/var/log/ejabberd/* + + - name: Check logs + if: always() + run: | + grep -q '^user1$' registered.log + grep -q '^user2$' registered.log + grep -q '^user3$' registered.log + grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'module Presence Demo' _build/prod/rel/ejabberd/logs/ejabberd.log + test $(find _build/prod/ -empty -name error.log) + grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'module Presence Demo' _build/dev/rel/ejabberd/logs/ejabberd.log + test $(find _build/dev/ -empty -name error.log) + grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'module Presence Demo' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) + + - name: View logs failures + if: failure() + run: | + cat _build/prod/rel/ejabberd/logs/ejabberd.log + cat _build/prod/rel/ejabberd/logs/error.log + cat _build/dev/rel/ejabberd/logs/ejabberd.log + cat _build/dev/rel/ejabberd/logs/error.log + cat /tmp/ejabberd/var/log/ejabberd/ejabberd.log + cat /tmp/ejabberd/var/log/ejabberd/error.log + mix: name: Mix strategy: From a1c81955d381c6a92e1e122e1bfb14842ff8d04f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 2 Jan 2024 15:55:51 +0100 Subject: [PATCH 0351/1302] ejabberdctl: Document to stop live shell with control+g, following cd421f9 --- .github/container/ejabberdctl.template | 4 ++-- ejabberdctl.template | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 1a8df3e12..9afc96a81 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -187,8 +187,8 @@ livewarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To exit this LIVE mode and stop ejabberd, press:" - echo " q(). and press the Enter key" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" diff --git a/ejabberdctl.template b/ejabberdctl.template index 8b3d90fc5..470b4b263 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -175,8 +175,8 @@ livewarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To exit this LIVE mode and stop ejabberd, press:" - echo " q(). and press the Enter key" + echo "To exit and detach this shell from ejabberd, press:" + echo " control+g and then q" echo "" echo "--------------------------------------------------------------------" echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" From abf07966bea8246ad207e44987f4c4e4ad8c702e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jan 2024 16:39:46 +0100 Subject: [PATCH 0352/1302] ejabberdctl: Detect problem running etop and show some help --- ejabberdctl.template | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 470b4b263..6924a8703 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -187,6 +187,21 @@ livewarning() fi } +check_etop_result() +{ + result=$? + if [ $result -eq 1 ] ; then + echo "" + echo "It seems there was some problem running 'ejabberdctl etop'." + echo "Is the error message something like this?" + echo " Failed to load module 'etop' because it cannot be found..." + echo "Then probably ejabberd was compiled with development tools disabled." + echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools" + echo "" + exit $result + fi +} + help() { echo "" @@ -310,6 +325,7 @@ case $1 in set_dist_client exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" -s etop \ -output text + check_etop_result ;; iexdebug) debugwarning From 35b727ac392f46972ca87b2f1f2c076cc995b9a0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jan 2024 16:38:26 +0100 Subject: [PATCH 0353/1302] ejabberdctl: Detect problem running iex and show explanation --- ejabberdctl.template | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 6924a8703..eb16e3839 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -202,6 +202,24 @@ check_etop_result() fi } +check_iex_result() +{ + result=$? + if [ $result -eq 127 ] ; then + echo "" + echo "It seems there was some problem finding 'iex' binary from Elixir." + echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:" + echo " ./configure" + echo "which is equivalent to:" + echo " ./configure --with-rebar=rebar3 --disable-elixir" + echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:" + echo " ./configure --enable-elixir" + echo " ./configure --with-rebar=mix" + echo "" + exit $result + fi +} + help() { echo "" @@ -331,10 +349,12 @@ case $1 in debugwarning set_dist_client exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" + check_iex_result ;; iexlive) livewarning exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" + check_iex_result ;; ping) PEER=${2:-$ERLANG_NODE} From aad3306bdf2931f5fb99e22d6f2b24a872606d01 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 3 Jan 2024 18:58:30 +0100 Subject: [PATCH 0354/1302] Makefile: Support using --with-rebar=/path/to/mix --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 40c14d894..c7c47f833 100644 --- a/Makefile.in +++ b/Makefile.in @@ -92,7 +92,7 @@ ifneq ($(INSTALLGROUP),) G_USER=-g $(INSTALLGROUP) endif -ifeq "$(MIX)" "mix" +ifeq "$(notdir $(MIX))" "mix" REBAR_VER:=6 REBAR_VER_318:=0 else From eeb4be6e4b18a5613dd120f255fe91d31365baa2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jan 2024 19:02:35 +0100 Subject: [PATCH 0355/1302] Makefile: Rename "make rel" to "make prod" Rebar2 could create a release, so it made sense to call it "make rel". Nowadays, Rebar3 and Mix support creating different types of releases: production, development, ... In this sense, our "make rel" target is more properly named "make prod" For backwards compatibility, "make rel" redirects to "make prod" --- Makefile.in | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index c7c47f833..833d006a2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -438,7 +438,9 @@ distclean: clean clean-rel rm -f Makefile rm -f vars.config -rel: +rel: prod + +prod: $(PREPARE_ELIXIR_SCRIPTS) $(REBARREL) @@ -506,7 +508,7 @@ test: @cd priv && ln -sf ../sql $(REBAR) $(SKIPDEPS) ct -.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \ +.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ install uninstall uninstall-binary uninstall-all translations deps test \ quicktest erlang_plt deps_plt ejabberd_plt xref hooks options @@ -524,7 +526,7 @@ help: @echo " uninstall Uninstall ejabberd (buggy)" @echo " uninstall-all Uninstall also configuration, logs, mnesia... (buggy)" @echo "" - @echo " rel Build a production release" + @echo " prod Build a production release" @echo " dev Build a development release" @echo " relive Start a live ejabberd in _build/relive/" @echo "" From a2ff5fbfdb5529aaf5e16a477bf8c1ec4bf6584c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jan 2024 14:54:10 +0100 Subject: [PATCH 0356/1302] Add observer and runtime_tools in releases when --enable-tools When --enable-tools, include observer and runtime_tools in the OTP releases, as they are required by "ejabberdctl etop". With this fix, "ejabberdctl etop" works correctly when: * rebar3 + make rel * mix + make dev * mix + make rel --- .github/workflows/runtime.yml | 1 + configure.ac | 2 +- mix.exs | 4 +++- src/ejabberd.app.src.script | 6 +++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index cc0e20c54..0409bd2f6 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -68,6 +68,7 @@ jobs: --prefix=/tmp/ejabberd \ --enable-all \ --disable-elixir \ + --disable-tools \ --disable-odbc make update make diff --git a/configure.ac b/configure.ac index fcc0ee71d..e0ffc0e47 100644 --- a/configure.ac +++ b/configure.ac @@ -254,7 +254,7 @@ AC_ARG_ENABLE(system_deps, esac],[if test "x$system_deps" = "x"; then system_deps=false; fi]) AC_ARG_ENABLE(tools, -[AS_HELP_STRING([--enable-tools],[build development tools (default: no)])], +[AS_HELP_STRING([--enable-tools],[build development tools: ejabberd-po, etop (default: no)])], [case "${enableval}" in yes) tools=true ;; no) tools=false ;; diff --git a/mix.exs b/mix.exs index d11a6854a..8227791b9 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,9 @@ defmodule Ejabberd.MixProject do end defp cond_apps do - for {:true, app} <- [{config(:stun), :stun}], do: + for {:true, app} <- [{config(:stun), :stun}, + {config(:tools), :observer}, + {config(:tools), :runtime_tools}], do: app end diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index 82048c4e0..83a639095 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -6,10 +6,14 @@ {elixir, true} -> [elixir, iex, logger, mix]; _ -> [] end, + Tools = case lists:keyfind(tools, 1, Terms) of + {tools, true} -> [observer, runtime_tools]; % for `ejabberdctl etop` + _ -> [] + end, {[lists:keyfind(description, 1, Terms), lists:keyfind(vsn, 1, Terms), {env, [{enabled_backends, EBs}]} - ], Elixirs}; + ], Elixirs ++ Tools}; _Err -> {[], []} end, From ee778ca8f9ea1e143e653294e09cd53212f9c698 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 26 Dec 2023 15:47:18 +0100 Subject: [PATCH 0357/1302] COMPILE.md: For Elixir, recommend at least 1.13.4 with Erlang/OTP 23.0 --- COMPILE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/COMPILE.md b/COMPILE.md index c0ebef4b8..80e932d65 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -28,7 +28,8 @@ Other optional libraries are: - PAM library, for Pluggable Authentication Modules (PAM) - ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA challenges -- Elixir ≥ 1.10.3, to support Elixir, and alternative to rebar/rebar3 +- Elixir ≥ 1.10.3, for Elixir support. It is recommended Elixir 1.13.4 or higher + and Erlang/OTP 23.0 or higher. If your system splits packages in libraries and development headers, install the development packages too. From ae2993ecae759e8061e0b11472db8175bd6945f8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 18:10:52 +0100 Subject: [PATCH 0358/1302] Rebar: Use excl_archive_filters only when Erlang < 26 That option is required when Erlang < 26 to disable the archive feature. The feature and the option were removed in Erlang 26, and the release building process fails if the option is used. https://www.erlang.org/patches/otp-26.0 --- rel/reltool.config.script | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rel/reltool.config.script b/rel/reltool.config.script index b7ccdf9c5..fea4d6619 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -53,6 +53,8 @@ OTPApps = RequiredOTPApps ++ ConfiguredOTPApps, DepApps = lists:usort(lists:flatten(GetDeps(filename:join(TopDir, "rebar.config"), GetDeps))), +SysVer = erlang:system_info(otp_release), + Sys = [{lib_dirs, []}, {erts, [{mod_cond, derived}, {app_file, strip}]}, {app_file, strip}, @@ -70,13 +72,17 @@ Sys = [{lib_dirs, []}, {boot_rel, "ejabberd"}, {profile, embedded}, {incl_cond, exclude}, - {excl_archive_filters, [".*"]}, %% Do not archive built libs {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, stdlib, [{incl_cond, include}]}, {app, kernel, [{incl_cond, include}]}, {app, ejabberd, [{incl_cond, include}, {lib_dir, ".."}]}] +++ if SysVer < "26" -> + [{excl_archive_filters, [".*"]}]; %% Do not archive built libs + true -> + [] + end ++ lists:map( fun(App) -> {app, App, [{incl_cond, include}, From ec7570f93e84535cdd4be0feefa7c6a7c56ec9db Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 13:51:42 +0100 Subject: [PATCH 0359/1302] Makefile: Add sections, headers and Vim folding --- Makefile.in | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Makefile.in b/Makefile.in index 833d006a2..a34eaa136 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,3 +1,7 @@ +#. +#' definitions +# + REBAR = @ESCRIPT@ @rebar@ MIX = @rebar@ INSTALL = @INSTALL@ @@ -71,6 +75,10 @@ SPOOLDIR = @localstatedir@/lib/ejabberd # /var/log/ejabberd/ LOGDIR = @localstatedir@/log/ejabberd +#. +#' install user +# + INSTALLUSER=@INSTALLUSER@ # if no user was enabled, don't set privileges or ownership ifeq ($(INSTALLUSER),) @@ -92,6 +100,10 @@ ifneq ($(INSTALLGROUP),) G_USER=-g $(INSTALLGROUP) endif +#. +#' rebar / rebar3 / mix +# + ifeq "$(notdir $(MIX))" "mix" REBAR_VER:=6 REBAR_VER_318:=0 @@ -165,6 +177,10 @@ else endif endif +#. +#' main targets +# + all: scripts deps src deps: $(DEPSDIR)/.got @@ -208,6 +224,10 @@ edoc: $(ERL) -noinput +B -eval \ 'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.' +#. +#' copy-files +# + JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1)) VERSIONED_DEP=$(if $(DEP_$(1)_VERSION),$(DEP_$(1)_VERSION),$(1)) @@ -293,6 +313,10 @@ copy-files: copy-files-sub: copy-files-sub2 +#. +#' relive +# + relive: $(RELIVECMD) @@ -302,6 +326,10 @@ CONFIG_DIR = ${relivedir}/conf SPOOL_DIR = ${relivedir}/database LOGS_DIR = ${relivedir}/logs +#. +#' scripts +# + ejabberdctl.relive: $(SED) -e "s*{{installuser}}*@INSTALLUSER@*g" \ -e "s*{{config_dir}}*${CONFIG_DIR}*g" \ @@ -340,6 +368,10 @@ ejabberdctl.example: vars.config scripts: ejabberd.init ejabberd.service ejabberdctl.example +#. +#' install +# + install: copy-files # # Configuration files @@ -379,6 +411,10 @@ install: copy-files || echo "Man page not included in sources" $(INSTALL) -m 644 COPYING $(DESTDIR)$(DOCDIR) +#. +#' uninstall +# + uninstall: uninstall-binary uninstall-binary: @@ -416,6 +452,10 @@ uninstall-all: uninstall-binary rm -rf $(DESTDIR)$(SPOOLDIR) rm -rf $(DESTDIR)$(LOGDIR) +#. +#' clean +# + clean: rm -rf $(DEPSDIR)/.got rm -rf $(DEPSDIR)/.built @@ -438,6 +478,10 @@ distclean: clean clean-rel rm -f Makefile rm -f vars.config +#. +#' releases +# + rel: prod prod: @@ -450,11 +494,23 @@ dev $(DEV_CONFIG): $(PREPARE_ELIXIR_SCRIPTS) $(REBARDEV) +#. +#' tags +# + TAGS: etags *.erl +#. +#' makefile +# + Makefile: Makefile.in +#. +#' dialyzer +# + ifeq "$(REBAR_VER)" "3" dialyzer: find src/*_opt.erl -type f \! -regex ".*git.*" -exec sed -i 's/re:mp/ tuple/g' {} \; @@ -501,6 +557,10 @@ dialyzer: erlang_plt deps_plt ejabberd_plt status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi endif +#. +#' test +# + test: @echo "************************** NOTICE ***************************************" @cat test/README @@ -508,10 +568,18 @@ test: @cd priv && ln -sf ../sql $(REBAR) $(SKIPDEPS) ct +#. +#' phony +# + .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ install uninstall uninstall-binary uninstall-all translations deps test \ quicktest erlang_plt deps_plt ejabberd_plt xref hooks options +#. +#' help +# + help: @echo "" @echo " [all] " @@ -540,3 +608,7 @@ help: @echo " hooks Run hooks validator" @echo " test Run Common Tests suite" @echo " xref Run cross reference analysis" + +#. +#' +# vim: foldmarker=#',#. foldmethod=marker: From 16e58a1911f2a7f3fcf33f79357465940abadeb1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 13:52:07 +0100 Subject: [PATCH 0360/1302] Makefile: Update PHONY targets --- Makefile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index a34eaa136..27e8752fb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -574,7 +574,8 @@ test: .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ install uninstall uninstall-binary uninstall-all translations deps test \ - quicktest erlang_plt deps_plt ejabberd_plt xref hooks options + all dev doap help relive scripts update \ + erlang_plt deps_plt ejabberd_plt xref hooks options #. #' help From 920e4ba6b131c01cb36995e1af7e1caba92cbbe9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 13:52:38 +0100 Subject: [PATCH 0361/1302] Makefile: Fix TAGS target --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 27e8752fb..ad4214400 100644 --- a/Makefile.in +++ b/Makefile.in @@ -499,7 +499,7 @@ dev $(DEV_CONFIG): # TAGS: - etags *.erl + etags src/*.erl #. #' makefile @@ -603,7 +603,7 @@ help: @echo " edoc Generate edoc documentation (unused)" @echo " options Generate ejabberd_option.erl" @echo " translations Extract translation files (requires --enable-tools)" - @echo " tags Generate tags file for text editors" + @echo " TAGS Generate tags file for text editors" @echo "" @echo " dialyzer Run Dialyzer static analyzer" @echo " hooks Run hooks validator" From 7cae0920b8124e12b53e9153d72b3b80a0d4d23d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 16:06:04 +0100 Subject: [PATCH 0362/1302] Makefile: Use AWK and MKDIR_P --- Makefile.in | 16 +++++++++------- configure.ac | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Makefile.in b/Makefile.in index ad4214400..b34b361ff 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,7 +4,9 @@ REBAR = @ESCRIPT@ @rebar@ MIX = @rebar@ +AWK = @AWK@ INSTALL = @INSTALL@ +MKDIR_P = @MKDIR_P@ SED = @SED@ ERL = @ERL@ @@ -108,8 +110,8 @@ ifeq "$(notdir $(MIX))" "mix" REBAR_VER:=6 REBAR_VER_318:=0 else -REBAR_VER:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print $$2}') -REBAR_VER_318:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') +REBAR_VER:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print $$2}') +REBAR_VER_318:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') endif REBAR_ENABLE_ELIXIR = @elixir@ @@ -137,7 +139,7 @@ ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" EXPLICIT_ELIXIR_COMPILE=MIX_ENV=default mix compile.elixir - PREPARE_ELIXIR_SCRIPTS=mkdir rel/overlays; cp $(ELIXIR_LIBDIR_RAW)/../bin/iex rel/overlays/; cp $(ELIXIR_LIBDIR_RAW)/../bin/elixir rel/overlays/; sed -i 's|ERTS_BIN=$$|ERTS_BIN=$$SCRIPT_PATH/../../erts-{{erts_vsn}}/bin/|' rel/overlays/elixir + PREPARE_ELIXIR_SCRIPTS=$(MKDIR_P) rel/overlays; cp $(ELIXIR_LIBDIR_RAW)/../bin/iex rel/overlays/; cp $(ELIXIR_LIBDIR_RAW)/../bin/elixir rel/overlays/; sed -i 's|ERTS_BIN=$$|ERTS_BIN=$$SCRIPT_PATH/../../erts-{{erts_vsn}}/bin/|' rel/overlays/elixir endif ifeq "$(REBAR_VER)" "3" SKIPDEPS= @@ -188,7 +190,7 @@ deps: $(DEPSDIR)/.got $(DEPSDIR)/.got: rm -rf $(DEPSDIR)/.got rm -rf $(DEPSDIR)/.built - mkdir -p $(DEPSDIR) + $(MKDIR_P) $(DEPSDIR) $(REBAR) $(GET_DEPS) && :> $(DEPSDIR)/.got $(CONFIGURE_DEPS) @@ -520,7 +522,7 @@ else deps := $(wildcard $(DEPSDIR)/*/ebin) dialyzer/erlang.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/erlang.plt \ -o dialyzer/erlang.log --apps kernel stdlib sasl crypto \ public_key ssl mnesia inets odbc compiler erts \ @@ -528,13 +530,13 @@ dialyzer/erlang.plt: status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi dialyzer/deps.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/deps.plt \ -o dialyzer/deps.log $(deps); \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi dialyzer/ejabberd.plt: - @mkdir -p dialyzer + @$(MKDIR_P) dialyzer @dialyzer --build_plt --output_plt dialyzer/ejabberd.plt \ -o dialyzer/ejabberd.log ebin; \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi diff --git a/configure.ac b/configure.ac index e0ffc0e47..065047cb9 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,9 @@ AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_MAKE_SET +AC_PROG_AWK AC_PROG_INSTALL +AC_PROG_MKDIR_P AC_PROG_SED if test "x$GCC" = "xyes"; then From c61b57f7ab30ad7895e3ed5d72ddb16c1a77c75f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 16:52:58 +0100 Subject: [PATCH 0363/1302] Makefile: Uninstall also the man file --- Makefile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.in b/Makefile.in index b34b361ff..788c37556 100644 --- a/Makefile.in +++ b/Makefile.in @@ -447,6 +447,7 @@ uninstall-binary: rm -fr $(DESTDIR)$(LUADIR) rm -fr $(DESTDIR)$(PRIVDIR) rm -fr $(DESTDIR)$(EJABBERDDIR) + rm -f $(DESTDIR)$(MANDIR)/ejabberd.yml.5 uninstall-all: uninstall-binary rm -rf $(DESTDIR)$(ETCDIR) From daabfeb0e2cfa61df3b10d5d6e132c94a23e0315 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jan 2024 13:53:43 +0100 Subject: [PATCH 0364/1302] Makefile: Add install-rel and uninstall-rel --- Makefile.in | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 788c37556..c287cbbd9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -134,6 +134,8 @@ ifeq "$(REBAR_VER)" "6" REBARREL=MIX_ENV=prod $(REBAR) release --overwrite REBARDEV=MIX_ENV=dev $(REBAR) release --overwrite RELIVECMD=escript rel/relive.escript && MIX_ENV=dev RELIVE=true iex --name ejabberd@localhost -S mix run + REL_LIB_DIR = _build/dev/rel/ejabberd/lib + COPY_REL_TARGET = dev else ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") @@ -160,6 +162,8 @@ endif REBARREL=$(REBAR) as prod tar REBARDEV=REBAR_PROFILE=dev $(REBAR) release RELIVECMD=$(REBAR) relive + REL_LIB_DIR = _build/dev/rel/ejabberd/lib + COPY_REL_TARGET = dev else SKIPDEPS=skip_deps=true LISTDEPS=-q list-deps @@ -176,6 +180,8 @@ else REBARDEV= RELIVECMD=@echo "Rebar2 detected... relive not supported.\ \nTry: ./configure --with-rebar=./rebar3 ; make relive" + REL_LIB_DIR = rel/ejabberd/lib + COPY_REL_TARGET = rel endif endif @@ -315,6 +321,29 @@ copy-files: copy-files-sub: copy-files-sub2 +#. +#' copy-files-rel +# + +copy-files-rel: $(COPY_REL_TARGET) + # + # Libraries + (cd $(REL_LIB_DIR) && find . -follow -type f ! -executable -exec $(INSTALL) -vDm 640 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) + # + # *.so: + (cd $(REL_LIB_DIR) && find . -follow -type f -executable -name *.so -exec $(INSTALL) -vDm 640 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) + # + # Executable files + (cd $(REL_LIB_DIR) && find . -follow -type f -executable ! -name *.so -exec $(INSTALL) -vDm 550 $(G_USER) {} $(DESTDIR)$(LIBDIR)/{} \;) + +#. +#' uninstall-librel +# + +uninstall-librel: + (cd $(REL_LIB_DIR) && find . -follow -type f -exec rm -fv -v $(DESTDIR)$(LIBDIR)/{} \;) + (cd $(REL_LIB_DIR) && find . -follow -depth -type d -exec rm -dv -v $(DESTDIR)$(LIBDIR)/{} \;) + #. #' relive # @@ -374,7 +403,11 @@ scripts: ejabberd.init ejabberd.service ejabberdctl.example #' install # -install: copy-files +install: copy-files install-main + +install-rel: copy-files-rel install-main + +install-main: # # Configuration files $(INSTALL) -d -m 750 $(G_USER) $(DESTDIR)$(ETCDIR) @@ -419,6 +452,8 @@ install: copy-files uninstall: uninstall-binary +uninstall-rel: uninstall-binary uninstall-librel + uninstall-binary: rm -f $(DESTDIR)$(SBINDIR)/ejabberdctl rm -f $(DESTDIR)$(BINDIR)/iex @@ -577,7 +612,7 @@ test: .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ install uninstall uninstall-binary uninstall-all translations deps test \ - all dev doap help relive scripts update \ + all dev doap help install-rel relive scripts uninstall-rel update \ erlang_plt deps_plt ejabberd_plt xref hooks options #. @@ -595,7 +630,9 @@ help: @echo " distclean Clean completely the development files" @echo "" @echo " install Install ejabberd to /usr/local" + @echo " install-rel Install ejabberd to /usr/local (using release)" @echo " uninstall Uninstall ejabberd (buggy)" + @echo " uninstall-rel Uninstall ejabberd (using release)" @echo " uninstall-all Uninstall also configuration, logs, mnesia... (buggy)" @echo "" @echo " prod Build a production release" From b4b85e569e14d674531199b8c6497105b18c4772 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 Jan 2024 13:55:50 +0100 Subject: [PATCH 0365/1302] Runtime: Clean Rebar tests and add Rebar2 to them --- .github/workflows/runtime.yml | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 0409bd2f6..714edd269 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -33,9 +33,6 @@ jobs: matrix: otp: ['20.3', '25.3', '26'] rebar: ['rebar', 'rebar3'] - exclude: - - otp: '26' - rebar: 'rebar' runs-on: ubuntu-latest container: image: erlang:${{ matrix.otp }} @@ -75,19 +72,13 @@ jobs: - run: make xref - - name: Test rel (rebar2) + - name: Prepare rel (rebar2) if: matrix.rebar == 'rebar' run: | - make rel - rel/ejabberd/bin/ejabberdctl start \ - && rel/ejabberd/bin/ejabberdctl started - rel/ejabberd/bin/ejabberdctl register user1 localhost s0mePass - rel/ejabberd/bin/ejabberdctl register user2 localhost s0mePass - rel/ejabberd/bin/ejabberdctl registered_users localhost > registered.log - cat rel/ejabberd/logs/* + mkdir -p _build/prod && ln -s `pwd`/rel/ _build/prod/rel + mkdir -p _build/dev && ln -s `pwd`/rel/ _build/dev/rel - name: Run rel - if: matrix.rebar != 'rebar' run: | make rel _build/prod/rel/ejabberd/bin/ejabberdctl start \ @@ -98,7 +89,6 @@ jobs: && _build/prod/rel/ejabberd/bin/ejabberdctl stopped - name: Run dev - if: matrix.rebar != 'rebar' run: | make dev _build/dev/rel/ejabberd/bin/ejabberdctl start \ @@ -109,7 +99,6 @@ jobs: && _build/dev/rel/ejabberd/bin/ejabberdctl stopped - name: Run install - if: matrix.rebar != 'rebar' run: | make install /tmp/ejabberd/sbin/ejabberdctl start \ @@ -120,7 +109,6 @@ jobs: && /tmp/ejabberd/sbin/ejabberdctl stopped - name: View logs - if: matrix.rebar != 'rebar' run: | echo "===> Registered:" cat registered.log @@ -132,23 +120,22 @@ jobs: cat /tmp/ejabberd/var/log/ejabberd/* - name: Check logs - if: matrix.rebar != 'rebar' run: | grep -q '^user1$' registered.log grep -q '^user2$' registered.log grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log - test $(find _build/prod/ -empty -name error.log) + test $(find _build/prod/rel/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log - test $(find _build/dev/ -empty -name error.log) + test $(find _build/dev/rel/ -empty -name error.log) grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures - if: matrix.rebar != 'rebar' + if: always() run: | cat _build/prod/rel/ejabberd/logs/ejabberd.log cat _build/prod/rel/ejabberd/logs/error.log From 65d585496c4f587ac3bd08a677702c541cff8ed6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Jan 2024 19:07:08 +0100 Subject: [PATCH 0366/1302] Document sql_flags option introduced in 00c7600 --- src/ejabberd_options_doc.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 25edecf80..b8c89f914 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1359,6 +1359,12 @@ doc() -> desc => ?T("This option is 'true' by default, and is useful to disable " "prepared statements. The option is valid for PostgreSQL and MySQL.")}}, + {sql_flags, + #{value => "[mysql_alternative_upsert]", + note => "added in 24.01", + desc => + ?T("This option accepts a list of SQL flags, and is empty by default. " + "'mysql_alternative_upsert' forces the alternative upsert implementation in MySQL.")}}, {sql_query_timeout, #{value => "timeout()", desc => From 78750bf753f0ffd5b90174f5f19558e812a462dc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Jan 2024 22:00:28 +0100 Subject: [PATCH 0367/1302] Annotate version when the commands v1 are available --- src/ejabberd_oauth.erl | 1 + src/mod_admin_extra.erl | 3 +++ src/mod_muc_admin.erl | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index c3d206e3f..72383afc1 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -95,6 +95,7 @@ get_commands_spec() -> desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", module = ?MODULE, function = oauth_issue_token, version = 1, + note = "updated in 24.01", args = [{jid, string}, {ttl, integer}, {scopes, {list, {scope, binary}}}], policy = restricted, args_example = ["user@server.com", 3600, ["connected_users_number", "muc_online_rooms"]], diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index cb9deab1d..c40720fa4 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -428,6 +428,7 @@ get_commands_spec() -> desc = "Set presence of a session", module = ?MODULE, function = set_presence, version = 1, + note = "updated in 24.01", args = [{user, binary}, {host, binary}, {resource, binary}, {type, binary}, {show, binary}, {status, binary}, @@ -515,6 +516,7 @@ get_commands_spec() -> desc = "Add an item to a user's roster (supports ODBC)", module = ?MODULE, function = add_rosteritem, version = 1, + note = "updated in 24.01", args = [{localuser, binary}, {localhost, binary}, {user, binary}, {host, binary}, {nick, binary}, {groups, {list, {group, binary}}}, @@ -715,6 +717,7 @@ get_commands_spec() -> desc = "Create a Shared Roster Group", module = ?MODULE, function = srg_create, version = 1, + note = "updated in 24.01", args = [{group, binary}, {host, binary}, {label, binary}, {description, binary}, {display, {list, {group, binary}}}], args_rename = [{name, label}], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 13f196278..14eee4f9e 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -316,6 +316,7 @@ get_commands_spec() -> "`password` and `message` can be set to `none`.", module = ?MODULE, function = send_direct_invitation, version = 1, + note = "updated in 24.01", args_desc = ["Room name", "MUC service", "Password, or `none`", "Reason text, or `none`", "List of users JIDs"], args_example = [<<"room1">>, <<"muc.example.com">>, @@ -364,6 +365,7 @@ get_commands_spec() -> desc = "Subscribe to a MUC conference", module = ?MODULE, function = subscribe_room, version = 1, + note = "updated in 24.01", args_desc = ["User JID", "a user's nick", "the room to subscribe", "list of nodes"], args_example = ["tom@localhost", "Tom", "room1@conference.localhost", @@ -399,12 +401,12 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], desc = "Subscribe several users to a MUC conference", - note = "added in 22.05", longdesc = "This command accepts up to 50 users at once " "(this is configurable with the *`mod_muc_admin`* option " "`subscribe_room_many_max_users`)", module = ?MODULE, function = subscribe_room_many, version = 1, + note = "updated in 24.01", args_desc = ["Users JIDs and nicks", "the room to subscribe", "nodes separated by commas: `,`"], From f34c2b17142f8710279121883579ed187316943c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Dec 2023 10:40:08 +0100 Subject: [PATCH 0368/1302] Document new XEPs supported thanks to its implementation in the xmpp library --- src/ejabberd.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 578a06d91..473fbb12c 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -39,6 +39,11 @@ -protocol({xep, 243, '1.0'}). -protocol({xep, 270, '1.0'}). -protocol({xep, 368, '1.1.0'}). +-protocol({xep, 386, '0.3.0', '24.01', "", ""}). +-protocol({xep, 388, '0.4.0', '24.01', "", ""}). +-protocol({xep, 424, '0.4.0', '24.01', "", ""}). +-protocol({xep, 440, '0.4.0', '24.01', "", ""}). +-protocol({xep, 474, '0.3.0', '24.01', "", ""}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). From 45863b4651dc447e352a445b507628b47328455a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:25:59 +0100 Subject: [PATCH 0369/1302] Grant execution permission to install-sh The file `src/install-sh` was added in c311ea1. Most files from that commit were removed in 4d8f770 and install-sh was moved. Since recent commit 7cae092, `./configure` checks for a race-free `mkdir -p`, the `install-sh` script may be used, and it needs execution permission. --- install-sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 install-sh diff --git a/install-sh b/install-sh old mode 100644 new mode 100755 From d802b6ab44231199ee7a6c9bd63b4990ab32ef13 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:30:57 +0100 Subject: [PATCH 0370/1302] Fix version number in ejabberd_xmlrpc.erl --- src/ejabberd_xmlrpc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index f6d55eba2..91c0f6e27 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -321,7 +321,7 @@ format_arg({array, [{struct, Elements}]}, when is_list(Elements) -> FormattedList = format_args(Elements, ElementsDef), list_to_tuple(FormattedList); -%% New ejabberd 24.xx +%% New ejabberd 24.01 format_arg({struct, Elements}, {tuple, ElementsDef}) when is_list(Elements) -> From 589521bfd8ef6ace3129da401cd97e0dbc94601c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:40:01 +0100 Subject: [PATCH 0371/1302] Update copyright year to 2024 (#4139) --- include/bosh.hrl | 2 +- include/ejabberd_auth.hrl | 2 +- include/ejabberd_commands.hrl | 2 +- include/ejabberd_ctl.hrl | 2 +- include/ejabberd_http.hrl | 2 +- include/ejabberd_oauth.hrl | 2 +- include/ejabberd_sm.hrl | 2 +- include/ejabberd_sql.hrl | 2 +- include/ejabberd_sql_pt.hrl | 2 +- include/ejabberd_stacktrace.hrl | 2 +- include/ejabberd_web_admin.hrl | 2 +- include/eldap.hrl | 2 +- include/http_bind.hrl | 2 +- include/logger.hrl | 2 +- include/mod_announce.hrl | 2 +- include/mod_caps.hrl | 2 +- include/mod_last.hrl | 2 +- include/mod_mam.hrl | 2 +- include/mod_muc.hrl | 2 +- include/mod_muc_room.hrl | 2 +- include/mod_offline.hrl | 2 +- include/mod_privacy.hrl | 2 +- include/mod_private.hrl | 2 +- include/mod_proxy65.hrl | 2 +- include/mod_push.hrl | 2 +- include/mod_roster.hrl | 2 +- include/mod_shared_roster.hrl | 2 +- include/mod_vcard.hrl | 2 +- include/mqtt.hrl | 2 +- include/pubsub.hrl | 2 +- man/ejabberd.yml.5 | 2 +- rebar.config | 2 +- rebar.config.script | 2 +- rel/reltool.config.script | 2 +- sql/lite.new.sql | 2 +- sql/lite.sql | 2 +- sql/mssql.new.sql | 2 +- sql/mssql.sql | 2 +- sql/mysql.new.sql | 2 +- sql/mysql.sql | 2 +- sql/pg.new.sql | 2 +- sql/pg.sql | 2 +- src/acl.erl | 2 +- src/econf.erl | 2 +- src/ejabberd.erl | 2 +- src/ejabberd_access_permissions.erl | 2 +- src/ejabberd_acme.erl | 2 +- src/ejabberd_admin.erl | 2 +- src/ejabberd_app.erl | 2 +- src/ejabberd_auth.erl | 2 +- src/ejabberd_auth_anonymous.erl | 2 +- src/ejabberd_auth_external.erl | 2 +- src/ejabberd_auth_jwt.erl | 2 +- src/ejabberd_auth_ldap.erl | 2 +- src/ejabberd_auth_mnesia.erl | 2 +- src/ejabberd_auth_pam.erl | 2 +- src/ejabberd_auth_sql.erl | 2 +- src/ejabberd_backend_sup.erl | 2 +- src/ejabberd_batch.erl | 2 +- src/ejabberd_bosh.erl | 2 +- src/ejabberd_c2s.erl | 2 +- src/ejabberd_c2s_config.erl | 2 +- src/ejabberd_captcha.erl | 2 +- src/ejabberd_cluster.erl | 2 +- src/ejabberd_cluster_mnesia.erl | 2 +- src/ejabberd_commands.erl | 2 +- src/ejabberd_commands_doc.erl | 2 +- src/ejabberd_config.erl | 2 +- src/ejabberd_config_transformer.erl | 2 +- src/ejabberd_ctl.erl | 2 +- src/ejabberd_db_sup.erl | 2 +- src/ejabberd_doc.erl | 2 +- src/ejabberd_hooks.erl | 2 +- src/ejabberd_http.erl | 2 +- src/ejabberd_http_ws.erl | 2 +- src/ejabberd_iq.erl | 2 +- src/ejabberd_listener.erl | 2 +- src/ejabberd_local.erl | 2 +- src/ejabberd_logger.erl | 2 +- src/ejabberd_mnesia.erl | 2 +- src/ejabberd_oauth.erl | 2 +- src/ejabberd_oauth_mnesia.erl | 2 +- src/ejabberd_oauth_rest.erl | 2 +- src/ejabberd_oauth_sql.erl | 2 +- src/ejabberd_old_config.erl | 2 +- src/ejabberd_options.erl | 2 +- src/ejabberd_options_doc.erl | 2 +- src/ejabberd_piefxis.erl | 2 +- src/ejabberd_pkix.erl | 2 +- src/ejabberd_redis.erl | 2 +- src/ejabberd_redis_sup.erl | 2 +- src/ejabberd_regexp.erl | 2 +- src/ejabberd_router.erl | 2 +- src/ejabberd_router_mnesia.erl | 2 +- src/ejabberd_router_multicast.erl | 2 +- src/ejabberd_router_redis.erl | 2 +- src/ejabberd_router_sql.erl | 2 +- src/ejabberd_s2s.erl | 2 +- src/ejabberd_s2s_in.erl | 2 +- src/ejabberd_s2s_out.erl | 2 +- src/ejabberd_service.erl | 2 +- src/ejabberd_shaper.erl | 2 +- src/ejabberd_sip.erl | 2 +- src/ejabberd_sm.erl | 2 +- src/ejabberd_sm_mnesia.erl | 2 +- src/ejabberd_sm_redis.erl | 2 +- src/ejabberd_sm_sql.erl | 2 +- src/ejabberd_sql.erl | 2 +- src/ejabberd_sql_pt.erl | 2 +- src/ejabberd_sql_schema.erl | 2 +- src/ejabberd_sql_sup.erl | 2 +- src/ejabberd_stun.erl | 2 +- src/ejabberd_sup.erl | 2 +- src/ejabberd_system_monitor.erl | 2 +- src/ejabberd_tmp_sup.erl | 2 +- src/ejabberd_update.erl | 2 +- src/ejabberd_web.erl | 2 +- src/ejabberd_web_admin.erl | 4 ++-- src/ejabberd_websocket.erl | 2 +- src/ejabberd_websocket_codec.erl | 2 +- src/ejabberd_xmlrpc.erl | 2 +- src/ejd2sql.erl | 2 +- src/eldap_filter.erl | 2 +- src/eldap_pool.erl | 2 +- src/eldap_utils.erl | 2 +- src/elixir_logger_backend.erl | 2 +- src/ext_mod.erl | 2 +- src/extauth.erl | 2 +- src/extauth_sup.erl | 2 +- src/gen_iq_handler.erl | 2 +- src/gen_mod.erl | 2 +- src/gen_pubsub_node.erl | 2 +- src/gen_pubsub_nodetree.erl | 2 +- src/jd2ejd.erl | 2 +- src/misc.erl | 2 +- src/mod_adhoc.erl | 2 +- src/mod_admin_extra.erl | 2 +- src/mod_admin_update_sql.erl | 2 +- src/mod_announce.erl | 2 +- src/mod_announce_mnesia.erl | 2 +- src/mod_announce_sql.erl | 2 +- src/mod_avatar.erl | 2 +- src/mod_block_strangers.erl | 2 +- src/mod_blocking.erl | 2 +- src/mod_bosh.erl | 2 +- src/mod_bosh_mnesia.erl | 2 +- src/mod_bosh_redis.erl | 2 +- src/mod_bosh_sql.erl | 2 +- src/mod_caps.erl | 2 +- src/mod_caps_mnesia.erl | 2 +- src/mod_caps_sql.erl | 2 +- src/mod_carboncopy.erl | 2 +- src/mod_client_state.erl | 2 +- src/mod_configure.erl | 2 +- src/mod_conversejs.erl | 2 +- src/mod_delegation.erl | 2 +- src/mod_disco.erl | 2 +- src/mod_fail2ban.erl | 2 +- src/mod_http_api.erl | 2 +- src/mod_http_fileserver.erl | 2 +- src/mod_http_upload.erl | 2 +- src/mod_http_upload_quota.erl | 2 +- src/mod_jidprep.erl | 2 +- src/mod_last.erl | 2 +- src/mod_last_mnesia.erl | 2 +- src/mod_last_sql.erl | 2 +- src/mod_legacy_auth.erl | 2 +- src/mod_mam.erl | 2 +- src/mod_mam_mnesia.erl | 2 +- src/mod_mam_sql.erl | 2 +- src/mod_metrics.erl | 2 +- src/mod_mqtt.erl | 2 +- src/mod_mqtt_bridge.erl | 2 +- src/mod_mqtt_bridge_session.erl | 2 +- src/mod_mqtt_mnesia.erl | 2 +- src/mod_mqtt_session.erl | 2 +- src/mod_mqtt_sql.erl | 2 +- src/mod_mqtt_ws.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_muc_admin.erl | 2 +- src/mod_muc_log.erl | 2 +- src/mod_muc_mnesia.erl | 2 +- src/mod_muc_occupantid.erl | 2 +- src/mod_muc_room.erl | 2 +- src/mod_muc_rtbl.erl | 2 +- src/mod_muc_sql.erl | 2 +- src/mod_muc_sup.erl | 2 +- src/mod_multicast.erl | 2 +- src/mod_offline.erl | 2 +- src/mod_offline_mnesia.erl | 2 +- src/mod_offline_sql.erl | 2 +- src/mod_ping.erl | 2 +- src/mod_pres_counter.erl | 2 +- src/mod_privacy.erl | 2 +- src/mod_privacy_mnesia.erl | 2 +- src/mod_privacy_sql.erl | 2 +- src/mod_private.erl | 2 +- src/mod_private_mnesia.erl | 2 +- src/mod_private_sql.erl | 2 +- src/mod_privilege.erl | 2 +- src/mod_proxy65.erl | 2 +- src/mod_proxy65_lib.erl | 2 +- src/mod_proxy65_mnesia.erl | 2 +- src/mod_proxy65_redis.erl | 2 +- src/mod_proxy65_service.erl | 2 +- src/mod_proxy65_sql.erl | 2 +- src/mod_proxy65_stream.erl | 2 +- src/mod_pubsub.erl | 2 +- src/mod_pubsub_mnesia.erl | 2 +- src/mod_pubsub_sql.erl | 2 +- src/mod_push.erl | 2 +- src/mod_push_keepalive.erl | 2 +- src/mod_push_mnesia.erl | 2 +- src/mod_push_sql.erl | 2 +- src/mod_register.erl | 2 +- src/mod_register_web.erl | 2 +- src/mod_roster.erl | 2 +- src/mod_roster_mnesia.erl | 2 +- src/mod_roster_sql.erl | 2 +- src/mod_s2s_dialback.erl | 2 +- src/mod_service_log.erl | 2 +- src/mod_shared_roster.erl | 2 +- src/mod_shared_roster_ldap.erl | 2 +- src/mod_shared_roster_mnesia.erl | 2 +- src/mod_shared_roster_sql.erl | 2 +- src/mod_sic.erl | 2 +- src/mod_sip.erl | 2 +- src/mod_sip_proxy.erl | 2 +- src/mod_sip_registrar.erl | 2 +- src/mod_stats.erl | 2 +- src/mod_stream_mgmt.erl | 2 +- src/mod_stun_disco.erl | 2 +- src/mod_time.erl | 2 +- src/mod_vcard.erl | 2 +- src/mod_vcard_ldap.erl | 2 +- src/mod_vcard_mnesia.erl | 2 +- src/mod_vcard_sql.erl | 2 +- src/mod_vcard_xupdate.erl | 2 +- src/mod_version.erl | 2 +- src/mqtt_codec.erl | 2 +- src/node_flat.erl | 2 +- src/node_flat_sql.erl | 2 +- src/node_pep.erl | 2 +- src/node_pep_sql.erl | 2 +- src/nodetree_tree.erl | 2 +- src/nodetree_tree_sql.erl | 2 +- src/nodetree_virtual.erl | 2 +- src/prosody2ejabberd.erl | 2 +- src/proxy_protocol.erl | 2 +- src/pubsub_db_sql.erl | 2 +- src/pubsub_index.erl | 2 +- src/pubsub_migrate.erl | 2 +- src/pubsub_subscription.erl | 2 +- src/pubsub_subscription_sql.erl | 2 +- src/rest.erl | 2 +- src/str.erl | 2 +- src/translate.erl | 2 +- src/win32_dns.erl | 2 +- test/announce_tests.erl | 2 +- test/carbons_tests.erl | 2 +- test/csi_tests.erl | 2 +- test/ejabberd_SUITE.erl | 2 +- test/example_tests.erl | 2 +- test/jidprep_tests.erl | 2 +- test/ldap_srv.erl | 2 +- test/mam_tests.erl | 2 +- test/muc_tests.erl | 2 +- test/offline_tests.erl | 2 +- test/privacy_tests.erl | 2 +- test/private_tests.erl | 2 +- test/proxy65_tests.erl | 2 +- test/pubsub_tests.erl | 2 +- test/push_tests.erl | 2 +- test/replaced_tests.erl | 2 +- test/roster_tests.erl | 2 +- test/sm_tests.erl | 2 +- test/stundisco_tests.erl | 2 +- test/suite.erl | 2 +- test/upload_tests.erl | 2 +- test/vcard_tests.erl | 2 +- test/webadmin_tests.erl | 2 +- tools/xml_compress_gen.erl | 2 +- vars.config.in | 2 +- 283 files changed, 284 insertions(+), 284 deletions(-) diff --git a/include/bosh.hrl b/include/bosh.hrl index baf39e187..dd853e6b2 100644 --- a/include/bosh.hrl +++ b/include/bosh.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_auth.hrl b/include/ejabberd_auth.hrl index c8f2ea95a..443cc3228 100644 --- a/include/ejabberd_auth.hrl +++ b/include/ejabberd_auth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index 8707575f5..00001bb0a 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_ctl.hrl b/include/ejabberd_ctl.hrl index 73bd1ae7c..a93544b5c 100644 --- a/include/ejabberd_ctl.hrl +++ b/include/ejabberd_ctl.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_http.hrl b/include/ejabberd_http.hrl index 76444731c..77ef5edb1 100644 --- a/include/ejabberd_http.hrl +++ b/include/ejabberd_http.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_oauth.hrl b/include/ejabberd_oauth.hrl index 8ab0e8eb8..7cb712a49 100644 --- a/include/ejabberd_oauth.hrl +++ b/include/ejabberd_oauth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sm.hrl b/include/ejabberd_sm.hrl index cfb3480ff..cb9e9ae19 100644 --- a/include/ejabberd_sm.hrl +++ b/include/ejabberd_sm.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index 629fc2b1c..608ff3459 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sql_pt.hrl b/include/ejabberd_sql_pt.hrl index 584e0d5d5..36ce40225 100644 --- a/include/ejabberd_sql_pt.hrl +++ b/include/ejabberd_sql_pt.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_stacktrace.hrl b/include/ejabberd_stacktrace.hrl index d51426fad..03cd78f6a 100644 --- a/include/ejabberd_stacktrace.hrl +++ b/include/ejabberd_stacktrace.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_web_admin.hrl b/include/ejabberd_web_admin.hrl index 8acfb2947..7e4df96ce 100644 --- a/include/ejabberd_web_admin.hrl +++ b/include/ejabberd_web_admin.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/eldap.hrl b/include/eldap.hrl index 45db1d314..3ae1047d0 100644 --- a/include/eldap.hrl +++ b/include/eldap.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/http_bind.hrl b/include/http_bind.hrl index 4b379b2db..d55e7bcca 100644 --- a/include/http_bind.hrl +++ b/include/http_bind.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/logger.hrl b/include/logger.hrl index b6e7067fe..20bd50c09 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_announce.hrl b/include/mod_announce.hrl index 975d77070..7d680514e 100644 --- a/include/mod_announce.hrl +++ b/include/mod_announce.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_caps.hrl b/include/mod_caps.hrl index 6180868f5..29c1528fe 100644 --- a/include/mod_caps.hrl +++ b/include/mod_caps.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_last.hrl b/include/mod_last.hrl index 923ed20ce..b0da17ce0 100644 --- a/include/mod_last.hrl +++ b/include/mod_last.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_mam.hrl b/include/mod_mam.hrl index f76e0780e..bca473c62 100644 --- a/include/mod_mam.hrl +++ b/include/mod_mam.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_muc.hrl b/include/mod_muc.hrl index 173d32afe..127b71579 100644 --- a/include/mod_muc.hrl +++ b/include/mod_muc.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index 54773415c..a278e3ae3 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_offline.hrl b/include/mod_offline.hrl index 73b2c2386..58e52aef2 100644 --- a/include/mod_offline.hrl +++ b/include/mod_offline.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_privacy.hrl b/include/mod_privacy.hrl index 4e2345c17..d6a1286e2 100644 --- a/include/mod_privacy.hrl +++ b/include/mod_privacy.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_private.hrl b/include/mod_private.hrl index 46fdc5c1d..412ca8f29 100644 --- a/include/mod_private.hrl +++ b/include/mod_private.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_proxy65.hrl b/include/mod_proxy65.hrl index dfd3bf923..1a309a663 100644 --- a/include/mod_proxy65.hrl +++ b/include/mod_proxy65.hrl @@ -2,7 +2,7 @@ %%% RFC 1928 constants. %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_push.hrl b/include/mod_push.hrl index 1408c74a3..5b6ea40c5 100644 --- a/include/mod_push.hrl +++ b/include/mod_push.hrl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_roster.hrl b/include/mod_roster.hrl index 68ad0bd88..7e213fe69 100644 --- a/include/mod_roster.hrl +++ b/include/mod_roster.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_shared_roster.hrl b/include/mod_shared_roster.hrl index 1605831ed..5fc9fd3c5 100644 --- a/include/mod_shared_roster.hrl +++ b/include/mod_shared_roster.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_vcard.hrl b/include/mod_vcard.hrl index 6c5cf716f..56506f207 100644 --- a/include/mod_vcard.hrl +++ b/include/mod_vcard.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mqtt.hrl b/include/mqtt.hrl index 097e22d4c..e618914a6 100644 --- a/include/mqtt.hrl +++ b/include/mqtt.hrl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/include/pubsub.hrl b/include/pubsub.hrl index 8e2c2bd81..e625e7f23 100644 --- a/include/pubsub.hrl +++ b/include/pubsub.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 64d22c8d5..8a658ea3b 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -7814,4 +7814,4 @@ Configuration Guide: https://docs\&.ejabberd\&.im/admin/configuration Source code: https://github\&.com/processone/ejabberd .SH "COPYING" .sp -Copyright (c) 2002\-2023 ProcessOne\&. +Copyright (c) 2002\-2024 ProcessOne\&. diff --git a/rebar.config b/rebar.config index 6a715b586..0ac9ce379 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/rebar.config.script b/rebar.config.script index 9a26b6472..0304bf37c 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/rel/reltool.config.script b/rel/reltool.config.script index fea4d6619..f1f0852d2 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeniy Khramtsov -%%% @copyright (C) 2013-2023, Evgeniy Khramtsov +%%% @copyright (C) 2013-2024, Evgeniy Khramtsov %%% @doc %%% %%% @end diff --git a/sql/lite.new.sql b/sql/lite.new.sql index b5ec2098f..34d3953a1 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/lite.sql b/sql/lite.sql index 67067480d..8ef925eae 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql index 2227d0a6b..d4220aabf 100644 --- a/sql/mssql.new.sql +++ b/sql/mssql.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mssql.sql b/sql/mssql.sql index 93b8c6ed5..dc8e8cc12 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 8df583cf7..fc19a71e2 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mysql.sql b/sql/mysql.sql index 1f97039b4..84ef27387 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/pg.new.sql b/sql/pg.new.sql index bfb01723e..ffc88368d 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/pg.sql b/sql/pg.sql index 3c54466c8..ee5590564 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2023 ProcessOne +-- ejabberd, Copyright (C) 2002-2024 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/src/acl.erl b/src/acl.erl index 6b8ea42bf..b1f12c82f 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/econf.erl b/src/econf.erl index 0c6db6317..595c24bac 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -3,7 +3,7 @@ %%% Purpose : Validator for ejabberd configuration options %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 473fbb12c..49ae4912f 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index 09d4c0594..ed2365ff7 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -5,7 +5,7 @@ %%% Created : 7 Sep 2016 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index eda2a3c59..17e083bcb 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index c6a380e46..56390eb0b 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -5,7 +5,7 @@ %%% Created : 7 May 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 24e0b74c0..b65b0380e 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -5,7 +5,7 @@ %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 321527eda..0034e6a92 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -5,7 +5,7 @@ %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 056d475f8..17289e6fb 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -5,7 +5,7 @@ %%% Created : 17 Feb 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index 03adbbf4b..33a58e833 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl index e4b251a03..bf3abe221 100644 --- a/src/ejabberd_auth_jwt.erl +++ b/src/ejabberd_auth_jwt.erl @@ -5,7 +5,7 @@ %%% Created : 16 Mar 2019 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 891f3d0b4..096b56fc6 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl index d52dde868..81faab2d2 100644 --- a/src/ejabberd_auth_mnesia.erl +++ b/src/ejabberd_auth_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl index b542cb48d..17940640d 100644 --- a/src/ejabberd_auth_pam.erl +++ b/src/ejabberd_auth_pam.erl @@ -5,7 +5,7 @@ %%% Created : 5 Jul 2007 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index a1cda8cab..a0e69cdb4 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_backend_sup.erl b/src/ejabberd_backend_sup.erl index 412fb987f..8a724955c 100644 --- a/src/ejabberd_backend_sup.erl +++ b/src/ejabberd_backend_sup.erl @@ -2,7 +2,7 @@ %%% Created : 24 Feb 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_batch.erl b/src/ejabberd_batch.erl index e64335d6a..750c5978f 100644 --- a/src/ejabberd_batch.erl +++ b/src/ejabberd_batch.erl @@ -5,7 +5,7 @@ %%% Created : 8 mar 2022 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index 06aaacda9..f6ccb4887 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1a21cebdb..c1273d0dc 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -2,7 +2,7 @@ %%% Created : 8 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl index 0dab51ca0..2abbbebd3 100644 --- a/src/ejabberd_c2s_config.erl +++ b/src/ejabberd_c2s_config.erl @@ -6,7 +6,7 @@ %%% Created : 2 Nov 2007 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 12ab5abb8..bf3b3df8d 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -5,7 +5,7 @@ %%% Created : 26 Apr 2008 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl index 201ea4575..ff85de059 100644 --- a/src/ejabberd_cluster.erl +++ b/src/ejabberd_cluster.erl @@ -3,7 +3,7 @@ %%% Created : 5 Jul 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_cluster_mnesia.erl b/src/ejabberd_cluster_mnesia.erl index 6dee1d941..91910ad67 100644 --- a/src/ejabberd_cluster_mnesia.erl +++ b/src/ejabberd_cluster_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 7 Oct 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 0114b1720..9323ad656 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index f6fe25616..7d59bcc78 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index ff823554e..376a458e0 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -5,7 +5,7 @@ %%% Created : 14 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl index d15f7ebf7..87a87a824 100644 --- a/src/ejabberd_config_transformer.erl +++ b/src/ejabberd_config_transformer.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index df2cce2ce..a0feb49d8 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jan 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_db_sup.erl b/src/ejabberd_db_sup.erl index ca8ccd54e..9f9ce11df 100644 --- a/src/ejabberd_db_sup.erl +++ b/src/ejabberd_db_sup.erl @@ -2,7 +2,7 @@ %%% Created : 13 June 2019 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 6d7b3ab59..c5398350f 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -2,7 +2,7 @@ %%% File : ejabberd_doc.erl %%% Purpose : Options documentation generator %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index b12b0782a..3102c91a0 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -5,7 +5,7 @@ %%% Created : 8 Aug 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index f29f36063..a1dbd240c 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -5,7 +5,7 @@ %%% Created : 27 Feb 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index e14ca69e6..e8bb42cd8 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -5,7 +5,7 @@ %%% Created : 09-10-2010 by Eric Cestari %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl index b4957dd55..bddfb5198 100644 --- a/src/ejabberd_iq.erl +++ b/src/ejabberd_iq.erl @@ -5,7 +5,7 @@ %%% Created : 10 Nov 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 26fddb8c8..809e4a239 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 5147a7481..6c9ca918d 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -5,7 +5,7 @@ %%% Created : 30 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 897a4352e..33906d2ae 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -5,7 +5,7 @@ %%% Created : 12 May 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2023 ProcessOne +%%% ejabberd, Copyright (C) 2013-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index 350ae34db..c835f95ac 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 17 Nov 2016 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 72383afc1..febf35a5a 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -5,7 +5,7 @@ %%% Created : 20 Mar 2015 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl index f0eaf4065..649fdb87c 100644 --- a/src/ejabberd_oauth_mnesia.erl +++ b/src/ejabberd_oauth_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl index 1a18e2655..9e9bdc17f 100644 --- a/src/ejabberd_oauth_rest.erl +++ b/src/ejabberd_oauth_rest.erl @@ -5,7 +5,7 @@ %%% Created : 26 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index da9581575..ffd571bec 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -5,7 +5,7 @@ %%% Created : 27 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl index 695f74da1..832ac1517 100644 --- a/src/ejabberd_old_config.erl +++ b/src/ejabberd_old_config.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% Purpose: Transform old-style Erlang config to YAML config %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 43f334a0d..9551ba37b 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index b8c89f914..c0c1c5383 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 01fa24852..78081b53e 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -5,7 +5,7 @@ %%% Created : 17 Jul 2008 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl index 3ce952b1f..c3cb2049c 100644 --- a/src/ejabberd_pkix.erl +++ b/src/ejabberd_pkix.erl @@ -3,7 +3,7 @@ %%% Created : 4 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 98f938540..12a44949c 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -4,7 +4,7 @@ %%% Created : 8 May 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl index a0c1dbaa2..350f1292d 100644 --- a/src/ejabberd_redis_sup.erl +++ b/src/ejabberd_redis_sup.erl @@ -3,7 +3,7 @@ %%% Created : 6 Apr 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl index 9694c76c9..d474f1127 100644 --- a/src/ejabberd_regexp.erl +++ b/src/ejabberd_regexp.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2011 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 3a2c6f318..94f25f80d 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -5,7 +5,7 @@ %%% Created : 27 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl index 65a9b7a37..f53ee679f 100644 --- a/src/ejabberd_router_mnesia.erl +++ b/src/ejabberd_router_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 11 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 773a3788e..312718fd7 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -5,7 +5,7 @@ %%% Created : 11 Aug 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl index 23b9ecb24..8ffde1f6a 100644 --- a/src/ejabberd_router_redis.erl +++ b/src/ejabberd_router_redis.erl @@ -3,7 +3,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index d2e369317..d334e0f6f 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -3,7 +3,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 378a55a43..7fb99a7ed 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 7 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index ab8bb4503..1af45a161 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -2,7 +2,7 @@ %%% Created : 12 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index b9ae1abb3..00e36acb5 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -2,7 +2,7 @@ %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 3afa64f37..1220b2ce1 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -2,7 +2,7 @@ %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl index 1fdb1ffde..930f8674b 100644 --- a/src/ejabberd_shaper.erl +++ b/src/ejabberd_shaper.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sip.erl b/src/ejabberd_sip.erl index 5594c6b65..499162966 100644 --- a/src/ejabberd_sip.erl +++ b/src/ejabberd_sip.erl @@ -5,7 +5,7 @@ %%% Created : 30 Apr 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2023 ProcessOne +%%% ejabberd, Copyright (C) 2013-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 37ef9094b..1ff539ad1 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -5,7 +5,7 @@ %%% Created : 24 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_mnesia.erl b/src/ejabberd_sm_mnesia.erl index 1e0d1a143..2b223c85b 100644 --- a/src/ejabberd_sm_mnesia.erl +++ b/src/ejabberd_sm_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_redis.erl b/src/ejabberd_sm_redis.erl index abee42b39..3a7a0f99a 100644 --- a/src/ejabberd_sm_redis.erl +++ b/src/ejabberd_sm_redis.erl @@ -4,7 +4,7 @@ %%% Created : 11 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index ff06d3dda..96c640b90 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -4,7 +4,7 @@ %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 1e60b6fa7..1b9a07769 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 61f9e421c..8484183d9 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jan 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index d5d89d5f7..5c6d95868 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -5,7 +5,7 @@ %%% Created : 15 Aug 2023 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index 7320174e6..ccbc2374c 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -5,7 +5,7 @@ %%% Created : 22 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index cb7f36b98..e13dbbddc 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -5,7 +5,7 @@ %%% Created : 8 May 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2023 ProcessOne +%%% ejabberd, Copyright (C) 2013-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index 117244516..84605297d 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -5,7 +5,7 @@ %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl index eec7556a1..8bcf81db8 100644 --- a/src/ejabberd_system_monitor.erl +++ b/src/ejabberd_system_monitor.erl @@ -5,7 +5,7 @@ %%% Created : 21 Mar 2007 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_tmp_sup.erl b/src/ejabberd_tmp_sup.erl index 0423bfd3e..70d01a236 100644 --- a/src/ejabberd_tmp_sup.erl +++ b/src/ejabberd_tmp_sup.erl @@ -5,7 +5,7 @@ %%% Created : 18 Jul 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index 8077d6445..c9cbd906e 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -5,7 +5,7 @@ %%% Created : 27 Jan 2006 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_web.erl b/src/ejabberd_web.erl index 5152e2b38..d7867d90d 100644 --- a/src/ejabberd_web.erl +++ b/src/ejabberd_web.erl @@ -6,7 +6,7 @@ %%% Created : 28 Feb 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index eeb30dd29..f34887f66 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -5,7 +5,7 @@ %%% Created : 9 Apr 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -336,7 +336,7 @@ make_xhtml(Els, Host, Node, Lang, JID, Level) -> [?XE(<<"p">>, [?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>), ?C(<<" ">>), ?C(ejabberd_option:version()), - ?C(<<" (c) 2002-2023 ">>), + ?C(<<" (c) 2002-2024 ">>), ?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)] )])])])]}}. diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 21123eb23..57a3e9606 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -33,7 +33,7 @@ %%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %%% ========================================================================================================== -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%%---------------------------------------------------------------------- -module(ejabberd_websocket). diff --git a/src/ejabberd_websocket_codec.erl b/src/ejabberd_websocket_codec.erl index 4cdc7a3db..fb8c5586d 100644 --- a/src/ejabberd_websocket_codec.erl +++ b/src/ejabberd_websocket_codec.erl @@ -5,7 +5,7 @@ % Created : 9 sty 2023 by Paweł Chmielowski % % -% ejabberd, Copyright (C) 2002-2023 ProcessOne +% ejabberd, Copyright (C) 2002-2024 ProcessOne % % This program is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index 91c0f6e27..176a6af8e 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -5,7 +5,7 @@ %%% Created : 21 Aug 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl index df302158c..1400ba0da 100644 --- a/src/ejd2sql.erl +++ b/src/ejd2sql.erl @@ -5,7 +5,7 @@ %%% Created : 22 Aug 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_filter.erl b/src/eldap_filter.erl index c2a1deac3..be266954f 100644 --- a/src/eldap_filter.erl +++ b/src/eldap_filter.erl @@ -6,7 +6,7 @@ %%% Author: Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_pool.erl b/src/eldap_pool.erl index 6186d7fb9..d35edbd7a 100644 --- a/src/eldap_pool.erl +++ b/src/eldap_pool.erl @@ -5,7 +5,7 @@ %%% Created : 12 Nov 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl index c9bdf3f6b..ddf9b8d55 100644 --- a/src/eldap_utils.erl +++ b/src/eldap_utils.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl index ec9b727a7..0579528b7 100644 --- a/src/elixir_logger_backend.erl +++ b/src/elixir_logger_backend.erl @@ -5,7 +5,7 @@ %%% Created : 9 March 2016 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 922b4da65..7e4639bd8 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -5,7 +5,7 @@ %%% Created : 19 Feb 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2006-2023 ProcessOne +%%% ejabberd, Copyright (C) 2006-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/extauth.erl b/src/extauth.erl index 509fc9342..85bf4e6a7 100644 --- a/src/extauth.erl +++ b/src/extauth.erl @@ -2,7 +2,7 @@ %%% Created : 7 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/extauth_sup.erl b/src/extauth_sup.erl index a396a035f..723d73f74 100644 --- a/src/extauth_sup.erl +++ b/src/extauth_sup.erl @@ -2,7 +2,7 @@ %%% Created : 7 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index 72fc0ae30..f4d6babbe 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -5,7 +5,7 @@ %%% Created : 22 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_mod.erl b/src/gen_mod.erl index d649da6a8..21cb166bc 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -5,7 +5,7 @@ %%% Created : 24 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl index baf0ae444..82e308bc8 100644 --- a/src/gen_pubsub_node.erl +++ b/src/gen_pubsub_node.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl index 1fd198208..cd4be7f47 100644 --- a/src/gen_pubsub_nodetree.erl +++ b/src/gen_pubsub_nodetree.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl index 1a8dbb87a..f6bba4b8f 100644 --- a/src/jd2ejd.erl +++ b/src/jd2ejd.erl @@ -5,7 +5,7 @@ %%% Created : 2 Feb 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/misc.erl b/src/misc.erl index c08610524..3dd805665 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -8,7 +8,7 @@ %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 8a76e2bfc..0b2cfa064 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -5,7 +5,7 @@ %%% Created : 15 Nov 2005 by Magnus Henoch %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index c40720fa4..57555dc43 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -5,7 +5,7 @@ %%% Created : 10 Aug 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 85fb1320d..2f9e1e92e 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -5,7 +5,7 @@ %%% Created : 9 Aug 2017 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 7f6df0df5..ba8b71d4b 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -5,7 +5,7 @@ %%% Created : 11 Aug 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce_mnesia.erl b/src/mod_announce_mnesia.erl index e0ef13907..e004d151c 100644 --- a/src/mod_announce_mnesia.erl +++ b/src/mod_announce_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl index f0d949fa1..230b4035a 100644 --- a/src/mod_announce_sql.erl +++ b/src/mod_announce_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index c4de349c3..210d5e854 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -3,7 +3,7 @@ %%% Created : 13 Sep 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index baeef36af..72c210f1d 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -5,7 +5,7 @@ %%% Created : 25 Dec 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index efc8f6e11..19e002c4d 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -5,7 +5,7 @@ %%% Created : 24 Aug 2008 by Stephan Maka %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index dfe7482e3..1a6cfbdf6 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -7,7 +7,7 @@ %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_mnesia.erl b/src/mod_bosh_mnesia.erl index 7213ae5c8..e396dbbd9 100644 --- a/src/mod_bosh_mnesia.erl +++ b/src/mod_bosh_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 12 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_redis.erl b/src/mod_bosh_redis.erl index a7b71c18d..6337540c0 100644 --- a/src/mod_bosh_redis.erl +++ b/src/mod_bosh_redis.erl @@ -5,7 +5,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index 321f37db3..da943da9a 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -5,7 +5,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 65287826c..47f2d523c 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -5,7 +5,7 @@ %%% Created : 7 Oct 2006 by Magnus Henoch %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps_mnesia.erl b/src/mod_caps_mnesia.erl index d5e55023e..1b83705eb 100644 --- a/src/mod_caps_mnesia.erl +++ b/src/mod_caps_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl index f6ea56236..2a40dc497 100644 --- a/src/mod_caps_sql.erl +++ b/src/mod_caps_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index d22f8619b..9b8a916d6 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -7,7 +7,7 @@ %%% {mod_carboncopy, []} %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index 5b38560e3..a33ad1b5e 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -5,7 +5,7 @@ %%% Created : 11 Sep 2014 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2014-2023 ProcessOne +%%% ejabberd, Copyright (C) 2014-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_configure.erl b/src/mod_configure.erl index a36439577..f73471e4c 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -5,7 +5,7 @@ %%% Created : 19 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 64e2a48d0..6824bdd37 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -5,7 +5,7 @@ %%% Created : 8 Nov 2021 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index 919c53a7b..f45fea09f 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -4,7 +4,7 @@ %%% Purpose : XEP-0355: Namespace Delegation %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_disco.erl b/src/mod_disco.erl index ed4a37caa..4f092da4e 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -5,7 +5,7 @@ %%% Created : 1 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index cd5fb07fc..3c15a0a59 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -5,7 +5,7 @@ %%% Created : 15 Aug 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2023 ProcessOne +%%% ejabberd, Copyright (C) 2014-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index c6a969091..50efb2859 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -5,7 +5,7 @@ %%% Created : 15 Sep 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index 997ffaa9f..f04a9d464 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -5,7 +5,7 @@ %%% Created : %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 4efce9c5c..036be03f0 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -5,7 +5,7 @@ %%% Created : 20 Aug 2015 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2015-2023 ProcessOne +%%% ejabberd, Copyright (C) 2015-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 7c2742280..3782079f8 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -5,7 +5,7 @@ %%% Created : 15 Oct 2015 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2015-2023 ProcessOne +%%% ejabberd, Copyright (C) 2015-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_jidprep.erl b/src/mod_jidprep.erl index 9eaad095a..0d827242c 100644 --- a/src/mod_jidprep.erl +++ b/src/mod_jidprep.erl @@ -5,7 +5,7 @@ %%% Created : 11 Sep 2019 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2019-2023 ProcessOne +%%% ejabberd, Copyright (C) 2019-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last.erl b/src/mod_last.erl index 55cc43657..9cdf64f76 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -5,7 +5,7 @@ %%% Created : 24 Oct 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl index 78b25d322..f4ce111cf 100644 --- a/src/mod_last_mnesia.erl +++ b/src/mod_last_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl index 3d305af66..0c8f0a222 100644 --- a/src/mod_last_sql.erl +++ b/src/mod_last_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl index 57f659dec..c57ddca51 100644 --- a/src/mod_legacy_auth.erl +++ b/src/mod_legacy_auth.erl @@ -2,7 +2,7 @@ %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam.erl b/src/mod_mam.erl index f81b6a46b..7363eb9f1 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -5,7 +5,7 @@ %%% Created : 4 Jul 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2023 ProcessOne +%%% ejabberd, Copyright (C) 2013-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 95c5451d4..2371ee1a6 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index ee208b197..f923b3e0b 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl index 38822bef5..00257ed42 100644 --- a/src/mod_metrics.erl +++ b/src/mod_metrics.erl @@ -5,7 +5,7 @@ %%% Created : 22 Oct 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index 5521182d3..6f9563c64 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 1ae08e9ab..95d5052e6 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Pawel Chmielowski -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_bridge_session.erl b/src/mod_mqtt_bridge_session.erl index 7d38632d4..887883ac4 100644 --- a/src/mod_mqtt_bridge_session.erl +++ b/src/mod_mqtt_bridge_session.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Pawel Chmielowski -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_mnesia.erl b/src/mod_mqtt_mnesia.erl index dea407d70..efab73224 100644 --- a/src/mod_mqtt_mnesia.erl +++ b/src/mod_mqtt_mnesia.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl index 37ea8ce02..c5a177565 100644 --- a/src/mod_mqtt_session.erl +++ b/src/mod_mqtt_session.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index a75554e1b..9e12e30cb 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_ws.erl b/src/mod_mqtt_ws.erl index 236f0b2d7..4525f9c0a 100644 --- a/src/mod_mqtt_ws.erl +++ b/src/mod_mqtt_ws.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index b6f9fc3ff..88e48868d 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -5,7 +5,7 @@ %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 14eee4f9e..705ff4009 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -5,7 +5,7 @@ %%% Created : 8 Sep 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index d9eef97d2..f86812f7f 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -5,7 +5,7 @@ %%% Created : 12 Mar 2006 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index d994110ce..5be5b7928 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 37ebfa574..8e77882ea 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -5,7 +5,7 @@ %%% Created : %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 5d3099cb4..72ec867be 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5,7 +5,7 @@ %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 739d2ff61..3a616fff7 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -5,7 +5,7 @@ %%% Created : 17 kwi 2023 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index e7eec9c0f..ea0c416b3 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_sup.erl b/src/mod_muc_sup.erl index 0a17f1fb4..01dd10a1c 100644 --- a/src/mod_muc_sup.erl +++ b/src/mod_muc_sup.erl @@ -2,7 +2,7 @@ %%% Created : 4 Jul 2019 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 9b418049a..038c987b5 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -5,7 +5,7 @@ %%% Created : 29 May 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline.erl b/src/mod_offline.erl index a00530cb5..ff48b162a 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -5,7 +5,7 @@ %%% Created : 5 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl index 2cd511dfa..df9a96651 100644 --- a/src/mod_offline_mnesia.erl +++ b/src/mod_offline_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index 933da38da..b1eb4ed89 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 1cdb92368..4f7aab9ba 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jul 2009 by Brian Cully %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl index 04319d1c2..efafe2a0f 100644 --- a/src/mod_pres_counter.erl +++ b/src/mod_pres_counter.erl @@ -5,7 +5,7 @@ %%% Created : 23 Sep 2010 by Ahmed Omar %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 25717cadb..e5c69844d 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -5,7 +5,7 @@ %%% Created : 21 Jul 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl index 78eeb9b82..c39221f3e 100644 --- a/src/mod_privacy_mnesia.erl +++ b/src/mod_privacy_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index eb53b72ed..2811c6a8e 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private.erl b/src/mod_private.erl index 5fb5e9c15..5eb0970e1 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -5,7 +5,7 @@ %%% Created : 16 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private_mnesia.erl b/src/mod_private_mnesia.erl index e3619dc2a..f08756d62 100644 --- a/src/mod_private_mnesia.erl +++ b/src/mod_private_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl index 1b9584afe..806867965 100644 --- a/src/mod_private_sql.erl +++ b/src/mod_private_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 51a82769f..ad9362368 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -4,7 +4,7 @@ %%% Purpose : XEP-0356: Privileged Entity %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index a3f7e290f..8f0ce6f8d 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_lib.erl b/src/mod_proxy65_lib.erl index e7502075c..554b2e070 100644 --- a/src/mod_proxy65_lib.erl +++ b/src/mod_proxy65_lib.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_mnesia.erl b/src/mod_proxy65_mnesia.erl index f46160107..94c892b51 100644 --- a/src/mod_proxy65_mnesia.erl +++ b/src/mod_proxy65_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 16 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_redis.erl b/src/mod_proxy65_redis.erl index 88adfff95..a6de6c2b0 100644 --- a/src/mod_proxy65_redis.erl +++ b/src/mod_proxy65_redis.erl @@ -3,7 +3,7 @@ %%% Created : 31 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index 297c59bb6..742649268 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl index ffe9c3557..f1f9ab322 100644 --- a/src/mod_proxy65_sql.erl +++ b/src/mod_proxy65_sql.erl @@ -3,7 +3,7 @@ %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl index abbd9689d..d21b483a8 100644 --- a/src/mod_proxy65_stream.erl +++ b/src/mod_proxy65_stream.erl @@ -4,7 +4,7 @@ %%% Purpose : Bytestream process. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 87a73c9be..4cd2bd59e 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub_mnesia.erl b/src/mod_pubsub_mnesia.erl index 7df12d514..e0d62d7c1 100644 --- a/src/mod_pubsub_mnesia.erl +++ b/src/mod_pubsub_mnesia.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl index 2bc14b972..8fd3865de 100644 --- a/src/mod_pubsub_sql.erl +++ b/src/mod_pubsub_sql.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push.erl b/src/mod_push.erl index b71449696..815539803 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 3d38e1d89..03cdc1e02 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_mnesia.erl b/src/mod_push_mnesia.erl index 4e3baa342..0fbfae10e 100644 --- a/src/mod_push_mnesia.erl +++ b/src/mod_push_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index eb4f498e5..13de8c406 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -5,7 +5,7 @@ %%% Created : 26 Oct 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2023 ProcessOne +%%% ejabberd, Copyright (C) 2017-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_register.erl b/src/mod_register.erl index 29624508a..8ffb512d6 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 2370bed44..0a84e170c 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -5,7 +5,7 @@ %%% Created : 4 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 273b21380..b205db2ee 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -5,7 +5,7 @@ %%% Created : 11 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl index 3ef93a4dc..75d1a7668 100644 --- a/src/mod_roster_mnesia.erl +++ b/src/mod_roster_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 12caba645..8012fca83 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 527388d87..54a7aa10a 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -2,7 +2,7 @@ %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl index b7686b693..0239942c4 100644 --- a/src/mod_service_log.erl +++ b/src/mod_service_log.erl @@ -5,7 +5,7 @@ %%% Created : 24 Aug 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 9f94086c1..05c7305d4 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -5,7 +5,7 @@ %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 665f52827..2e294140b 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -7,7 +7,7 @@ %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl index 447fa174d..e4a487c94 100644 --- a/src/mod_shared_roster_mnesia.erl +++ b/src/mod_shared_roster_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl index 485b76b13..9c5233d25 100644 --- a/src/mod_shared_roster_sql.erl +++ b/src/mod_shared_roster_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 0079caf51..95911a466 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -5,7 +5,7 @@ %%% Created : 6 Mar 2010 by Karim Gemayel %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip.erl b/src/mod_sip.erl index 1e649579a..cc9e62744 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -5,7 +5,7 @@ %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2023 ProcessOne +%%% ejabberd, Copyright (C) 2014-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl index dbd17a9d6..8fae2f545 100644 --- a/src/mod_sip_proxy.erl +++ b/src/mod_sip_proxy.erl @@ -5,7 +5,7 @@ %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2023 ProcessOne +%%% ejabberd, Copyright (C) 2014-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl index 970a45256..6bd8bad26 100644 --- a/src/mod_sip_registrar.erl +++ b/src/mod_sip_registrar.erl @@ -5,7 +5,7 @@ %%% Created : 23 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2023 ProcessOne +%%% ejabberd, Copyright (C) 2014-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 83c179e62..7040680dd 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 1557091dc..339eec55b 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -3,7 +3,7 @@ %%% Created : 25 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index 9f77cc1a8..3021ac217 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -5,7 +5,7 @@ %%% Created : 18 Apr 2020 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2020-2023 ProcessOne +%%% ejabberd, Copyright (C) 2020-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_time.erl b/src/mod_time.erl index 9e580e304..8ee814baa 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -6,7 +6,7 @@ %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 87bb22080..43721e38d 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -5,7 +5,7 @@ %%% Created : 2 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index b07a83cfc..0c32923fc 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -4,7 +4,7 @@ %%% Created : 29 Jul 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl index f877f91bd..a8e3ed24e 100644 --- a/src/mod_vcard_mnesia.erl +++ b/src/mod_vcard_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index b663a4b00..cb8a1e441 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index 50a82ed78..e6a26864c 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -5,7 +5,7 @@ %%% Created : 9 Mar 2007 by Igor Goryachev %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_version.erl b/src/mod_version.erl index 64b7ba278..88235fb17 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -5,7 +5,7 @@ %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mqtt_codec.erl b/src/mqtt_codec.erl index 32bfac14d..7e0d31bff 100644 --- a/src/mqtt_codec.erl +++ b/src/mqtt_codec.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2023 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/node_flat.erl b/src/node_flat.erl index 22fe3dd32..b385e66e1 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 24f6c75f4..6db0dd06b 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_pep.erl b/src/node_pep.erl index 88e1225b3..3a79a7848 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl index 3a742a9f5..26b458e36 100644 --- a/src/node_pep_sql.erl +++ b/src/node_pep_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index 026f32014..32841fcc6 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index 608e8ea53..64af45b5d 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_virtual.erl b/src/nodetree_virtual.erl index d532dff64..455db2619 100644 --- a/src/nodetree_virtual.erl +++ b/src/nodetree_virtual.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 951993cc0..2ed6ea8e5 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -4,7 +4,7 @@ %%% Created : 20 Jan 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/proxy_protocol.erl b/src/proxy_protocol.erl index 9fb5eaf8b..8405cb037 100644 --- a/src/proxy_protocol.erl +++ b/src/proxy_protocol.erl @@ -5,7 +5,7 @@ %%% Created : 27 Nov 2018 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl index 4fe596290..e3e16fb45 100644 --- a/src/pubsub_db_sql.erl +++ b/src/pubsub_db_sql.erl @@ -5,7 +5,7 @@ %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_index.erl b/src/pubsub_index.erl index cd4b6faa1..ffa11adc4 100644 --- a/src/pubsub_index.erl +++ b/src/pubsub_index.erl @@ -5,7 +5,7 @@ %%% Created : 30 Apr 2009 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl index 285531752..9834c3022 100644 --- a/src/pubsub_migrate.erl +++ b/src/pubsub_migrate.erl @@ -5,7 +5,7 @@ %%% Created : 26 Jul 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl index db947bb64..74766d1fb 100644 --- a/src/pubsub_subscription.erl +++ b/src/pubsub_subscription.erl @@ -5,7 +5,7 @@ %%% Created : 29 May 2009 by Brian Cully %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl index 839d1589c..64207b696 100644 --- a/src/pubsub_subscription_sql.erl +++ b/src/pubsub_subscription_sql.erl @@ -6,7 +6,7 @@ %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/rest.erl b/src/rest.erl index 3ffe746d2..03c1f35b5 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -5,7 +5,7 @@ %%% Created : 16 Oct 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/str.erl b/src/str.erl index 8b2d53749..e69e8693d 100644 --- a/src/str.erl +++ b/src/str.erl @@ -5,7 +5,7 @@ %%% Created : 23 Feb 2012 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/translate.erl b/src/translate.erl index 88536a3c0..e5d6d8b09 100644 --- a/src/translate.erl +++ b/src/translate.erl @@ -5,7 +5,7 @@ %%% Created : 6 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/win32_dns.erl b/src/win32_dns.erl index 09b524b7f..cdbc6f4dd 100644 --- a/src/win32_dns.erl +++ b/src/win32_dns.erl @@ -5,7 +5,7 @@ %%% Created : 5 Mar 2009 by Geoff Cant %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/announce_tests.erl b/test/announce_tests.erl index 587b003f4..570f67df5 100644 --- a/test/announce_tests.erl +++ b/test/announce_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/carbons_tests.erl b/test/carbons_tests.erl index eb21f2a4c..2d9c4b606 100644 --- a/test/carbons_tests.erl +++ b/test/carbons_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/csi_tests.erl b/test/csi_tests.erl index 8803d8ffc..8886bb519 100644 --- a/test/csi_tests.erl +++ b/test/csi_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 5bd1e43b7..1e2fb3c37 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -3,7 +3,7 @@ %%% Created : 2 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/example_tests.erl b/test/example_tests.erl index 3d2841a66..ce699208b 100644 --- a/test/example_tests.erl +++ b/test/example_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/jidprep_tests.erl b/test/jidprep_tests.erl index db9196204..2091c4fc0 100644 --- a/test/jidprep_tests.erl +++ b/test/jidprep_tests.erl @@ -3,7 +3,7 @@ %%% Created : 11 Sep 2019 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2019-2023 ProcessOne +%%% ejabberd, Copyright (C) 2019-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/ldap_srv.erl b/test/ldap_srv.erl index 695eff12a..077c3de4b 100644 --- a/test/ldap_srv.erl +++ b/test/ldap_srv.erl @@ -3,7 +3,7 @@ %%% Created : 21 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/mam_tests.erl b/test/mam_tests.erl index ed2fecae9..c726bcbe7 100644 --- a/test/mam_tests.erl +++ b/test/mam_tests.erl @@ -3,7 +3,7 @@ %%% Created : 14 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/muc_tests.erl b/test/muc_tests.erl index 21a8488a6..ec3e2a334 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -3,7 +3,7 @@ %%% Created : 15 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/offline_tests.erl b/test/offline_tests.erl index 5dac6923e..4bd11a257 100644 --- a/test/offline_tests.erl +++ b/test/offline_tests.erl @@ -3,7 +3,7 @@ %%% Created : 7 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/privacy_tests.erl b/test/privacy_tests.erl index 1f60ee4ab..7faf59f5d 100644 --- a/test/privacy_tests.erl +++ b/test/privacy_tests.erl @@ -3,7 +3,7 @@ %%% Created : 18 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/private_tests.erl b/test/private_tests.erl index 25f55b1a6..5bb8b3a50 100644 --- a/test/private_tests.erl +++ b/test/private_tests.erl @@ -3,7 +3,7 @@ %%% Created : 23 Nov 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/proxy65_tests.erl b/test/proxy65_tests.erl index 6ff1100d5..6de5c4fea 100644 --- a/test/proxy65_tests.erl +++ b/test/proxy65_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/pubsub_tests.erl b/test/pubsub_tests.erl index d06bdcea0..427893e1a 100644 --- a/test/pubsub_tests.erl +++ b/test/pubsub_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/push_tests.erl b/test/push_tests.erl index 2aaecf5fa..931471353 100644 --- a/test/push_tests.erl +++ b/test/push_tests.erl @@ -3,7 +3,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/replaced_tests.erl b/test/replaced_tests.erl index 29ec6e94f..c8a81c9d3 100644 --- a/test/replaced_tests.erl +++ b/test/replaced_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/roster_tests.erl b/test/roster_tests.erl index c64cd27b0..f218a85d6 100644 --- a/test/roster_tests.erl +++ b/test/roster_tests.erl @@ -3,7 +3,7 @@ %%% Created : 22 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/sm_tests.erl b/test/sm_tests.erl index 3c2ffdcee..f013ccfff 100644 --- a/test/sm_tests.erl +++ b/test/sm_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/stundisco_tests.erl b/test/stundisco_tests.erl index 3e85304de..d699cebf7 100644 --- a/test/stundisco_tests.erl +++ b/test/stundisco_tests.erl @@ -3,7 +3,7 @@ %%% Created : 22 Apr 2020 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2020-2023 ProcessOne +%%% ejabberd, Copyright (C) 2020-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/suite.erl b/test/suite.erl index 2afc6165c..38803c517 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -3,7 +3,7 @@ %%% Created : 27 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/upload_tests.erl b/test/upload_tests.erl index e78df901d..0fb22dc29 100644 --- a/test/upload_tests.erl +++ b/test/upload_tests.erl @@ -3,7 +3,7 @@ %%% Created : 17 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/vcard_tests.erl b/test/vcard_tests.erl index e3170f78d..955077460 100644 --- a/test/vcard_tests.erl +++ b/test/vcard_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/webadmin_tests.erl b/test/webadmin_tests.erl index 9bd2626da..a8251dca7 100644 --- a/test/webadmin_tests.erl +++ b/test/webadmin_tests.erl @@ -3,7 +3,7 @@ %%% Created : 23 Mar 2020 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/tools/xml_compress_gen.erl b/tools/xml_compress_gen.erl index c3c494c8c..8d9e770b0 100644 --- a/tools/xml_compress_gen.erl +++ b/tools/xml_compress_gen.erl @@ -4,7 +4,7 @@ %% Created : 14 Sep 2018 Pawel Chmielowski %% %% -%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%% ejabberd, Copyright (C) 2002-2024 ProcessOne %% %% This program is free software; you can redistribute it and/or %% modify it under the terms of the GNU General Public License as diff --git a/vars.config.in b/vars.config.in index d0b4a392a..8bd4c9361 100644 --- a/vars.config.in +++ b/vars.config.in @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2023 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as From 399d0a5bf0298a09a796294110d513526606e45f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:51:01 +0100 Subject: [PATCH 0372/1302] Update Bulgarian translation (thanks to Mr. EddX) --- priv/msgs/bg.msg | 152 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 11 deletions(-) diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg index 4369cd41e..e19799fd4 100644 --- a/priv/msgs/bg.msg +++ b/priv/msgs/bg.msg @@ -48,6 +48,7 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Всеки, който има абонамент за присъствие на двете или от: може да се абонира и да извлича елементи"}. {"Anyone with Voice","Всеки, с възможност за гласово обаждане"}. {"Anyone","Всеки"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Очевидно Вашият акаунт няма административни права за този сървър. Моля, проверете как да предоставите администраторски права на: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Април"}. {"Attribute 'channel' is required for this request","Атрибутът 'канал' е задължителен за тази заявка"}. {"Attribute 'id' is mandatory for MIX messages","Атрибутът 'id' е задължителен за MIX съобщения"}. @@ -123,6 +124,9 @@ {"Dynamically specify a replyto of the item publisher","Динамично задаване на отговор към публикувалия елемента"}. {"Edit Properties","Редактиране на свойства"}. {"Either approve or decline the voice request.","Одобрете или отхвърлете заявката за гласова връзка."}. +{"ejabberd vCard module","ejabberd vCard модул"}. +{"ejabberd Web Admin","Уеб администрация на ejabberd"}. +{"ejabberd","ejabberd"}. {"Elements","Елементи"}. {"Email Address","Имейл адрес"}. {"Email","Илейл"}. @@ -247,7 +251,7 @@ {"Maximum file size","Максимален размер на файла"}. {"Maximum Number of History Messages Returned by Room","Максимален брой съобщения от хронологията, върнати от стая"}. {"Maximum number of items to persist","Максимален брой елементи за запазване"}. -{"Maximum Number of Occupants","Максимален брой потребители"}. +{"Maximum Number of Occupants","Максимален брой участници"}. {"May","Май"}. {"Members not added (inexistent vhost!): ","Членовете не са добавени (несъществуващ vhost!): "}. {"Membership is required to enter this room","Изисква се членство, за вход в тази стая"}. @@ -329,7 +333,7 @@ {"Notify subscribers when the node is deleted","Уведоми абонатите, когато нодът бъде изтрит"}. {"November","Ноември"}. {"Number of answers required","Брой на необходимите отговори"}. -{"Number of occupants","Брой потребители"}. +{"Number of occupants","Брой участници"}. {"Number of Offline Messages","Брой офлайн съобщения"}. {"Number of online users","Брой онлайн потребители"}. {"Number of registered users","Брой регистрирани потребители"}. @@ -346,6 +350,7 @@ {"Online Users:","Онлайн потребители:"}. {"Online","Онлайн"}. {"Only admins can see this","Само администратори могат да видят това"}. +{"Only collection node owners may associate leaf nodes with the collection","Само собственици на колекционни нодове имат право да свързват листови нодове към колекцията"}. {"Only deliver notifications to available users","Доставяне на известия само до наличните потребители"}. {"Only or tags are allowed","Само тагове и са разрешени"}. {"Only element is allowed in this query","Само елементът е разрешен за тази заявка"}. @@ -354,11 +359,12 @@ {"Only moderators are allowed to change the subject in this room","Само модераторите имат право да сменят темата в тази стая"}. {"Only moderators are allowed to retract messages","Само модераторите имат право да оттеглят съобщения"}. {"Only moderators can approve voice requests","Само модераторите могат да одобряват гласови заявки"}. -{"Only occupants are allowed to send messages to the conference","Само обитателите имат право да изпращат съобщения до конференцията"}. -{"Only occupants are allowed to send queries to the conference","Само обитателите имат право да изпращат запитвания до конференцията"}. +{"Only occupants are allowed to send messages to the conference","Само участници имат право да изпращат съобщения до конференцията"}. +{"Only occupants are allowed to send queries to the conference","Само участници имат право да изпращат запитвания до конференцията"}. {"Only publishers may publish","Само издателите могат да публикуват"}. {"Only service administrators are allowed to send service messages","Само администраторите на услуги имат право да изпращат системни съобщения"}. -{"Only those on a whitelist may subscribe and retrieve items","Само тези в белия списък могат да се абонират и да извличат елементи"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Само тези от списъка с позволени могат да свързват листови нодове с колекцията"}. +{"Only those on a whitelist may subscribe and retrieve items","Само тези от списъка с позволени могат да се абонират и да извличат елементи"}. {"Organization Name","Име на организацията"}. {"Organization Unit","Отдел"}. {"Other Modules Available:","Други налични модули:"}. @@ -375,6 +381,7 @@ {"Password:","Парола:"}. {"Path to Dir","Път към директория"}. {"Path to File","Път до файл"}. +{"Payload semantic type information","Информация за семантичен тип полезен товар"}. {"Pending","В очакване"}. {"Period: ","Период: "}. {"Persist items to storage","Запазване на елементите в хранилището"}. @@ -395,9 +402,9 @@ {"Publish model","Модел за публикуване"}. {"Publish-Subscribe","Публикуване-Абониране"}. {"PubSub subscriber request","Заявка от абонат за PubSub"}. -{"Purge all items when the relevant publisher goes offline","Изчисти всички елементи, когато съответният издател премине в режим офлайн"}. +{"Purge all items when the relevant publisher goes offline","Изчисти всички елементи, когато съответният публикуващ премине в режим офлайн"}. {"Push record not found","Push записът не е намерен"}. -{"Queries to the conference members are not allowed in this room","В тази зала не се допускат запитвания към членовете на конференцията"}. +{"Queries to the conference members are not allowed in this room","В тази стая не се допускат запитвания към членовете на конференцията"}. {"Query to another users is forbidden","Заявка към други потребители е забранена"}. {"RAM and disc copy","Копие в RAM и на диск"}. {"RAM copy","Копие в RAM"}. @@ -406,7 +413,7 @@ {"Receive notification from direct child nodes only","Получаване на известия само от директни подчинени нодове"}. {"Receive notification of new items only","Получаване на известия само за нови елементи"}. {"Receive notification of new nodes only","Получаване на известия само за нови нодове"}. -{"Recipient is not in the conference room","Получателят не е в конферентната зала"}. +{"Recipient is not in the conference room","Получателят не е в конферентната стая"}. {"Register an XMPP account","Регистрирай XMPP акаунт"}. {"Registered Users","Регистрирани потребители"}. {"Registered Users:","Регистрирани потребители:"}. @@ -434,7 +441,7 @@ {"Room Configuration","Конфигурация на стаята"}. {"Room creation is denied by service policy","Създаването на стая е отказано поради политика на услугата"}. {"Room description","Описание на стаята"}. -{"Room Occupants","Обитатели на стаята"}. +{"Room Occupants","Участници в стаята"}. {"Room terminates","Стаята се прекратява"}. {"Room title","Заглавие на стаята"}. {"Roster groups allowed to subscribe","Групи от списъци с контакти, на които е разрешено да се абонират"}. @@ -443,6 +450,7 @@ {"Roster:","Списък с контакти:"}. {"RPC Call Error","Грешка при RPC повикване"}. {"Running Nodes","Работещи нодове"}. +{"~s invites you to the room ~s","~s ви кани в стая ~s"}. {"Saturday","Събота"}. {"Script check","Проверка на скрипт"}. {"Search from the date","Търси от дата"}. @@ -470,7 +478,7 @@ {"Sources Specs:","Спецификации на източниците:"}. {"Specify the access model","Задай модела за достъп"}. {"Specify the event message type","Задай типа на съобщението за събитие"}. -{"Specify the publisher model","Задай модела на издателя"}. +{"Specify the publisher model","Задайте модела на публикуващия"}. {"Stanza id is not valid","Невалидно ID на строфата"}. {"Stanza ID","ID на строфа"}. {"Statically specify a replyto of the node owner(s)","Статично задаване на replyto на собственика(ците) на нода"}. @@ -496,7 +504,7 @@ {"Text associated with a sound","Текст, свързан със звук"}. {"Text associated with a video","Текст, свързан с видео"}. {"Text associated with speech","Текст, свързан с реч"}. -{"That nickname is already in use by another occupant","Този псевдоним вече се използва от друг потребител"}. +{"That nickname is already in use by another occupant","Този псевдоним вече се използва от друг участник"}. {"That nickname is registered by another person","Този псевдоним е регистриран от друго лице"}. {"The account already exists","Профилът вече съществува"}. {"The account was not unregistered","Профилът не е дерегистриран"}. @@ -504,6 +512,8 @@ {"The CAPTCHA is valid.","CAPTCHA предизвикателството е валидно."}. {"The CAPTCHA verification has failed","Проверката на CAPTCHA предизвикателството е неуспешна"}. {"The captcha you entered is wrong","Въведеният captcha код е грешен"}. +{"The child nodes (leaf or collection) associated with a collection","Дъщерните нодове (листови или колекция), свързани с колекция"}. +{"The collections with which a node is affiliated","Колекциите, с които даден нод е свързан"}. {"The DateTime at which a leased subscription will end or has ended","Датата и часът, на който абонамент ще приключи или е приключил"}. {"The datetime when the node was created","Датата, когато нодът е бил създаден"}. {"The default language of the node","Езикът по подразбиране на нода"}. @@ -514,10 +524,12 @@ {"The JIDs of those with an affiliation of publisher","JID-та на лица с принадлежност към публикуващи"}. {"The list of all online users","Списък на всички онлайн потребители"}. {"The list of all users","Списък на всички потребители"}. +{"The list of JIDs that may associate leaf nodes with a collection","Списъкът с JID, които могат да асоциират листови нодове с колекция"}. {"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимален брой подчинени нодове, които могат да бъдат свързани с колекция, или `max` за липса на конкретен лимит, различен от наложения от сървъра максимум"}. {"The minimum number of milliseconds between sending any two notification digests","Минималният брой милисекунди между изпращането на две извадки на известия"}. {"The name of the node","Името на нода"}. {"The node is a collection node","Нодът е от тип колекция"}. +{"The node is a leaf node (default)","Нодът е листов (по подразбиране)"}. {"The NodeID of the relevant node","NodeID на съответния нод"}. {"The number of pending incoming presence subscription requests","Броят на чакащите входящи заявки за абонамент за присъствие"}. {"The number of subscribers to the node","Бротят абонати на нода"}. @@ -534,6 +546,124 @@ {"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Информацията за семантичния тип данни на нода, обикновено зададена от пространството на имената на прикачените данни (ако има такива)"}. {"The sender of the last received message","Подателят на последното получено съобщение"}. {"The stanza MUST contain only one element, one element, or one element","Строфата ТРЯБВА да съдържа само един елемент, един елемент или един елемент"}. +{"The subscription identifier associated with the subscription request","Идентификаторът на абонамента, свързан със заявката за абонамент"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL адрес на XSL трансформацията, която може да се приложи към прикачените данни, за да се генерира подходящ елемент от тялото на съобщението."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms."}. +{"There was an error changing the password: ","Възникна грешка при промяна на паролата: "}. +{"There was an error creating the account: ","Възникна грешка при създаването на профила: "}. +{"There was an error deleting the account: ","Възникна грешка при изтриването на профила: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Не е чувствително към регистъра (главни и малки букви): \"macbeth\"е същото като \"MacBeth\"и \"Macbeth\"."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Тази страница позволява регистриране на XMPP профил на този сървър. Вашият JID (Jabber ID) ще бъде във формата: username@server. Моля, прочетете внимателно инструкциите, за да попълните правилно полетата."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Тази страница позволява премахване на XMPP профил от този сървър."}. +{"This room is not anonymous","Тази стая не е анонимна"}. +{"This service can not process the address: ~s","Тази услуга не може да обработи адреса: ~s"}. +{"Thursday","Четвъртък"}. +{"Time delay","Закъснение"}. +{"Timed out waiting for stream resumption","Времето за изчакване за възобновяване на потока изтече"}. +{"Time","Час"}. +{"To register, visit ~s","За да се регистрирате, посетете ~s"}. +{"To ~ts","До ~ts"}. +{"Token TTL","Токен TTL"}. +{"Too many active bytestreams","Твърде много активни \"bytestreams\" потоци"}. +{"Too many CAPTCHA requests","Твърде много CAPTCHA заявки"}. +{"Too many child elements","Твърде много дъщерни елементи"}. +{"Too many elements","Твърде много елементи"}. +{"Too many elements","Твърде много елементи"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Твърде много (~p) неуспешни опити за удостоверявания от този IP адрес (~s). Адресът ще бъде деблокиран в ~s UTC"}. +{"Too many receiver fields were specified","Посочени са твърде много полета за получател"}. +{"Too many unacked stanzas","Твърде много непотвърдени строфи"}. +{"Too many users in this conference","Твърде много потребители в тази конференция"}. +{"Total rooms","Общо стаи"}. +{"To","До"}. +{"Traffic rate limit is exceeded","Лимитът за трафик е надвишен"}. +{"Transactions Aborted:","Прекратени транзакции:"}. +{"Transactions Committed:","Извършени транзакции:"}. +{"Transactions Logged:","Регистрирани транзакции:"}. +{"Transactions Restarted:","Рестартирани транзакции:"}. +{"~ts's Offline Messages Queue","Офлайн съобщения на ~ts"}. +{"Tuesday","Вторник"}. +{"Unable to generate a CAPTCHA","Не може да се генерира CAPTCHA"}. +{"Unable to register route on existing local domain","Не може да се регистрира маршрут в съществуващ локален домейн"}. +{"Unauthorized","Неоторизиран"}. +{"Unexpected action","Неочаквано действие"}. +{"Unexpected error condition: ~p","Неочаквано състояние на грешка: ~p"}. +{"Uninstall","Деинсталирай"}. +{"Unregister an XMPP account","Дерегистрирай XMPP профил"}. +{"Unregister","Дерегистрирай"}. +{"Unselect All","Размаркирай всички"}. +{"Unsupported element","Неподдържан елемент "}. +{"Unsupported version","Неподдържана версия"}. +{"Update message of the day (don't send)","Актуализирай съобщението на деня (не изпращай)"}. +{"Update message of the day on all hosts (don't send)","Актуализирай съобщението на деня на всички хостове (не изпращай)"}. +{"Update plan","План за актуализация"}. +{"Update ~p","Актуализирай ~p"}. +{"Update script","Актуализиращ скрипт"}. +{"Update specs to get modules source, then install desired ones.","Актуализирайте спецификациите, за да получите източник на модули, след което инсталирайте желаните."}. +{"Update Specs","Актуализирай спецификациите"}. +{"Update","Актуализирай"}. +{"Upgrade","Обнови"}. +{"Uptime:","Време на работа:"}. +{"URL for Archived Discussion Logs","URL адрес за дневници на архивирани дискусии"}. +{"User already exists","Потребителят вече съществува"}. +{"User (jid)","Потребител (jid)"}. +{"User JID","Потребител JID"}. +{"User Management","Управление на потребители"}. +{"User removed","Потребителят е премахнат"}. +{"User session not found","Потребителската сесия не е намерена"}. +{"User session terminated","Потребителската сесия е прекратена"}. +{"User ~ts","Потребител ~ts"}. +{"Username:","Потребителско име:"}. +{"Users are not allowed to register accounts so quickly","Не е разрешено потребителите да регистрират профили толкова бързо"}. +{"Users Last Activity","Последна активност на потребителите"}. +{"Users","Потребители"}. +{"User","Потребител"}. +{"Validate","Валидирай"}. +{"Value 'get' of 'type' attribute is not allowed","Стойността 'get' на атрибут 'type' не е разрешена"}. +{"Value of '~s' should be boolean","Стойността на '~s' трябва да е булева"}. +{"Value of '~s' should be datetime string","Стойността на '~s' трябва да бъде низ за дата и час"}. +{"Value of '~s' should be integer","Стойността на '~s' трябва да бъде цяло число"}. +{"Value 'set' of 'type' attribute is not allowed","Стойността 'set' на атрибут 'type' не е разрешена"}. +{"View joined MIX channels","Вижте присъединените MIX канали"}. +{"View Queue","Вижте опашката"}. {"View Roster","Преглед на списъка с контакти"}. +{"Virtual Hosts","Виртуални хостове"}. +{"Visitors are not allowed to change their nicknames in this room","Посетителите нямат право да променят псевдонимите си в тази стая"}. +{"Visitors are not allowed to send messages to all occupants","На посетителите не е разрешено да изпращат съобщения до всички участници"}. +{"Visitor","Посетител"}. +{"Voice requests are disabled in this conference","Гласовите обаждания са деактивирани в тази конференция"}. +{"Voice request","Заявка за гласово обаждане"}. +{"Wednesday","Сряда"}. +{"When a new subscription is processed and whenever a subscriber comes online","Когато се обработва нов абонамент и всеки път, когато абонат се появи онлайн"}. +{"When a new subscription is processed","Когато се обработва нов абонамент"}. +{"When to send the last published item","Кога да изпратите последния публикуван елемент"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Дали даден обект иска да получи тяло на XMPP съобщение в допълнение към формата на полезен товар"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Дали даден обект желае да получава обобщения за известия или всички известия поотделно"}. +{"Whether an entity wants to receive or disable notifications","Дали даден обект желае да получава или деактивира известия"}. +{"Whether owners or publisher should receive replies to items","Дали собствениците или публикуващите трябва да получават отговори на елементи"}. +{"Whether the node is a leaf (default) or a collection","Дали нодът е листов (по подразбиране) или колекция"}. +{"Whether to allow subscriptions","Дали да се разрешат абонаменти"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Дали всички абонаменти да бъдат временни въз основа на присъствието на абонат"}. +{"Whether to notify owners about new subscribers and unsubscribes","Дали да се уведомяват собствениците за нови абонати и откази от абонамент"}. +{"Who can send private messages","Кой може да изпраща лични съобщения"}. +{"Who may associate leaf nodes with a collection","Кой може да асоциира листови нодове с колекция"}. +{"Wrong parameters in the web formulary","Грешни параметри в уеб формуляра"}. +{"Wrong xmlns","Грешен xmlns"}. +{"XMPP Account Registration","Регистриране на XMPP профил"}. +{"XMPP Domains","XMPP домейни"}. +{"XMPP Show Value of Away","XMPP покажи стойност на Отсъства"}. +{"XMPP Show Value of Chat","XMPP покажи стойност на Чат"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP покажи стойност на DND (Не ме безпокой)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP покажи стойност на Продължително отсъствие"}. +{"You are being removed from the room because of a system shutdown","Премахнати сте от стаята поради изключване на системата"}. +{"You are not allowed to send private messages","Нямате право да изпращате лични съобщения"}. +{"You are not joined to the channel","Не сте присъединени към канала"}. +{"You can later change your password using an XMPP client.","По-късно можете да промените паролата си с помощта на XMPP клиент."}. +{"You have been banned from this room","Достъпът ви до тази стая е забранен"}. +{"You have joined too many conferences","Присъединили сте се към твърде много конференции"}. +{"You must fill in field \"Nickname\" in the form","Трябва да попълните полето \"Псевдоним\" във формата"}. +{"You need a client that supports x:data and CAPTCHA to register","За да се регистрирате Ви е нужен клиент, който поддържа x:data и CAPTCHA"}. +{"You need a client that supports x:data to register the nickname","За да регистрирате псевдонима, Ви е необходим клиент, който поддържа x:data"}. +{"You need an x:data capable client to search","За да търсите, Ви е нужен клиент, който поддържа x:data"}. +{"Your XMPP account was successfully registered.","Вашият XMPP акаунт, беше регистриран успешно."}. +{"Your XMPP account was successfully unregistered.","Вашият XMPP акаунт, беше успешно дерегистриран."}. +{"You're not allowed to create nodes","Нямате право да създавате нодове"}. From f5652cf9462ca956e9467b4116c674231588d89f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:52:57 +0100 Subject: [PATCH 0373/1302] Update Czech translation (thanks to Jan Aschenbrenner) --- priv/msgs/cs.msg | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/priv/msgs/cs.msg b/priv/msgs/cs.msg index c910528cf..991eb2871 100644 --- a/priv/msgs/cs.msg +++ b/priv/msgs/cs.msg @@ -115,6 +115,7 @@ {"February",". února"}. {"File larger than ~w bytes","Soubor větší než ~w bytů"}. {"Friday","Pátek"}. +{"From ~ts","Od ~ts"}. {"From","Od"}. {"Full Name","Celé jméno"}. {"Get Number of Online Users","Získat počet online uživatelů"}. @@ -151,16 +152,18 @@ {"Incorrect value of 'action' attribute","Nesprávná hodnota atributu 'action'"}. {"Incorrect value of 'action' in data form","Nesprávná hodnota atributu 'action' v datovém formuláři"}. {"Incorrect value of 'path' in data form","Nesprávná hodnota atributu 'path' v datovém formuláři"}. +{"Installed Modules:","Instalované moduly:"}. {"Insufficient privilege","Nedostatečné oprávnění"}. {"Invalid 'from' attribute in forwarded message","Nesprávný atribut 'from' v přeposlané zprávě"}. {"Invitations are not allowed in this conference","Pozvánky nejsou povoleny v této místnosti"}. {"IP addresses","IP adresy"}. {"is now known as","se přejmenoval(a) na"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Není povoleno posílat chybové zprávy do místnosti. Účastník (~s) odeslal chybovou zprávu (~s) a byl vyhozen z místnosti"}. -{"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromé zprávy typu \"skupinová zpráva\" "}. +{"It is not allowed to send private messages of type \"groupchat\"","Není dovoleno odeslání soukromých zpráv typu \"skupinová zpráva\""}. {"It is not allowed to send private messages to the conference","Není povoleno odesílat soukromé zprávy v této místnosti"}. {"Jabber ID","Jabber ID"}. {"January",". ledna"}. +{"Joined MIX channels:","Připojené MIX kanály:"}. {"joins the room","vstoupil(a) do místnosti"}. {"July",". července"}. {"June",". června"}. @@ -181,7 +184,7 @@ {"Malformed username","Chybně formátováné jméno uživatele"}. {"March",". března"}. {"Max payload size in bytes","Maximální náklad v bajtech"}. -{"Maximum Number of Occupants","Počet účastníků"}. +{"Maximum Number of Occupants","Maximální počet účastníků"}. {"May",". května"}. {"Members:","Členové:"}. {"Membership is required to enter this room","Pro vstup do místnosti musíte být členem"}. @@ -200,7 +203,7 @@ {"Name","Jméno"}. {"Name:","Jméno:"}. {"Neither 'jid' nor 'nick' attribute found","Nebyl nalezen atribut 'jid' ani 'nick'"}. -{"Neither 'role' nor 'affiliation' attribute found","Nebyl nalezen atribut 'role' ani 'affiliation'"}. +{"Neither 'role' nor 'affiliation' attribute found","Nebyl nalezen atribut 'role' ani 'affiliation'"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. {"Nickname Registration at ","Registrace přezdívky na "}. @@ -266,6 +269,7 @@ {"Only service administrators are allowed to send service messages","Pouze správci služby smí odesílat servisní zprávy"}. {"Organization Name","Název firmy"}. {"Organization Unit","Oddělení"}. +{"Other Modules Available:","Ostatní dostupné moduly:"}. {"Outgoing s2s Connections","Odchozí s2s spojení"}. {"Outgoing s2s Connections:","Odchozí s2s spojení:"}. {"Owner privileges required","Jsou vyžadována práva vlastníka"}. @@ -319,7 +323,9 @@ {"Room Occupants","Počet účastníků"}. {"Room title","Název místnosti"}. {"Roster groups allowed to subscribe","Skupiny kontaktů, které mohou odebírat"}. +{"Roster of ~ts","Seznam kontaktů ~ts"}. {"Roster size","Velikost seznamu kontaktů"}. +{"Roster:","Seznam kontaktů:"}. {"RPC Call Error","Chyba RPC volání"}. {"Running Nodes","Běžící uzly"}. {"Saturday","Sobota"}. @@ -333,7 +339,7 @@ {"September",". září"}. {"Server:","Server:"}. {"Set message of the day and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. -{"Set message of the day on all hosts and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}. +{"Set message of the day on all hosts and send to online users","Nastavit zprávu dne na všech hostitelích a odeslat ji online uživatelům"}. {"Shared Roster Groups","Skupiny pro sdílený seznam kontaktů"}. {"Show Integral Table","Zobrazit kompletní tabulku"}. {"Show Ordinary Table","Zobrazit běžnou tabulku"}. @@ -357,17 +363,20 @@ {"Sunday","Neděle"}. {"That nickname is already in use by another occupant","Přezdívka je již používána jiným členem"}. {"That nickname is registered by another person","Přezdívka je zaregistrována jinou osobou"}. +{"The account was not unregistered","Účet nebyl smazán"}. {"The CAPTCHA is valid.","CAPTCHA souhlasí."}. {"The CAPTCHA verification has failed","Ověření CAPTCHA se nezdařilo"}. {"The collections with which a node is affiliated","Kolekce, se kterými je uzel spřízněn"}. {"The feature requested is not supported by the conference","Požadovaná vlastnost není podporována touto místností"}. +{"The number of subscribers to the node","Počet odběratelů uzlu"}. {"The password contains unacceptable characters","Heslo obsahuje nepovolené znaky"}. {"The password is too weak","Heslo je příliš slabé"}. {"the password is","heslo je"}. {"The query is only allowed from local users","Dotaz je povolen pouze pro místní uživatele"}. {"The query must not contain elements","Dotaz nesmí obsahovat elementy "}. -{"The stanza MUST contain only one element, one element, or one element","Stanza MUSÍ obsahovat pouze jeden element , jeden element nebo jeden element "}. -{"There was an error creating the account: ","Při vytváření účtu došlo k chybě:"}. +{"The stanza MUST contain only one element, one element, or one element","Stanza MUSÍ obsahovat pouze jeden element , jeden element nebo jeden element "}. +{"There was an error changing the password: ","Při změně hesla došlo k chybě: "}. +{"There was an error creating the account: ","Při vytváření účtu došlo k chybě: "}. {"There was an error deleting the account: ","Při mazání účtu došlo k chybě: "}. {"This room is not anonymous","Tato místnost není anonymní"}. {"Thursday","Čtvrtek"}. @@ -416,8 +425,8 @@ {"User","Uživatel"}. {"Validate","Ověřit"}. {"Value 'get' of 'type' attribute is not allowed","Hodnota 'get' atrubutu 'type' není povolena"}. -{"Value of '~s' should be boolean","Hodnota '~s' by měla být boolean"}. -{"Value of '~s' should be datetime string","Hodnota '~s' by měla být datetime řetězec"}. +{"Value of '~s' should be boolean","Hodnota '~s' by měla být boolean"}. +{"Value of '~s' should be datetime string","Hodnota '~s' by měla být datetime řetězec"}. {"Value of '~s' should be integer","Hodnota '~s' by měla být celé číslo"}. {"Value 'set' of 'type' attribute is not allowed","Hodnota 'set' atrubutu 'type' není povolena"}. {"vCard User Search","Hledání uživatelů ve vizitkách"}. From e65d5543090369a26999d1fb43585e0d1a721ee3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:54:41 +0100 Subject: [PATCH 0374/1302] Update Dutch translation (thanks to Ranforingus) --- priv/msgs/nl.msg | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/msgs/nl.msg b/priv/msgs/nl.msg index d6092595e..64fbbdab2 100644 --- a/priv/msgs/nl.msg +++ b/priv/msgs/nl.msg @@ -3,6 +3,7 @@ %% To improve translations please read: %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ +{" (Add * to the end of field to match substring)"," Gebruik de velden om te zoeken (Voeg achteraan het teken * toe om te zoeken naar alles wat met het eerste deel begint.)."}. {" has set the subject to: "," veranderde het onderwerp in: "}. {"A friendly name for the node","Bijnaam voor deze knoop"}. {"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. From 07e34566315b488b790de6d8bbbac470419b4c4a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:55:10 +0100 Subject: [PATCH 0375/1302] =?UTF-8?q?Update=20Portuguese=20translation=20(?= =?UTF-8?q?thanks=20to=20Silv=C3=A9rio=20Santos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/pt.msg | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index 474f8e29f..8d247022b 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -48,6 +48,7 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente, a sua conta não tem direitos de administração neste servidor. Verifique como conceder os direitos administrativos em: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. @@ -369,6 +370,7 @@ {"Only members may query archives of this room","Somente os membros podem procurar nos arquivos desta sala"}. {"Only moderators and participants are allowed to change the subject in this room","Somente os moderadores e os participamentes podem alterar o assunto desta sala"}. {"Only moderators are allowed to change the subject in this room","Somente os moderadores podem alterar o assunto desta sala"}. +{"Only moderators are allowed to retract messages","Apenas moderadores estão autorizados de retirar mensagens"}. {"Only moderators can approve voice requests","Somente moderadores podem aprovar requisições de voz"}. {"Only occupants are allowed to send messages to the conference","Só os ocupantes podem enviar mensagens para a sala"}. {"Only occupants are allowed to send queries to the conference","Só os ocupantes podem enviar consultas para a sala"}. @@ -392,6 +394,7 @@ {"Password:","Palavra-chave:"}. {"Path to Dir","Caminho para o directório"}. {"Path to File","Caminho do ficheiro"}. +{"Payload semantic type information","Informações de tipo semântico de carga útil"}. {"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. @@ -490,6 +493,7 @@ {"Specify the access model","Especificar os modelos de acesso"}. {"Specify the event message type","Especificar o tipo de mensagem para o evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. +{"Stanza id is not valid","A Stanza ID é inválido"}. {"Stanza ID","ID da estrofe"}. {"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. {"Statistics of ~p","Estatísticas de ~p"}. @@ -554,6 +558,7 @@ {"The query is only allowed from local users","Esta consulta só é permitida a partir de utilizadores locais"}. {"The query must not contain elements","A consulta não pode conter elementos "}. {"The room subject can be modified by participants","O tema da sala pode ser alterada pelos próprios participantes"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Informações de tipo semântico dos dados no nó, geralmente especificadas pelo espaço de nomes da carga útil (se houver)"}. {"The sender of the last received message","O remetente da última mensagem que foi recebida"}. {"The stanza MUST contain only one element, one element, or one element","A instância DEVE conter apenas um elemento , um elemento , ou um elemento "}. {"The subscription identifier associated with the subscription request","O identificador da assinatura associado à solicitação da assinatura"}. @@ -655,6 +660,7 @@ {"Whether to allow subscriptions","Permitir subscrições"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Caso todas as assinaturas devam ser temporárias, com base na presença do assinante"}. {"Whether to notify owners about new subscribers and unsubscribes","Caso deva notificar os proprietários sobre os novos assinantes e aqueles que cancelaram a assinatura"}. +{"Who can send private messages","Quem pode enviar mensagens privadas"}. {"Who may associate leaf nodes with a collection","Quem pode associar as folhas dos nós numa coleção"}. {"Wrong parameters in the web formulary","O formulário web está com os parâmetros errados"}. {"Wrong xmlns","Xmlns errado"}. @@ -666,6 +672,7 @@ {"XMPP Show Value of XA (Extended Away)","XMPP Exiba o valor do XA (Ausência Estendida)"}. {"XMPP URI of Associated Publish-Subscribe Node","XMPP URI da publicação do nó associado da assinatura"}. {"You are being removed from the room because of a system shutdown","Está a ser removido da sala devido ao desligamento do sistema"}. +{"You are not allowed to send private messages","Não tem permissão de enviar mensagens privadas"}. {"You are not joined to the channel","Não está inscrito no canal"}. {"You can later change your password using an XMPP client.","Pode alterar a sua palavra-passe mais tarde usando um cliente XMPP."}. {"You have been banned from this room","Foi banido desta sala"}. From 0c8d2ea5576d777cfc6efb08998a8d81501f18b5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:56:03 +0100 Subject: [PATCH 0376/1302] =?UTF-8?q?Update=20Ukrainian=20translation=20(t?= =?UTF-8?q?hanks=20to=20=D0=9E=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D1=80=20=D0=9A=D1=80=D0=B5=D0=B2=D1=81=D1=8C=D0=BA=D0=B8=D0=B9?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/uk.msg | 68 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index ea241c09a..47e0029bf 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -12,6 +12,7 @@ {"A Web Page","Веб-сторінка"}. {"Accept","Прийняти"}. {"Access denied by service policy","Доступ заборонений політикою служби"}. +{"Access model","Права доступу"}. {"Account doesn't exist","Обліковий запис не існує"}. {"Action on user","Дія над користувачем"}. {"Add a hat to a user","Додати капелюх користувачу"}. @@ -94,6 +95,7 @@ {"Configuration","Конфігурація"}. {"Connected Resources:","Підключені ресурси:"}. {"Contact Addresses (normally, room owner or owners)","Контактні адреси (зазвичай, власника або власників кімнати)"}. +{"Contrib Modules","Запуск модулів"}. {"Country","Країна"}. {"CPU Time:","Час роботи процесора:"}. {"Current Discussion Topic","Поточна тема обговорення"}. @@ -166,11 +168,15 @@ {"Full List of Room Admins","Повний перелік адміністраторів кімнати"}. {"Full List of Room Owners","Повний перелік власників кімнати"}. {"Full Name","Повне ім'я"}. +{"Get List of Online Users","Отримати кількість користувачів в мережі"}. +{"Get List of Registered Users","Отримати кількість зареєстрованих користувачів"}. {"Get Number of Online Users","Отримати Кількість Підключених Користувачів"}. {"Get Number of Registered Users","Отримати Кількість Зареєстрованих Користувачів"}. +{"Get Pending","Очікування"}. {"Get User Last Login Time","Отримати Час Останнього Підключення Користувача"}. {"Get User Password","Отримати Пароль Користувача"}. {"Get User Statistics","Отримати Статистику по Користувачу"}. +{"Given Name","По-батькові"}. {"Grant voice to this person?","Надати голос персоні?"}. {"Groups that will be displayed to the members","Групи, які показуватимуться учасникам"}. {"Groups","Групи"}. @@ -180,6 +186,9 @@ {"has been kicked because of an affiliation change","вигнано з кімнати внаслідок зміни рангу"}. {"has been kicked because the room has been changed to members-only","вигнано з кімнати тому, що вона стала тільки для учасників"}. {"has been kicked","вигнали з кімнати"}. +{"Hat title","Назва кімнати"}. +{"Hat URI","Назва URI"}. +{"Hats limit exceeded","Перевищено швидкість передачі інформації"}. {"Host unknown","Невідоме ім'я сервера"}. {"Host","Хост"}. {"HTTP File Upload","Відвантаження файлів по HTTP"}. @@ -202,6 +211,8 @@ {"Incorrect value of 'action' attribute","Неправильне значення атрибута \"action\""}. {"Incorrect value of 'action' in data form","Неправильне значення \"action\" у формі даних"}. {"Incorrect value of 'path' in data form","Неправильне значення \"path\" у формі даних"}. +{"Installed Modules:","Запуск модулів:"}. +{"Install","Встановлення"}. {"Insufficient privilege","Недостатньо привілеїв"}. {"Internal server error","Внутрішня помилка сервера"}. {"Invalid 'from' attribute in forwarded message","Неприйнятний атрибут \"from\" у пересланому повідомленні"}. @@ -215,6 +226,7 @@ {"It is not allowed to send private messages to the conference","Не дозволяється надсилати приватні повідомлення в конференцію"}. {"Jabber ID","Jabber ID"}. {"January","січня"}. +{"JID normalization denied by service policy","Створювати конференцію заборонено політикою служби"}. {"JID normalization failed","Помилка нормалізації JID"}. {"joins the room","увійшов(ла) в кімнату"}. {"July","липня"}. @@ -226,6 +238,7 @@ {"Last message","Останнє повідомлення"}. {"Last month","За останній місяць"}. {"Last year","За останній рік"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Найменш значущі біти хеш-функції SHA-256 від тексту мають відповідати шістнадцятковій мітці."}. {"leaves the room","вийшов(ла) з кімнати"}. {"List of rooms","Перелік кімнат"}. {"List of users with hats","Список користувачів із капелюхами"}. @@ -240,7 +253,9 @@ {"Make room persistent","Зробити кімнату постійною"}. {"Make room public searchable","Зробити кімнату видимою всім"}. {"Malformed username","Неправильне ім’я користувача"}. +{"MAM preference modification denied by service policy","Зміна налаштувань MAM відхилена через політики сервісу"}. {"March","березня"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Максимальна кількість елементів для зберігання, або max для встановлення не конкретизованого ліміту, окрім максимального, накладеного сервером"}. {"Max payload size in bytes","Максимальний розмір корисного навантаження в байтах"}. {"Maximum file size","Макс. розмір файлу"}. {"Maximum Number of History Messages Returned by Room","Максимальна кількість повідомлень історії на кімнату"}. @@ -250,7 +265,9 @@ {"Members not added (inexistent vhost!): ","Учасників не додано (вірт. сервер не існує!): "}. {"Membership is required to enter this room","В цю конференцію можуть входити тільки її члени"}. {"Members:","Члени:"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запам'ятайте свій пароль або запишіть його на папері та зберігайте у безпечному місці. У XMPP не існує автоматизованого способу відновлення паролю, якщо ви його забудете."}. {"Memory","Пам'ять"}. +{"Mere Availability in XMPP (No Show Value)","Проста доступність у XMPP (без показу статусу)"}. {"Message body","Тіло повідомлення"}. {"Message not found in forwarded payload","Повідомлення не знайдено в пересланому вмісті"}. {"Messages from strangers are rejected","Повідомлення від незнайомців відхиляються"}. @@ -259,6 +276,7 @@ {"Middle Name","По-батькові"}. {"Minimum interval between voice requests (in seconds)","Мінімальний інтервал між голосовими запитами (в секундах)"}. {"Moderator privileges required","Необхідні права модератора"}. +{"Moderators Only","Тільки модераторам"}. {"Moderator","Модератор"}. {"Modified modules","Змінені модулі"}. {"Module failed to handle the query","Модулю не вдалося обробити запит"}. @@ -266,8 +284,11 @@ {"Multicast","Мультікаст"}. {"Multiple elements are not allowed by RFC6121","Кілька елементів не дозволені RFC6121"}. {"Multi-User Chat","Багато-користувальницький чат"}. +{"Name in the rosters where this group will be displayed","Назва у списку контактів, де ця група буде відображена"}. {"Name","Назва"}. {"Name:","Назва:"}. +{"Natural Language for Room Discussions","Розмовна мова для обговорень у кімнаті"}. +{"Natural-Language Room Name","Назва кімнати розмовною мовою"}. {"Neither 'jid' nor 'nick' attribute found","Не знайдено ні атрибута \"jid\", ні \"nick\""}. {"Neither 'role' nor 'affiliation' attribute found","Не знайдено ні атрибута \"role\", ні \"affiliation\""}. {"Never","Ніколи"}. @@ -303,6 +324,7 @@ {"No services available","Немає доступних сервісів"}. {"No statistics found for this item","Для цього елемента статистичні дані не знайдено"}. {"No 'to' attribute found in the invitation","У запрошенні не знайдено атрибут \"до\""}. +{"Nobody","Ніхто"}. {"Node already exists","Вузол уже існує"}. {"Node ID","ID вузла"}. {"Node index not found","Індекс вузла не знайдено"}. @@ -324,6 +346,10 @@ {"Number of Offline Messages","Кількість автономних повідомлень"}. {"Number of online users","Кількість підключених користувачів"}. {"Number of registered users","Кількість зареєстрованих користувачів"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Кількість секунд, після яких автоматично видаляти елементи, або `max` для встановлення невизначеного ліміту, окрім максимального, накладеного сервером."}. +{"Occupants are allowed to invite others","Учасникам дозволено запрошувати інших"}. +{"Occupants are allowed to query others","Учасникам дозволено ставити запитання іншим"}. +{"Occupants May Change the Subject","Учасникам дозволено змінювати тему"}. {"October","грудня"}. {"Offline Messages","Офлайнові повідомлення"}. {"Offline Messages:","Офлайнові повідомлення:"}. @@ -340,19 +366,23 @@ {"Only members may query archives of this room","Тільки модератори можуть запитувати архіви цієї кімнати"}. {"Only moderators and participants are allowed to change the subject in this room","Тільки модератори та учасники можуть змінювати тему в цій кімнаті"}. {"Only moderators are allowed to change the subject in this room","Тільки модератори можуть змінювати тему в цій кімнаті"}. +{"Only moderators are allowed to retract messages","Тільки модераторам дозволено відкликати повідомлення"}. {"Only moderators can approve voice requests","Тільки модератори можуть схвалювати голосові запити"}. {"Only occupants are allowed to send messages to the conference","Тільки присутнім дозволяється надсилати повідомленняя в конференцію"}. {"Only occupants are allowed to send queries to the conference","Тільки присутнім дозволяється відправляти запити в конференцію"}. {"Only publishers may publish","Тільки видавці можуть публікувати"}. {"Only service administrators are allowed to send service messages","Тільки адміністратор сервісу може надсилати службові повідомлення"}. {"Only those on a whitelist may associate leaf nodes with the collection","Лише ті, хто входить до білого списку, можуть асоціювати листові вузли з колекцією"}. +{"Only those on a whitelist may subscribe and retrieve items","Тільки ті, хто є в білому списку, можуть підписуватися та отримувати елементи"}. {"Organization Name","Назва організації"}. {"Organization Unit","Відділ організації"}. +{"Other Modules Available:","Інші доступні модулі:"}. {"Outgoing s2s Connections","Вихідні s2s-з'єднання"}. {"Outgoing s2s Connections:","Вихідні s2s-з'єднання:"}. {"Owner privileges required","Необхідні права власника"}. {"Packet relay is denied by service policy","Пересилання пакетів заборонене політикою сервісу"}. {"Packet","Пакет"}. +{"Participant ID","ID учасника"}. {"Participant","Учасник"}. {"Password Verification","Перевірка Пароля"}. {"Password Verification:","Перевірка Пароля:"}. @@ -360,24 +390,36 @@ {"Password:","Пароль:"}. {"Path to Dir","Шлях до директорії"}. {"Path to File","Шлях до файла"}. +{"Payload semantic type information","Інформація про тип вмісту даних"}. {"Pending","Очікування"}. {"Period: ","Період: "}. -{"Persist items to storage","Зберегати публікації до сховища"}. -{"Ping","Пінг"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Зауважте, що ця опція відповідає за резервне копіювання тільки вбудованної бази даних Mnesia. Якщо Ви також використовуєте інше сховище для даних (наприклад за допомогою модуля ODBC), то його резервне копіювання потрібно робити окремо."}. -{"Please, wait for a while before sending new voice request","Будь ласка, почекайте деякий час перед тим, як знову відправляти голосовий запит"}. -{"Pong","Понг"}. -{"Present real Jabber IDs to","Зробити реальні Jabber ID учасників видимими"}. -{"Previous session not found","Попередній сеанс не знайдено"}. +{"Persist items to storage","Зберігати елементи в сховищі"}. +{"Persistent","Тривалий"}. +{"Ping query is incorrect","Запит ping некоректний"}. +{"Ping","Затримка (пінг)"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Зверніть увагу, що ці налаштування створюють резервну копію лише вбудованої бази даних Mnesia. Якщо ви використовуєте модуль ODBC, вам також потрібно окремо створити резервну копію вашої SQL-бази даних."}. +{"Please, wait for a while before sending new voice request","Будь ласка, зачекайте трохи перед тим, як відправляти новий голосовий запит"}. +{"Pong","Pong (відповідь на Ping, підтвердження взаємодії)"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Наявність атрибуту 'ask' не дозволена згідно з RFC6121"}. +{"Present real Jabber IDs to","Показувати реальні Jabber ID для"}. +{"Previous session not found","Попередню сесію не знайдено"}. +{"Previous session PID has been killed","PID попередньої сесії був зупинений"}. +{"Previous session PID has exited","PID попередньої сесії завершив роботу"}. +{"Previous session PID is dead","PID попередньої сесії більше не активний"}. +{"Previous session timed out","Час попередньої сесії вичерпано"}. {"private, ","приватна, "}. +{"Public","Публічний"}. +{"Publish model","Модель публікації"}. {"Publish-Subscribe","Публікація-Підписка"}. -{"PubSub subscriber request","Запит на підписку PubSub"}. -{"Purge all items when the relevant publisher goes offline","Видалити всі елементи, коли особа, що їх опублікувала, вимикається від мережі"}. +{"PubSub subscriber request","Запит підписника PubSub"}. +{"Purge all items when the relevant publisher goes offline","Видалити всі елементи, коли автор публікації виходить офлайн"}. {"Push record not found","Push-запис не знайдено"}. -{"Queries to the conference members are not allowed in this room","Запити до користувачів в цій конференції заборонені"}. -{"RAM and disc copy","ОЗП та диск"}. -{"RAM copy","ОЗП"}. -{"Really delete message of the day?","Насправді, видалити повідомлення дня?"}. +{"Queries to the conference members are not allowed in this room","Запити до учасників конференції не дозволені в цій кімнаті"}. +{"Query to another users is forbidden","Запит до інших користувачів заборонено"}. +{"RAM and disc copy","Копія оперативної пам'яті та диску"}. +{"RAM copy","Копія оперативної пам'яті"}. +{"Really delete message of the day?","Дійсно видалити повідомлення дня?"}. +{"Receive notification from all descendent nodes","Отримувати сповіщення від усіх підпорядкованих вузлів"}. {"Recipient is not in the conference room","Адресата немає в конференції"}. {"Register an XMPP account","Зареєструвати XMPP-запис"}. {"Registered Users","Зареєстровані користувачі"}. From e94edbdb8f88fb3ec194e8f523d1cfaf3998bb32 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 16:56:56 +0100 Subject: [PATCH 0377/1302] Update Chinese translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 413 ++++++++++++++++++++++++----------------------- 1 file changed, 210 insertions(+), 203 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index bd295bcc6..64924a826 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -4,8 +4,8 @@ %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ {" (Add * to the end of field to match substring)"," (在字段末添加*来匹配子串)"}. -{" has set the subject to: "," 已将标题设置为: "}. -{"# participants","# 个参与人"}. +{" has set the subject to: "," 已将标题设置为: "}. +{"# participants","# 参与者"}. {"A description of the node","该节点的描述"}. {"A friendly name for the node","该节点的友好名称"}. {"A password is required to enter this room","进入此房间需要密码"}. @@ -14,9 +14,9 @@ {"Access denied by service policy","访问被服务策略拒绝"}. {"Access model","访问模型"}. {"Account doesn't exist","账号不存在"}. -{"Action on user","对用户的动作"}. +{"Action on user","对用户执行的操作"}. {"Add a hat to a user","给用户添加头衔"}. -{"Add Jabber ID","添加Jabber ID"}. +{"Add Jabber ID","添加 Jabber ID"}. {"Add New","添加新用户"}. {"Add User","添加用户"}. {"Administration of ","管理 "}. @@ -25,18 +25,18 @@ {"All activity","所有活动"}. {"All Users","所有用户"}. {"Allow subscription","允许订阅"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","允许该Jabber ID订阅该pubsub节点?"}. -{"Allow this person to register with the room?","允许此人注册到该房间?"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","允许此 Jabber ID 订阅此 pubsub 节点?"}. +{"Allow this person to register with the room?","允许此用户注册此房间?"}. {"Allow users to change the subject","允许用户更改主题"}. {"Allow users to query other users","允许用户查询其它用户"}. {"Allow users to send invites","允许用户发送邀请"}. -{"Allow users to send private messages","允许用户发送私聊消息"}. -{"Allow visitors to change nickname","允许用户更改昵称"}. -{"Allow visitors to send private messages to","允许访客发送私聊消息至"}. -{"Allow visitors to send status text in presence updates","更新在线状态时允许用户发送状态文本"}. -{"Allow visitors to send voice requests","允许访客发送声音请求"}. -{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义房间会员资格相关联的LDAP群组; 按群组特定于实现或特定于部署的定义, 应该是一个LDAP专有名称."}. -{"Announcements","通知"}. +{"Allow users to send private messages","允许用户发送私信"}. +{"Allow visitors to change nickname","允许访客更改昵称"}. +{"Allow visitors to send private messages to","允许访客发送私信至"}. +{"Allow visitors to send status text in presence updates","允许访客在在线状态更新中发送状态文本"}. +{"Allow visitors to send voice requests","允许访客发送发言请求"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义房间成员资格相关联的 LDAP 群组;按群组特定于实现或特定于部署的定义,应该是一个 LDAP 专有名称。"}. +{"Announcements","公告"}. {"Answer associated with a picture","与图片关联的回答"}. {"Answer associated with a video","与视频关联的回答"}. {"Answer associated with speech","与讲话关联的回答"}. @@ -46,11 +46,12 @@ {"Anyone may publish","任何人都可以发布"}. {"Anyone may subscribe and retrieve items","任何人都可以订阅和检索内容项"}. {"Anyone with a presence subscription of both or from may subscribe and retrieve items","对全部或来源进行了状态订阅的任何人均可订阅并检索内容项"}. -{"Anyone with Voice","任何带声音的人"}. +{"Anyone with Voice","任何有发言权的人"}. {"Anyone","任何人"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","显然,您的账号在此服务器上没有管理权限。请检查如何授予管理员权限:https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","四月"}. {"Attribute 'channel' is required for this request","此请求要求'频道'属性"}. -{"Attribute 'id' is mandatory for MIX messages","对MIX消息, 'id' 属性为必填项"}. +{"Attribute 'id' is mandatory for MIX messages","对 MIX 消息,'id' 属性为必填项"}. {"Attribute 'jid' is not allowed here","此处不允许 'jid' 属性"}. {"Attribute 'node' is not allowed here","此处不允许 'node' 属性"}. {"Attribute 'to' of stanza that triggered challenge","触发挑战一节的 'to' 属性"}. @@ -64,46 +65,46 @@ {"Birthday","出生日期"}. {"Both the username and the resource are required","用户名和资源均为必填项"}. {"Bytestream already activated","字节流已经被激活"}. -{"Cannot remove active list","无法移除活跃列表"}. -{"Cannot remove default list","无法移除缺省列表"}. +{"Cannot remove active list","无法移除活动列表"}. +{"Cannot remove default list","无法移除默认列表"}. {"CAPTCHA web page","验证码网页"}. -{"Challenge ID","挑战ID"}. +{"Challenge ID","挑战 ID"}. {"Change Password","更改密码"}. {"Change User Password","更改用户密码"}. {"Changing password is not allowed","不允许修改密码"}. -{"Changing role/affiliation is not allowed","不允许修改角色/单位"}. +{"Changing role/affiliation is not allowed","不允许修改角色/从属关系"}. {"Channel already exists","频道已存在"}. {"Channel does not exist","频道不存在"}. {"Channel JID","频道 JID"}. {"Channels","频道"}. {"Characters not allowed:","不允许字符:"}. {"Chatroom configuration modified","聊天室配置已修改"}. -{"Chatroom is created","聊天室已被创建"}. -{"Chatroom is destroyed","聊天室已被销毁"}. -{"Chatroom is started","聊天室已被启动"}. -{"Chatroom is stopped","聊天室已被停用"}. +{"Chatroom is created","聊天室已创建"}. +{"Chatroom is destroyed","聊天室已解散"}. +{"Chatroom is started","聊天室已启动"}. +{"Chatroom is stopped","聊天室已停用"}. {"Chatrooms","聊天室"}. -{"Choose a username and password to register with this server","请选择在此服务器上注册所需的用户名和密码"}. +{"Choose a username and password to register with this server","选择要在此服务器上注册的用户名和密码"}. {"Choose storage type of tables","请选择表格的存储类型"}. -{"Choose whether to approve this entity's subscription.","选择是否允许该实体的订阅."}. +{"Choose whether to approve this entity's subscription.","选择是否允许该实体的订阅。"}. {"City","城市"}. {"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. {"Commands","命令"}. {"Conference room does not exist","会议室不存在"}. {"Configuration of room ~s","房间~s的配置"}. {"Configuration","配置"}. -{"Connected Resources:","已连接资源:"}. -{"Contact Addresses (normally, room owner or owners)","联系人地址 (通常为房间持有人)"}. +{"Connected Resources:","已连接资源:"}. +{"Contact Addresses (normally, room owner or owners)","联系人地址(通常为房间所有者)"}. {"Contrib Modules","Contrib 模块"}. -{"Country","国家"}. -{"CPU Time:","CPU时间:"}. +{"Country","国家/地区"}. +{"CPU Time:","CPU 时间:"}. {"Current Discussion Topic","当前讨论话题"}. {"Database failure","数据库失败"}. {"Database Tables at ~p","位于~p的数据库表"}. {"Database Tables Configuration at ","数据库表格配置位于 "}. {"Database","数据库"}. {"December","十二月"}. -{"Default users as participants","用户默认被视为参与人"}. +{"Default users as participants","默认用户为参与者"}. {"Delete content","删除内容"}. {"Delete message of the day on all hosts","删除所有主机上的每日消息"}. {"Delete message of the day","删除每日消息"}. @@ -112,62 +113,62 @@ {"Delete User","删除用户"}. {"Deliver event notifications","传递事件通知"}. {"Deliver payloads with event notifications","用事件通告传输有效负载"}. -{"Description:","描述:"}. +{"Description:","描述:"}. {"Disc only copy","仅磁盘复制"}. -{"'Displayed groups' not added (they do not exist!): ","'显示的群组' 未被添加 (它们不存在!): "}. -{"Displayed:","已显示:"}. -{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人, 就算是XMPP服务器的管理员也不可以."}. +{"'Displayed groups' not added (they do not exist!): ","'显示的群组' 未被添加(它们不存在!): "}. +{"Displayed:","已显示:"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人,甚至是 XMPP 服务器的管理员。"}. {"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. -{"Duplicated groups are not allowed by RFC6121","按照RFC6121的规则,不允许有重复的群组"}. +{"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许有重复的群组"}. {"Dynamically specify a replyto of the item publisher","为项目发布者动态指定一个 replyto"}. {"Edit Properties","编辑属性"}. -{"Either approve or decline the voice request.","接受或拒绝声音请求."}. +{"Either approve or decline the voice request.","批准或拒绝发言请求。"}. {"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. -{"ejabberd Multicast service","ejabberd多重映射服务"}. +{"ejabberd Multicast service","ejabberd 多重映射服务"}. {"ejabberd Publish-Subscribe module","ejabberd 发行-订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. -{"ejabberd vCard module","ejabberd vCard模块"}. -{"ejabberd Web Admin","ejabberd网页管理"}. +{"ejabberd vCard module","ejabberd vCard 模块"}. +{"ejabberd Web Admin","ejabberd Web 管理员"}. {"ejabberd","ejabberd"}. {"Elements","元素"}. -{"Email Address","电邮地址"}. +{"Email Address","电子邮件地址"}. {"Email","电子邮件"}. {"Enable hats","启用头衔"}. {"Enable logging","启用服务器端聊天记录"}. -{"Enable message archiving","启用消息归档"}. +{"Enable message archiving","启用消息存档"}. {"Enabling push without 'node' attribute is not supported","不支持未使用'node'属性就开启推送"}. {"End User Session","结束用户会话"}. {"Enter nickname you want to register","请输入您想要注册的昵称"}. {"Enter path to backup file","请输入备份文件的路径"}. -{"Enter path to jabberd14 spool dir","请输入jabberd14 spool目录的路径"}. +{"Enter path to jabberd14 spool dir","请输入 jabberd14 spool 目录的路径"}. {"Enter path to jabberd14 spool file","请输入 jabberd14 spool 文件的路径"}. {"Enter path to text file","请输入文本文件的路径"}. {"Enter the text you see","请输入您所看到的文本"}. {"Erlang XMPP Server","Erlang XMPP 服务器"}. {"Error","错误"}. -{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除Jabber ID"}. -{"Export all tables as SQL queries to a file:","将所有表以SQL查询语句导出到文件:"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器上所有用户的数据导出到 PIEFXIS 文件 (XEP-0227):"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","将某主机的用户数据导出到 PIEFXIS 文件 (XEP-0227):"}. +{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除 Jabber ID"}. +{"Export all tables as SQL queries to a file:","将所有表以 SQL 查询语句导出到文件:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器上所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","将某主机的用户数据导出到 PIEFXIS 文件(XEP-0227):"}. {"External component failure","外部组件失败"}. {"External component timeout","外部组件超时"}. {"Failed to activate bytestream","激活字节流失败"}. -{"Failed to extract JID from your voice request approval","无法从你的声音请求确认信息中提取JID"}. +{"Failed to extract JID from your voice request approval","从您的发言请求批准提取 JID 失败"}. {"Failed to map delegated namespace to external component","未能将代理命名空间映射到外部组件"}. -{"Failed to parse HTTP response","HTTP响应解析失败"}. +{"Failed to parse HTTP response","HTTP 响应解析失败"}. {"Failed to process option '~s'","选项'~s'处理失败"}. {"Family Name","姓氏"}. {"FAQ Entry","常见问题入口"}. {"February","二月"}. {"File larger than ~w bytes","文件大于 ~w 字节"}. -{"Fill in the form to search for any matching XMPP User","填充表单来搜索任何匹配的XMPP用户"}. +{"Fill in the form to search for any matching XMPP User","填充表单来搜索任何匹配的 XMPP 用户"}. {"Friday","星期五"}. {"From ~ts","来自 ~ts"}. {"From","从"}. {"Full List of Room Admins","房间管理员完整列表"}. -{"Full List of Room Owners","房间持有人完整列表"}. +{"Full List of Room Owners","房间所有者完整列表"}. {"Full Name","全名"}. {"Get List of Online Users","获取在线用户列表"}. {"Get List of Registered Users","获取注册用户列表"}. @@ -178,34 +179,34 @@ {"Get User Password","获取用户密码"}. {"Get User Statistics","获取用户统计"}. {"Given Name","中间名"}. -{"Grant voice to this person?","为此人授权声音?"}. -{"Groups that will be displayed to the members","将显示给会员的群组"}. +{"Grant voice to this person?","允许此用户发言?"}. +{"Groups that will be displayed to the members","将显示给成员的群组"}. {"Groups","组"}. {"Group","组"}. {"has been banned","已被禁止"}. {"has been kicked because of a system shutdown","因系统关机而被踢出"}. -{"has been kicked because of an affiliation change","因联属关系改变而被踢出"}. -{"has been kicked because the room has been changed to members-only","因该房间改为只对会员开放而被踢出"}. +{"has been kicked because of an affiliation change","因从属关系改变而被踢出"}. +{"has been kicked because the room has been changed to members-only","被踢了,因为房间已改为仅成员进入"}. {"has been kicked","已被踢出"}. {"Hat title","头衔标题"}. {"Hat URI","头衔 URI"}. {"Hats limit exceeded","已超过头衔限制"}. -{"Host unknown","主人未知"}. +{"Host unknown","主机未知"}. {"Host","主机"}. -{"HTTP File Upload","HTTP文件上传"}. -{"Idle connection","空闲的连接"}. -{"If you don't see the CAPTCHA image here, visit the web page.","如果您在这里没有看到验证码图片, 请访问网页."}. +{"HTTP File Upload","HTTP 文件上传"}. +{"Idle connection","空闲连接"}. +{"If you don't see the CAPTCHA image here, visit the web page.","如果您在此处没有看到 CAPTCHA 图片,请访问网页。"}. {"Import Directory","导入目录"}. {"Import File","导入文件"}. -{"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. +{"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. {"Import User from File at ","从以下位置的文件导入用户 "}. -{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件 (XEP-0227) 导入用户数据:"}. -{"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. +{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件(XEP-0227) 导入用户数据:"}. +{"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. {"Import Users from Dir at ","从以下位置目录导入用户 "}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. {"Improper domain part of 'from' attribute","不恰当的'from'属性域名部分"}. {"Improper message type","不恰当的消息类型"}. -{"Incoming s2s Connections:","入站 s2s 连接:"}. +{"Incoming s2s Connections:","入站 s2s 连接:"}. {"Incorrect CAPTCHA submit","提交的验证码不正确"}. {"Incorrect data form","数据形式不正确"}. {"Incorrect password","密码不正确"}. @@ -219,65 +220,65 @@ {"Invalid 'from' attribute in forwarded message","转发的信息中 'from' 属性的值无效"}. {"Invalid node name","无效的节点名称"}. {"Invalid 'previd' value","无效的 'previd' 值"}. -{"Invitations are not allowed in this conference","此会议不允许邀请"}. -{"IP addresses","IP地址"}. +{"Invitations are not allowed in this conference","此群聊不允许邀请"}. +{"IP addresses","IP 地址"}. {"is now known as","现在称呼为"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间"}. -{"It is not allowed to send private messages of type \"groupchat\"","\"群组聊天\"类型不允许发送私聊消息"}. -{"It is not allowed to send private messages to the conference","不允许向会议发送私聊消息"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此房间发送错误消息。参与者(~s) 发送了错误消息(~s),被踢出了此房间"}. +{"It is not allowed to send private messages of type \"groupchat\"","不允许发送“群聊”类型的私信"}. +{"It is not allowed to send private messages to the conference","不允许向群聊发送私信"}. {"Jabber ID","Jabber ID"}. {"January","一月"}. -{"JID normalization denied by service policy","JID规范化被服务策略拒绝"}. -{"JID normalization failed","JID规范化失败"}. +{"JID normalization denied by service policy","JID 规范化被服务策略拒绝"}. +{"JID normalization failed","JID 规范化失败"}. {"Joined MIX channels of ~ts","加入了 ~ts 的 MIX 频道"}. {"Joined MIX channels:","加入了 MIX 频道:"}. {"joins the room","加入房间"}. {"July","七月"}. {"June","六月"}. {"Just created","刚刚创建"}. -{"Label:","标签:"}. +{"Label:","标签:"}. {"Last Activity","上次活动"}. {"Last login","上次登录"}. {"Last message","最近消息"}. {"Last month","上个月"}. {"Last year","上一年"}. -{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的SHA-256哈希的最低有效位应等于十六进制标签"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的 SHA-256 哈希的最低有效位应等于十六进制标签"}. {"leaves the room","离开房间"}. {"List of rooms","房间列表"}. {"List of users with hats","有头衔用户的列表"}. {"List users with hats","有头衔用户列表"}. {"Logging","正在记录"}. {"Low level update script","低级别更新脚本"}. -{"Make participants list public","公开参与人列表"}. -{"Make room CAPTCHA protected","保护房间验证码"}. -{"Make room members-only","设置房间只接收会员"}. -{"Make room moderated","设置房间只接收主持人"}. -{"Make room password protected","进入此房间需要密码"}. -{"Make room persistent","永久保存该房间"}. -{"Make room public searchable","使房间可被公开搜索"}. -{"Malformed username","用户名无效"}. -{"MAM preference modification denied by service policy","MAM偏好被服务策略拒绝"}. +{"Make participants list public","公开参与者列表"}. +{"Make room CAPTCHA protected","使房间受验证码保护"}. +{"Make room members-only","使房间仅成员进入"}. +{"Make room moderated","对房间进行审核"}. +{"Make room password protected","使房间受密码保护"}. +{"Make room persistent","使房间持久"}. +{"Make room public searchable","使房间可公开搜索"}. +{"Malformed username","用户名格式不正确"}. +{"MAM preference modification denied by service policy","MAM 偏好被服务策略拒绝"}. {"March","三月"}. {"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要保留的最大项目数 #,`max`表示除了服务器强加的最大值之外没有特定限制"}. {"Max payload size in bytes","最大有效负载字节数"}. {"Maximum file size","最大文件大小"}. {"Maximum Number of History Messages Returned by Room","房间返回的历史消息最大值"}. {"Maximum number of items to persist","持久化内容的最大条目数"}. -{"Maximum Number of Occupants","允许的与会人最大数"}. +{"Maximum Number of Occupants","最大占用人数"}. {"May","五月"}. -{"Members not added (inexistent vhost!): ","成员未添加 (不存在的vhost!): "}. -{"Membership is required to enter this room","进入此房间需要会员身份"}. -{"Members:","会员:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","记住你的密码, 或将其记到纸上并放于安全位置. 如果你忘记了密码, XMPP也没有自动恢复密码的方式."}. +{"Members not added (inexistent vhost!): ","成员未添加(不存在的vhost!): "}. +{"Membership is required to enter this room","进入此房间需要成员资格"}. +{"Members:","成员:"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住您的密码,或将其写在纸上并放在安全的地方。 在 XMPP 中,如果您忘记密码,则没有自动恢复密码的方法。"}. {"Memory","内存"}. -{"Mere Availability in XMPP (No Show Value)","仅XMPP中的可用性 (不显示值)"}. +{"Mere Availability in XMPP (No Show Value)","仅 XMPP 中的可用性(不显示值)"}. {"Message body","消息主体"}. {"Message not found in forwarded payload","转发的有效载荷中找不到消息"}. -{"Messages from strangers are rejected","陌生人的消息会被拒绝"}. +{"Messages from strangers are rejected","拒绝来自陌生人的消息"}. {"Messages of type headline","标题类型的消息"}. {"Messages of type normal","普通类型的消息"}. {"Middle Name","中间名"}. -{"Minimum interval between voice requests (in seconds)","声音请求的最小间隔(以秒为单位)"}. +{"Minimum interval between voice requests (in seconds)","发言请求的最小间隔(秒)"}. {"Moderator privileges required","需要主持人权限"}. {"Moderators Only","仅限主持人"}. {"Moderator","主持人"}. @@ -289,13 +290,13 @@ {"Multi-User Chat","多用户聊天"}. {"Name in the rosters where this group will be displayed","花名册中将显示的该分组的名称"}. {"Name","姓名"}. -{"Name:","姓名:"}. +{"Name:","姓名:"}. {"Natural Language for Room Discussions","房间讨论的自然语言"}. {"Natural-Language Room Name","自然语言房间名称"}. {"Neither 'jid' nor 'nick' attribute found","属性 'jid' 或 'nick' 均未发现"}. {"Neither 'role' nor 'affiliation' attribute found","属性 'role' 或 'affiliation' 均未发现"}. {"Never","从未"}. -{"New Password:","新密码:"}. +{"New Password:","新密码:"}. {"Nickname can't be empty","昵称不能为空"}. {"Nickname Registration at ","昵称注册于 "}. {"Nickname ~s does not exist in the room","昵称~s不在该房间"}. @@ -329,7 +330,7 @@ {"No 'to' attribute found in the invitation","邀请中未发现 'to' 标签"}. {"Nobody","没有人"}. {"Node already exists","节点已存在"}. -{"Node ID","节点ID"}. +{"Node ID","节点 ID"}. {"Node index not found","没有找到节点索引"}. {"Node not found","没有找到节点"}. {"Node ~p","节点~p"}. @@ -340,82 +341,84 @@ {"Not allowed","不允许"}. {"Not Found","没有找到"}. {"Not subscribed","未订阅"}. -{"Notify subscribers when items are removed from the node","当从节点删除内容条目时通知订阅人"}. -{"Notify subscribers when the node configuration changes","当节点设置改变时通知订阅人"}. -{"Notify subscribers when the node is deleted","当节点被删除时通知订阅人"}. +{"Notify subscribers when items are removed from the node","当从节点删除内容条目时通知订阅者"}. +{"Notify subscribers when the node configuration changes","当节点设置改变时通知订阅者"}. +{"Notify subscribers when the node is deleted","当节点被删除时通知订阅者"}. {"November","十一月"}. {"Number of answers required","需要的回答数量"}. -{"Number of occupants","驻留人数"}. +{"Number of occupants","占用人数"}. {"Number of Offline Messages","离线消息数量"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. {"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","等待多少秒后自动清除项目,“max”表示除服务器施加的最大值外没有特定限制"}. -{"Occupants are allowed to invite others","允许成员邀请其他人"}. -{"Occupants are allowed to query others","成员可查询其他人"}. -{"Occupants May Change the Subject","成员可以修改主题"}. +{"Occupants are allowed to invite others","允许占用者邀请别人"}. +{"Occupants are allowed to query others","允许占用者查询别人"}. +{"Occupants May Change the Subject","占用者可以修改主题"}. {"October","十月"}. {"Offline Messages","离线消息"}. -{"Offline Messages:","离线消息:"}. +{"Offline Messages:","离线消息:"}. {"OK","确定"}. -{"Old Password:","旧密码:"}. +{"Old Password:","旧密码:"}. {"Online Users","在线用户"}. -{"Online Users:","在线用户:"}. +{"Online Users:","在线用户:"}. {"Online","在线"}. {"Only admins can see this","仅管理员可以看见此内容"}. {"Only collection node owners may associate leaf nodes with the collection","只有集合节点所有者可以将叶子节点与集合关联"}. {"Only deliver notifications to available users","仅将通知发送给可发送的用户"}. {"Only or tags are allowed","仅允许 标签"}. {"Only element is allowed in this query","此查询中只允许 元素"}. -{"Only members may query archives of this room","只有会员可以查询本房间的存档"}. -{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与人可以在此房间里更改主题"}. -{"Only moderators are allowed to change the subject in this room","只有主持人可以在此房间里更改主题"}. -{"Only moderators can approve voice requests","仅主持人能确认声音请求"}. -{"Only occupants are allowed to send messages to the conference","只有与会人可以向大会发送消息"}. -{"Only occupants are allowed to send queries to the conference","只有与会人可以向大会发出查询请求"}. +{"Only members may query archives of this room","只有成员才能查询此房间的存档"}. +{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与者才允许在此房间更改主题"}. +{"Only moderators are allowed to change the subject in this room","只有主持人才允许在此房间更改主题"}. +{"Only moderators are allowed to retract messages","只有主持人才允许撤回消息"}. +{"Only moderators can approve voice requests","只有主持人才能批准发言请求"}. +{"Only occupants are allowed to send messages to the conference","只有占用者才允许向群聊发送消息"}. +{"Only occupants are allowed to send queries to the conference","只有占用者才允许向群聊发送查询"}. {"Only publishers may publish","只有发布人可以发布"}. -{"Only service administrators are allowed to send service messages","只有服务管理员可以发送服务消息"}. +{"Only service administrators are allowed to send service messages","只有服务管理员才允许发送服务消息"}. {"Only those on a whitelist may associate leaf nodes with the collection","仅白名单用户可以将叶节点与集合关联"}. {"Only those on a whitelist may subscribe and retrieve items","仅白名单用户可以订阅和检索内容项"}. {"Organization Name","组织名称"}. {"Organization Unit","组织单位"}. {"Other Modules Available:","其他可用模块:"}. {"Outgoing s2s Connections","出站 s2s 连接"}. -{"Outgoing s2s Connections:","出站 s2s 连接:"}. -{"Owner privileges required","需要持有人权限"}. +{"Outgoing s2s Connections:","出站 s2s 连接:"}. +{"Owner privileges required","需要所有者权限"}. {"Packet relay is denied by service policy","包中继被服务策略拒绝"}. {"Packet","数据包"}. {"Participant ID","参与者 ID"}. -{"Participant","参与人"}. -{"Password Verification:","密码确认:"}. +{"Participant","参与者"}. +{"Password Verification:","密码确认:"}. {"Password Verification","确认密码"}. {"Password","密码"}. -{"Password:","密码:"}. +{"Password:","密码:"}. {"Path to Dir","目录的路径"}. {"Path to File","文件路径"}. +{"Payload semantic type information","有效载荷语义类型信息"}. {"Pending","挂起"}. -{"Period: ","持续时间: "}. +{"Period: ","持续时间: "}. {"Persist items to storage","持久化内容条目"}. -{"Persistent","永久"}. +{"Persistent","持久"}. {"Ping query is incorrect","Ping 查询不正确"}. {"Ping","Ping"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库. 如果您正在使用 ODBC 模块, 您还需要分别备份您的数据库."}. -{"Please, wait for a while before sending new voice request","请稍后再发送新的声音请求"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库。如果您正在使用 ODBC 模块,您还需要分别备份您的数据库。"}. +{"Please, wait for a while before sending new voice request","请稍候,然后再发送新的发言请求"}. {"Pong","Pong"}. {"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许处理 'ask' 属性"}. -{"Present real Jabber IDs to","将真实Jabber ID显示给"}. +{"Present real Jabber IDs to","将真实 Jabber ID 显示给"}. {"Previous session not found","上一个会话未找到"}. -{"Previous session PID has been killed","上一个会话的PID已被杀掉"}. -{"Previous session PID has exited","上一个会话的PID已退出"}. -{"Previous session PID is dead","上一个会话的PID已死"}. +{"Previous session PID has been killed","上一个会话的 PID 已被杀掉"}. +{"Previous session PID has exited","上一个会话的 PID 已退出"}. +{"Previous session PID is dead","上一个会话的 PID 已死"}. {"Previous session timed out","上一个会话已超时"}. -{"private, ","保密, "}. +{"private, ","保密, "}. {"Public","公开"}. {"Publish model","发布模型"}. {"Publish-Subscribe","发布-订阅"}. -{"PubSub subscriber request","PubSub订阅人请求"}. +{"PubSub subscriber request","PubSub 订阅者请求"}. {"Purge all items when the relevant publisher goes offline","相关发布人离线后清除所有选项"}. {"Push record not found","没有找到推送记录"}. -{"Queries to the conference members are not allowed in this room","本房间不可以查询会议成员信息"}. +{"Queries to the conference members are not allowed in this room","不允许在此群聊中查询群聊成员"}. {"Query to another users is forbidden","禁止查询其他用户"}. {"RAM and disc copy","内存与磁盘复制"}. {"RAM copy","内存(RAM)复制"}. @@ -425,9 +428,9 @@ {"Receive notification of new items only","仅接收新内容项的通知"}. {"Receive notification of new nodes only","仅接收新节点的通知"}. {"Recipient is not in the conference room","接收人不在会议室"}. -{"Register an XMPP account","注册XMPP帐户"}. +{"Register an XMPP account","注册 XMPP 账号"}. {"Registered Users","注册用户"}. -{"Registered Users:","注册用户:"}. +{"Registered Users:","注册用户:"}. {"Register","注册"}. {"Remote copy","远程复制"}. {"Remove a hat from a user","移除用户头衔"}. @@ -442,26 +445,26 @@ {"Restart Service","重启服务"}. {"Restart","重启"}. {"Restore Backup from File at ","从以下位置的文件恢复备份 "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. -{"Restore binary backup immediately:","立即恢复二进制备份:"}. -{"Restore plain text backup immediately:","立即恢复普通文本备份:"}. +{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. +{"Restore binary backup immediately:","立即恢复二进制备份:"}. +{"Restore plain text backup immediately:","立即恢复普通文本备份:"}. {"Restore","恢复"}. -{"Roles and Affiliations that May Retrieve Member List","可能会检索成员列表的角色和从属关系"}. +{"Roles and Affiliations that May Retrieve Member List","可以检索成员列表的角色和从属关系"}. {"Roles for which Presence is Broadcasted","被广播状态的角色"}. -{"Roles that May Send Private Messages","可以发送私聊消息的角色"}. +{"Roles that May Send Private Messages","可以发送私信的角色"}. {"Room Configuration","房间配置"}. {"Room creation is denied by service policy","创建房间被服务策略拒绝"}. {"Room description","房间描述"}. -{"Room Occupants","房间人数"}. +{"Room Occupants","房间占用者"}. {"Room terminates","房间终止"}. {"Room title","房间标题"}. {"Roster groups allowed to subscribe","允许订阅的花名册组"}. {"Roster of ~ts","~ts的花名册"}. {"Roster size","花名册大小"}. -{"Roster:","花名册:"}. +{"Roster:","花名册:"}. {"RPC Call Error","RPC 调用错误"}. {"Running Nodes","运行中的节点"}. -{"~s invites you to the room ~s","~s邀请你到房间~s"}. +{"~s invites you to the room ~s","~s 邀请您到房间 ~s"}. {"Saturday","星期六"}. {"Script check","脚本检查"}. {"Search from the date","从日期搜索"}. @@ -475,7 +478,7 @@ {"Send announcement to all users on all hosts","发送通知给所有主机上的所有用户"}. {"Send announcement to all users","发送通知给所有用户"}. {"September","九月"}. -{"Server:","服务器:"}. +{"Server:","服务器:"}. {"Service list retrieval timed out","服务列表检索超时"}. {"Session state copying timed out","会话状态复制超时"}. {"Set message of the day and send to online users","设定每日消息并发送给所有在线用户"}. @@ -485,28 +488,29 @@ {"Show Ordinary Table","显示普通列表"}. {"Shut Down Service","关闭服务"}. {"SOCKS5 Bytestreams","SOCKS5 字节流"}. -{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以在计算机里存储你的密码. 处于安全考虑, 请仅在你的个人计算机里使用该功能."}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以将您的密码存储在计算机中,但出于安全原因,您应该仅在个人计算机中这样做。"}. {"Sources Specs:","源参数:"}. {"Specify the access model","指定访问范例"}. {"Specify the event message type","指定事件消息类型"}. {"Specify the publisher model","指定发布人范例"}. -{"Stanza ID","节ID"}. +{"Stanza id is not valid","节 id 无效"}. +{"Stanza ID","节 ID"}. {"Statically specify a replyto of the node owner(s)","静态指定节点所有者的回复"}. {"Statistics of ~p","~p的统计"}. {"Statistics","统计"}. {"Stopped Nodes","已经停止的节点"}. {"Stop","停止"}. {"Storage Type","存储类型"}. -{"Store binary backup:","存储为二进制备份:"}. -{"Store plain text backup:","存储为普通文本备份:"}. +{"Store binary backup:","存储为二进制备份:"}. +{"Store plain text backup:","存储为普通文本备份:"}. {"Stream management is already enabled","流管理已启用"}. {"Stream management is not enabled","流管理未启用"}. {"Subject","标题"}. {"Submitted","已提交"}. {"Submit","提交"}. -{"Subscriber Address","订阅人地址"}. -{"Subscribers may publish","订阅人可以发布"}. -{"Subscription requests must be approved and only subscribers may retrieve items","订阅请求必须得到批准, 只有订阅人才能检索项目"}. +{"Subscriber Address","订阅者地址"}. +{"Subscribers may publish","订阅者可以发布"}. +{"Subscription requests must be approved and only subscribers may retrieve items","订阅请求必须得到批准,只有订阅者才能检索项目"}. {"Subscriptions are not allowed","不允许订阅"}. {"Subscription","订阅"}. {"Sunday","星期天"}. @@ -514,92 +518,93 @@ {"Text associated with a sound","与声音相关的文字"}. {"Text associated with a video","与视频相关的文字"}. {"Text associated with speech","与语音相关的文字"}. -{"That nickname is already in use by another occupant","该昵称已被另一用户使用"}. -{"That nickname is registered by another person","该昵称已被另一个人注册了"}. -{"The account already exists","帐户已存在"}. -{"The account was not unregistered","帐户未注册"}. +{"That nickname is already in use by another occupant","该昵称已被另一占用者使用了"}. +{"That nickname is registered by another person","该昵称已被另一用户注册了"}. +{"The account already exists","此账号已存在"}. +{"The account was not unregistered","此账号未注销"}. {"The body text of the last received message","最后收到的消息的正文"}. -{"The CAPTCHA is valid.","验证码有效."}. +{"The CAPTCHA is valid.","验证码有效。"}. {"The CAPTCHA verification has failed","验证码检查失败"}. {"The captcha you entered is wrong","您输入的验证码有误"}. -{"The child nodes (leaf or collection) associated with a collection","关联集合的字节点 (叶子或集合)"}. +{"The child nodes (leaf or collection) associated with a collection","关联集合的字节点(叶子或集合)"}. {"The collections with which a node is affiliated","加入结点的集合"}. {"The DateTime at which a leased subscription will end or has ended","租赁订阅将结束或已结束的日期时间"}. {"The datetime when the node was created","节点创建的日期时间"}. {"The default language of the node","该节点的默认语言"}. {"The feature requested is not supported by the conference","会议不支持所请求的特征"}. -{"The JID of the node creator","节点创建人的JID"}. -{"The JIDs of those to contact with questions","问题联系人的JID"}. -{"The JIDs of those with an affiliation of owner","隶属所有人的JID"}. -{"The JIDs of those with an affiliation of publisher","隶属发布人的JID"}. +{"The JID of the node creator","节点创建人的 JID"}. +{"The JIDs of those to contact with questions","问题联系人的 JID"}. +{"The JIDs of those with an affiliation of owner","拥有所有者从属关系的用户的 JID"}. +{"The JIDs of those with an affiliation of publisher","隶属发布人的 JID"}. {"The list of all online users","所有在线用户列表"}. {"The list of all users","所有用户列表"}. -{"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的JID列表"}. +{"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的 JID 列表"}. {"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合相关联的最大子节点数,“max”表示除服务器施加的最大值外没有特定限制"}. {"The minimum number of milliseconds between sending any two notification digests","发送任何两个通知摘要之间的最小毫秒数"}. {"The name of the node","该节点的名称"}. {"The node is a collection node","该节点是集合节点"}. -{"The node is a leaf node (default)","该节点是叶子节点 (默认)"}. -{"The NodeID of the relevant node","相关节点的NodeID"}. +{"The node is a leaf node (default)","该节点是叶子节点(默认)"}. +{"The NodeID of the relevant node","相关节点的 NodeID"}. {"The number of pending incoming presence subscription requests","待处理的传入状态订阅请求数"}. {"The number of subscribers to the node","该节点的订阅用户数"}. {"The number of unread or undelivered messages","未读或未发送的消息数"}. {"The password contains unacceptable characters","密码包含不可接受的字符"}. {"The password is too weak","密码强度太弱"}. {"the password is","密码是"}. -{"The password of your XMPP account was successfully changed.","你的XMPP帐户密码更新成功."}. +{"The password of your XMPP account was successfully changed.","您的 XMPP 账号密码已成功更改。"}. {"The password was not changed","密码未更新"}. {"The passwords are different","密码不一致"}. {"The presence states for which an entity wants to receive notifications","实体要为其接收通知的状态"}. {"The query is only allowed from local users","仅本地用户可以查询"}. {"The query must not contain elements","查询不能包含 元素"}. -{"The room subject can be modified by participants","房间主题可以被参与者修改"}. +{"The room subject can be modified by participants","房间主题可由参与者修改"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. {"The sender of the last received message","最后收到的消息的发送者"}. {"The stanza MUST contain only one element, one element, or one element","本节必须只含一个 元素, 元素,或 元素"}. {"The subscription identifier associated with the subscription request","与订阅请求关联的订阅标识符"}. -{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL转换的URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. -{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL转换的URL, 可以将其应用于有效负载格式, 以生成有效的数据表单结果, 客户端可以使用通用数据表单呈现引擎来显示该结果"}. -{"There was an error changing the password: ","修改密码出错: "}. -{"There was an error creating the account: ","帐户创建出错: "}. -{"There was an error deleting the account: ","帐户删除失败: "}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的."}. -{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上注册XMPP帐户. 你的JID (Jabber ID) 的形式如下: 用户名@服务器. 请仔细阅读说明并正确填写相应字段."}. -{"This page allows to unregister an XMPP account in this XMPP server.","此页面允许在此 XMPP 服务器上注销 XMPP 帐户。"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL 转换的 URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL 转换的 URL,可以将其应用于有效负载格式,以生成有效的数据表单结果,客户端可以使用通用数据表单呈现引擎来显示该结果"}. +{"There was an error changing the password: ","修改密码出错: "}. +{"There was an error creating the account: ","创建账号时出错: "}. +{"There was an error deleting the account: ","删除账号时出错: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:macbeth 与 MacBeth 和 Macbeth 是一样的。"}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上注册 XMPP 账号。您的 JID (Jabber ID) 的形式如下:username@server。请仔细阅读说明以正确填写字段。"}. +{"This page allows to unregister an XMPP account in this XMPP server.","此页面允许在此 XMPP 服务器上注销 XMPP 账号。"}. {"This room is not anonymous","此房间不是匿名房间"}. -{"This service can not process the address: ~s","此服务无法处理地址: ~s"}. +{"This service can not process the address: ~s","此服务无法处理地址:~s"}. {"Thursday","星期四"}. {"Time delay","时间延迟"}. {"Timed out waiting for stream resumption","等待流恢复超时"}. {"Time","时间"}. {"To register, visit ~s","要注册,请访问 ~s"}. {"To ~ts","发送到~ts"}. -{"Token TTL","TTL令牌"}. +{"Token TTL","TTL 令牌"}. {"Too many active bytestreams","活跃的字节流太多"}. {"Too many CAPTCHA requests","验证码请求太多"}. {"Too many child elements","太多子元素"}. {"Too many elements","太多 元素"}. {"Too many elements","太多 元素"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自IP地址(~p)的(~s)失败认证太多。将在UTC时间 ~s 解除对该地址的封锁"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自 IP 地址(~p)的(~s)失败认证太多。将在 UTC 时间 ~s 解除对该地址的封锁"}. {"Too many receiver fields were specified","指定的接收者字段太多"}. {"Too many unacked stanzas","未被确认的节太多"}. {"Too many users in this conference","该会议的用户太多"}. {"Total rooms","所有房间"}. {"To","到"}. {"Traffic rate limit is exceeded","已经超过传输率限制"}. -{"Transactions Aborted:","取消的事务:"}. -{"Transactions Committed:","提交的事务:"}. -{"Transactions Logged:","记入日志的事务:"}. -{"Transactions Restarted:","重启的事务:"}. +{"Transactions Aborted:","取消的事务:"}. +{"Transactions Committed:","提交的事务:"}. +{"Transactions Logged:","记入日志的事务:"}. +{"Transactions Restarted:","重启的事务:"}. {"~ts's Offline Messages Queue","~ts的离线消息队列"}. {"Tuesday","星期二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. {"Unable to register route on existing local domain","在已存在的本地域上无法注册路由"}. {"Unauthorized","未认证的"}. {"Unexpected action","意外行为"}. -{"Unexpected error condition: ~p","意外错误条件: ~p"}. +{"Unexpected error condition: ~p","意外错误条件:~p"}. {"Uninstall","卸载"}. -{"Unregister an XMPP account","注销XMPP帐户"}. -{"Unregister","取消注册"}. +{"Unregister an XMPP account","注销 XMPP 账号"}. +{"Unregister","注销"}. {"Unselect All","取消全选"}. {"Unsupported element","不支持的 元素"}. {"Unsupported version","不支持的版本"}. @@ -612,18 +617,18 @@ {"Update Specs","更新参数"}. {"Update","更新"}. {"Upgrade","升级"}. -{"Uptime:","正常运行时间:"}. -{"URL for Archived Discussion Logs","已归档对话日志的URL"}. +{"Uptime:","正常运行时间:"}. +{"URL for Archived Discussion Logs","已存档对话日志的 URL"}. {"User already exists","用户已存在"}. {"User (jid)","用户 (jid)"}. -{"User JID","用户JID"}. +{"User JID","用户 JID"}. {"User Management","用户管理"}. {"User removed","用户已移除"}. {"User session not found","用户会话未找到"}. {"User session terminated","用户会话已终止"}. {"User ~ts","用户~ts"}. -{"Username:","用户名:"}. -{"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册帐户"}. +{"Username:","用户名:"}. +{"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册账号"}. {"Users Last Activity","用户上次活动"}. {"Users","用户"}. {"User","用户"}. @@ -633,50 +638,52 @@ {"Value of '~s' should be datetime string","'~s' 的值应为日期时间字符串"}. {"Value of '~s' should be integer","'~s' 的值应为整数"}. {"Value 'set' of 'type' attribute is not allowed","不允许 'type' 属性的 'set' 值"}. -{"vCard User Search","vCard用户搜索"}. +{"vCard User Search","vCard 用户搜索"}. {"View joined MIX channels","查看已加入的 MIX 频道"}. {"View Queue","查看队列"}. {"View Roster","查看花名册"}. {"Virtual Hosts","虚拟主机"}. -{"Visitors are not allowed to change their nicknames in this room","此房间不允许用户更改昵称"}. -{"Visitors are not allowed to send messages to all occupants","不允许访客给所有占有者发送消息"}. +{"Visitors are not allowed to change their nicknames in this room","访客不允许在此房间更改其昵称"}. +{"Visitors are not allowed to send messages to all occupants","访客不允许向所有占用者发送信息"}. {"Visitor","访客"}. -{"Voice requests are disabled in this conference","该会议的声音请求已被禁用"}. -{"Voice request","声音请求"}. +{"Voice requests are disabled in this conference","此群聊中禁用发言请求"}. +{"Voice request","发言请求"}. {"Wednesday","星期三"}. {"When a new subscription is processed and whenever a subscriber comes online","当新的订阅被处理和当订阅者上线"}. {"When a new subscription is processed","当新的订阅被处理"}. {"When to send the last published item","何时发送最新发布的内容条目"}. -{"Whether an entity wants to receive an XMPP message body in addition to the payload format","除有效载荷格式外,实体是否还希望接收XMPP消息正文"}. -{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","实体是否要接收通知的摘要(汇总)或单独接收所有通知"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","除有效载荷格式外,实体是否还希望接收 XMPP 消息正文"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","实体是否要接收通知的摘要(汇总)或单独接收所有通知"}. {"Whether an entity wants to receive or disable notifications","实体是否要接收或禁用通知"}. -{"Whether owners or publisher should receive replies to items","持有人或创建人是否要接收项目回复"}. +{"Whether owners or publisher should receive replies to items","所有者或发布者是否应收到对项目的回复"}. {"Whether the node is a leaf (default) or a collection","节点是叶子(默认)还是集合"}. {"Whether to allow subscriptions","是否允许订阅"}. {"Whether to make all subscriptions temporary, based on subscriber presence","是否根据订阅者的存在将所有订阅设为临时"}. -{"Whether to notify owners about new subscribers and unsubscribes","是否将新订阅人和退订通知所有者"}. +{"Whether to notify owners about new subscribers and unsubscribes","是否通知所有者新的订阅者和退订者"}. +{"Who can send private messages","谁可以发送私信"}. {"Who may associate leaf nodes with a collection","谁可以将叶子节点与集合关联"}. {"Wrong parameters in the web formulary","网络配方中的参数错误"}. {"Wrong xmlns","错误的 xmlns"}. -{"XMPP Account Registration","XMPP帐户注册"}. -{"XMPP Domains","XMPP域"}. -{"XMPP Show Value of Away","XMPP的不在显示值"}. -{"XMPP Show Value of Chat","XMPP的聊天显示值"}. -{"XMPP Show Value of DND (Do Not Disturb)","XMPP的DND(勿扰)显示值"}. -{"XMPP Show Value of XA (Extended Away)","XMPP的XA (扩展不在)显示值"}. -{"XMPP URI of Associated Publish-Subscribe Node","发布-订阅节点关联的XMPP URI"}. -{"You are being removed from the room because of a system shutdown","因系统关机, 你正在被从房间移除"}. +{"XMPP Account Registration","XMPP 账号注册"}. +{"XMPP Domains","XMPP 域"}. +{"XMPP Show Value of Away","XMPP 的离开显示值"}. +{"XMPP Show Value of Chat","XMPP 的聊天显示值"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND (勿扰)显示值"}. +{"XMPP Show Value of XA (Extended Away)","XMPP 的 XA (延长离开)显示值"}. +{"XMPP URI of Associated Publish-Subscribe Node","发布-订阅节点关联的 XMPP URI"}. +{"You are being removed from the room because of a system shutdown","由于系统关闭,您将被从房间中移除"}. +{"You are not allowed to send private messages","不允许您发送私信"}. {"You are not joined to the channel","您未加入频道"}. -{"You can later change your password using an XMPP client.","你可以稍后用XMPP客户端修改你的密码."}. +{"You can later change your password using an XMPP client.","您稍后可以使用 XMPP 客户端更改密码。"}. {"You have been banned from this room","您已被禁止进入该房间"}. {"You have joined too many conferences","您加入的会议太多"}. -{"You must fill in field \"Nickname\" in the form","您必须填充表单中\"昵称\"项"}. +{"You must fill in field \"Nickname\" in the form","您必须填充表单中“昵称”项"}. {"You need a client that supports x:data and CAPTCHA to register","您需要一个支持 x:data 和验证码的客户端进行注册"}. {"You need a client that supports x:data to register the nickname","您需要一个支持 x:data 的客户端来注册昵称"}. {"You need an x:data capable client to search","您需要一个兼容 x:data 的客户端来搜索"}. -{"Your active privacy list has denied the routing of this stanza.","你的活跃私聊列表拒绝了在此房间进行路由分发."}. +{"Your active privacy list has denied the routing of this stanza.","您的活动隐私列表已拒绝路由此节。"}. {"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满。消息已被丢弃。"}. -{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s"}. -{"Your XMPP account was successfully registered.","你的XMPP帐户注册成功."}. -{"Your XMPP account was successfully unregistered.","你的XMPP帐户注销成功."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发送给~s的消息已被阻止。要解除阻止,请访问 ~s"}. +{"Your XMPP account was successfully registered.","您的 XMPP 账号注册成功。"}. +{"Your XMPP account was successfully unregistered.","您的 XMPP 账号注销成功。"}. {"You're not allowed to create nodes","您不可以创建节点"}. From e58be4d57e306cd14e82cf9902a240b27fd74637 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 17:04:17 +0100 Subject: [PATCH 0378/1302] Update man page --- man/ejabberd.yml.5 | 90 +++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 8a658ea3b..a165c07a7 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 10/16/2023 +.\" Date: 01/22/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "10/16/2023" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "01/22/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,7 +82,7 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/23\&.10/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.01/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" @@ -405,7 +405,7 @@ will be used\&. .RS 4 Supplement check for user existence based on \fImod_last\fR -data, for authentication methods that don\(cqt have a way to reliable tell if user exists (like is the case for +data, for authentication methods that don\(cqt have a way to reliably tell if a user exists (like is the case for \fIjwt\fR and certificate based authentication)\&. This helps with processing offline message for those users\&. The default value is \fItrue\fR\&. @@ -430,7 +430,9 @@ Git repository\&. Please refer to that module\(cqs README file for details\&. .PP \fBauth_password_format\fR: \fIplain | scram\fR .RS 4 -The option defines in what format the users passwords are stored: +The option defines in what format the users passwords are stored, plain text or in +SCRAM +format: .sp .RS 4 .ie n \{\ @@ -440,7 +442,7 @@ The option defines in what format the users passwords are stored: .sp -1 .IP \(bu 2.3 .\} -\fIplain\fR: The password is stored as plain text in the database\&. This is risky because the passwords can be read if your database gets compromised\&. This is the default value\&. This format allows clients to authenticate using: the old Jabber Non\-SASL (XEP\-0078), SASL PLAIN, SASL DIGEST\-MD5, and SASL SCRAM\-SHA\-1\&. +\fIplain\fR: The password is stored as plain text in the database\&. This is risky because the passwords can be read if your database gets compromised\&. This is the default value\&. This format allows clients to authenticate using: the old Jabber Non\-SASL (XEP\-0078), SASL PLAIN, SASL DIGEST\-MD5, and SASL SCRAM\-SHA\-1/256/512(\-PLUS)\&. .RE .sp .RS 4 @@ -451,14 +453,19 @@ The option defines in what format the users passwords are stored: .sp -1 .IP \(bu 2.3 .\} -\fIscram\fR: The password is not stored, only some information that allows to verify the hash provided by the client\&. It is impossible to obtain the original plain password from the stored information; for this reason, when this value is configured it cannot be changed to plain anymore\&. This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM\-SHA\-1\&. The default value is -\fIplain\fR\&. +\fIscram\fR: The password is not stored, only some information required to verify the hash provided by the client\&. It is impossible to obtain the original plain password from the stored information; for this reason, when this value is configured it cannot be changed to plain anymore\&. This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM\-SHA\-1/256/512(\-PLUS)\&. The SCRAM variant depends on the +\fIauth_scram_hash\fR +option\&. .RE .RE +.sp +The default value is \fIplain\fR\&. .PP \fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR .RS 4 -Hash algorithm that should be used to store password in SCRAM format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. The default value is +Hash algorithm that should be used to store password in +SCRAM +format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. The default value is \fIsha\fR\&. .RE .PP @@ -725,6 +732,14 @@ or is case\-insensitive\&. The default value is an empty list, i\&.e\&. no mechanisms are disabled by default\&. .RE .PP +\fBdisable_sasl_scram_downgrade_protection\fR: \fItrue | false\fR +.RS 4 +Allows to disable sending data required by +\fIXEP\-0474: SASL SCRAM Downgrade Protection\fR\&. There are known buggy clients (like those that use strophejs 1\&.6\&.2) which will not be able to authenticatate when servers sends data from that specification\&. This options allows server to disable it to allow even buggy clients connects, but in exchange decrease MITM protection\&. The default value of this option is +\fIfalse\fR +which enables this extension\&. +.RE +.PP \fBdomain_balancing\fR: \fI{Domain: Options}\fR .RS 4 An algorithm to load balance the components that are plugged on an ejabberd cluster\&. It means that you can plug one or several instances of the same component on each ejabberd node and that the traffic will be automatically distributed\&. The algorithm to deliver messages to the component(s) can be specified by this option\&. For any component connected as @@ -751,7 +766,7 @@ attribute; \fIfrom\fR attribute; \fIbare_destination\fR -\- by the the bare JID (without resource) of the packet\(cqs +\- by the bare JID (without resource) of the packet\(cqs \fIto\fR attribute; \fIbare_source\fR @@ -916,7 +931,7 @@ This ACL rule defines accounts that can use only this auth method, even if other .RS 4 By default, the JID is defined in the \fI"jid"\fR -JWT field\&. This option allows to specify other JWT field name where the JID is defined\&. +JWT field\&. In this option you can specify other JWT field name where the JID is defined\&. .RE .PP \fBjwt_key\fR: \fIFilePath\fR @@ -1123,7 +1138,7 @@ section for details\&. \fBnegotiation_timeout\fR: \fItimeout()\fR .RS 4 Time to wait for an XMPP stream negotiation to complete\&. When timeout occurs, the corresponding XMPP stream is closed\&. The default value is -\fI30\fR +\fI120\fR seconds\&. .RE .PP @@ -1139,9 +1154,9 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/23\&.10/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/23\&.10/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR -schema allows to handle several XMPP domains in a single ejabberd database\&. Using this +schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR schema is best when serving several XMPP domains and/or changing domains from time to time\&. This avoid need to manage several databases and handle complex configuration changes\&. The default depends on configuration flag \fI\-\-enable\-new\-sql\-schema\fR @@ -1689,6 +1704,15 @@ seconds\&. An SQL database name\&. For SQLite this must be a full path to a database file\&. The default value is \fIejabberd\fR\&. .RE +.sp +\fINote\fR about the next option: added in 24\&.01: +.PP +\fBsql_flags\fR: \fI[mysql_alternative_upsert]\fR +.RS 4 +This option accepts a list of SQL flags, and is empty by default\&. +\fImysql_alternative_upsert\fR +forces the alternative upsert implementation in MySQL\&. +.RE .PP \fBsql_keepalive_interval\fR: \fItimeout()\fR .RS 4 @@ -1738,7 +1762,7 @@ for MS SQL\&. The option has no effect for SQLite\&. .RS 4 This option is \fItrue\fR -by default, and is useful to disable prepared statements\&. The option is valid for PostgreSQL\&. +by default, and is useful to disable prepared statements\&. The option is valid for PostgreSQL and MySQL\&. .RE .PP \fBsql_query_timeout\fR: \fItimeout()\fR @@ -1831,12 +1855,18 @@ A user name for SQL authentication\&. The default value is Specify what proxies are trusted when an HTTP request contains the header \fIX\-Forwarded\-For\fR\&. You can specify \fIall\fR -to allow all proxies, or specify a list of IPs, possibly with masks\&. The default value is an empty list\&. This allows, if enabled, to be able to know the real IP of the request, for admin purpose, or security configuration (for example using +to allow all proxies, or specify a list of IPs, possibly with masks\&. The default value is an empty list\&. Using this option you can know the real IP of the request, for admin purpose, or security configuration (for example using \fImod_fail2ban\fR)\&. IMPORTANT: The proxy MUST be configured to set the \fIX\-Forwarded\-For\fR header if you enable this option as, otherwise, the client can set it itself and as a result the IP value cannot be trusted for security rules in ejabberd\&. .RE .PP +\fBupdate_sql_schema\fR: \fItrue | false\fR +.RS 4 +Allow ejabberd to update SQL schema\&. The default value is +\fItrue\fR\&. +.RE +.PP \fBuse_cache\fR: \fItrue | false\fR .RS 4 Enable or disable cache\&. The default is @@ -2186,7 +2216,7 @@ Limit any given JID by the number of avatars it is able to convert per minute\&. .RE .SS "mod_block_strangers" .sp -This module allows to block/log messages coming from an unknown entity\&. If a writing entity is not in your roster, you can let this module drop and/or log the message\&. By default you\(cqll just not receive message from that entity\&. Enable this module if you want to drop SPAM messages\&. +This module blocks and logs any messages coming from an unknown entity\&. If a writing entity is not in your roster, you can let this module drop and/or log the message\&. By default you\(cqll just not receive message from that entity\&. Enable this module if you want to drop SPAM messages\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3913,7 +3943,7 @@ or \fIsubscribe\fR or both, and \fIauthentication\fR -section with username/password field or certfile pointing to client certificate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certifcate authentication can be only used with mqtts, mqtt5s, wss, wss5\&. +section with username/password field or certfile pointing to client certificate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certificate authentication can be only used with mqtts, mqtt5s, wss, wss5\&. .RE .RE .sp @@ -4027,7 +4057,7 @@ option, but applied to this module only\&. .PP \fBdefault_room_options\fR: \fIOptions\fR .RS 4 -This option allows to define the desired default room options\&. Note that the creator of a room can modify the options of his room at any time using an XMPP client with MUC capability\&. The +Define the default room options\&. Note that the creator of a room can modify the options of his room at any time using an XMPP client with MUC capability\&. The \fIOptions\fR are: .PP @@ -4609,7 +4639,7 @@ to a remote file\&. By default a predefined CSS will be embedded into the HTML p .PP \fBdirname\fR: \fIroom_jid | room_name\fR .RS 4 -Allows to configure the name of the room directory\&. If set to +Configure the name of the room directory\&. If set to \fIroom_jid\fR, the room directory name will be the full room JID\&. Otherwise, the room directory name will be only the room name, not including the MUC service name\&. The default value is \fIroom_jid\fR\&. .RE @@ -5050,7 +5080,11 @@ This module implements support for XEP\-0199: XMPP Ping and periodic keepalives\ .PP \fBping_ack_timeout\fR: \fItimeout()\fR .RS 4 -How long to wait before deeming that a client has not answered a given server ping request\&. The default value is +How long to wait before deeming that a client has not answered a given server ping request\&. NOTE: when +\fImod_stream_mgmt\fR +is loaded and stream management is enabled by a client, this value is ignored, and the +ack_timeout +applies instead\&. The default value is \fIundefined\fR\&. .RE .PP @@ -5689,7 +5723,7 @@ or To specify whether or not pubsub should cache last items\&. Value is \fItrue\fR or -\fIfalse\fR\&. If not defined, pubsub does not cache last items\&. On systems with not so many nodes, caching last items speeds up pubsub and allows to raise user connection rate\&. The cost is memory usage, as every item is stored in memory\&. +\fIfalse\fR\&. If not defined, pubsub does not cache last items\&. On systems with not so many nodes, caching last items speeds up pubsub and allows you to raise the user connection rate\&. The cost is memory usage, as every item is stored in memory\&. .RE .sp \fINote\fR about the next option: added in 21\&.12: @@ -5770,7 +5804,7 @@ nodetree before\&. .PP \fBpep_mapping\fR: \fIList of Key:Value\fR .RS 4 -This allows to define a list of key\-value to choose defined node plugins on given PEP namespace\&. The following example will use +In this option you can provide a list of key\-value to choose defined node plugins on given PEP namespace\&. The following example will use \fInode_tune\fR instead of \fInode_pep\fR @@ -5823,7 +5857,7 @@ plugin handles the default behaviour and follows standard XEP\-0060 implementati .IP \(bu 2.3 .\} \fIpep\fR -plugin adds extension to handle Personal Eventing Protocol (XEP\-0163) to the PubSub engine\&. Adding pep allows to handle PEP automatically\&. +plugin adds extension to handle Personal Eventing Protocol (XEP\-0163) to the PubSub engine\&. When enabled, PEP is handled automatically\&. .RE .RE .PP @@ -6094,7 +6128,7 @@ on the requested username, registration of that user name is denied\&. There are .RS 4 By default, \fIejabberd\fR -doesn\(cqt allow to register new accounts from s2s or existing c2s sessions\&. You can change it by defining access rule in this option\&. Use with care: allowing registration from s2s leads to uncontrolled massive accounts creation by rogue users\&. +doesn\(cqt allow the client to register new accounts from s2s or existing c2s sessions\&. You can change it by defining access rule in this option\&. Use with care: allowing registration from s2s leads to uncontrolled massive accounts creation by rogue users\&. .RE .PP \fBaccess_remove\fR: \fIAccessName\fR @@ -6954,7 +6988,7 @@ It is not enough to just load this module\&. You should also configure listeners .PP \fBalways_record_route\fR: \fItrue | false\fR .RS 4 -Always insert "Record\-Route" header into SIP messages\&. This approach allows to bypass NATs/firewalls a bit more easily\&. The default value is +Always insert "Record\-Route" header into SIP messages\&. With this approach it is possible to bypass NATs/firewalls a bit more easily\&. The default value is \fItrue\fR\&. .RE .PP @@ -7797,13 +7831,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 23\&.10\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 23\&.10\&.118\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/23\&.10/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.01/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 6e9c96a0e5306df2c712990ab4ee8cdb6b50b313 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jan 2024 17:06:21 +0100 Subject: [PATCH 0379/1302] Run: make doap --- ejabberd.doap | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ejabberd.doap b/ejabberd.doap index 6eda6e7aa..6821e2890 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -629,6 +629,24 @@ mod_mix + + + + 0.3.0 + 24.01 + + + + + + + + 0.4.0 + 24.01 + + + + @@ -683,6 +701,15 @@ mod_muc_occupantid + + + + 0.4.0 + 24.01 + + + + @@ -692,6 +719,15 @@ mod_mam + + + + 0.4.0 + 24.01 + + + + @@ -701,5 +737,14 @@ mod_mam + + + + 0.3.0 + 24.01 + + + + From 506152d4e7b7d6710d4d99c5dcb68fa4c30650e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 23 Jan 2024 13:39:09 +0100 Subject: [PATCH 0380/1302] Use tagged version of dependencies --- mix.exs | 8 ++++---- mix.lock | 16 ++++++++-------- rebar.config | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mix.exs b/mix.exs index 8227791b9..f7e252533 100644 --- a/mix.exs +++ b/mix.exs @@ -102,8 +102,8 @@ defmodule Ejabberd.MixProject do {:cache_tab, "~> 1.0"}, {:eimp, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "34e46e8a122e4dd83730eda1aa8b566ab55eea62", override: true}, - {:fast_xml, "~> 1.1"}, + {:fast_tls, ">= 1.1.18"}, + {:fast_xml, ">= 1.1.51"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:jiffy, "~> 1.1.1"}, @@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "db6d730f0e1cd36645c32d7c7e89e19bb27642e3", override: true}, + {:xmpp, ">= 1.8.0"}, {:yconf, "~> 1.0"}] ++ cond_deps() end @@ -137,7 +137,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.0"}}, - {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql.git", ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"}}, + {config(:mysql), {:p1_mysql, ">= 1.0.23" }}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: diff --git a/mix.lock b/mix.lock index 27029c1fd..3dc411e16 100644 --- a/mix.lock +++ b/mix.lock @@ -11,11 +11,11 @@ "ejabberd": {:git, "https://github.com/processone/ejabberd", "d55955f7d8f81d491a0707852f119a615a87df46", [ref: "d55955f7d8f81d491a0707852f119a615a87df46"]}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, - "esip": {:hex, :esip, "1.0.50", "e657d3af332c711311f4eb540e73eb540ea485a25977aef8736fb8cd3845ed9f", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.10", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "7dfb9f16c65c5e49eeba77025d0f894b5fb240be745d11b978ea1438cd47533d"}, + "esip": {:hex, :esip, "1.0.51", "e57a50e86e86667f4ed0a58d69d9ccf9c63ef45d40e78a7bfa377b7020d09eda", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.11", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a66bb62ce9c67207341efb3e6f91072f3f611452ab22f57197a61b03e96f9456"}, "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "34e46e8a122e4dd83730eda1aa8b566ab55eea62", [ref: "34e46e8a122e4dd83730eda1aa8b566ab55eea62"]}, - "fast_xml": {:hex, :fast_xml, "1.1.49", "67d9bfcadd04efd930e0ee1412b5ea09d3e791f1fdbd4d3e9a8c8f29f8bfed8c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "01da064d2f740818956961036637fee2475c17bf8aab9442217f90dc77883593"}, + "fast_tls": {:hex, :fast_tls, "1.1.18", "1f8edd5ae29ccfb7f7f9e44b93e530324586bcca87b850b85e77722e6899fadd", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d98c90c088eb90b20a2586def040ea00cbc04d9430b6755a1824383cf0e6dcaf"}, + "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, "gun": {:hex, :gun, "2.0.1", "160a9a5394800fcba41bc7e6d421295cf9a7894c2252c0678244948e3336ad73", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "a10bc8d6096b9502205022334f719cc9a08d9adcfbfc0dbee9ef31b56274a20b"}, @@ -32,17 +32,17 @@ "neotoma": {:git, "https://github.com/seancribbs/neotoma.git", "9e57d8ebd4ebb02c3e2428b08f3a01e2ff834ce2", [ref: "master"]}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, - "p1_mysql": {:git, "https://github.com/processone/p1_mysql.git", "f685408b910c425b9905d4ddcdbedba717a5b48c", [ref: "f685408b910c425b9905d4ddcdbedba717a5b48c"]}, - "p1_oauth2": {:hex, :p1_oauth2, "0.6.11", "96b4e85c08355720523c2f892011a81a07994d15c179ce4dd82d704fecad15b2", [:rebar3], [], "hexpm", "9c3c6ae59382b9525473bb02a32949889808f33f95f6db10594fd92acd1f63db"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.23", "4a8c17b642dcf5265a910d1a0b86ffb2a9dd057c7b51c3def5eacbcc4f27ced9", [:rebar3], [{:xmpp, "1.7.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "819222bcb5a74581263282ff9cdc679adeefc663dcf49ddc778aeaae90be05a6"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, + "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.24", "73995e54224483f42e8b8f50be1d9fbb45dc391b5ddb88bd4ec57f3c63ddbfac", [:rebar3], [{:xmpp, "1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "4d2be56310a490c44bda853b870cc510e3fca54103fc3029f0d4415a54c271f4"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, "p1db": {:git, "ssh://git@github.com/processone/p1db.git", "5aac407d361bba9d14603182455e68a7c0300237", [ref: "5aac407d361bba9d14603182455e68a7c0300237"]}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, - "stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"}, + "stun": {:hex, :stun, "1.2.11", "89ae34da50cb8d179724d389d4f8000cf3c248fc9cdead97b681a5c1973f13e2", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b4d9f980af3d89653b405e0a3ee0463677e7fdaa4f824efe5b8ad4a753604ff3"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "db6d730f0e1cd36645c32d7c7e89e19bb27642e3", [ref: "db6d730f0e1cd36645c32d7c7e89e19bb27642e3"]}, + "xmpp": {:hex, :xmpp, "1.8.0", "8e1380a2dc3092c9442207749c0fbc508d801a7484c6b743e62e397ce161eae3", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "5ecc613bcb39b3ef58ecdc10c698dced5f5fb186357f500a59e984b9b7dcd447"}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 0ac9ce379..4242f5d0b 100644 --- a/rebar.config +++ b/rebar.config @@ -32,11 +32,11 @@ {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.50"}}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.51"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "34e46e8a122e4dd83730eda1aa8b566ab55eea62"}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.49"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.18"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_above, "19", @@ -61,18 +61,18 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", "f685408b910c425b9905d4ddcdbedba717a5b48c"}}}, - {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.11"}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.23"}}}}, + {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.23"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.24"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "db6d730f0e1cd36645c32d7c7e89e19bb27642e3"}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.11"}}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.8.0"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From 6eff14a71d875b211e895d2e911de6813f227cc7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Jan 2024 14:41:23 +0100 Subject: [PATCH 0381/1302] Update changelog --- CHANGELOG.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd8fcb055..5a1f840be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,64 @@ +# Version 24.01 + +Core: +- Support SASL2 and Bind2 +- Support tls-server-end-point channel binding and sasl2 codec +- Support tls-exporter channel binding +- Support XEP-0474: SASL SCRAM Downgrade Protection +- Fix presenting features and returning results of inline bind2 elements +- [`disable_sasl_scram_downgrade_protection`](https://docs.ejabberd.im/admin/configuration/toplevel/#disable-sasl-scram-downgrade-protection): New option to disable XEP-0474 +- [`negotiation_timeout`](https://docs.ejabberd.im/admin/configuration/toplevel/#negotiation-timeout): Increase default value from 30s to 2m +- mod_carboncopy: Teach how to interact with bind2 inline requests + +Other: +- configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135)) +- ejabberdctl: Fix startup problem when having set `EJABBERD_OPTS` and logger options +- ejabberdctl: Set EJABBERD_OPTS back to "", and use previous flags as example +- eldap: Change logic for `eldap tls_verify=soft` and `false` +- eldap: Don't set `fail_if_no_peer_cert` for eldap ssl client connections +- mod_mam: Support XEP-0424 Message Retraction +- mod_ping: Support XEP-0198 pings when stream management is enabled +- mod_pubsub: Normalize pubsub `max_items` node options on read +- mod_pubsub: PEP nodetree: Fix reversed logic in node fixup function +- mod_pubsub: Only care about PEP bookmarks options when creating node from scratch + +SQL: +- MySQL: Support `sha256_password` auth plugin +- ejabberd_sql_schema: Use the first unique index as a primary key +- Update SQL schema files for MAM's XEP-0424 +- New option [`sql_flags`](https://docs.ejabberd.im/admin/configuration/toplevel/#sql-flags): right now only useful to enable `mysql_alternative_upsert` + +Commands API: +- Commands: Add a new [`muc_sub`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#muc-sub) tag to all the relevant commands +- Commands: Improve syntax of many commands documentation +- Commands: Use list arguments in many commands that used separators +- Commands: [`set_presence`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#set-presence): switch priority argument from string to integer +- ejabberd_commands: Add the command API version as [a tag `vX`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#v1) +- ejabberd_ctl: Add support for list and tuple arguments +- ejabberd_xmlrpc: Fix support for restuple error response +- mod_http_api: When no specific API version is requested, use the latest + +Rebar3/Elixir/Mix: +- Add observer and runtime_tools in releases when `--enable-tools` +- Makefile: Add `install-rel` and `uninstall-rel` +- Makefile: Rename `make rel` to `make prod` +- ejabberdctl: Detect problem running iex or etop and show explanation +- configure: Use Mix or Rebar3 by default instead of Rebar2 to compile ejabberd +- Rebar3: Include Elixir files when making a release +- Rebar3: Workaround to fix protocol consolidation +- Rebar3: Add support to compile Elixir dependencies +- Rebar3: Compile explicitly our Elixir files when `--enable-elixir` +- Rebar3: Provide proper path to iex +- Rebar/Rebar3: Remove Elixir as a rebar dependency +- Rebar/Rebar3: Update binaries to work with Erlang/OTP 23-26 +- Elixir: Fix compiling ejabberd as a dependency ([#4128](https://github.com/processone/ejabberd/issues/4128)) +- Elixir: Fix ejabberdctl start/live when installed +- Elixir: Fix: FORMATTER ERROR: bad return value ([#4087](https://github.com/processone/ejabberd/issues/4087)) +- Elixir: Fix: Couldn't find file `Elixir.Hex.API` +- Mix: Enable stun by default when `vars.config` not found +- Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128)) +- Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release + # Version 23.10 Compilation: From 66d701e788af7d1f86c253481ab2d8411bc92a3c Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 24 Jan 2024 22:49:59 +0300 Subject: [PATCH 0382/1302] Add print_sql_schema ejabberdctl command --- include/ejabberd_sql.hrl | 5 + src/ejabberd_auth_sql.erl | 5 +- src/ejabberd_ctl.erl | 6 +- src/ejabberd_oauth_sql.erl | 5 +- src/ejabberd_router_sql.erl | 5 +- src/ejabberd_sm_sql.erl | 5 +- src/ejabberd_sql.erl | 53 +++++--- src/ejabberd_sql_schema.erl | 244 +++++++++++++++++++++++++--------- src/mod_announce_sql.erl | 5 +- src/mod_bosh_sql.erl | 5 +- src/mod_caps_sql.erl | 5 +- src/mod_last_sql.erl | 5 +- src/mod_mam_sql.erl | 18 +-- src/mod_mix_pam_sql.erl | 5 +- src/mod_mix_sql.erl | 5 +- src/mod_mqtt_sql.erl | 5 +- src/mod_muc_sql.erl | 5 +- src/mod_offline_sql.erl | 5 +- src/mod_privacy_sql.erl | 6 +- src/mod_private_sql.erl | 5 +- src/mod_proxy65_sql.erl | 5 +- src/mod_pubsub_sql.erl | 5 +- src/mod_push_sql.erl | 5 +- src/mod_roster_sql.erl | 5 +- src/mod_shared_roster_sql.erl | 5 +- src/mod_vcard_sql.erl | 5 +- 26 files changed, 293 insertions(+), 139 deletions(-) diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index 608ff3459..b5a4933fc 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -68,3 +68,8 @@ update = []}). -record(sql_references, {table :: binary(), column :: binary()}). + +-record(sql_schema_info, + {db_type :: pgsql | mysql | sqlite, + db_version :: any(), + new_schema = true :: boolean()}). diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index a0e69cdb4..dd58f0af5 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -34,6 +34,7 @@ get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, plain_password_required/1, export/1, which_users_exists/2]). +-export([sql_schemas/0]). -include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). @@ -46,10 +47,10 @@ %%% API %%%---------------------------------------------------------------------- start(Host) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index a0feb49d8..4d243a392 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -157,6 +157,9 @@ process(["mnesia", Arg], _Version) -> end, ?STATUS_SUCCESS; +process(["print_sql_schema", DBType, DBVersion, NewSchema], _Version) -> + ejabberd_sql_schema:print_schema(DBType, DBVersion, NewSchema); + %% The arguments --long and --dual are not documented because they are %% automatically selected depending in the number of columns of the shell process(["help" | Mode], Version) -> @@ -555,7 +558,8 @@ print_usage(HelpMode, MaxC, ShCode, Version) -> {"status", [], "Get ejabberd status"}, {"stop", [], "Stop ejabberd"}, {"restart", [], "Restart ejabberd"}, - {"mnesia", ["[info]"], "show information of Mnesia system"}] ++ + {"mnesia", ["[info]"], "show information of Mnesia system"}, + {"print_sql_schema", ["db_type", "db_version", "new_schema"], "print SQL schema for the given RDBMS"}] ++ get_list_commands(Version), print( diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index ffd571bec..17c38f20c 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -34,6 +34,7 @@ lookup_client/1, store_client/1, remove_client/1, revoke/1]). +-export([sql_schemas/0]). -include("ejabberd_oauth.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -42,10 +43,10 @@ init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index d334e0f6f..03e617654 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -27,6 +27,7 @@ %% API -export([init/0, register_route/5, unregister_route/3, find_routes/1, get_all_routes/0]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,7 +39,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'route' table...", []), case ejabberd_sql:sql_query( @@ -50,7 +51,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index 96c640b90..df8e0970d 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -34,6 +34,7 @@ get_sessions/0, get_sessions/1, get_sessions/2]). +-export([sql_schemas/0]). -include("ejabberd_sm.hrl"). -include("logger.hrl"). @@ -48,7 +49,7 @@ init() -> ?DEBUG("Cleaning SQL SM table...", []), lists:foldl( fun(Host, ok) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), case ejabberd_sql:sql_query( Host, ?SQL("delete from sm where node=%(Node)s")) of {updated, _} -> @@ -61,7 +62,7 @@ init() -> Err end, ok, ejabberd_sm:get_vh_by_backend(?MODULE)). -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 1b9a07769..8c192a784 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -56,7 +56,8 @@ init_mssql/1, keep_alive/2, to_list/2, - to_array/2]). + to_array/2, + parse_mysql_version/2]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, @@ -1134,6 +1135,31 @@ to_odbc({error, Reason}) when is_list(Reason) -> to_odbc(Res) -> Res. +parse_mysql_version(SVersion, DefaultUpsert) -> + case re:run(SVersion, <<"(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-([^-]*))?">>, + [{capture, all_but_first, binary}]) of + {match, [V1, V2, V3, Type]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + TypeA = binary_to_atom(Type, utf8), + Flags = case TypeA of + 'MariaDB' -> DefaultUpsert; + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> DefaultUpsert + end, + {ok, {V, TypeA, Flags}}; + {match, [V1, V2, V3]} -> + V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), + Flags = case V of + _ when V >= 5007026 andalso V < 8000000 -> 1; + _ when V >= 8000020 -> 1; + _ -> DefaultUpsert + end, + {ok, {V, unknown, Flags}}; + _ -> + error + end. + get_db_version(#state{db_type = pgsql} = State) -> case pgsql:squery(State#state.db_ref, <<"select current_setting('server_version_num')">>) of @@ -1159,27 +1185,10 @@ get_db_version(#state{db_type = mysql, host = Host} = State) -> [{timeout, 5000}, {result_type, binary}])) of {selected, _, [SVersion]} -> - case re:run(SVersion, <<"(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-([^-]*))?">>, - [{capture, all_but_first, binary}]) of - {match, [V1, V2, V3, Type]} -> - V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), - TypeA = binary_to_atom(Type, utf8), - Flags = case TypeA of - 'MariaDB' -> DefaultUpsert; - _ when V >= 5007026 andalso V < 8000000 -> 1; - _ when V >= 8000020 -> 1; - _ -> DefaultUpsert - end, - State#state{db_version = {V, TypeA, Flags}}; - {match, [V1, V2, V3]} -> - V = ((bin_to_int(V1)*1000)+bin_to_int(V2))*1000+bin_to_int(V3), - Flags = case V of - _ when V >= 5007026 andalso V < 8000000 -> 1; - _ when V >= 8000020 -> 1; - _ -> DefaultUpsert - end, - State#state{db_version = {V, unknown, Flags}}; - _ -> + case parse_mysql_version(SVersion, DefaultUpsert) of + {ok, V} -> + State#state{db_version = V}; + error -> ?WARNING_MSG("Error parsing mysql version: ~p", [SVersion]), State end; diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 5c6d95868..29bcc55b1 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -28,10 +28,12 @@ -author('alexey@process-one.net'). -export([start/1, update_schema/3, - get_table_schema/2, get_table_indices/2, test/0]). + get_table_schema/2, get_table_indices/2, print_schema/3, + test/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). +-include("ejabberd_ctl.hrl"). start(Host) -> case should_update_schema(Host) of @@ -40,8 +42,17 @@ start(Host) -> true -> ok; false -> - Table = filter_table_sh(schema_table()), - Res = create_table(Host, Table), + SchemaInfo = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = ejabberd_sql:use_new_schema()} + end), + Table = filter_table_sh(SchemaInfo, schema_table()), + Res = create_table(Host, SchemaInfo, Table), case Res of {error, Error} -> ?ERROR_MSG("Failed to create table ~s: ~p", @@ -249,8 +260,8 @@ table_exists(Host, Table) -> end end). -filter_table_sh(Table) -> - case {ejabberd_sql:use_new_schema(), Table#sql_table.name} of +filter_table_sh(SchemaInfo, Table) -> + case {SchemaInfo#sql_schema_info.new_schema, Table#sql_table.name} of {true, _} -> Table; {_, <<"route">>} -> @@ -383,7 +394,7 @@ get_current_version(Host, Module, Schemas) -> Version end. -format_type(pgsql, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, _} -> <<"text">>; @@ -397,7 +408,7 @@ format_type(pgsql, _DBVersion, Column) -> {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; bigserial -> <<"bigserial">> end; -format_type(sqlite, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = sqlite}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, _} -> <<"text">>; @@ -411,7 +422,7 @@ format_type(sqlite, _DBVersion, Column) -> {char, N} -> [<<"character(">>, integer_to_binary(N), <<")">>]; bigserial -> <<"integer primary key autoincrement">> end; -format_type(mysql, _DBVersion, Column) -> +format_type(#sql_schema_info{db_type = mysql}, Column) -> case Column#sql_column.type of text -> <<"text">>; {text, big} -> <<"mediumtext">>; @@ -429,7 +440,7 @@ format_type(mysql, _DBVersion, Column) -> bigserial -> <<"bigint auto_increment primary key">> end. -format_default(pgsql, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of text -> <<"''">>; {text, _} -> <<"''">>; @@ -443,7 +454,7 @@ format_default(pgsql, _DBVersion, Column) -> %{char, N} -> <<"''">>; %bigserial -> <<"0">> end; -format_default(sqlite, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = sqlite}, Column) -> case Column#sql_column.type of text -> <<"''">>; {text, _} -> <<"''">>; @@ -457,7 +468,7 @@ format_default(sqlite, _DBVersion, Column) -> %{char, N} -> <<"''">>; %bigserial -> <<"0">> end; -format_default(mysql, _DBVersion, Column) -> +format_default(#sql_schema_info{db_type = mysql}, Column) -> case Column#sql_column.type of text -> <<"('')">>; {text, _} -> <<"('')">>; @@ -472,20 +483,20 @@ format_default(mysql, _DBVersion, Column) -> %bigserial -> <<"0">> end. -escape_name(pgsql, _DBVersion, <<"type">>) -> +escape_name(#sql_schema_info{db_type = pgsql}, <<"type">>) -> <<"\"type\"">>; -escape_name(_DBType, _DBVersion, ColumnName) -> +escape_name(_SchemaInfo, ColumnName) -> ColumnName. -format_column_def(DBType, DBVersion, Column) -> +format_column_def(SchemaInfo, Column) -> [<<" ">>, - escape_name(DBType, DBVersion, Column#sql_column.name), <<" ">>, - format_type(DBType, DBVersion, Column), + escape_name(SchemaInfo, Column#sql_column.name), <<" ">>, + format_type(SchemaInfo, Column), <<" NOT NULL">>, case Column#sql_column.default of false -> []; true -> - [<<" DEFAULT ">>, format_default(DBType, DBVersion, Column)] + [<<" DEFAULT ">>, format_default(SchemaInfo, Column)] end, case lists:keyfind(sql_references, 1, Column#sql_column.opts) of false -> []; @@ -511,7 +522,7 @@ format_mysql_index_column(Table, ColumnName) -> ColumnName end. -format_create_index(pgsql, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = pgsql}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -528,7 +539,7 @@ format_create_index(pgsql, _DBVersion, Table, Index) -> <<", ">>, Index#sql_index.columns), <<");">>]; -format_create_index(sqlite, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = sqlite}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -545,7 +556,7 @@ format_create_index(sqlite, _DBVersion, Table, Index) -> <<", ">>, Index#sql_index.columns), <<");">>]; -format_create_index(mysql, _DBVersion, Table, Index) -> +format_create_index(#sql_schema_info{db_type = mysql}, Table, Index) -> TableName = Table#sql_table.name, Unique = case Index#sql_index.unique of @@ -567,7 +578,7 @@ format_create_index(mysql, _DBVersion, Table, Index) -> end, Index#sql_index.columns)), <<");">>]. -format_primary_key(mysql, _DBVersion, Table) -> +format_primary_key(#sql_schema_info{db_type = mysql}, Table) -> case lists:filter( fun(#sql_index{meta = #{primary_key := true}}) -> true; (_) -> false @@ -584,7 +595,7 @@ format_primary_key(mysql, _DBVersion, Table) -> end, I#sql_index.columns)), <<")">>]] end; -format_primary_key(_DBType, _DBVersion, Table) -> +format_primary_key(_SchemaInfo, Table) -> case lists:filter( fun(#sql_index{meta = #{primary_key := true}}) -> true; (_) -> false @@ -597,16 +608,17 @@ format_primary_key(_DBType, _DBVersion, Table) -> <<")">>]] end. -format_add_primary_key(sqlite = DBType, DBVersion, Table, Index) -> - format_create_index(DBType, DBVersion, Table, Index); -format_add_primary_key(mysql, _DBVersion, Table, Index) -> +format_add_primary_key(#sql_schema_info{db_type = sqlite} = SchemaInfo, + Table, Index) -> + format_create_index(SchemaInfo, Table, Index); +format_add_primary_key(#sql_schema_info{db_type = pgsql}, Table, Index) -> TableName = Table#sql_table.name, [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, lists:join( <<", ">>, Index#sql_index.columns), <<");">>]; -format_add_primary_key(_DBType, _DBVersion, Table, Index) -> +format_add_primary_key(#sql_schema_info{db_type = mysql}, Table, Index) -> TableName = Table#sql_table.name, [<<"ALTER TABLE ">>, TableName, <<" ADD PRIMARY KEY (">>, lists:join( @@ -617,16 +629,16 @@ format_add_primary_key(_DBType, _DBVersion, Table, Index) -> end, Index#sql_index.columns)), <<");">>]. -format_create_table(pgsql = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = pgsql} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n);\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -635,20 +647,20 @@ format_create_table(pgsql = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices); -format_create_table(sqlite = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = sqlite} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n);\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -657,20 +669,20 @@ format_create_table(sqlite = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices); -format_create_table(mysql = DBType, DBVersion, Table) -> +format_create_table(#sql_schema_info{db_type = mysql} = SchemaInfo, Table) -> TableName = Table#sql_table.name, [iolist_to_binary( [<<"CREATE TABLE ">>, TableName, <<" (\n">>, lists:join( <<",\n">>, lists:map( - fun(C) -> format_column_def(DBType, DBVersion, C) end, + fun(C) -> format_column_def(SchemaInfo, C) end, Table#sql_table.columns) ++ - format_primary_key(DBType, DBVersion, Table)), + format_primary_key(SchemaInfo, Table)), <<"\n) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\n">>])] ++ lists:flatmap( fun(#sql_index{meta = #{primary_key := true}}) -> @@ -679,20 +691,16 @@ format_create_table(mysql = DBType, DBVersion, Table) -> []; (I) -> [iolist_to_binary( - [format_create_index(DBType, DBVersion, Table, I), + [format_create_index(SchemaInfo, Table, I), <<"\n">>])] end, Table#sql_table.indices). -%format_create_table(DBType, _DBVersion, Table) -> -% ?ERROR_MSG("Can't create SQL table ~p on ~p", -% [Table#sql_table.name, DBType]), -% error. -create_table(Host, Table) -> +create_table(Host, SchemaInfo, Table) -> ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> - SQLs = format_create_table(DBType, DBVersion, Table), + fun() -> + SQLs = format_create_table(SchemaInfo, Table), ?INFO_MSG("Creating table ~s:~n~s~n", [Table#sql_table.name, SQLs]), lists:foreach( @@ -700,15 +708,18 @@ create_table(Host, Table) -> case Table#sql_table.post_create of undefined -> ok; - F -> - F(DBType, DBVersion) + F when is_function(F, 1) -> + PostSQLs = F(SchemaInfo), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, + PostSQLs) end end). -create_tables(Host, Module, Schema) -> +create_tables(Host, Module, SchemaInfo, Schema) -> lists:foreach( fun(Table) -> - Res = create_table(Host, Table), + Res = create_table(Host, SchemaInfo, Table), case Res of {error, Error} -> ?ERROR_MSG("Failed to create table ~s: ~p", @@ -740,10 +751,10 @@ should_update_schema(Host) -> false end. -preprocess_table(Host, Table) -> - Table1 = filter_table_sh(Table), +preprocess_table(SchemaInfo, Table) -> + Table1 = filter_table_sh(SchemaInfo, Table), ImplicitPK = - case ejabberd_option:sql_type(Host) of + case SchemaInfo#sql_schema_info.db_type of pgsql -> false; sqlite -> case lists:keyfind(bigserial, #sql_column.type, @@ -782,18 +793,30 @@ preprocess_table(Host, Table) -> end, Table1#sql_table{indices = Indices}. -preprocess_schemas(Host, Schemas) -> +preprocess_schemas(SchemaInfo, Schemas) -> lists:map( fun(Schema) -> Schema#sql_schema{ - tables = lists:map(fun(T) -> preprocess_table(Host, T) end, - Schema#sql_schema.tables)} + tables = lists:map( + fun(T) -> + preprocess_table(SchemaInfo, T) + end, + Schema#sql_schema.tables)} end, Schemas). update_schema(Host, Module, RawSchemas) -> case should_update_schema(Host) of true -> - Schemas = preprocess_schemas(Host, RawSchemas), + SchemaInfo = + ejabberd_sql:sql_query( + Host, + fun(DBType, DBVersion) -> + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = ejabberd_sql:use_new_schema()} + end), + Schemas = preprocess_schemas(SchemaInfo, RawSchemas), Version = get_current_version(Host, Module, Schemas), LastSchema = lists:max(Schemas), LastVersion = LastSchema#sql_schema.version, @@ -801,7 +824,7 @@ update_schema(Host, Module, RawSchemas) -> _ when Version < 0 -> ?ERROR_MSG("Can't update SQL schema for module ~p, please do it manually", [Module]); 0 -> - create_tables(Host, Module, LastSchema); + create_tables(Host, Module, SchemaInfo, LastSchema); LastVersion -> ok; _ when LastVersion < Version -> @@ -811,7 +834,8 @@ update_schema(Host, Module, RawSchemas) -> fun(Schema) -> if Schema#sql_schema.version > Version -> - do_update_schema(Host, Module, Schema); + do_update_schema(Host, Module, + SchemaInfo, Schema); true -> ok end @@ -821,7 +845,7 @@ update_schema(Host, Module, RawSchemas) -> ok end. -do_update_schema(Host, Module, Schema) -> +do_update_schema(Host, Module, SchemaInfo, Schema) -> lists:foreach( fun({add_column, TableName, ColumnName}) -> {value, Table} = @@ -833,9 +857,9 @@ do_update_schema(Host, Module, Schema) -> Res = ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> - Def = format_column_def(DBType, DBVersion, Column), - Default = format_default(DBType, DBVersion, Column), + fun(DBType, _DBVersion) -> + Def = format_column_def(SchemaInfo, Column), + Default = format_default(SchemaInfo, Column), SQLs = [[<<"ALTER TABLE ">>, TableName, @@ -914,11 +938,11 @@ do_update_schema(Host, Module, Schema) -> Res = ejabberd_sql:sql_query( Host, - fun(DBType, DBVersion) -> + fun() -> case Index#sql_index.meta of #{primary_key := true} -> SQL1 = format_add_primary_key( - DBType, DBVersion, Table, Index), + SchemaInfo, Table, Index), SQL = iolist_to_binary(SQL1), ?INFO_MSG("Add primary key ~s/~p:~n~s~n", [Table#sql_table.name, @@ -927,7 +951,7 @@ do_update_schema(Host, Module, Schema) -> ejabberd_sql:sql_query_t(SQL); _ -> SQL1 = format_create_index( - DBType, DBVersion, Table, Index), + SchemaInfo, Table, Index), SQL = iolist_to_binary(SQL1), ?INFO_MSG("Create index ~s/~p:~n~s~n", [Table#sql_table.name, @@ -993,6 +1017,96 @@ do_update_schema(Host, Module, Schema) -> end, Schema#sql_schema.update), store_version(Host, Module, Schema#sql_schema.version). + +print_schema(SDBType, SDBVersion, SNewSchema) -> + {DBType, DBVersion} = + case SDBType of + "pgsql" -> + case string:split(SDBVersion, ".") of + [SMajor, SMinor] -> + try {list_to_integer(SMajor), list_to_integer(SMinor)} of + {Major, Minor} -> + {pgsql, Major * 10000 + Minor} + catch _:_ -> + io:format("pgsql version be in the form of " + "Major.Minor, e.g. 16.1~n"), + {error, error} + end; + _ -> + io:format("pgsql version be in the form of " + "Major.Minor, e.g. 16.1~n"), + {error, error} + end; + "mysql" -> + case ejabberd_sql:parse_mysql_version(SDBVersion, 0) of + {ok, V} -> + {mysql, V}; + error -> + io:format("pgsql version be in the same form as " + "SELECT VERSION() returns~n"), + {error, error} + end; + "sqlite" -> + {sqlite, undefined}; + _ -> + io:format("db_type must be one of the following: " + "'pgsql', 'mysql', 'sqlite'~n"), + {error, error} + end, + NewSchema = + case SNewSchema of + "0" -> false; + "1" -> true; + "false" -> false; + "true" -> true; + _ -> + io:format("new_schema must be one of the following: " + "'0', '1', 'false', 'true'~n"), + error + end, + case {DBType, NewSchema} of + {error, _} -> ?STATUS_ERROR; + {_, error} -> ?STATUS_ERROR; + _ -> + SchemaInfo = + #sql_schema_info{ + db_type = DBType, + db_version = DBVersion, + new_schema = NewSchema}, + Mods = ejabberd_config:beams(all), + lists:foreach( + fun(Mod) -> + case erlang:function_exported(Mod, sql_schemas, 0) of + true -> + Schemas = Mod:sql_schemas(), + Schemas2 = preprocess_schemas(SchemaInfo, Schemas), + Schema = lists:max(Schemas2), + SQLs = + lists:flatmap( + fun(Table) -> + SQLs = format_create_table(SchemaInfo, Table), + PostSQLs = + case Table#sql_table.post_create of + undefined -> + []; + F when is_function(F, 1) -> + PSQLs = F(SchemaInfo), + lists:map( + fun(S) -> + [S, <<"\n">>] + end, PSQLs) + end, + SQLs ++ PostSQLs + end, Schema#sql_schema.tables), + io:format("~s~n", [SQLs]); + false -> + ok + end + end, Mods), + ?STATUS_SUCCESS + end. + + test() -> Schemas = [#sql_schema{ diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl index 230b4035a..7b1f9bbc9 100644 --- a/src/mod_announce_sql.erl +++ b/src/mod_announce_sql.erl @@ -31,6 +31,7 @@ -export([init/2, set_motd_users/2, set_motd/2, delete_motd/1, get_motd/1, is_motd_user/2, set_motd_user/2, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_announce.hrl"). @@ -41,10 +42,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index da943da9a..0e54ee56c 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -29,6 +29,7 @@ %% API -export([init/0, open_session/2, close_session/1, find_session/1]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,7 +39,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), Node = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'bosh' table...", []), case ejabberd_sql:sql_query( @@ -50,7 +51,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl index 2a40dc497..06449175a 100644 --- a/src/mod_caps_sql.erl +++ b/src/mod_caps_sql.erl @@ -29,6 +29,7 @@ %% API -export([init/2, caps_read/2, caps_write/3, export/1, import/3]). +-export([sql_schemas/0]). -include("mod_caps.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -38,10 +39,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl index 0c8f0a222..317dc1daa 100644 --- a/src/mod_last_sql.erl +++ b/src/mod_last_sql.erl @@ -30,6 +30,7 @@ %% API -export([init/2, get_last/2, store_last_info/4, remove_user/2, import/2, export/1]). +-export([sql_schemas/0]). -include("mod_last.hrl"). -include("logger.hrl"). @@ -39,10 +40,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index f923b3e0b..8bc27b65d 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -32,6 +32,7 @@ extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, is_empty_for_user/2, is_empty_for_room/3, select_with_mucsub/6, delete_old_messages_batch/4, count_messages_to_delete/3]). +-export([sql_schemas/0]). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 2, tables = @@ -79,11 +80,10 @@ schemas() -> columns = [<<"server_host">>, <<"username">>, <<"origin_id">>]} ], post_create = - fun(mysql, _) -> - ejabberd_sql:sql_query_t( - <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); - (_, _) -> - ok + fun(#sql_schema_info{db_type = mysql}) -> + [<<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>]; + (_) -> + [] end}, #sql_table{ name = <<"archive_prefs">>, @@ -131,10 +131,10 @@ schemas() -> columns = [<<"server_host">>, <<"timestamp">>]} ], post_create = - fun(mysql, _) -> + fun(#sql_schema_info{db_type = mysql}) -> ejabberd_sql:sql_query_t( <<"CREATE FULLTEXT INDEX i_archive_txt ON archive(txt);">>); - (_, _) -> + (_) -> ok end}, #sql_table{ diff --git a/src/mod_mix_pam_sql.erl b/src/mod_mix_pam_sql.erl index 606d306ce..af22c74f4 100644 --- a/src/mod_mix_pam_sql.erl +++ b/src/mod_mix_pam_sql.erl @@ -26,6 +26,7 @@ %% API -export([init/2, add_channel/3, get_channel/2, get_channels/1, del_channel/2, del_channels/1]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -34,10 +35,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl index 74f341fb8..5d0c3d95d 100644 --- a/src/mod_mix_sql.erl +++ b/src/mod_mix_sql.erl @@ -27,6 +27,7 @@ -export([set_channel/6, get_channels/2, get_channel/3, del_channel/3]). -export([set_participant/6, get_participant/4, get_participants/3, del_participant/4]). -export([subscribe/5, unsubscribe/4, unsubscribe/5, get_subscribed/4]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -35,10 +36,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index 9e12e30cb..a57628372 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -25,6 +25,7 @@ -export([init/0]). -export([subscribe/4, unsubscribe/2, find_subscriber/2]). -export([open_session/1, close_session/1, lookup_session/1, get_sessions/2]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -37,10 +38,10 @@ init() -> {error, db_failure}. init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index ea0c416b3..9e193a7df 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -42,6 +42,7 @@ find_online_room_by_pid/2, remove_user/2]). -export([set_affiliation/6, set_affiliations/4, get_affiliation/5, get_affiliations/3, search_affiliation/4]). +-export([sql_schemas/0]). -include_lib("xmpp/include/jid.hrl"). -include("mod_muc.hrl"). @@ -52,7 +53,7 @@ %%% API %%%=================================================================== init(Host, Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), case gen_mod:ram_db_mod(Opts, mod_muc) of ?MODULE -> clean_tables(Host); @@ -60,7 +61,7 @@ init(Host, Opts) -> ok end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index b1eb4ed89..1f48b7ea9 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -31,6 +31,7 @@ remove_old_messages/2, remove_user/2, read_message_headers/2, read_message/3, remove_message/3, read_all_messages/2, remove_all_messages/2, count_messages/2, import/1, export/1, remove_old_messages_batch/3]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_offline.hrl"). @@ -41,10 +42,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index 2811c6a8e..b00b62741 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -34,6 +34,8 @@ -export([item_to_raw/1, raw_to_item/1]). +-export([sql_schemas/0]). + -include_lib("xmpp/include/xmpp.hrl"). -include("mod_privacy.hrl"). -include("logger.hrl"). @@ -43,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl index 806867965..2c242a78a 100644 --- a/src/mod_private_sql.erl +++ b/src/mod_private_sql.erl @@ -28,6 +28,7 @@ %% API -export([init/2, set_data/3, get_data/3, get_all_data/2, del_data/2, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_private.hrl"). @@ -38,10 +39,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl index f1f9ab322..67ceeac21 100644 --- a/src/mod_proxy65_sql.erl +++ b/src/mod_proxy65_sql.erl @@ -26,6 +26,7 @@ %% API -export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]). +-export([sql_schemas/0]). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -35,7 +36,7 @@ %%%=================================================================== init() -> ejabberd_sql_schema:update_schema( - ejabberd_config:get_myname(), ?MODULE, schemas()), + ejabberd_config:get_myname(), ?MODULE, sql_schemas()), NodeS = erlang:atom_to_binary(node(), latin1), ?DEBUG("Cleaning SQL 'proxy65' table...", []), case ejabberd_sql:sql_query( @@ -49,7 +50,7 @@ init() -> Err end. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl index 8fd3865de..f0b65a6b2 100644 --- a/src/mod_pubsub_sql.erl +++ b/src/mod_pubsub_sql.erl @@ -20,6 +20,7 @@ %% API -export([init/3]). +-export([sql_schemas/0]). -include("ejabberd_sql_pt.hrl"). @@ -27,13 +28,13 @@ %%% API %%%=================================================================== init(_Host, ServerHost, _Opts) -> - ejabberd_sql_schema:update_schema(ServerHost, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(ServerHost, ?MODULE, sql_schemas()), ok. %%%=================================================================== %%% Internal functions %%%=================================================================== -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index 13de8c406..8e4cc1f1c 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -30,6 +30,7 @@ -export([init/2, store_session/6, lookup_session/4, lookup_session/3, lookup_sessions/3, lookup_sessions/2, lookup_sessions/1, delete_session/3, delete_old_sessions/2, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -40,10 +41,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 8012fca83..565861751 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -34,6 +34,7 @@ update_roster/4, del_roster/3, transaction/2, process_rosteritems/5, import/3, export/1, raw_to_record/2]). +-export([sql_schemas/0]). -include("mod_roster.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl index 9c5233d25..0bdd531d9 100644 --- a/src/mod_shared_roster_sql.erl +++ b/src/mod_shared_roster_sql.erl @@ -34,6 +34,7 @@ get_user_displayed_groups/3, is_user_in_group/3, add_user_to_group/3, remove_user_from_group/3, import/3, export/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/jid.hrl"). -include("mod_roster.hrl"). @@ -44,10 +45,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index cb8a1e441..c67338f51 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -31,6 +31,7 @@ -export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2, search_fields/1, search_reported/1, import/3, export/1]). -export([is_search_supported/1]). +-export([sql_schemas/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_vcard.hrl"). @@ -42,10 +43,10 @@ %%% API %%%=================================================================== init(Host, _Opts) -> - ejabberd_sql_schema:update_schema(Host, ?MODULE, schemas()), + ejabberd_sql_schema:update_schema(Host, ?MODULE, sql_schemas()), ok. -schemas() -> +sql_schemas() -> [#sql_schema{ version = 1, tables = From 01c70868efdc1890ee99cba473b4c37bc4939c10 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 3 Feb 2024 13:20:34 +0100 Subject: [PATCH 0383/1302] Ignore hints when checking for chat states Ignore XEP-0334 elements when checking whether a stanza is a stand-alone XEP-0085 chat state notification. This allows for CSI-filtering chat states with (e.g.) a no-store hint. Thanks to Thilo Molitor for reporting the issue. --- src/misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.erl b/src/misc.erl index 3dd805665..6d3491a90 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -195,7 +195,7 @@ get_mucsub_event_type(_Packet) -> is_standalone_chat_state(Stanza) -> case unwrap_carbon(Stanza) of #message{body = [], subject = [], sub_els = Els} -> - IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT], + IgnoreNS = [?NS_CHATSTATES, ?NS_DELAY, ?NS_EVENT, ?NS_HINTS], Stripped = [El || El <- Els, not lists:member(xmpp:get_ns(El), IgnoreNS)], Stripped == []; From ea40afbe038ab3195b3f6c697d391eb1d3a84900 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 25 Jan 2024 11:29:41 +0100 Subject: [PATCH 0384/1302] update_sql_schema: Fix typo, add example MySQL version number --- src/ejabberd_sql_schema.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 29bcc55b1..c1a3c0168 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -1042,8 +1042,8 @@ print_schema(SDBType, SDBVersion, SNewSchema) -> {ok, V} -> {mysql, V}; error -> - io:format("pgsql version be in the same form as " - "SELECT VERSION() returns~n"), + io:format("mysql version be in the same form as " + "SELECT VERSION() returns, e.g. 8.2.0~n"), {error, error} end; "sqlite" -> From e9de3748bdddad4ea73900214f72b3f6382e5e1c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 31 Jan 2024 16:54:02 +0100 Subject: [PATCH 0385/1302] ejabberdctl: Reorganize commands related to mnesia info - ejabberdctl mnesia: already implemented in ejabberd_admin.erl - ejabberdctl mnesia info: renamed to mnesia_info_ctl - ejabberdctl mnesia Arg: command removed, use mnesia_info_ctl instead --- src/ejabberd_ctl.erl | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 4d243a392..f6c70efc2 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -142,21 +142,10 @@ process(["status"], _Version) -> %% TODO: Mnesia operations should not be hardcoded in ejabberd_ctl module. %% For now, I leave them there to avoid breaking those commands for people that %% may be using it (as format of response is going to change). -process(["mnesia"], _Version) -> - print("~p~n", [mnesia:system_info(all)]), - ?STATUS_SUCCESS; - -process(["mnesia", "info"], _Version) -> +process(["mnesia_info_ctl"], _Version) -> mnesia:info(), ?STATUS_SUCCESS; -process(["mnesia", Arg], _Version) -> - case catch mnesia:system_info(list_to_atom(Arg)) of - {'EXIT', Error} -> print("Error: ~p~n", [Error]); - Return -> print("~p~n", [Return]) - end, - ?STATUS_SUCCESS; - process(["print_sql_schema", DBType, DBVersion, NewSchema], _Version) -> ejabberd_sql_schema:print_schema(DBType, DBVersion, NewSchema); From 1d65f4471b6671cbc937fd20275590d6266f5d01 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 31 Jan 2024 16:08:05 +0100 Subject: [PATCH 0386/1302] ejabberdctl: Document exclusive ejabberdctl commands like all the others --- src/ejabberd_ctl.erl | 46 ++++++++++++++++++++++++++++++-------------- src/ejabberd_sup.erl | 2 +- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index f6c70efc2..439f9c6a9 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -32,6 +32,7 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([get_commands_spec/0]). -include("ejabberd_ctl.hrl"). -include("ejabberd_commands.hrl"). @@ -90,6 +91,7 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), {ok, #state{}}. handle_call(Request, From, State) -> @@ -105,6 +107,7 @@ handle_info(Info, State) -> {noreply, State}. terminate(_Reason, _State) -> + ejabberd_commands:unregister_commands(get_commands_spec()), ok. code_change(_OldVsn, State, _Extra) -> @@ -478,11 +481,7 @@ make_status(Error) -> get_list_commands(Version) -> try ejabberd_commands:list_commands(Version) of Commands -> - [tuple_command_help(Command) - || {N,_,_}=Command <- Commands, - %% Don't show again those commands, because they are already - %% announced by ejabberd_ctl itself - N /= status, N /= stop, N /= restart] + [tuple_command_help(Command) || Command <- Commands] catch exit:_ -> [] @@ -541,15 +540,7 @@ print_usage(Version) -> {MaxC, ShCode} = get_shell_info(), print_usage(dual, MaxC, ShCode, Version). print_usage(HelpMode, MaxC, ShCode, Version) -> - AllCommands = - [ - {"help", ["[arguments]"], "Get help"}, - {"status", [], "Get ejabberd status"}, - {"stop", [], "Stop ejabberd"}, - {"restart", [], "Restart ejabberd"}, - {"mnesia", ["[info]"], "show information of Mnesia system"}, - {"print_sql_schema", ["db_type", "db_version", "new_schema"], "print SQL schema for the given RDBMS"}] ++ - get_list_commands(Version), + AllCommands = get_list_commands(Version), print( ["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("nodename"), "] [--version ", ?A("api_version"), "] ", @@ -956,3 +947,30 @@ disable_logging() -> disable_logging() -> logger:set_primary_config(level, none). -endif. + +%%----------------------------- +%% Register commands +%%----------------------------- + +get_commands_spec() -> + [ + #ejabberd_commands{name = help, tags = [ejabberdctl], + desc = "Get list of commands, or help of a command (only ejabberdctl)", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia], + desc = "Show information of Mnesia system (only ejabberdctl)", + note = "renamed in 24.01", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = print_sql_schema, tags = [ejabberdctl, sql], + desc = "Print SQL schema for the given RDBMS (only ejabberdctl)", + longdesc = "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend.", + note = "added in 24.01", + args = [{db_type, string}, {db_version, string}, {new_schema, string}], + args_desc = ["Database type: pgsql | mysql | sqlite", + "Your database version: 16.1, 8.2.0...", + "Use new schema: 0, false, 1 or true"], + args_example = ["pgsql", "16.1", "true"]} + ]. diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index 84605297d..0977265a6 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -42,8 +42,8 @@ init([]) -> worker(ejabberd_cluster), worker(translate), worker(ejabberd_access_permissions), - worker(ejabberd_ctl), worker(ejabberd_commands), + worker(ejabberd_ctl), worker(ejabberd_admin), supervisor(ejabberd_listener), worker(ejabberd_pkix), From c6878f9c9df11af51d7a637d505239875fbbfa63 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Feb 2024 13:11:38 +0100 Subject: [PATCH 0387/1302] ejabberdctl: Print argument description, examples and note in help --- src/ejabberd_ctl.erl | 176 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 439f9c6a9..61731f65c 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -408,6 +408,12 @@ format_result(Int, {_Name, integer}, _Version) -> format_result([A|_]=String, {_Name, string}, _Version) when is_list(String) and is_integer(A) -> io_lib:format("~ts", [String]); +format_result(Binary, {_Name, binary}, _Version) when is_binary(Binary) -> + io_lib:format("~ts", [binary_to_list(Binary)]); + +format_result(String, {_Name, binary}, _Version) when is_list(String) -> + io_lib:format("~ts", [String]); + format_result(Binary, {_Name, string}, _Version) when is_binary(Binary) -> io_lib:format("~ts", [binary_to_list(Binary)]); @@ -852,15 +858,21 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> definer = Definer, desc = Desc, args = ArgsDefPreliminary, + args_desc = ArgsDesc, + args_example = ArgsExample, + result_example = ResultExample, policy = Policy, longdesc = LongDesc, + note = Note, result = ResultDef} = C, NameFmt = [" ", ?B("Command Name"), ": ", ?C(Cmd), "\n"], %% Initial indentation of result is 13 = length(" Arguments: ") ArgsDef = maybe_add_policy_arguments(ArgsDefPreliminary, Policy), - Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef], + ArgsDetailed = add_args_desc(ArgsDef, ArgsDesc), + Args = [format_usage_ctype1(ArgDetailed, 13, ShCode) || ArgDetailed <- ArgsDetailed], + ArgsMargin = lists:duplicate(13, $\s), ArgsListFmt = case Args of [] -> "\n"; @@ -870,9 +882,17 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> %% Initial indentation of result is 11 = length(" Returns: ") ResultFmt = format_usage_ctype(ResultDef, 11), - ReturnsFmt = [" ",?B("Returns"),": ", ResultFmt], + ReturnsFmt = [" ",?B("Result"),": ", ResultFmt], - XmlrpcFmt = "", %%+++ [" ",?B("XML-RPC"),": ", format_usage_xmlrpc(ArgsDef, ResultDef), "\n\n"], + ExampleMargin = lists:duplicate(11, $\s), + Example = format_usage_example(Cmd, ArgsExample, ResultExample, ExampleMargin), + ExampleFmt = case Example of + [] -> + ""; + _ -> + ExampleListFmt = [ [Ex, "\n", ExampleMargin] || Ex <- Example], + [" ",?B("Example"),": ", ExampleListFmt, "\n"] + end, TagsFmt = [" ",?B("Tags"),":", prepare_long_line(8, MaxC, [?G(atom_to_list(TagA)) || TagA <- TagsAtoms])], @@ -885,6 +905,11 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> false -> [] end, + NoteFmt = case Note of + "" -> []; + _ -> [" ",?B("Note"),": ", Note, "\n\n"] + end, + DescFmt = [" ",?B("Description"),":", prepare_description(15, MaxC, Desc)], LongDescFmt = case LongDesc of @@ -892,11 +917,11 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> _ -> ["", prepare_description(0, MaxC, LongDesc), "\n\n"] end, - NoteEjabberdctlList = case has_list_args(ArgsDef) of + NoteEjabberdctlList = case has_list_args(ArgsDefPreliminary) of true -> [" ", ?B("Note:"), " In a list argument, separate the elements using the , character for example: one,two,three\n\n"]; false -> "" end, - NoteEjabberdctlTuple = case has_tuple_args(ArgsDef) of + NoteEjabberdctlTuple = case has_tuple_args(ArgsDefPreliminary) of true -> [" ", ?B("Note:"), " In a tuple argument, separate the elements using the : character for example: members_only:true\n\n"]; false -> "" end, @@ -904,10 +929,48 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> case Cmd of "help" -> ok; _ -> print([NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, - "\n\n", XmlrpcFmt, TagsFmt, "\n\n", ModuleFmt, DescFmt, "\n\n"], []) + "\n\n", ExampleFmt, TagsFmt, "\n\n", ModuleFmt, NoteFmt, DescFmt, "\n\n"], []) end, print([LongDescFmt, NoteEjabberdctlList, NoteEjabberdctlTuple], []). +%%----------------------------- +%% Format Arguments Help +%%----------------------------- + +add_args_desc(Definitions, none) -> + Descriptions = lists:duplicate(length(Definitions), ""), + add_args_desc(Definitions, Descriptions); +add_args_desc(Definitions, Descriptions) -> + lists:zipwith(fun({Name, Type}, Description) -> + {Name, Type, Description} end, + Definitions, + Descriptions). + +format_usage_ctype1({_Name, _Type} = Definition, Indentation, ShCode) -> + [Arg] = add_args_desc([Definition], none), + format_usage_ctype1(Arg, Indentation, ShCode); +format_usage_ctype1({Name, Type, Description}, Indentation, ShCode) -> + TypeString = case Type of + {list, ElementDef} -> + NameFmt = atom_to_list(Name), + Indentation2 = Indentation + length(NameFmt) + 4, + ElementFmt = format_usage_ctype1(ElementDef, Indentation2, ShCode), + io_lib:format("[ ~s ]", [lists:flatten(ElementFmt)]); + {tuple, ElementsDef} -> + NameFmt = atom_to_list(Name), + Indentation2 = Indentation + length(NameFmt) + 4, + ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), + io_lib:format("{ ~s }", [lists:flatten(ElementsFmt)]); + _ -> + Type + end, + DescriptionText = case Description of + "" -> ""; + Description -> " : "++Description + end, + io_lib:format("~p::~s~s", [Name, TypeString, DescriptionText]). + + format_usage_ctype(Type, _Indentation) when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> io_lib:format("~p", [Type]); @@ -926,12 +989,13 @@ format_usage_ctype({Name, {tuple, ElementsDef}}, Indentation) -> NameFmt = atom_to_list(Name), Indentation2 = Indentation + length(NameFmt) + 4, ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), - [NameFmt, "::{ " | ElementsFmt]. + [NameFmt, "::{ "] ++ ElementsFmt ++ [" }"]. + format_usage_tuple([], _Indentation) -> []; format_usage_tuple([ElementDef], Indentation) -> - [format_usage_ctype(ElementDef, Indentation) , " }"]; + format_usage_ctype(ElementDef, Indentation); format_usage_tuple([ElementDef | ElementsDef], Indentation) -> ElementFmt = format_usage_ctype(ElementDef, Indentation), MarginString = lists:duplicate(Indentation, $\s), % Put spaces @@ -948,6 +1012,102 @@ disable_logging() -> logger:set_primary_config(level, none). -endif. +%%----------------------------- +%% Format Example Help +%%----------------------------- + +format_usage_example(_Cmd, none, _ResultExample, _Indentation) -> + ""; +format_usage_example(Cmd, ArgsExample, ResultExample, Indentation) -> + Arguments = format_usage_arguments(ArgsExample, []), + Result = format_usage_result([ResultExample], [], Indentation), + [lists:join(" ", ["ejabberdctl", Cmd] ++ Arguments) | Result]. + +format_usage_arguments([], R) -> + lists:reverse(R); + +format_usage_arguments([Argument | Arguments], R) + when is_integer(Argument) -> + format_usage_arguments(Arguments, [integer_to_list(Argument) | R]); + +format_usage_arguments([[Integer|_] = Argument | Arguments], R) + when is_list(Argument) and is_integer(Integer) -> + Result = case contains_more_than_letters(Argument) of + true -> ["\"", Argument, "\""]; + false -> [Argument] + end, + format_usage_arguments(Arguments, [Result | R]); + +format_usage_arguments([[Element | _] = Argument | Arguments], R) + when is_list(Argument) and is_tuple(Element) -> + ArgumentFmt = format_usage_arguments(Argument, []), + format_usage_arguments(Arguments, [lists:join(",", ArgumentFmt) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_list(Argument) -> + Result = format_usage_arguments(Argument, []), + format_usage_arguments(Arguments, [lists:join(",", Result) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_tuple(Argument) -> + Result = format_usage_arguments(tuple_to_list(Argument), []), + format_usage_arguments(Arguments, [lists:join(":", Result) | R]); + +format_usage_arguments([Argument | Arguments], R) + when is_binary(Argument) -> + Result = case contains_more_than_letters(binary_to_list(Argument)) of + true -> ["\"", Argument, "\""]; + false -> [Argument] + end, + format_usage_arguments(Arguments, [Result | R]); + +format_usage_arguments([Argument | Arguments], R) -> + format_usage_arguments(Arguments, [Argument | R]). + +format_usage_result([none], _R, _Indentation) -> + ""; +format_usage_result([], R, _Indentation) -> + lists:reverse(R); + +format_usage_result([{Code, Text} | Arguments], R, Indentation) + when is_atom(Code) and is_binary(Text) -> + format_usage_result(Arguments, [Text | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_atom(Argument) -> + format_usage_result(Arguments, [["\'", atom_to_list(Argument), "\'"] | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_integer(Argument) -> + format_usage_result(Arguments, [integer_to_list(Argument) | R], Indentation); + +format_usage_result([[Integer|_] = Argument | Arguments], R, Indentation) + when is_list(Argument) and is_integer(Integer) -> + format_usage_result(Arguments, [Argument | R], Indentation); + +format_usage_result([[Element | _] = Argument | Arguments], R, Indentation) + when is_list(Argument) and is_tuple(Element) -> + ArgumentFmt = format_usage_result(Argument, [], Indentation), + format_usage_result(Arguments, [lists:join("\n"++Indentation, ArgumentFmt) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_list(Argument) -> + format_usage_result(Arguments, [lists:join("\n"++Indentation, Argument) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) + when is_tuple(Argument) -> + Result = format_usage_result(tuple_to_list(Argument), [], Indentation), + format_usage_result(Arguments, [lists:join("\t", Result) | R], Indentation); + +format_usage_result([Argument | Arguments], R, Indentation) -> + format_usage_result(Arguments, [Argument | R], Indentation). + +contains_more_than_letters(Argument) -> + lists:any(fun(I) when (I < $A) -> true; + (I) when (I > $z) -> true; + (_) -> false end, + Argument). + %%----------------------------- %% Register commands %%----------------------------- From a33be2d67a2ddc6644a40e16a819307e78db627e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 31 Jan 2024 18:28:48 +0100 Subject: [PATCH 0388/1302] ejabberdctl.bc: Improve caching of commands list Improvements: - Keep command cache for one hour - Fix parsing command list with ECMA-48 SGR escape sequence for font attributes --- tools/ejabberdctl.bc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tools/ejabberdctl.bc b/tools/ejabberdctl.bc index 62a5b1301..b0c88fa15 100644 --- a/tools/ejabberdctl.bc +++ b/tools/ejabberdctl.bc @@ -1,18 +1,27 @@ # # bash completion for ejabberdctl # +# For installation and details see: +# https://docs.ejabberd.im/admin/guide/managing/#bash-completion +# + get_help() { - local COMMANDCACHE=/tmp/ejabberd_bash_completion_$RANDOM - ejabberdctl $CTLARGS help tags >$COMMANDCACHE.tags - ejabberdctl $CTLARGS >$COMMANDCACHE - if [[ $? == 2 ]] ; then - ISRUNNING=1 - runningcommands=`cat $COMMANDCACHE | grep "^ [a-z]" | awk '{print $1}' | xargs` - runningtags=`cat $COMMANDCACHE.tags | grep "^ [a-z]" | awk '{print $1}' | xargs` + local CACHE_BASE=/tmp/ejabberd_bash_completion + local DATESTRING=`date +%F-%H` + local CACHE=$CACHE_BASE.$DATESTRING + local CACHE_COMS=$CACHE.coms + local CACHE_TAGS=$CACHE.tags + if [[ ! -f $CACHE_COMS ]] ; then + rm -f $CACHE_BASE.* + [ -f $CACHE_COMS ] || ejabberdctl $CTLARGS | sed "s|\x1B\[[0-9]*m||g" | grep "^ [a-z]" | awk '{print $1}' | xargs >$CACHE_COMS + [ -f $CACHE_TAGS ] || ejabberdctl $CTLARGS help tags | sed "s|\x1B\[[0-9]*m||g" | grep "^ [a-z]" | awk '{print $1}' | xargs >$CACHE_TAGS + fi + if [[ $? == 2 ]] || [[ -f $CACHE_COMS ]] ; then + ISRUNNING=1 + runningcommands=`cat $CACHE_COMS` + runningtags=`cat $CACHE_TAGS` fi - rm $COMMANDCACHE - rm $COMMANDCACHE.tags } _ejabberdctl() From 2979fb4a9b8f8695e524be6e6055a91a8646889b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Feb 2024 11:52:54 +0100 Subject: [PATCH 0389/1302] ext_mod: Support in WebAdmin when a module spec lacks some information --- src/ext_mod.erl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 7e4639bd8..2c74017b0 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -823,7 +823,7 @@ write_commit_json(Url, RepDir) -> end. find_commit_json(Attrs) -> - {_, FromPath} = lists:keyfind(path, 1, Attrs), + FromPath = get_module_path(Attrs), case {find_commit_json_path(FromPath), find_commit_json_path(filename:join(FromPath, ".."))} of @@ -946,7 +946,7 @@ get_page(Node, Query, Lang) -> Title ++ Result ++ Contents. get_module_home(Module, Attrs) -> - case element(2, lists:keyfind(home, 1, Attrs)) of + case get_module_information(home, Attrs) of "https://github.com/processone/ejabberd-contrib/tree/master/" = P1 -> P1 ++ atom_to_list(Module); Other -> @@ -954,17 +954,26 @@ get_module_home(Module, Attrs) -> end. get_module_summary(Attrs) -> - element(2, lists:keyfind(summary, 1, Attrs)). + get_module_information(summary, Attrs). get_module_author(Attrs) -> - element(2, lists:keyfind(author, 1, Attrs)). + get_module_information(author, Attrs). + +get_module_path(Attrs) -> + get_module_information(path, Attrs). + +get_module_information(Attribute, Attrs) -> + case lists:keyfind(Attribute, 1, Attrs) of + false -> ""; + {_, Value} -> Value + end. get_installed_module_el({ModAtom, Attrs}, Lang) -> Mod = misc:atom_to_binary(ModAtom), Home = list_to_binary(get_module_home(ModAtom, Attrs)), Summary = list_to_binary(get_module_summary(Attrs)), Author = list_to_binary(get_module_author(Attrs)), - {_, FromPath} = lists:keyfind(path, 1, Attrs), + FromPath = get_module_path(Attrs), FromFile = case find_commit_json_path(FromPath) of {ok, FF} -> FF; {error, _} -> "dummypath" From 70bf316f9dfd89f5728274aa69c1f84b83f82d08 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 6 Feb 2024 12:09:48 +0100 Subject: [PATCH 0390/1302] Explain that reopen_log and rotate_log only affect some modules (#4156) --- src/ejabberd_admin.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 56390eb0b..4d89e09d2 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -126,15 +126,19 @@ get_commands_spec() -> module = ?MODULE, function = restart, args = [], result = {res, rescode}}, #ejabberd_commands{name = reopen_log, tags = [logs], - desc = "Reopen the log files after being renamed", - longdesc = "This can be useful when an external tool is " + desc = "Reopen maybe the log files after being renamed", + longdesc = "Has no effect on ejabberd main log files, " + "only on log files generated by some modules.\n" + "This can be useful when an external tool is " "used for log rotation. See " "[Log Files](https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files).", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, #ejabberd_commands{name = rotate_log, tags = [logs], - desc = "Rotate the log files", + desc = "Rotate maybe log file of some module", + longdesc = "Has no effect on ejabberd main log files, " + "only on log files generated by some modules.", module = ?MODULE, function = rotate_log, args = [], result = {res, rescode}}, #ejabberd_commands{name = stop_kindly, tags = [server], From a182ec12cfa77c800befdc3a0ad6bbe364415126 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 1 Feb 2024 17:02:14 +0100 Subject: [PATCH 0391/1302] Workflows: Use explicit ubuntu-22.04 instead of a varying ubuntu-latest Also notice that ubuntu-22.04 doesn't include Erlang/OTP. --- .github/workflows/container.yml | 2 +- .github/workflows/installers.yml | 4 ++-- .github/workflows/runtime.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 0461cb9b8..02c584054 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -19,7 +19,7 @@ env: jobs: container: name: Container - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: packages: write steps: diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 4a026c31c..59e44c70f 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -21,7 +21,7 @@ on: jobs: binaries: name: Binaries - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Cache build directory uses: actions/cache@v3 @@ -70,7 +70,7 @@ jobs: release: name: Release needs: [binaries] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 if: github.ref_type == 'tag' steps: - name: Download packages diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 714edd269..98d81564d 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -33,7 +33,7 @@ jobs: matrix: otp: ['20.3', '25.3', '26'] rebar: ['rebar', 'rebar3'] - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 container: image: erlang:${{ matrix.otp }} From c664d6dc325a7d810680ab1636e875ca34b92334 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 1 Feb 2024 17:02:44 +0100 Subject: [PATCH 0392/1302] Workflows: Update actions to use Node.js 20 as recommended by Github Actions --- .github/workflows/ci.yml | 4 ++-- .github/workflows/container.yml | 2 +- .github/workflows/installers.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b71dbccfa..a04993d2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script - name: Cache rebar - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cache/rebar3/ @@ -220,7 +220,7 @@ jobs: echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/" - name: Check for changes to trigger schema upgrade test - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v3 id: filter with: filters: | diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 02c584054..28c0c2ec0 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -29,7 +29,7 @@ jobs: fetch-depth: 0 - name: Cache build directory - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/build/ key: ${{runner.os}}-ctr-ct-ng-1.25.0 diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 59e44c70f..a298c4531 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Cache build directory - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/build/ key: ${{runner.os}}-ct-ng-1.26.0 @@ -55,7 +55,7 @@ jobs: mkdir ejabberd-packages mv ejabberd_*.deb ejabberd-*.rpm ejabberd-*.run ejabberd-packages - name: Upload packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ejabberd-packages # @@ -74,7 +74,7 @@ jobs: if: github.ref_type == 'tag' steps: - name: Download packages - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ejabberd-packages - name: Draft Release From 3bd9fc9f433a3e8c6edbf4fd094b86530627ea4b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Feb 2024 11:26:00 +0100 Subject: [PATCH 0393/1302] Container: Fix typo in cache key name 1.25->1.26 --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 28c0c2ec0..a1962fe82 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -32,7 +32,7 @@ jobs: uses: actions/cache@v4 with: path: ~/build/ - key: ${{runner.os}}-ctr-ct-ng-1.25.0 + key: ${{runner.os}}-ctr-ct-ng-1.26.0 - name: Get erlang/OTP version for bootstrapping run: | From c81a47a692494fb36c3e169e07f34ef6686612fc Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 5 Feb 2024 18:49:07 +0100 Subject: [PATCH 0394/1302] Container: Update make-binaries to support setup-beam v1.17.2 With setup-beam v1.17.2, make-binaries failed in the Container action with: * ERROR: No usable Erlang/OTP system for the build machine found! Cannot * cross compile without such a system. * * Either build a bootstrap system for the build machine, or provide * an Erlang/OTP-26 system in the $PATH, and try again. For more * information on cross compiling Erlang/OTP-26, see the * $ERL_TOP/xcomp/README file. The problematic commit is: https://github.com/erlef/setup-beam/commit/cf854bf14995e965ed7a4f7ec68e426278929376 more concretely this change: - core.exportVariable(installDirForVarName, cachePath) + core.exportVariable(installDirForVarName, catchPathBin) Up until setup-beam@v1.17.1, the INSTALL_DIR_FOR_OTP was something like /opt/hostedtoolcache/otp/ubuntu-22.04/OTP-26.1.1/x64 but starting in v1.17.2, the path contains /bin, for example: /opt/hostedtoolcache/otp/ubuntu-22.04/OTP-26.1.1/x64/bin --- .github/workflows/container.yml | 2 +- tools/make-binaries | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index a1962fe82..b9c655ffa 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -49,7 +49,7 @@ jobs: sudo apt-get -qq install python3-dev texinfo unzip - name: Install erlang/OTP - uses: erlef/setup-beam@v1 + uses: erlef/setup-beam@v1.17.2 with: otp-version: ${{ env.OTP_VSN }} elixir-version: ${{ env.ELIXIR_VSN }} diff --git a/tools/make-binaries b/tools/make-binaries index 51d2261e7..30165290c 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -350,8 +350,8 @@ add_otp_path() then # For github runners to build for non-native systems: # https://github.com/erlef/setup-beam#environment-variables - native_otp_bin="$INSTALL_DIR_FOR_OTP/bin" - native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin" + native_otp_bin="$INSTALL_DIR_FOR_OTP" + native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR" export PATH="$native_elixir_bin:$PATH" fi export PATH="$native_otp_bin:$PATH" From 95135af6b384d21ec3ca81fe85c18c48f5485579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 7 Feb 2024 12:10:01 +0100 Subject: [PATCH 0395/1302] Rework temporary node name generation in ejabberdctl This should limit number of possible node names generated by and with that prevent atom space exhaustion in ejabberd process. On R23+ we switch to using native dynamic node features and on older versions we iterate over small number of possible names and skip those already in use. --- .github/container/ejabberdctl.template | 47 ++++++++++++++++++-------- ejabberdctl.template | 43 ++++++++++++++++------- 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 9afc96a81..6098b7edb 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -227,16 +227,27 @@ help() } # dynamic node name helper -uid() -{ - uuid=$(uuidgen 2>/dev/null) - random=$(awk 'BEGIN { srand(); print int(rand()*32768) }' /dev/null) - [ -z "$uuid" ] && [ -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid) - [ -z "$uuid" ] && uuid=$(printf "%X" "${random:-$$}$(date +%M%S)") - uuid=$(printf '%s' $uuid | sed 's/^\(...\).*$/\1/') - [ $# -eq 0 ] && echo "${uuid}-${ERLANG_NODE}" - [ $# -eq 1 ] && echo "${uuid}-${1}-${ERLANG_NODE}" - [ $# -eq 2 ] && echo "${uuid}-${1}@${2}" +uid() { + if erl -noinput -boot start_clean -eval 'case erlang:system_info(otp_release) >= "23" of true -> halt(1); _ -> halt(0) end.' ; then + N=1 + PF=$(( $$ % 97 )) + while + case $# in + 0) NN="${PF}-${N}-${ERLANG_NODE}" + ;; + 1) NN="${PF}-${N}-${1}-${ERLANG_NODE}" + ;; + 2) NN="${PF}-${N}-${1}@${2}" + ;; + esac + N=$(( N + 1 + ( $$ % 5 ) )) + epmd -names 2>/dev/null | grep -q " ${NN%@*} " + do :; done + echo $NN + else + # for R23+ use native dynamic node code + echo undefined + fi } # stop epmd if there is no other running node @@ -307,7 +318,9 @@ wait_status() if [ $timeout -eq 0 ] ; then status="$1" else - run_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ + run_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi @@ -350,7 +363,9 @@ case $1 in ;; etop) set_dist_client - exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \ + exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s etop \ -s erlang halt -output text ;; iexdebug) @@ -367,7 +382,9 @@ case $1 in [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ - -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ + -noinput -hidden \ + -eval 'net_kernel:connect_node('"'$PEER'"')' \ + -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; started) @@ -383,7 +400,9 @@ case $1 in ;; *) set_dist_client - run_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ + run_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" result=$? case $result in diff --git a/ejabberdctl.template b/ejabberdctl.template index eb16e3839..80fcab41b 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -248,16 +248,27 @@ help() } # dynamic node name helper -uid() -{ - uuid=$(uuidgen 2>/dev/null) - random=$(awk 'BEGIN { srand(); print int(rand()*32768) }' /dev/null) - [ -z "$uuid" ] && [ -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid) - [ -z "$uuid" ] && uuid=$(printf "%X" "${random:-$$}$(date +%M%S)") - uuid=$(printf '%s' $uuid | sed 's/^\(...\).*$/\1/') - [ $# -eq 0 ] && echo "${uuid}-${ERLANG_NODE}" - [ $# -eq 1 ] && echo "${uuid}-${1}-${ERLANG_NODE}" - [ $# -eq 2 ] && echo "${uuid}-${1}@${2}" +uid() { + if erl -noinput -boot start_clean -eval 'case erlang:system_info(otp_release) >= "23" of true -> halt(1); _ -> halt(0) end.' ; then + N=1 + PF=$(( $$ % 97 )) + while + case $# in + 0) NN="${PF}-${N}-${ERLANG_NODE}" + ;; + 1) NN="${PF}-${N}-${1}-${ERLANG_NODE}" + ;; + 2) NN="${PF}-${N}-${1}@${2}" + ;; + esac + N=$(( N + 1 + ( $$ % 5 ) )) + epmd -names 2>/dev/null | grep -q " ${NN%@*} " + do :; done + echo $NN + else + # for R23+ use native dynamic node code + echo undefined + fi } # stop epmd if there is no other running node @@ -300,7 +311,9 @@ wait_status() if [ $timeout -eq 0 ] ; then status="$1" else - exec_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ + exec_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi @@ -361,7 +374,9 @@ case $1 in [ "$PEER" = "${PEER%.*}" ] && PS="-s" set_dist_client exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ - -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ + -noinput -hidden \ + -eval 'net_kernel:connect_node('"'$PEER'"')' \ + -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ -s erlang halt -output text ;; started) @@ -374,7 +389,9 @@ case $1 in ;; *) set_dist_client - exec_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl \ + exec_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" result=$? case $result in From 7e6ffc34fbb3709293b595ac609b42d1afea42ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 7 Feb 2024 19:32:49 +0100 Subject: [PATCH 0396/1302] Update mix.lock --- mix.lock | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/mix.lock b/mix.lock index 3dc411e16..98973ebf7 100644 --- a/mix.lock +++ b/mix.lock @@ -1,44 +1,31 @@ %{ - "artificery": {:hex, :artificery, "0.4.3", "0bc4260f988dcb9dda4b23f9fc3c6c8b99a6220a331534fdf5bf2fd0d4333b02", [:mix], [], "hexpm", "12e95333a30e20884e937abdbefa3e7f5e05609c2ba8cf37b33f000b9ffc0504"}, "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, - "cheap_counters": {:git, "ssh://git@github.com/processone/cheap_counters.git", "f9a287ee1aad53b7ad29cc5d3883f8887d386021", [tag: "1.0.10"]}, - "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, - "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, - "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm", "bbc7008b0161a6f130d8d903b5b3232351fccc9c31a991f8fcbf2a12ace22995"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, - "ejabberd": {:git, "https://github.com/processone/ejabberd", "d55955f7d8f81d491a0707852f119a615a87df46", [ref: "d55955f7d8f81d491a0707852f119a615a87df46"]}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "esip": {:hex, :esip, "1.0.51", "e57a50e86e86667f4ed0a58d69d9ccf9c63ef45d40e78a7bfa377b7020d09eda", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.11", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a66bb62ce9c67207341efb3e6f91072f3f611452ab22f57197a61b03e96f9456"}, - "ex_doc": {:hex, :ex_doc, "0.30.8", "cf3eb2eb32137966aab0929bb3af42773b2d08e2f785a5fee9caabf664082cb3", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bfb981d8e0a8ab23857e502d611c612ae2c24536dd3b530e741d1d94ea44e6e2"}, + "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.18", "1f8edd5ae29ccfb7f7f9e44b93e530324586bcca87b850b85e77722e6899fadd", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d98c90c088eb90b20a2586def040ea00cbc04d9430b6755a1824383cf0e6dcaf"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, - "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, - "gun": {:hex, :gun, "2.0.1", "160a9a5394800fcba41bc7e6d421295cf9a7894c2252c0678244948e3336ad73", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "a10bc8d6096b9502205022334f719cc9a08d9adcfbfc0dbee9ef31b56274a20b"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, - "lager": {:hex, :lager, "3.6.10", "6172b43ab720ac33914ccd0aeb21fdbdf88213847707d4b91e6af57b2ae5c4d2", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm", "5d10499461826b79c5abee18bb594b3949cbdf76d9d9fd7e66d0a558137c21c9"}, - "luerl": {:hex, :luerl, "1.0.0", "1b68c30649323590d5339b967b419260500ffe520cd3abc1987482a82d3b5a6c", [:rebar3], [], "hexpm", "c17bc45cb4b0845ec975387f9a5d8c81ab60456698527a29c96f78992af86bd1"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, + "luerl": {:hex, :luerl, "1.1.1", "083518e437586f6631150d39c4bff242ed2ec80cb14a3299a0c2628f07a2ff7f", [:rebar3], [], "hexpm", "e17ef246a7ff876ec90e68792a39708979416004d4eacfe8a7643206b9470773"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.4", "29563475afa9b8a2add1b7a9c8fb68d06ca7737648f28398e04461f008b69521", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29"}, "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, - "myproto": {:git, "https://github.com/bosqueviejo/myproto", "a5ddd9f89625bbfe2c19840806f70b9061ca33d3", [ref: "a5ddd9f89625bbfe2c19840806f70b9061ca33d3"]}, - "neotoma": {:git, "https://github.com/seancribbs/neotoma.git", "9e57d8ebd4ebb02c3e2428b08f3a01e2ff834ce2", [ref: "master"]}, - "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.24", "73995e54224483f42e8b8f50be1d9fbb45dc391b5ddb88bd4ec57f3c63ddbfac", [:rebar3], [{:xmpp, "1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "4d2be56310a490c44bda853b870cc510e3fca54103fc3029f0d4415a54c271f4"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, - "p1db": {:git, "ssh://git@github.com/processone/p1db.git", "5aac407d361bba9d14603182455e68a7c0300237", [ref: "5aac407d361bba9d14603182455e68a7c0300237"]}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, - "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.11", "89ae34da50cb8d179724d389d4f8000cf3c248fc9cdead97b681a5c1973f13e2", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b4d9f980af3d89653b405e0a3ee0463677e7fdaa4f824efe5b8ad4a753604ff3"}, From 4680954112248c182fae46fba08572545a9c655d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 7 Feb 2024 18:49:34 +0100 Subject: [PATCH 0397/1302] README.md: Add links to nightly builds --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b3fa7809..a46c42ced 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ Installation There are several ways to install ejabberd: - Source code: compile yourself, see [COMPILE](COMPILE.md) -- Installers from [ProcessOne Download][p1download] or [ejabberd GitHub Releases][releases] (run/deb/rpm for x64 and arm64) -- `ecs` container image available in [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for x64) -- `ejabberd` container image available in [Github Packages][packages], see [CONTAINER](CONTAINER.md) (for x64 and arm64) +- Installers: [ProcessOne Download][p1download] and [GitHub Releases][releases] for releases, [GitHub Actions](https://github.com/processone/ejabberd/actions/workflows/installers.yml) for master branch (run/deb/rpm for x64 and arm64) +- `ecs` container image: [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for x64) +- `ejabberd` container image: [Github Packages][packages] for releases and master branch, see [CONTAINER](CONTAINER.md) (for x64 and arm64) - Using your [Operating System package][osp] - Using the [Homebrew][homebrew] package manager @@ -71,6 +71,10 @@ or in your local machine as explained in [Localization][localization]. Documentation for developers is available in [ejabberd docs: Developers][docs-dev]. +There are nightly builds of ejabberd, both for `master` branch and for Pull Requests: +- Installers: go to [GitHub Actions: Installers](https://github.com/processone/ejabberd/actions/workflows/installers.yml), open the most recent commit, on the bottom of that commit page, download the `ejabberd-packages.zip` artifact. +- `ejabberd` container image: go to [ejabberd Github Packages][packages] + Security reports or concerns should preferably be reported privately, please send an email to the address: contact at process-one dot net or some other method from [ProcessOne Contact][p1contact]. @@ -123,3 +127,4 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [xeps]: https://www.process-one.net/en/ejabberd/protocols/ [xmpp]: https://xmpp.org/ [xmppej]: https://xmpp.org/software/servers/ejabberd/ + From 9f934abd480507b4a06fad9c680aad759a1ff1c9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 13:26:47 +0100 Subject: [PATCH 0398/1302] ejabberdctl: Fix crash running defined commands in container and installers --- src/ejabberd_ctl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 61731f65c..783b290ed 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -898,7 +898,7 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> IsDefinerMod = case Definer of unknown -> true; - _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) + _ -> lists:member([gen_mod], proplists:get_all_values(behaviour, Definer:module_info(attributes))) end, ModuleFmt = case IsDefinerMod of true -> [" ",?B("Module"),": ", atom_to_list(Definer), "\n\n"]; From fa12301e085562962fc865e72ad9361ba41fcb7d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Feb 2024 13:56:33 +0100 Subject: [PATCH 0399/1302] ejabberdctl: Fix problem when running ejabberdctl in container --- .github/container/ejabberdctl.template | 9 ++++++--- ejabberdctl.template | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 6098b7edb..84eafcb5c 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -228,7 +228,9 @@ help() # dynamic node name helper uid() { - if erl -noinput -boot start_clean -eval 'case erlang:system_info(otp_release) >= "23" of true -> halt(1); _ -> halt(0) end.' ; then + ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.*\([0-9][0-9]\).*|\1|g')" + if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 + # Erlang/OTP lower than 23, which doesn's support dynamic node code N=1 PF=$(( $$ % 97 )) while @@ -241,11 +243,12 @@ uid() { ;; esac N=$(( N + 1 + ( $$ % 5 ) )) - epmd -names 2>/dev/null | grep -q " ${NN%@*} " + "$EPMD" -names 2>/dev/null | grep -q " ${NN%@*} " do :; done echo $NN else - # for R23+ use native dynamic node code + # Erlang/OTP 23 or higher: use native dynamic node code + # https://www.erlang.org/patches/otp-23.0#OTP-13812 echo undefined fi } diff --git a/ejabberdctl.template b/ejabberdctl.template index 80fcab41b..f558e93b2 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -249,7 +249,9 @@ help() # dynamic node name helper uid() { - if erl -noinput -boot start_clean -eval 'case erlang:system_info(otp_release) >= "23" of true -> halt(1); _ -> halt(0) end.' ; then + ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.*\([0-9][0-9]\).*|\1|g')" + if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 + # Erlang/OTP lower than 23, which doesn's support dynamic node code N=1 PF=$(( $$ % 97 )) while @@ -262,11 +264,12 @@ uid() { ;; esac N=$(( N + 1 + ( $$ % 5 ) )) - epmd -names 2>/dev/null | grep -q " ${NN%@*} " + "$EPMD" -names 2>/dev/null | grep -q " ${NN%@*} " do :; done echo $NN else - # for R23+ use native dynamic node code + # Erlang/OTP 23 or higher: use native dynamic node code + # https://www.erlang.org/patches/otp-23.0#OTP-13812 echo undefined fi } From 1962fc88d62ede7676bf7f91f72094ef3f105839 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 7 Feb 2024 19:06:20 +0100 Subject: [PATCH 0400/1302] make-binaries: Bump OpenSSL 3.2.1, Erlang/OTP 26.2.2, Elixir 1.16.1 The update of OpenSSL follows: https://github.com/processone/eturnal/commit/eae4ab473de7042daf28d538bbc9be2bfbbda12a https://github.com/processone/eturnal/commit/39823a8009f97e309d4144ef66761f11263481f1 https://github.com/processone/eturnal/commit/92e9a41888f1a3008ed7b89a263c84e5e3381082 --- tools/make-binaries | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 30165290c..c32771d28 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,9 +70,9 @@ termcap_vsn='1.3.1' expat_vsn='2.5.0' zlib_vsn='1.3' yaml_vsn='0.2.5' -ssl_vsn='1.1.1w' -otp_vsn='26.1.1' -elixir_vsn='1.15.6' +ssl_vsn='3.2.1' +otp_vsn='26.2.2' +elixir_vsn='1.16.1' pam_vsn='1.5.2' png_vsn='1.6.40' jpeg_vsn='9e' @@ -180,7 +180,7 @@ check_configured_dep_vsns() { check_vsn 'OpenSSL' "$ssl_vsn" \ 'https://www.openssl.org/source/' \ - 'openssl-\(1\.[0-9][0-9a-z.]*\)\.tar\.gz' + 'openssl-\(3\.[1-9]\.[0-9.]*\)\.tar\.gz' check_vsn 'LibYAML' "$yaml_vsn" \ 'https://pyyaml.org/wiki/LibYAML' \ 'yaml-\([0-9][0-9.]*\)\.tar\.gz' @@ -578,9 +578,11 @@ build_deps() info "Building OpenSSL $ssl_vsn for $arch-$libc ..." cd "$target_src_dir/$ssl_dir" - CFLAGS="$CFLAGS -O3 -fPIC" ./Configure no-shared no-ui-console \ + CFLAGS="$CFLAGS -O3 -fPIC" \ + ./Configure no-shared no-module no-ui-console \ --prefix="$prefix" \ --openssldir="$prefix" \ + --libdir='lib' \ "linux-${target%-linux-*}" make build_libs make install_dev From 021f0be119845532470763299518af9d373db1a3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 12:56:24 +0100 Subject: [PATCH 0401/1302] Container: Update to Erlang/OTP 26.2, Elixir 1.16.1 and Alpine 3.19 --- .github/container/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 94a9422f2..24029d2b6 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,9 +1,9 @@ #' Define default build variables ## specifc ARGs for METHOD='direct' -ARG OTP_VSN='25.3' -ARG ELIXIR_VSN='1.14.4' +ARG OTP_VSN='26.2' +ARG ELIXIR_VSN='1.16.1' ## specifc ARGs for METHOD='package' -ARG ALPINE_VSN='3.17' +ARG ALPINE_VSN='3.19' ## general ARGs ARG UID='9000' ARG USER='ejabberd' From 66645baa75468227cf867f56f476fb5ce0f64651 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:00:27 +0100 Subject: [PATCH 0402/1302] Container: Apply commit 122af79 move spool dir to make it fully configurable per ejabberdctl.cfg (#3863) --- .github/container/ejabberdctl.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 84eafcb5c..3ece1c287 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -60,7 +60,6 @@ done # define ejabberd variables if not already defined from the command line : "${CONFIG_DIR:="{{config_dir}}"}" : "${LOGS_DIR:="{{logs_dir}}"}" -: "${SPOOL_DIR:="{{spool_dir}}"}" : "${EJABBERD_CONFIG_PATH:="$CONFIG_DIR/ejabberd.yml"}" : "${EJABBERDCTL_CONFIG_PATH:="$CONFIG_DIR/ejabberdctl.cfg"}" # Allows passing extra Erlang command-line arguments in vm.args file @@ -69,6 +68,7 @@ done [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" +: "${SPOOL_DIR:="{{spool_dir}}"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" # define erl parameters From 413ffd7470ef941562d320f7a761554f50bbd73d Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:06:18 +0100 Subject: [PATCH 0403/1302] Container: Apply commit 19e2e16 Let "ejabberdctl etop" work in a release (if observer is available) --- .github/container/ejabberdctl.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 3ece1c287..945488c06 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -366,10 +366,10 @@ case $1 in ;; etop) set_dist_client - exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" \ + exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" \ -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ -s etop \ - -s erlang halt -output text + -output text ;; iexdebug) debugwarning From 39ea08b7cb6d58a6064f9e13d0e6f1cf176c42b3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:07:32 +0100 Subject: [PATCH 0404/1302] Container: Apply commit 841d5c0 and 81ceefe Fix startup problem when having set EJABBERD_OPTS and logger options Remove spurious line --- .github/container/ejabberdctl.template | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 945488c06..c8c1d1fb1 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -90,11 +90,12 @@ ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump ERL_INETRC="$CONFIG_DIR"/inetrc # define ejabberd parameters -EJABBERD_OPTS="$EJABBERD_OPTS\ -$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\ -$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")" +EJABBERD_OPTS="\ +$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ +$EJABBERD_OPTS" [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" From 01909b07ce4ed74094d12387b24a7617feb58ca7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:09:10 +0100 Subject: [PATCH 0405/1302] Container: Apply commit e1f14ac Rebar3: Provide proper path to iex --- .github/container/ejabberdctl.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index c8c1d1fb1..938f5d8f8 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -15,8 +15,8 @@ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" # shellcheck disable=SC2034 ERTS_VSN="{{erts_vsn}}" ERL="{{erl}}" -IEX="{{bindir}}/iex" EPMD="{{epmd}}" +IEX="{{iexpath}}" COOKIE_FILE="$HOME"/.erlang.cookie [ -n "$ERLANG_COOKIE" ] && [ ! -f "$COOKIE_FILE" ] && echo "$ERLANG_COOKIE" > "$COOKIE_FILE" && chmod 400 "$COOKIE_FILE" From 2531ebf685b715a3fd1caac2680079da02ceba07 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:10:08 +0100 Subject: [PATCH 0406/1302] Container: Apply commit abf0796 ejabberdctl: Detect problem running etop and show some help --- .github/container/ejabberdctl.template | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 938f5d8f8..7a56ae96e 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -200,6 +200,21 @@ livewarning() fi } +check_etop_result() +{ + result=$? + if [ $result -eq 1 ] ; then + echo "" + echo "It seems there was some problem running 'ejabberdctl etop'." + echo "Is the error message something like this?" + echo " Failed to load module 'etop' because it cannot be found..." + echo "Then probably ejabberd was compiled with development tools disabled." + echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools" + echo "" + exit $result + fi +} + help() { echo "" @@ -371,6 +386,7 @@ case $1 in -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ -s etop \ -output text + check_etop_result ;; iexdebug) debugwarning From e1b94487ef1379efe5f3262d0a441e2f43589591 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 11:10:48 +0100 Subject: [PATCH 0407/1302] Container: Apply commit 35b727a ejabberdctl: Detect problem running iex and show explanation --- .github/container/ejabberdctl.template | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 7a56ae96e..22e4177b1 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -215,6 +215,24 @@ check_etop_result() fi } +check_iex_result() +{ + result=$? + if [ $result -eq 127 ] ; then + echo "" + echo "It seems there was some problem finding 'iex' binary from Elixir." + echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:" + echo " ./configure" + echo "which is equivalent to:" + echo " ./configure --with-rebar=rebar3 --disable-elixir" + echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:" + echo " ./configure --enable-elixir" + echo " ./configure --with-rebar=mix" + echo "" + exit $result + fi +} + help() { echo "" @@ -392,10 +410,12 @@ case $1 in debugwarning set_dist_client exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" + check_iex_result ;; iexlive) livewarning exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" + check_iex_result ;; ping) PEER=${2:-$ERLANG_NODE} From cdaf45f91ce5b4473e5fb9383f4419fdeaeddbe2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Jan 2024 16:08:57 +0100 Subject: [PATCH 0408/1302] Test: Check that the message we get is the welcome message, not other --- test/ejabberd_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 1e2fb3c37..d2394083c 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -897,7 +897,8 @@ presence_broadcast(Config) -> IQ = #iq{type = get, from = JID, sub_els = [#disco_info{node = Node}]} = recv_iq(Config), - #message{type = normal} = recv_message(Config), + #message{type = normal, + subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config), #presence{from = JID, to = JID} = recv_presence(Config), send(Config, #iq{type = result, id = IQ#iq.id, to = JID, sub_els = [Info]}), From 67a6776fba9e313df32fcc6cc8cc8a9a6d5a3d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 13 Feb 2024 11:55:12 +0100 Subject: [PATCH 0409/1302] Add ability to ignore failures in execution of container CTL_ON_* commands This will allow to use register in CTL_ON_CREATE and not abort if used with existing data that have account already registered. --- .github/container/ejabberdctl.template | 15 ++++++++++----- CONTAINER.md | 11 ++++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 22e4177b1..e7e112ffa 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -331,13 +331,18 @@ post_waiter_loop() LIST=$@ HEAD=${LIST%% ; *} TAIL=${LIST#* ; } - echo ":> ejabberdctl $HEAD" - $0 $HEAD + HEAD2=${HEAD#\! *} + echo ":> ejabberdctl $HEAD2" + $0 $HEAD2 ctlstatus=$? if [ $ctlstatus -ne 0 ] ; then - echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..." - $0 halt > /dev/null - exit $ctlstatus + if [ "$HEAD" != "$HEAD2" ] ; then + echo ":> FAILURE in command '$HEAD2' !!! Ignoring result" + else + echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..." + $0 halt > /dev/null + exit $ctlstatus + fi fi [ "$HEAD" = "$TAIL" ] || post_waiter_loop $TAIL } diff --git a/CONTAINER.md b/CONTAINER.md index 7c7c233ef..06d01dbcc 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -221,10 +221,13 @@ and reads `CTL_ON_START` every time the container is started. Those variables can contain one ejabberdctl command, or several commands separated with the blankspace and `;` characters. +By default failure of any of commands executed that way would +abort start, this can be disabled by prefixing commands with `!` + Example usage (or check the [full example](#customized-example)): ```yaml environment: - - CTL_ON_CREATE=register admin localhost asd + - CTL_ON_CREATE=\! register admin localhost asd - CTL_ON_START=stats registeredusers ; check_password admin localhost asd ; status @@ -411,7 +414,9 @@ In this example, the main container is created first. Once it is fully started and healthy, a second container is created, and once ejabberd is started in it, it joins the first one. -An account is registered in the first node when created, +An account is registered in the first node when created (and +we ignore errors that can happen when doing that - for example +whenn account already exists), and it should exist in the second node after join. Notice that in this example the main container does not have access @@ -428,7 +433,7 @@ services: environment: - ERLANG_NODE_ARG=ejabberd@main - ERLANG_COOKIE=dummycookie123 - - CTL_ON_CREATE=register admin localhost asd + - CTL_ON_CREATE=\! register admin localhost asd replica: image: ghcr.io/processone/ejabberd From f44e23b8cc2c3ab7d1c36f702f00a6b5b947c5d0 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Wed, 14 Feb 2024 06:57:59 +0300 Subject: [PATCH 0410/1302] Add mod_matrix_gw --- include/mod_matrix_gw.hrl | 28 + src/mod_matrix_gw.erl | 913 +++++++++++++ src/mod_matrix_gw_opt.erl | 48 + src/mod_matrix_gw_room.erl | 2652 ++++++++++++++++++++++++++++++++++++ src/mod_matrix_gw_s2s.erl | 583 ++++++++ src/mod_matrix_gw_sup.erl | 77 ++ 6 files changed, 4301 insertions(+) create mode 100644 include/mod_matrix_gw.hrl create mode 100644 src/mod_matrix_gw.erl create mode 100644 src/mod_matrix_gw_opt.erl create mode 100644 src/mod_matrix_gw_room.erl create mode 100644 src/mod_matrix_gw_s2s.erl create mode 100644 src/mod_matrix_gw_sup.erl diff --git a/include/mod_matrix_gw.hrl b/include/mod_matrix_gw.hrl new file mode 100644 index 000000000..7ebaa0854 --- /dev/null +++ b/include/mod_matrix_gw.hrl @@ -0,0 +1,28 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-record(room_version, + {id :: binary(), + %% use the same field names as in Synapse + knock_restricted_join_rule :: boolean(), + enforce_int_power_levels :: boolean(), + implicit_room_creator :: boolean(), + updated_redaction_rules :: boolean() + }). diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl new file mode 100644 index 000000000..929cdc477 --- /dev/null +++ b/src/mod_matrix_gw.erl @@ -0,0 +1,913 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_matrix_gw.erl +%%% Author : Alexey Shchepin +%%% Purpose : Matrix gateway +%%% Created : 23 Apr 2022 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_matrix_gw). +-if(?OTP_RELEASE >= 24). + +-author('alexey@process-one.net'). + +-ifndef(GEN_SERVER). +-define(GEN_SERVER, gen_server). +-endif. +-behaviour(?GEN_SERVER). +-behaviour(gen_mod). + +-export([start/2, stop/1, reload/3, process/2, + start_link/1, + procname/1, + init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3, + depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]). +-export([parse_auth/1, encode_canonical_json/1, + get_id_domain_exn/1, + base64_decode/1, base64_encode/1, + prune_event/2, get_event_id/2, content_hash/1, + sign_event/3, sign_pruned_event/2, sign_json/2, + send_request/8, s2s_out_bounce_packet/2, user_receive_packet/1, + route/1]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("ejabberd_http.hrl"). +-include("translate.hrl"). +-include("ejabberd_web_admin.hrl"). +-include("mod_matrix_gw.hrl"). + +-define(MAX_REQUEST_SIZE, 1000000). + +process([<<"key">>, <<"v2">>, <<"server">> | _], + #request{method = 'GET', host = _Host} = _Request) -> + Host = ejabberd_config:get_myname(), + KeyName = mod_matrix_gw_opt:key_name(Host), + KeyID = <<"ed25519:", KeyName/binary>>, + ServerName = mod_matrix_gw_opt:matrix_domain(Host), + TS = erlang:system_time(millisecond) + timer:hours(24 * 7), + {PubKey, _PrivKey} = mod_matrix_gw_opt:key(Host), + JSON = #{<<"old_verify_keys">> => #{}, + <<"server_name">> => ServerName, + <<"valid_until_ts">> => TS, + <<"verify_keys">> => #{ + KeyID => #{ + <<"key">> => base64_encode(PubKey) + } + }}, + SJSON = sign_json(Host, JSON), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(SJSON)}; +process([<<"federation">>, <<"v1">>, <<"version">>], + #request{method = 'GET', host = _Host} = _Request) -> + JSON = #{<<"server">> => #{<<"name">> => <<"ejabberd/mod_matrix_gw">>, + <<"version">> => <<"0.1">>}}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(JSON)}; +process([<<"federation">>, <<"v1">>, <<"query">>, <<"profile">>], + #request{method = 'GET', host = _Host} = Request) -> + case proplists:get_value(<<"user_id">>, Request#request.q) of + UserID when is_binary(UserID) -> + Field = + case proplists:get_value(<<"field">>, Request#request.q) of + <<"displayname">> -> displayname; + <<"avatar_url">> -> avatar_url; + undefined -> all; + _ -> error + end, + case Field of + error -> + {400, [], <<"400 Bad Request: bad 'field' parameter">>}; + _ -> + case preprocess_federation_request(Request) of + {ok, _JSON, _Origin} -> + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], <<"{}">>}; + {result, HTTPResult} -> + HTTPResult + end + end; + undefined -> + {400, [], <<"400 Bad Request: missing 'user_id' parameter">>} + end; +process([<<"federation">>, <<"v1">>, <<"user">>, <<"devices">>, UserID], + #request{method = 'GET', host = _Host} = Request) -> + case preprocess_federation_request(Request) of + {ok, _JSON, _Origin} -> + Res = #{<<"devices">> => + [#{<<"device_id">> => <<"ejabberd/mod_matrix_gw">>, + <<"keys">> => []}], + <<"stream_id">> => 1, + <<"user_id">> => UserID}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], jiffy:encode(Res)}; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v1">>, <<"user">>, <<"keys">>, <<"query">>], + #request{method = 'POST', host = _Host} = Request) -> + case preprocess_federation_request(Request, false) of + {ok, #{<<"device_keys">> := DeviceKeys}, _Origin} -> + DeviceKeys2 = maps:map(fun(_Key, _) -> #{} end, DeviceKeys), + Res = #{<<"device_keys">> => DeviceKeys2}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {ok, _JSON, _Origin} -> + {400, [], <<"400 Bad Request: invalid format">>}; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], + #request{method = 'PUT', host = _Host} = Request) -> + case preprocess_federation_request(Request) of + {ok, #{<<"event">> := #{%<<"origin">> := Origin, + <<"room_id">> := RoomID, + <<"sender">> := Sender, + <<"state_key">> := UserID} = Event, + <<"room_version">> := RoomVer}, + Origin} -> + case mod_matrix_gw_room:binary_to_room_version(RoomVer) of + #room_version{} = RoomVersion -> + %% TODO: check type and userid + Host = ejabberd_config:get_myname(), + PrunedEvent = prune_event(Event, RoomVersion), + ?DEBUG("invite ~p~n", [{RoomID, EventID, Event, RoomVer, catch mod_matrix_gw_s2s:check_signature(Host, PrunedEvent), get_pruned_event_id(PrunedEvent)}]), + case mod_matrix_gw_s2s:check_signature(Host, PrunedEvent) of + true -> + case get_pruned_event_id(PrunedEvent) of + EventID -> + SEvent = sign_pruned_event(Host, PrunedEvent), + ?DEBUG("sign event ~p~n", [SEvent]), + ResJSON = #{<<"event">> => SEvent}, + mod_matrix_gw_room:join(Host, Origin, RoomID, Sender, UserID), + ?DEBUG("res ~s~n", [jiffy:encode(ResJSON)]), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], jiffy:encode(ResJSON)}; + _ -> + {400, [], <<"400 Bad Request: bad event id">>} + end; + false -> + {400, [], <<"400 Bad Request: signature check failed">>} + end; + false -> + {400, [], <<"400 Bad Request: unsupported room version">>} + end; + {ok, _JSON, _Origin} -> + {400, [], <<"400 Bad Request: invalid format">>}; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v1">>, <<"send">>, _TxnID], + #request{method = 'PUT', host = _Host} = Request) -> + case preprocess_federation_request(Request, false) of + {ok, #{<<"origin">> := Origin, + <<"pdus">> := PDUs} = JSON, + Origin} -> + ?DEBUG("send request ~p~n", [JSON]), + Host = ejabberd_config:get_myname(), + Res = lists:map( + fun(PDU) -> + case mod_matrix_gw_room:process_pdu(Host, Origin, PDU) of + {ok, EventID} -> {EventID, #{}}; + {error, Error} -> + {get_event_id(PDU, mod_matrix_gw_room:binary_to_room_version(<<"9">>)), + #{<<"error">> => Error}} + end + end, PDUs), + ?DEBUG("send res ~p~n", [Res]), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(maps:from_list(Res))}; + {ok, _JSON, _Origin} -> + {400, [], <<"400 Bad Request: invalid format">>}; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v1">>, <<"get_missing_events">>, RoomID], + #request{method = 'POST', host = _Host} = Request) -> + case preprocess_federation_request(Request, false) of + {ok, #{<<"earliest_events">> := EarliestEvents, + <<"latest_events">> := LatestEvents} = JSON, + Origin} -> + ?DEBUG("get_missing_events request ~p~n", [JSON]), + Limit = maps:get(<<"limit">>, JSON, 10), + MinDepth = maps:get(<<"min_depth">>, JSON, 0), + Host = ejabberd_config:get_myname(), + PDUs = mod_matrix_gw_room:get_missing_events( + Host, Origin, RoomID, EarliestEvents, LatestEvents, Limit, MinDepth), + ?DEBUG("get_missing_events res ~p~n", [PDUs]), + Res = #{<<"events">> => PDUs}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {ok, _JSON, _Origin} -> + {400, [], <<"400 Bad Request: invalid format">>}; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v1">>, <<"backfill">>, RoomID], + #request{method = 'GET', host = _Host} = Request) -> + case catch binary_to_integer(proplists:get_value(<<"limit">>, Request#request.q)) of + Limit when is_integer(Limit) -> + case preprocess_federation_request(Request, false) of + {ok, _JSON, Origin} -> + LatestEvents = proplists:get_all_values(<<"v">>, Request#request.q), + ?DEBUG("backfill request ~p~n", [{Limit, LatestEvents}]), + Host = ejabberd_config:get_myname(), + PDUs1 = mod_matrix_gw_room:get_missing_events( + Host, Origin, RoomID, [], LatestEvents, Limit, 0), + PDUs2 = lists:flatmap( + fun(EventID) -> + case mod_matrix_gw_room:get_event(Host, RoomID, EventID) of + {ok, PDU} -> + [PDU]; + _ -> + [] + end + end, LatestEvents), + PDUs = PDUs2 ++ PDUs1, + ?DEBUG("backfill res ~p~n", [PDUs]), + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + Res = #{<<"origin">> => MatrixServer, + <<"origin_server_ts">> => erlang:system_time(millisecond), + <<"pdus">> => PDUs}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {result, HTTPResult} -> + HTTPResult + end; + _ -> + {400, [], <<"400 Bad Request: bad 'limit' parameter">>} + end; +process([<<"federation">>, <<"v1">>, <<"state_ids">>, RoomID], + #request{method = 'GET', host = _Host} = Request) -> + case proplists:get_value(<<"event_id">>, Request#request.q) of + EventID when is_binary(EventID) -> + case preprocess_federation_request(Request) of + {ok, _JSON, Origin} -> + Host = ejabberd_config:get_myname(), + case mod_matrix_gw_room:get_state_ids(Host, Origin, RoomID, EventID) of + {ok, AuthChain, PDUs} -> + Res = #{<<"auth_chain_ids">> => AuthChain, + <<"pdu_ids">> => PDUs}, + ?DEBUG("get_state_ids res ~p~n", [Res]), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {error, room_not_found} -> + {400, [], <<"400 Bad Request: room not found">>}; + {error, not_allowed} -> + {403, [], <<"403 Forbidden: origin not in room">>}; + {error, event_not_found} -> + {400, [], <<"400 Bad Request: 'event_id' not found">>} + end; + {result, HTTPResult} -> + HTTPResult + end; + undefined -> + {400, [], <<"400 Bad Request: missing 'event_id' parameter">>} + end; +process([<<"federation">>, <<"v1">>, <<"event">>, EventID], + #request{method = 'GET', host = _Host} = Request) -> + case preprocess_federation_request(Request) of + {ok, _JSON, _Origin} -> + Host = ejabberd_config:get_myname(), + %% TODO: very inefficient, replace with an SQL call + PDU = + lists:foldl( + fun(RoomID, undefined) -> + case mod_matrix_gw_room:get_event(Host, RoomID, EventID) of + {ok, PDU} -> + PDU; + _ -> + undefined + end; + (_, Acc) -> + Acc + end, undefined, mod_matrix_gw_room:get_rooms_list()), + ?DEBUG("get_event res ~p~n", [PDU]), + case PDU of + undefined -> + {400, [], <<"400 Bad Request: event not found">>}; + _ -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + Res = #{<<"origin">> => MatrixServer, + <<"origin_server_ts">> => erlang:system_time(millisecond), + <<"pdus">> => [PDU]}, + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)} + end; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v1">>, <<"make_join">>, RoomID, UserID], + #request{method = 'GET', host = _Host, q = Params} = Request) -> + case preprocess_federation_request(Request) of + {ok, _JSON, Origin} -> + Host = ejabberd_config:get_myname(), + case get_id_domain_exn(UserID) of + Origin -> + case mod_matrix_gw_room:make_join(Host, RoomID, UserID, Params) of + {error, room_not_found} -> + Res = #{<<"errcode">> => <<"M_NOT_FOUND">>, + <<"error">> => <<"Unknown room">>}, + {404, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {error, not_invited} -> + Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, + <<"error">> => <<"You are not invited to this room">>}, + {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {error, {incompatible_version, Ver}} -> + Res = #{<<"errcode">> => <<"M_INCOMPATIBLE_ROOM_VERSION">>, + <<"error">> => <<"Your homeserver does not support the features required to join this room">>, + <<"room_version">> => Ver}, + {400, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {ok, Res} -> + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)} + end; + _ -> + Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, + <<"error">> => <<"User not from origin">>}, + {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)} + end; + {result, HTTPResult} -> + HTTPResult + end; +process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], + #request{method = 'PUT', host = _Host} = Request) -> + case preprocess_federation_request(Request) of + {ok, #{<<"content">> := #{<<"membership">> := <<"join">>}, + %<<"origin">> := Origin, + <<"room_id">> := RoomID, + <<"sender">> := Sender, + <<"state_key">> := Sender, + <<"type">> := <<"m.room.member">>} = JSON, Origin} -> + Host = ejabberd_config:get_myname(), + case get_id_domain_exn(Sender) of + Origin -> + case mod_matrix_gw_room:send_join(Host, Origin, RoomID, EventID, JSON) of + {error, room_not_found} -> + Res = #{<<"errcode">> => <<"M_NOT_FOUND">>, + <<"error">> => <<"Unknown room">>}, + {404, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {error, not_invited} -> + Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, + <<"error">> => <<"You are not invited to this room">>}, + {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {error, Error} when is_binary(Error) -> + Res = #{<<"errcode">> => <<"M_BAD_REQUEST">>, + <<"error">> => Error}, + {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {ok, Res} -> + ?DEBUG("send_join res: ~p~n", [Res]), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)} + end; + _ -> + Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, + <<"error">> => <<"User not from origin">>}, + {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)} + end; + {ok, _JSON, _Origin} -> + Res = #{<<"errcode">> => <<"M_BAD_REQUEST">>, + <<"error">> => <<"Invalid event format">>}, + {400, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], + jiffy:encode(Res)}; + {result, HTTPResult} -> + HTTPResult + end; +process(_Path, _Request) -> + ?DEBUG("matrix 404: ~p~n~p~n", [_Path, _Request]), + ejabberd_web:error(not_found). + +preprocess_federation_request(Request) -> + preprocess_federation_request(Request, true). + +preprocess_federation_request(Request, DoSignCheck) -> + ?DEBUG("matrix federation: ~p~n", [Request]), + case proplists:get_value('Authorization', Request#request.headers) of + Auth when is_binary(Auth) -> + case parse_auth(Auth) of + #{<<"origin">> := MatrixServer, + <<"key">> := _, + <<"sig">> := _} = AuthParams -> + ?DEBUG("auth ~p~n", [AuthParams]), + if + Request#request.length =< ?MAX_REQUEST_SIZE -> + Request2 = recv_data(Request), + JSON = + if + Request#request.length > 0 -> + try + jiffy:decode(Request2#request.data, + [return_maps]) + catch + _:_ -> + error + end; + true -> + none + end, + ?DEBUG("json ~p~n", [JSON]), + case JSON of + error -> + {result, {400, [], <<"400 Bad Request: invalid JSON">>}}; + JSON when not DoSignCheck -> + {ok, JSON, MatrixServer}; + JSON -> + Host = ejabberd_config:get_myname(), + case mod_matrix_gw_s2s:check_auth( + Host, MatrixServer, + AuthParams, JSON, + Request2) of + true -> + ?DEBUG("auth ok~n", []), + {ok, JSON, MatrixServer}; + false -> + ?DEBUG("auth failed~n", []), + {result, {401, [], <<"401 Unauthorized">>}} + end + end; + true -> + {result, {400, [], <<"400 Bad Request: size limit">>}} + end; + _ -> + {result, {400, [], <<"400 Bad Request: bad 'Authorization' header">>}} + end; + undefined -> + {result, {400, [], <<"400 Bad Request: no 'Authorization' header">>}} + end. + +recv_data(#request{length = Len, data = Trail, + sockmod = SockMod, socket = Socket} = Request) -> + NewLen = Len - byte_size(Trail), + if NewLen > 0 -> + case SockMod:recv(Socket, NewLen, 60000) of + {ok, Data} -> + Request#request{data = <>}; + {error, _} -> Request + end; + true -> + Request + end. + + +-record(state, + {host :: binary(), + server_host :: binary()}). + +-type state() :: #state{}. + +start(Host, _Opts) -> + case mod_matrix_gw_sup:start(Host) of + {ok, _} -> + {ok, [{hook, s2s_out_bounce_packet, s2s_out_bounce_packet, 50}, + {hook, user_receive_packet, user_receive_packet, 50}]}; + Err -> + Err + end. + +stop(Host) -> + Proc = mod_matrix_gw_sup:procname(Host), + supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), + supervisor:delete_child(ejabberd_gen_mod_sup, Proc). + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +start_link(Host) -> + Proc = procname(Host), + ?GEN_SERVER:start_link({local, Proc}, ?MODULE, [Host], + ejabberd_config:fsm_limit_opts([])). + +-spec init(list()) -> {ok, state()}. +init([Host]) -> + process_flag(trap_exit, true), + mod_matrix_gw_s2s:create_db(), + mod_matrix_gw_room:create_db(), + Opts = gen_mod:get_module_opts(Host, ?MODULE), + MyHost = gen_mod:get_opt(host, Opts), + register_routes(Host, [MyHost]), + {ok, #state{server_host = Host, host = MyHost}}. + +-spec handle_call(term(), {pid(), term()}, state()) -> + {reply, ok | {ok, pid()} | {error, any()}, state()} | + {stop, normal, ok, state()}. +handle_call(stop, _From, State) -> + {stop, normal, ok, State}. + +-spec handle_cast(term(), state()) -> {noreply, state()}. +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {noreply, State}. + +-spec handle_info(term(), state()) -> {noreply, state()}. +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {noreply, State}. + +-spec terminate(term(), state()) -> any(). +terminate(_Reason, #state{host = Host}) -> + unregister_routes([Host]). + +-spec code_change(term(), state(), term()) -> {ok, state()}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. + + +-spec register_routes(binary(), [binary()]) -> ok. +register_routes(ServerHost, Hosts) -> + lists:foreach( + fun(Host) -> + ejabberd_router:register_route( + Host, ServerHost, {apply, ?MODULE, route}) + end, Hosts). + +unregister_routes(Hosts) -> + lists:foreach( + fun(Host) -> + ejabberd_router:unregister_route(Host) + end, Hosts). + +procname(Host) -> + binary_to_atom( + <<(atom_to_binary(?MODULE, latin1))/binary, "_", Host/binary>>, utf8). + +parse_auth(<<"X-Matrix ", S/binary>>) -> + parse_auth1(S, <<>>, []); +parse_auth(_) -> + error. + +parse_auth1(<<$=, Cs/binary>>, S, Ts) -> + parse_auth2(Cs, S, <<>>, Ts); +parse_auth1(<<$,, Cs/binary>>, <<>>, Ts) -> parse_auth1(Cs, [], Ts); +parse_auth1(<<$\s, Cs/binary>>, <<>>, Ts) -> parse_auth1(Cs, [], Ts); +parse_auth1(<>, S, Ts) -> parse_auth1(Cs, <>, Ts); +parse_auth1(<<>>, <<>>, T) -> maps:from_list(T); +parse_auth1(<<>>, _S, _T) -> error. + +parse_auth2(<<$", Cs/binary>>, Key, Val, Ts) -> + parse_auth3(Cs, Key, Val, Ts); +parse_auth2(<>, Key, Val, Ts) -> + parse_auth4(Cs, Key, <>, Ts); +parse_auth2(<<>>, _, _, _) -> error. + +parse_auth3(<<$", Cs/binary>>, Key, Val, Ts) -> + parse_auth4(Cs, Key, Val, Ts); +parse_auth3(<<$\\, C, Cs/binary>>, Key, Val, Ts) -> + parse_auth3(Cs, Key, <>, Ts); +parse_auth3(<>, Key, Val, Ts) -> + parse_auth3(Cs, Key, <>, Ts); +parse_auth3(<<>>, _, _, _) -> error. + +parse_auth4(<<$,, Cs/binary>>, Key, Val, Ts) -> + parse_auth1(Cs, <<>>, [{Key, Val} | Ts]); +parse_auth4(<<$\s, Cs/binary>>, Key, Val, Ts) -> + parse_auth4(Cs, Key, Val, Ts); +parse_auth4(<>, Key, Val, Ts) -> + parse_auth4(Cs, Key, <>, Ts); +parse_auth4(<<>>, Key, Val, Ts) -> + parse_auth1(<<>>, <<>>, [{Key, Val} | Ts]). + +prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event, + RoomVersion) -> + Event2 = + case RoomVersion#room_version.updated_redaction_rules of + false -> + maps:with( + [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, + <<"state_key">>, <<"content">>, <<"hashes">>, + <<"signatures">>, <<"depth">>, <<"prev_events">>, + <<"prev_state">>, <<"auth_events">>, <<"origin">>, + <<"origin_server_ts">>, <<"membership">>], Event); + true -> + maps:with( + [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, + <<"state_key">>, <<"content">>, <<"hashes">>, + <<"signatures">>, <<"depth">>, <<"prev_events">>, + <<"auth_events">>, <<"origin_server_ts">>], Event) + end, + Content2 = + case Type of + <<"m.room.member">> -> + C3 = maps:with([<<"membership">>, + <<"join_authorised_via_users_server">>], + Content), + case RoomVersion#room_version.updated_redaction_rules of + false -> + C3; + true -> + case Content of + #{<<"third_party_invite">> := + #{<<"signed">> := InvSign}} -> + C3#{<<"third_party_invite">> => + #{<<"signed">> => InvSign}}; + _ -> + C3 + end + end; + <<"m.room.create">> -> + case RoomVersion#room_version.updated_redaction_rules of + false -> + maps:with([<<"creator">>], Content); + true -> + Content + end; + <<"m.room.join_rules">> -> + maps:with([<<"join_rule">>, <<"allow">>], Content); + <<"m.room.power_levels">> -> + case RoomVersion#room_version.updated_redaction_rules of + false -> + maps:with( + [<<"ban">>, <<"events">>, <<"events_default">>, + <<"kick">>, <<"redact">>, <<"state_default">>, + <<"users">>, <<"users_default">>], Content); + true -> + maps:with( + [<<"ban">>, <<"events">>, <<"events_default">>, + <<"invite">>, + <<"kick">>, <<"redact">>, <<"state_default">>, + <<"users">>, <<"users_default">>], Content) + end; + <<"m.room.history_visibility">> -> + maps:with([<<"history_visibility">>], Content); + <<"m.room.redaction">> -> + case RoomVersion#room_version.updated_redaction_rules of + false -> + #{}; + true -> + maps:with([<<"redacts">>], Content) + end; + _ -> #{} + end, + Event2#{<<"content">> := Content2}. + +reference_hash(PrunedEvent) -> + Event2 = maps:without([<<"signatures">>, <<"age_ts">>, <<"unsigned">>], + PrunedEvent), + S = encode_canonical_json(Event2), + crypto:hash(sha256, S). + +content_hash(Event) -> + Event2 = maps:without([<<"signatures">>, <<"age_ts">>, <<"unsigned">>, + <<"hashes">>, <<"outlier">>, <<"destinations">>], + Event), + S = encode_canonical_json(Event2), + crypto:hash(sha256, S). + +get_event_id(Event, RoomVersion) -> + PrunedEvent = prune_event(Event, RoomVersion), + get_pruned_event_id(PrunedEvent). + +get_pruned_event_id(PrunedEvent) -> + B = base64url_encode(reference_hash(PrunedEvent)), + <<$$, B/binary>>. + +encode_canonical_json(JSON) -> + JSON2 = sort_json(JSON), + jiffy:encode(JSON2). + +sort_json(#{} = Map) -> + Map2 = maps:map(fun(_K, V) -> + sort_json(V) + end, Map), + {lists:sort(maps:to_list(Map2))}; +sort_json(List) when is_list(List) -> + lists:map(fun sort_json/1, List); +sort_json(JSON) -> + JSON. + +base64_decode(B) -> + Fixed = + case size(B) rem 4 of + 0 -> B; + %1 -> <>; + 2 -> <>; + 3 -> <>, <<"-">>, [global]), + binary:replace(D1, <<"/">>, <<"_">>, [global]). + +sign_event(Host, Event, RoomVersion) -> + PrunedEvent = prune_event(Event, RoomVersion), + case sign_pruned_event(Host, PrunedEvent) of + #{<<"signatures">> := Signatures} -> + Event#{<<"signatures">> => Signatures} + end. + +sign_pruned_event(Host, PrunedEvent) -> + Event2 = maps:without([<<"age_ts">>, <<"unsigned">>], PrunedEvent), + sign_json(Host, Event2). + +sign_json(Host, JSON) -> + Signatures = maps:get(<<"signatures">>, JSON, #{}), + JSON2 = maps:without([<<"signatures">>, <<"unsigned">>], JSON), + Msg = encode_canonical_json(JSON2), + SignatureName = mod_matrix_gw_opt:matrix_domain(Host), + KeyName = mod_matrix_gw_opt:key_name(Host), + {PubKey, PrivKey} = mod_matrix_gw_opt:key(Host), + KeyID = <<"ed25519:", KeyName/binary>>, + Sig = public_key:sign(Msg, ignored, {ed_pri, ed25519, PubKey, PrivKey}), + Sig64 = base64_encode(Sig), + Signatures2 = Signatures#{SignatureName => #{KeyID => Sig64}}, + JSON#{<<"signatures">> => Signatures2}. + +send_request(Host, Method, MatrixServer, Path, Query, JSON, + HTTPOptions, Options) -> + URI1 = iolist_to_binary( + lists:map(fun(P) -> [$/, http_uri:encode(P)] end, Path)), + URI = + case Query of + [] -> URI1; + _ -> + URI2 = str:join( + lists:map( + fun({K, V}) -> + [http_uri:encode(K), $=, http_uri:encode(V)] + end, Query), $&), + <> + end, + % TODO + {MHost, MPort} = mod_matrix_gw_s2s:get_matrix_host_port(Host, MatrixServer), + %{MHost, MPort} = {MatrixServer, 8008}, + URL = <<"https://", MHost/binary, + ":", (integer_to_binary(MPort))/binary, + URI/binary>>, + SMethod = + case Method of + get -> <<"GET">>; + put -> <<"PUT">>; + post -> <<"POST">> + end, + Auth = make_auth_header(Host, MatrixServer, SMethod, URI, JSON), + Headers = [{"Authorization", binary_to_list(Auth)}], + Content = + case JSON of + none -> <<>>; + _ -> jiffy:encode(JSON) + end, + Request = + case Method of + get -> + {URL, Headers}; + _ -> + {URL, Headers, "application/json;charset=UTF-8", Content} + end, + httpc:request(Method, + Request, + HTTPOptions, + Options). + +make_auth_header(Host, MatrixServer, Method, URI, Content) -> + Origin = mod_matrix_gw_opt:matrix_domain(Host), + JSON = #{<<"method">> => Method, + <<"uri">> => URI, + <<"origin">> => Origin, + <<"destination">> => MatrixServer + }, + JSON2 = + case Content of + none -> JSON; + _ -> + JSON#{<<"content">> => Content} + end, + JSON3 = sign_json(Host, JSON2), + #{<<"signatures">> := #{Origin := #{} = KeySig}} = JSON3, + {KeyID, Sig, _} = maps:next(maps:iterator(KeySig)), + <<"X-Matrix origin=", Origin/binary, ",key=\"", KeyID/binary, + "\",sig=\"", Sig/binary, "\",", + "destination=\"", MatrixServer/binary, "\"">>. + +get_id_domain_exn(B) -> + case binary:split(B, <<":">>) of + [_, Tail] -> Tail; + _ -> error({invalid_id, B}) + end. + +s2s_out_bounce_packet(S2SState, Pkt) -> + #{server_host := Host} = S2SState, + case mod_matrix_gw_opt:matrix_id_as_jid(Host) of + false -> + S2SState; + true -> + To = xmpp:get_to(Pkt), + ServiceHost = mod_matrix_gw_opt:host(Host), + EscU = mod_matrix_gw_room:escape(To#jid.user), + EscS = mod_matrix_gw_room:escape(To#jid.lserver), + NewTo = jid:make(<>, ServiceHost), + ejabberd_router:route(xmpp:set_to(Pkt, NewTo)), + {stop, ignore} + end. + +user_receive_packet({Pkt, C2SState} = Acc) -> + #{lserver := Host} = C2SState, + case mod_matrix_gw_opt:matrix_id_as_jid(Host) of + false -> + Acc; + true -> + ServiceHost = mod_matrix_gw_opt:host(Host), + From = xmpp:get_from(Pkt), + case From#jid.lserver of + ServiceHost -> + case binary:split(From#jid.user, <<"%">>) of + [EscU, EscS] -> + U = mod_matrix_gw_room:unescape(EscU), + S = mod_matrix_gw_room:unescape(EscS), + NewFrom = jid:make(U, S), + {xmpp:set_from(Pkt, NewFrom), C2SState}; + _ -> + Acc + end; + _ -> + Acc + end + end. + +route(Pkt) -> + mod_matrix_gw_room:route(Pkt). + +depends(_Host, _Opts) -> + []. + +mod_opt_type(host) -> + econf:host(); +mod_opt_type(matrix_domain) -> + econf:binary(); +mod_opt_type(key_name) -> + econf:binary(); +mod_opt_type(key) -> + fun(Key) -> + Key1 = (yconf:binary())(Key), + Key2 = base64_decode(Key1), + crypto:generate_key(eddsa, ed25519, Key2) + end; +mod_opt_type(matrix_id_as_jid) -> + econf:bool(); +mod_opt_type(persist) -> + econf:bool(). + +mod_options(Host) -> + [{matrix_domain, <<"@HOST@">>}, + {host, <<"matrix.", Host/binary>>}, + {key_name, <<"">>}, + {key, {<<"">>, <<"">>}}, + {matrix_id_as_jid, false}, + {persist, false}]. + +mod_doc() -> + #{desc => + [?T("TODO")], + example => + [{?T("TODO"), + ["listen:", + " -", + " port: 5280", + " module: ejabberd_http", + " request_handlers:", + " /bosh: mod_bosh", + " /websocket: ejabberd_http_ws", + " /conversejs: mod_conversejs", + "", + "modules:", + " mod_bosh: {}", + " mod_conversejs:", + " websocket_url: \"ws://@HOST@:5280/websocket\""]} + ], + opts => + [{matrix_domain, + #{value => ?T("Domain"), + desc => + ?T("TODO Specify a domain to act as the default for user JIDs. " + "The keyword '@HOST@' is replaced with the hostname. " + "The default value is '@HOST@'.")}} + ] + }. +-endif. diff --git a/src/mod_matrix_gw_opt.erl b/src/mod_matrix_gw_opt.erl new file mode 100644 index 000000000..4648896f9 --- /dev/null +++ b/src/mod_matrix_gw_opt.erl @@ -0,0 +1,48 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_matrix_gw_opt). + +-export([host/1]). +-export([key/1]). +-export([key_name/1]). +-export([matrix_domain/1]). +-export([matrix_id_as_jid/1]). +-export([persist/1]). + +-spec host(gen_mod:opts() | global | binary()) -> binary(). +host(Opts) when is_map(Opts) -> + gen_mod:get_opt(host, Opts); +host(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, host). + +-spec key(gen_mod:opts() | global | binary()) -> any(). +key(Opts) when is_map(Opts) -> + gen_mod:get_opt(key, Opts); +key(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, key). + +-spec key_name(gen_mod:opts() | global | binary()) -> binary(). +key_name(Opts) when is_map(Opts) -> + gen_mod:get_opt(key_name, Opts); +key_name(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, key_name). + +-spec matrix_domain(gen_mod:opts() | global | binary()) -> binary(). +matrix_domain(Opts) when is_map(Opts) -> + gen_mod:get_opt(matrix_domain, Opts); +matrix_domain(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, matrix_domain). + +-spec matrix_id_as_jid(gen_mod:opts() | global | binary()) -> boolean(). +matrix_id_as_jid(Opts) when is_map(Opts) -> + gen_mod:get_opt(matrix_id_as_jid, Opts); +matrix_id_as_jid(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, matrix_id_as_jid). + +-spec persist(gen_mod:opts() | global | binary()) -> boolean(). +persist(Opts) when is_map(Opts) -> + gen_mod:get_opt(persist, Opts); +persist(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, persist). + diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl new file mode 100644 index 000000000..a56c2496b --- /dev/null +++ b/src/mod_matrix_gw_room.erl @@ -0,0 +1,2652 @@ +%%%------------------------------------------------------------------- +%%% File : mod_matrix_gw_room.erl +%%% Author : Alexey Shchepin +%%% Purpose : Matrix rooms +%%% Created : 1 May 2022 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(mod_matrix_gw_room). + +-if(?OTP_RELEASE >= 24). +-behaviour(gen_statem). + +%% API +-export([start_link/2, supervisor/1, create_db/0, + get_room_pid/2, join/5, process_pdu/3, + get_missing_events/7, get_state_ids/4, + get_rooms_list/0, get_event/3, + make_join/4, send_join/5, + binary_to_room_version/1, + escape/1, unescape/1, + route/1]). + +%% gen_statem callbacks +-export([init/1, terminate/3, code_change/4, callback_mode/0]). +-export([handle_event/4]). + +-define(SERVER, ?MODULE). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("ejabberd_http.hrl"). +-include("mod_matrix_gw.hrl"). + +-record(matrix_room, + {room_id :: binary(), + pid :: pid()}). + +-record(matrix_direct, + {local_remote, + room_id :: binary()}). + +-record(event, + {id :: binary(), + room_version :: #room_version{}, + room_id :: binary(), + type :: binary(), + state_key :: binary() | undefined, + depth :: integer(), + auth_events :: [binary()], + sender :: binary(), + prev_events :: [binary()], + origin_server_ts :: integer(), + json :: jiffy:json_object(), + state_map}). + +-record(data, + {host :: binary(), + local_user :: jid(), + remote_user :: binary() | undefined, + remote_servers = #{}, + room_id :: binary(), + room_version :: #room_version{}, + events = #{}, + latest_events = sets:new([{version, 2}]), + nonlatest_events = sets:new([{version, 2}]), + outgoing_txns = #{}, + client_state}). + +-define(ROOM_CREATE, <<"m.room.create">>). +-define(ROOM_MEMBER, <<"m.room.member">>). +-define(ROOM_JOIN_RULES, <<"m.room.join_rules">>). +-define(ROOM_POWER_LEVELS, <<"m.room.power_levels">>). +-define(ROOM_3PI, <<"m.room.third_party_invite">>). +-define(ROOM_MESSAGE, <<"m.room.message">>). +-define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>). + +-define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @doc +%% Creates a gen_statem process which calls Module:init/1 to +%% initialize. To ensure a synchronized start-up procedure, this +%% function does not return until Module:init/1 has returned. +%% +%% @end +%%-------------------------------------------------------------------- +-spec start_link(binary(), binary()) -> + {ok, Pid :: pid()} | + ignore | + {error, Error :: term()}. +start_link(Host, RoomID) -> + gen_statem:start_link(?MODULE, [Host, RoomID], + ejabberd_config:fsm_limit_opts([])). + +-spec supervisor(binary()) -> atom(). +supervisor(Host) -> + gen_mod:get_module_proc(Host, mod_matrix_gw_room_sup). + +create_db() -> + ejabberd_mnesia:create( + ?MODULE, matrix_room, + [{ram_copies, [node()]}, + {type, set}, + {attributes, record_info(fields, matrix_room)}]), + ejabberd_mnesia:create( + ?MODULE, matrix_direct, + [{ram_copies, [node()]}, + {type, set}, + {attributes, record_info(fields, matrix_direct)}]), + ok. + +get_room_pid(Host, RoomID) -> + case get_existing_room_pid(Host, RoomID) of + {error, not_found} -> + case supervisor:start_child(supervisor(Host), + [Host, RoomID]) of + {ok, undefined} -> {error, ignored}; + Res -> Res + end; + {ok, Pid} -> + {ok, Pid} + end. + +get_existing_room_pid(_Host, RoomID) -> + case mnesia:dirty_read(matrix_room, RoomID) of + [] -> + {error, not_found}; + [#matrix_room{pid = Pid}] -> + {ok, Pid} + end. + +join(Host, MatrixServer, RoomID, Sender, UserID) -> + case get_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:cast(Pid, {join, MatrixServer, RoomID, Sender, UserID}); + {error, _} = Error -> + Error + end. + +route(#message{from = From, to = To, body = Body} = _Pkt) -> + case binary:split(To#jid.luser, <<"%">>) of + [EscU, EscS] -> + U = unescape(EscU), + S = unescape(EscS), + ToMatrixID = <<$@, U/binary, $:, S/binary>>, + Key = {{From#jid.luser, From#jid.lserver}, ToMatrixID}, + Text = xmpp:get_text(Body), + Host = ejabberd_config:get_myname(), + case mnesia:dirty_read(matrix_direct, Key) of + [#matrix_direct{room_id = RoomID}] -> + ?DEBUG("msg ~p~n", [{RoomID, From, ToMatrixID, Text}]), + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + FromMatrixID = + <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, + JSON = + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>}, + <<"sender">> => FromMatrixID, + <<"type">> => ?ROOM_MESSAGE}, + gen_statem:cast(Pid, {add_event, JSON}), + ok; + {error, _} -> + %%TODO + ok + end; + _ -> + RoomID = new_room_id(), + ?DEBUG("new room id ~p~n", [RoomID]), + case get_room_pid(Host, RoomID) of + {ok, Pid} -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + FromMatrixID = + <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, + gen_statem:cast(Pid, {create, MatrixServer, RoomID, + FromMatrixID, ToMatrixID}), + JSONs = + [#{<<"content">> => + #{<<"creator">> => FromMatrixID, + <<"room_version">> => <<"9">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_CREATE}, + #{<<"content">> => + #{<<"membership">> => <<"join">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => FromMatrixID, + <<"type">> => ?ROOM_MEMBER}, + #{<<"content">> => + #{<<"ban">> => 50, + <<"events">> => + #{<<"m.room.avatar">> => 50, + <<"m.room.canonical_alias">> => 50, + <<"m.room.encryption">> => 100, + <<"m.room.history_visibility">> => 100, + <<"m.room.name">> => 50, + <<"m.room.power_levels">> => 100, + <<"m.room.server_acl">> => 100, + <<"m.room.tombstone">> => 100}, + <<"events_default">> => 0, + <<"historical">> => 100, + <<"invite">> => 0, + <<"kick">> => 50, + <<"redact">> => 50, + <<"state_default">> => 50, + <<"users">> => + #{FromMatrixID => 100, + ToMatrixID => 100}, + <<"users_default">> => 0}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_POWER_LEVELS}, + #{<<"content">> => #{<<"join_rule">> => <<"invite">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_JOIN_RULES}, + #{<<"content">> => #{<<"history_visibility">> => <<"shared">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_HISTORY_VISIBILITY}, + #{<<"content">> => #{<<"guest_access">> => <<"can_join">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => <<"m.room.guest_access">>}, + #{<<"content">> => + #{<<"is_direct">> => true, + <<"membership">> => <<"invite">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => ToMatrixID, + <<"type">> => ?ROOM_MEMBER}, + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>}, + <<"sender">> => FromMatrixID, + <<"type">> => ?ROOM_MESSAGE} + ], + lists:foreach(fun(JSON) -> + gen_statem:cast(Pid, {add_event, JSON}) + end, JSONs), + ok; + {error, _} -> + %%TODO + ok + end + end; + _ -> + ok + end; +route(_) -> + ok. + +get_missing_events(Host, Origin, RoomID, EarliestEvents, LatestEvents, Limit, MinDepth) -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + Events = gen_statem:call( + Pid, {get_missing_events, Origin, EarliestEvents, LatestEvents, + Limit, MinDepth}), + [E#event.json || E <- Events]; + {error, _} -> + %%TODO + [] + end. + +get_state_ids(Host, Origin, RoomID, EventID) -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:call( + Pid, {get_state_ids, Origin, EventID}); + {error, _} -> + %%TODO + {error, room_not_found} + end. + +get_rooms_list() -> + mnesia:dirty_all_keys(matrix_room). + +get_event(Host, RoomID, EventID) -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:call(Pid, {get_event, EventID}); + {error, _} -> + %%TODO + {error, room_not_found} + end. + +make_join(Host, RoomID, UserID, Params) -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:call(Pid, {make_join, UserID, Params}); + {error, _} -> + {error, room_not_found} + end. + +send_join(Host, Origin, RoomID, EventID, JSON) -> + case process_pdu(Host, Origin, JSON) of + {ok, EventID} -> + {ok, EventJSON} = get_event(Host, RoomID, EventID), + {ok, AuthChain, StateMap} = get_state_ids(Host, Origin, RoomID, EventID), + AuthChainJSON = + lists:map(fun(EID) -> {ok, E} = get_event(Host, RoomID, EID), E end, AuthChain), + StateMapJSON = + lists:map(fun(EID) -> {ok, E} = get_event(Host, RoomID, EID), E end, StateMap), + MyOrigin = mod_matrix_gw_opt:matrix_domain(Host), + Res = #{<<"event">> => EventJSON, + <<"state">> => StateMapJSON, + <<"auth_chain">> => AuthChainJSON, + <<"origin">> => MyOrigin}, + {ok, Res}; + {ok, _} -> + {error, <<"Bad event id">>}; + {error, _} = Error -> + Error + end. + +%%%=================================================================== +%%% gen_statem callbacks +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Whenever a gen_statem is started using gen_statem:start/[3,4] or +%% gen_statem:start_link/[3,4], this function is called by the new +%% process to initialize. +%% @end +%%-------------------------------------------------------------------- +-spec init(Args :: term()) -> + {gen_statem:callback_mode(), + State :: term(), Data :: term()} | + {gen_statem:callback_mode(), + State :: term(), Data :: term(), + [gen_statem:action()] | gen_statem:action()} | + ignore | + {stop, Reason :: term()}. +init([Host, RoomID]) -> + mnesia:dirty_write( + #matrix_room{room_id = RoomID, + pid = self()}), + {ok, state_name, + #data{host = Host, + room_id = RoomID, + room_version = binary_to_room_version(<<"9">>)}}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% If the gen_statem runs with CallbackMode =:= handle_event_function +%% this function is called for every event a gen_statem receives. +%% @end +%%-------------------------------------------------------------------- +-spec handle_event( + gen_statem:event_type(), Msg :: term(), + State :: term(), Data :: term()) -> + gen_statem:handle_event_result(). +handle_event({call, From}, get_room_version, _State, Data) -> + {keep_state, Data, [{reply, From, Data#data.room_version}]}; +handle_event({call, From}, get_latest_events, _State, Data) -> + {keep_state, Data, [{reply, From, Data#data.latest_events}]}; +%% set_latest_events is for debugging only +handle_event({call, From}, {set_latest_events, LE}, _State, Data) -> + {keep_state, Data#data{latest_events = LE}, [{reply, From, ok}]}; +handle_event({call, From}, {find_event, EventID}, _State, Data) -> + Res = maps:find(EventID, Data#data.events), + {keep_state, Data, [{reply, From, Res}]}; +handle_event({call, From}, {partition_missed_events, EventIDs}, _State, Data) -> + Res = lists:partition( + fun(EventID) -> + maps:is_key(EventID, Data#data.events) + end, EventIDs), + {keep_state, Data, [{reply, From, Res}]}; +handle_event({call, From}, {partition_events_with_statemap, EventIDs}, _State, Data) -> + Res = lists:partition( + fun(EventID) -> + case maps:find(EventID, Data#data.events) of + {ok, #event{state_map = undefined}} -> false; + {ok, _} -> true; + error -> false + end + end, EventIDs), + {keep_state, Data, [{reply, From, Res}]}; +handle_event({call, From}, {auth_and_store_external_events, EventList}, _State, Data) -> + try + Data2 = do_auth_and_store_external_events(EventList, Data), + {keep_state, Data2, [{reply, From, ok}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed auth_and_store_external_events: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}, + {next_event, internal, update_client}]} + end; +handle_event({call, From}, {resolve_auth_store_event, Event}, _State, Data) -> + try + Data2 = do_resolve_auth_store_event(Event, Data), + {keep_state, Data2, [{reply, From, ok}, + {next_event, internal, update_client}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed resolve_auth_store_event: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}, + {next_event, internal, update_client}]} + end; +handle_event({call, From}, + {get_missing_events, Origin, EarliestEvents, LatestEvents, Limit, MinDepth}, + _State, Data) -> + try + PDUs = do_get_missing_events(Origin, EarliestEvents, LatestEvents, Limit, MinDepth, Data), + {keep_state_and_data, [{reply, From, PDUs}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed get_missing_events: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}]} + end; +handle_event({call, From}, + {get_state_ids, Origin, EventID}, + _State, Data) -> + try + Reply = do_get_state_ids(Origin, EventID, Data), + {keep_state_and_data, [{reply, From, Reply}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed get_state_ids: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}]} + end; +handle_event({call, From}, + {get_event, EventID}, + _State, Data) -> + try + Reply = + case maps:find(EventID, Data#data.events) of + {ok, Event} -> + {ok, Event#event.json}; + _ -> + {error, event_not_found} + end, + {keep_state_and_data, [{reply, From, Reply}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed get_event: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}]} + end; +handle_event({call, From}, + {make_join, UserID, Params}, + _State, Data) -> + try + Ver = (Data#data.room_version)#room_version.id, + Reply = + case lists:member({<<"ver">>, Ver}, Params) of + true -> + JSON = #{<<"content">> => + #{<<"membership">> => <<"join">>}, + <<"sender">> => UserID, + <<"state_key">> => UserID, + <<"type">> => ?ROOM_MEMBER}, + {JSON2, _} = fill_event(JSON, Data), + Event = json_to_event(JSON2, Data#data.room_version), + case check_event_auth(Event, Data) of + true -> + Res = #{<<"event">> => JSON2, + <<"room_version">> => Ver}, + {ok, Res}; + false -> + {error, not_invited} + end; + false -> + {error, {incompatible_version, Ver}} + end, + {keep_state_and_data, [{reply, From, Reply}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed make_join: ~p", [{Class, Reason, ST}]), + {keep_state, Data, [{reply, From, {error, Reason}}]} + end; +handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> + Host = Data#data.host, + %% TODO: check if there is another solution to "You are not invited to this room" and not receiving the first messages in the room + timer:sleep(1000), + case user_id_to_jid(UserID, Data) of + #jid{lserver = Host} = UserJID -> + mnesia:dirty_write( + #matrix_direct{local_remote = {{UserJID#jid.luser, UserJID#jid.lserver}, Sender}, + room_id = RoomID}), + MakeJoinRes = + mod_matrix_gw:send_request( + Host, get, MatrixServer, + [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, + RoomID, UserID], + [{<<"ver">>, <<"9">>}, + {<<"ver">>, <<"10">>}, + {<<"ver">>, <<"11">>}], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("make_join ~p~n", [MakeJoinRes]), + case MakeJoinRes of + {ok, {{_, 200, _}, _Headers, Body}} -> + try jiffy:decode(Body, [return_maps]) of + #{<<"event">> := Event, + <<"room_version">> := SRoomVersion} -> + case binary_to_room_version(SRoomVersion) of + false -> + ?DEBUG("unsupported room version on make_join: ~p", [MakeJoinRes]), + {keep_state, Data, []}; + #room_version{} = RoomVersion -> + Origin = mod_matrix_gw_opt:matrix_domain(Host), + Event2 = + Event#{<<"origin">> => Origin, + <<"origin_server_ts">> => + erlang:system_time(millisecond)}, + CHash = mod_matrix_gw:content_hash(Event2), + Event3 = + Event2#{<<"hashes">> => + #{<<"sha256">> => + mod_matrix_gw:base64_encode(CHash)}}, + Event4 = mod_matrix_gw:sign_event(Host, Event3, RoomVersion), + EventID = mod_matrix_gw:get_event_id(Event4, RoomVersion), + SendJoinRes = + mod_matrix_gw:send_request( + Data#data.host, put, MatrixServer, + [<<"_matrix">>, <<"federation">>, + <<"v2">>, <<"send_join">>, + RoomID, EventID], + [], + Event4, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("send_join ~p~n", [SendJoinRes]), + process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, + Data#data{local_user = UserJID, + remote_user = Sender, + room_version = RoomVersion}) + end; + _JSON -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + {next_state, State, Data, []} + catch + _:_ -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + {next_state, State, Data, []} + end; + _ -> + ?DEBUG("failed make_join: ~p", [MakeJoinRes]), + {next_state, State, Data, []} + end; + UserJID -> + ?INFO_MSG("bad join user id: ~p", [{UserID, UserJID}]), + {stop, normal} + end; +handle_event(cast, {create, _MatrixServer, RoomID, LocalUserID, RemoteUserID}, _State, Data) -> + Host = Data#data.host, + case user_id_to_jid(LocalUserID, Data) of + #jid{lserver = Host} = UserJID -> + mnesia:dirty_write( + #matrix_direct{local_remote = {{UserJID#jid.luser, UserJID#jid.lserver}, RemoteUserID}, + room_id = RoomID}), + {keep_state, Data#data{local_user = UserJID, + remote_user = RemoteUserID}, []}; + UserJID -> + ?INFO_MSG("bad create user id: ~p", [{LocalUserID, UserJID}]), + {stop, normal} + end; +handle_event(cast, {add_event, JSON}, _State, Data) -> + try + Data2 = add_event(JSON, Data), + {keep_state, Data2, [{next_event, internal, update_client}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed add_event: ~p", [{Class, Reason, ST}]), + {keep_state, Data, []} + end; +handle_event(cast, Msg, State, Data) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {next_state, State, Data, []}; +handle_event(internal, update_client, _State, Data) -> + try + case update_client(Data) of + {ok, Data2} -> + {keep_state, Data2, []}; + {leave, LeaveReason, Data2} -> + ?INFO_MSG("leaving ~p: ~p", [Data#data.room_id, LeaveReason]), + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, + LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, + JSON = #{<<"content">> => + #{<<"membership">> => <<"leave">>}, + <<"sender">> => LocalUserID, + <<"state_key">> => LocalUserID, + <<"type">> => ?ROOM_MEMBER}, + {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; + stop -> + {stop, normal} + end + catch + Class:Reason:ST -> + ?INFO_MSG("failed update_client: ~p", [{Class, Reason, ST}]), + {keep_state, Data, []} + end; +handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) -> + case Data#data.outgoing_txns of + #{Server := {{RequestID, TxnID, _Events}, Queue}} -> + case Res of + {{_, 200, _}, _Headers, _Body} -> + Data2 = + case Queue of + [] -> + Data#data{outgoing_txns = + maps:remove(Server, Data#data.outgoing_txns)}; + _ -> + send_new_txn(lists:reverse(Queue), Server, Data) + end, + {keep_state, Data2, []}; + _ -> + %% TODO + erlang:send_after(30000, self(), {resend_txn, Server}), + {keep_state, Data, []} + end; + _ -> + {keep_state, Data, []} + end; +handle_event(info, {resend_txn, Server}, _State, Data) -> + case Data#data.outgoing_txns of + #{Server := {{_RequestID, TxnID, Events}, Queue}} -> + Data2 = send_txn(TxnID, Events, Server, Queue, Data), + {keep_state, Data2, []}; + _ -> + {keep_state, Data, []} + end; +handle_event(info, Info, State, Data) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {next_state, State, Data, []}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% This function is called by a gen_statem when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_statem terminates with +%% Reason. The return value is ignored. +%% @end +%%-------------------------------------------------------------------- +-spec terminate(Reason :: term(), State :: term(), Data :: term()) -> + any(). +terminate(Reason, _State, Data) -> + mnesia:dirty_delete_object( + #matrix_room{room_id = Data#data.room_id, + pid = self()}), + %% TODO: wait for messages + case Data#data.local_user of + #jid{} = LocalUserJID -> + mnesia:dirty_delete_object( + #matrix_direct{local_remote = {{LocalUserJID#jid.luser, LocalUserJID#jid.lserver}, + Data#data.remote_user}, + room_id = Data#data.room_id}); + _ -> + ok + end, + ?INFO_MSG("terminated ~p: ~p", [Data#data.room_id, Reason]), + ok. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Convert process state when code is changed +%% @end +%%-------------------------------------------------------------------- +-spec code_change( + OldVsn :: term() | {down,term()}, + State :: term(), Data :: term(), Extra :: term()) -> + {ok, NewState :: term(), NewData :: term()}. +code_change(_OldVsn, State, Data, _Extra) -> + {ok, State, Data}. + +callback_mode() -> + handle_event_function. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +get_event_exn(EventID, Data) -> + maps:get(EventID, Data#data.events). + +process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> + case SendJoinRes of + {ok, {{_, 200, _}, _Headers, Body}} -> + try + case jiffy:decode(Body, [return_maps]) of + #{<<"auth_chain">> := JSONAuthChain, + <<"event">> := JSONEvent, + <<"state">> := JSONState} = JSON when is_list(JSONAuthChain), + is_list(JSONState) -> + AuthChain = + lists:map(fun(J) -> json_to_event(J, RoomVersion) end, + JSONAuthChain), + State = + lists:map(fun(J) -> json_to_event(J, RoomVersion) end, + JSONState), + Event = json_to_event(JSONEvent, RoomVersion), + ?DEBUG("send_join res: ~p~n", [JSON]), + lists:foreach( + fun(E) -> + case check_event_sig_and_hash(Data#data.host, E) of + {ok, _} -> ok; + {error, Error} -> error(Error) + end + end, [Event] ++ AuthChain ++ State), + CreateEvents = + lists:filter( + fun(#event{type = ?ROOM_CREATE, + state_key = <<"">>}) -> true; + (_) -> false + end, State), + RoomVersionID = RoomVersion#room_version.id, + case CreateEvents of + [#event{ + id = CreateEventID, + json = + #{<<"content">> := + #{<<"room_version">> := + RoomVersionID}}} = + CreateEvent] -> + ?DEBUG("create event: ~p~n", [CreateEvent]), + AuthCreateEvents = + lists:filtermap( + fun(#event{id = ID, + type = ?ROOM_CREATE, + state_key = <<"">>}) -> + {true, ID}; + (_) -> false + end, AuthChain), + case AuthCreateEvents of + [CreateEventID] -> + Data2 = process_send_join_res2( + MatrixServer, AuthChain, Event, State, + Data), + {keep_state, Data2, []}; + _ -> + ?DEBUG("bad auth create events: ~p, expected: ~p", [AuthCreateEvents, [CreateEventID]]), + {keep_state, Data, []} + end; + _ -> + ?DEBUG("bad create event: ~p", [CreateEvents]), + {keep_state, Data, []} + end + end + catch + error:{invalid_signature, EventID} -> + ?INFO_MSG("failed signature check on event ~p", [EventID]), + {keep_state, Data, []}; + Class:Reason:ST -> + ?INFO_MSG("failed send_join: ~p", [{Class, Reason, ST}]), + {keep_state, Data, []} + end; + _ -> + ?DEBUG("failed send_join: ~p", [SendJoinRes]), + {keep_state, Data, []} + end. + +process_send_join_res2(MatrixServer, AuthChain, Event, State, Data) -> + Data2 = do_auth_and_store_external_events(AuthChain ++ State, Data), + StateMap = + lists:foldl( + fun(E, Acc) -> + Acc#{{E#event.type, E#event.state_key} => E#event.id} + end, #{}, State), + StateMap2 = + case Event#event.state_key of + undefined -> + StateMap; + _ -> + StateMap#{{Event#event.type, Event#event.state_key} => Event#event.id} + end, + Event2 = Event#event{state_map = StateMap2}, + Data3 = + case check_event_auth(Event2, Data2) of + true -> + store_event(Event2, Data2); + false -> + error({event_auth_error, Event2#event.id}) + end, + MissingEventsQuery = + #{<<"earliest_events">> => [], + <<"latest_events">> => [Event#event.id], + <<"limit">> => 10}, + Host = Data3#data.host, + Pid = self(), + RoomID = Data3#data.room_id, + RoomVersion = Data3#data.room_version, + mod_matrix_gw:send_request( + Host, post, MatrixServer, + [<<"_matrix">>, <<"federation">>, <<"v1">>, + <<"get_missing_events">>, RoomID], + [], + MissingEventsQuery, + [{timeout, 60000}], + [{sync, false}, + {body_format, binary}, + {receiver, + fun({_, Res}) -> + process_missing_events_res(Host, MatrixServer, Pid, RoomID, RoomVersion, + {ok, Res}) + end}]), + Data3. + +do_auth_and_store_external_events(EventList, Data) -> + Events = maps:from_list(lists:map(fun(E) -> {E#event.id, E} end, + EventList)), + SortedEvents = simple_toposort(Events), + ?DEBUG("topo ~p~n", [SortedEvents]), + %% TODO: add more checks + Data2 = + lists:foldl( + fun(E, Acc) -> + Ev = maps:get(E, Events), + case check_event_auth(Ev, Acc) of + true -> + store_event(Ev, Acc); + false -> + error({event_auth_error, E}) + end + end, Data, SortedEvents), + Data2. + +auth_and_store_external_events(Pid, EventList) -> + gen_statem:call(Pid, {auth_and_store_external_events, EventList}). + +check_event_auth(Event, Data) -> + StateMap = + maps:from_list( + lists:map( + fun(EID) -> + E = get_event_exn(EID, Data), + {{E#event.type, E#event.state_key}, E} + end, Event#event.auth_events)), + check_event_auth(Event, StateMap, Data). + +check_event_auth(Event, StateMap, Data) -> + RoomVersion = Data#data.room_version, + case Event#event.type of + ?ROOM_CREATE -> + case maps:size(StateMap) of + 0 -> + RDomain = mod_matrix_gw:get_id_domain_exn(Data#data.room_id), + SDomain = mod_matrix_gw:get_id_domain_exn(Event#event.sender), + if + RDomain == SDomain -> + %% TODO: check version + case RoomVersion#room_version.implicit_room_creator of + false -> + case Event#event.json of + #{<<"content">> := + #{<<"creator">> := _}} -> + true; + _ -> + false + end; + true -> + true + end; + true -> + false + end; + _ -> + false + end; + _ -> + case StateMap of + #{{?ROOM_CREATE, <<"">>} := _} -> + case Event#event.type of + ?ROOM_MEMBER -> + case Event#event.json of + #{<<"content">> := + #{<<"membership">> := Membership}} -> + %% TODO: join_authorised_via_users_server + case Membership of + <<"join">> -> + check_event_auth_join( + Event, StateMap, Data); + <<"invite">> -> + check_event_auth_invite( + Event, StateMap, Data); + <<"leave">> -> + check_event_auth_leave( + Event, StateMap, Data); + <<"ban">> -> + check_event_auth_ban( + Event, StateMap, Data); + <<"knock">> -> + check_event_auth_knock( + Event, StateMap, Data); + _ -> + false + end; + _ -> + false + end; + _ -> + Sender = Event#event.sender, + case maps:find({?ROOM_MEMBER, Sender}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + case Event#event.type of + ?ROOM_3PI -> + %% TODO + {todo, Event}; + _ -> + case check_event_power_level( + Event, StateMap, Data) of + true -> + case Event#event.type of + ?ROOM_POWER_LEVELS -> + check_event_auth_power_levels( + Event, StateMap, Data); + _ -> + true + end; + false -> + false + end + end; + _ -> + false + end + end; + _ -> + false + end + end. + +check_event_auth_join(Event, StateMap, Data) -> + RoomVersion = Data#data.room_version, + StateKey = Event#event.state_key, + case {length(Event#event.auth_events), + RoomVersion#room_version.implicit_room_creator, + maps:get({?ROOM_CREATE, <<"">>}, StateMap, undefined)} of + {1, false, #event{json = #{<<"content">> := #{<<"creator">> := StateKey}}}} -> + ?DEBUG("creator join ~p~n", [Event]), + true; + {1, true, #event{sender = StateKey}} -> + ?DEBUG("creator join ~p~n", [Event]), + true; + _ -> + case Event#event.sender of + StateKey -> + JoinRule = + case maps:find({?ROOM_JOIN_RULES, <<"">>}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"join_rule">> := JR}}}} -> + JR; + _ -> + <<"invite">> + end, + case maps:find({?ROOM_MEMBER, StateKey}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"ban">>}}}} -> + false; + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + true; + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + SenderMembership}}}} -> + case {JoinRule, SenderMembership} of + {<<"public">>, _} -> true; + {<<"invite">>, <<"invite">>} -> true; + {<<"knock">>, <<"invite">>} -> true; + {<<"restricted">>, <<"invite">>} -> + %% TODO + true; + {<<"knock_restricted">>, <<"invite">>} + when (Data#data.room_version)#room_version.knock_restricted_join_rule -> + %% TODO + true; + _ -> false + end; + error -> + case JoinRule of + <<"public">> -> true; + _ -> false + end + end; + _ -> + false + end + end. + +check_event_auth_invite(Event, StateMap, Data) -> + StateKey = Event#event.state_key, + case Event#event.json of + #{<<"content">> := #{<<"third_party_invite">> := _}} -> + %% TODO + {todo, Event}; + _ -> + case maps:find({?ROOM_MEMBER, Event#event.sender}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + case maps:find({?ROOM_MEMBER, StateKey}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"ban">>}}}} -> + false; + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + false; + _ -> + UserLevel = get_user_power_level(Event#event.sender, StateMap, Data), + InviteLevel = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := #{<<"invite">> := S}}}} -> + get_int(S); + _ -> 0 + end, + UserLevel >= InviteLevel + end; + _ -> + false + end + end. + +check_event_auth_leave(Event, StateMap, Data) -> + StateKey = Event#event.state_key, + case maps:find({?ROOM_MEMBER, Event#event.sender}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := SenderMembership}}}} -> + case Event#event.sender of + StateKey -> + case SenderMembership of + <<"invite">> -> true; + <<"join">> -> true; + <<"knock">> -> true; + _ -> false + end; + _ -> + case SenderMembership of + <<"join">> -> + SenderLevel = get_user_power_level(Event#event.sender, StateMap, Data), + CheckBan = + case maps:find({?ROOM_MEMBER, StateKey}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"ban">>}}}} -> + BanLevel = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := #{<<"ban">> := S}}}} -> + get_int(S); + _ -> 50 + end, + SenderLevel >= BanLevel; + _ -> + true + end, + if + CheckBan -> + KickLevel = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := #{<<"kick">> := S1}}}} -> + get_int(S1); + _ -> 50 + end, + TargetLevel = get_user_power_level(StateKey, StateMap, Data), + SenderLevel >= KickLevel andalso SenderLevel > TargetLevel; + true -> + false + end; + _ -> + false + end + end; + _ -> + false + end. + +check_event_auth_ban(Event, StateMap, Data) -> + StateKey = Event#event.state_key, + case maps:find({?ROOM_MEMBER, Event#event.sender}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := SenderMembership}}}} -> + case SenderMembership of + <<"join">> -> + SenderLevel = get_user_power_level(Event#event.sender, StateMap, Data), + BanLevel = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := #{<<"ban">> := S}}}} -> + get_int(S); + _ -> 50 + end, + TargetLevel = get_user_power_level(StateKey, StateMap, Data), + SenderLevel >= BanLevel andalso SenderLevel > TargetLevel; + _ -> + false + end; + _ -> + false + end. + +check_event_auth_knock(Event, StateMap, Data) -> + StateKey = Event#event.state_key, + case Event#event.sender of + StateKey -> + JoinRule = + case maps:find({?ROOM_JOIN_RULES, <<"">>}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"join_rule">> := JR}}}} -> + JR; + _ -> + <<"invite">> + end, + IsKnock = + case JoinRule of + <<"knock">> -> + true; + <<"knock_restricted">> when (Data#data.room_version)#room_version.knock_restricted_join_rule -> + true; + _ -> + false + end, + case IsKnock of + true -> + case maps:find({?ROOM_MEMBER, StateKey}, StateMap) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"ban">>}}}} -> + false; + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + false; + _ -> + true + end; + false -> + false + end; + _ -> + false + end. + +check_event_power_level(Event, StateMap, Data) -> + PLContent = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := C}}} -> C; + _ -> #{} + end, + RequiredLevel = get_event_power_level(Event#event.type, PLContent), + UserLevel = get_user_power_level(Event#event.sender, StateMap, Data), + if + UserLevel >= RequiredLevel -> + Sender = Event#event.sender, + case Event#event.state_key of + Sender -> true; + <<$@, _/binary>> -> false; + _ -> true + end; + true -> + false + end. + +get_event_power_level(Type, PL) -> + case PL of + #{Type := Level} -> get_int(Level); + #{<<"events_default">> := Level} -> get_int(Level); + _ -> 0 + end. + +get_user_power_level(User, StateMap, Data) -> + RoomVersion = Data#data.room_version, + PL = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := C}}} -> C; + _ -> #{} + end, + case PL of + #{<<"users">> := #{User := Level}} -> get_int(Level); + #{<<"users_default">> := Level} -> get_int(Level); + _ -> + case {RoomVersion#room_version.implicit_room_creator, StateMap} of + {false, + #{{?ROOM_CREATE, <<"">>} := + #event{json = #{<<"content">> := #{<<"creator">> := User}}}}} -> + 100; + {true, #{{?ROOM_CREATE, <<"">>} := #event{sender = User}}} -> + 100; + _ -> + 0 + end + end. + +check_event_auth_power_levels(Event, StateMap, Data) -> + try + case Event#event.json of + #{<<"content">> := NewPL = #{<<"users">> := Users}} when is_map(Users) -> + case (Data#data.room_version)#room_version.enforce_int_power_levels of + true -> + lists:foreach( + fun(Field) -> + case NewPL of + #{Field := V} when is_integer(V) -> ok; + #{Field := _V} -> error(not_allowed); + _ -> ok + end + end, + [<<"users_default">>, <<"events_default">>, <<"state_default">>, + <<"ban">>, <<"redact">>, <<"kick">>, <<"invite">>]), + lists:foreach( + fun(Key) -> + NewMap = maps:get(Key, NewPL, #{}), + maps:fold( + fun(_Field, V, _) -> + if + is_integer(V) -> ok; + true -> error(not_allowed) + end + end, [], NewMap) + end, + [<<"events">>, <<"users">>, <<"notifications">>]); + false -> + ok + end, + maps:fold( + fun(K, _V, _) -> + case check_user_id(K) of + true -> ok; + false -> error(not_allowed) + end + end, ok, Users), + StateKey = Event#event.state_key, + case StateMap of + #{{?ROOM_POWER_LEVELS, StateKey} := + #event{json = #{<<"content">> := OldPL}}} -> + UserLevel = get_user_power_level(Event#event.sender, StateMap, Data), + lists:foreach( + fun(Field) -> + case check_event_auth_power_levels_aux( + Field, OldPL, NewPL, UserLevel, none) of + true -> ok; + false -> error(not_allowed) + end + end, + [<<"users_default">>, <<"events_default">>, <<"state_default">>, + <<"ban">>, <<"redact">>, <<"kick">>, <<"invite">>]), + lists:foreach( + fun(Key) -> + OldMap = maps:get(Key, OldPL, #{}), + NewMap = maps:get(Key, NewPL, #{}), + UserID = + case Key of + <<"users">> -> + {some, Event#event.sender}; + _ -> none + end, + maps:fold( + fun(Field, _, _) -> + case check_event_auth_power_levels_aux( + Field, OldMap, NewMap, UserLevel, UserID) of + true -> ok; + false -> error(not_allowed) + end + end, [], maps:merge(OldMap, NewMap)) + end, + [<<"events">>, <<"users">>, <<"notifications">>]), + true; + _ -> + true + end; + _ -> + false + end + catch + error:not_allowed -> + false + end. + +check_event_auth_power_levels_aux(Field, OldDict, NewDict, UserLevel, UserID) -> + UserLevel2 = + case UserID of + none -> UserLevel; + {some, Field} -> UserLevel; + {some, _} -> UserLevel - 1 + end, + case {maps:find(Field, OldDict), maps:find(Field, NewDict)} of + {error, error} -> true; + {error, {ok, S}} -> + get_int(S) =< UserLevel; + {{ok, S}, error} -> + get_int(S) =< UserLevel2; + {{ok, S1}, {ok, S2}} -> + OldLevel = get_int(S1), + NewLevel = get_int(S2), + if + OldLevel == NewLevel -> true; + true -> + OldLevel =< UserLevel2 andalso NewLevel =< UserLevel + end + end. + +check_user_id(S) -> + case S of + <<$@, Parts/binary>> -> + case binary:split(Parts, <<":">>) of + [_, _] -> true; + _ -> false + end; + _ -> + false + end. + +parse_user_id(Str) -> + case Str of + <<$@, Parts/binary>> -> + case binary:split(Parts, <<":">>) of + [U, S] -> {ok, U, S}; + _ -> error + end; + _ -> + error + end. + +get_int(I) when is_integer(I) -> I; +get_int(S) when is_binary(S) -> binary_to_integer(S). + +fill_event(JSON, Data) -> + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + PrevEvents = sets:to_list(Data#data.latest_events), + Depth = + lists:max( + [0 | lists:map( + fun(EID) -> + (maps:get(EID, Data#data.events))#event.depth + end, PrevEvents)]), + Depth2 = min(Depth + 1, ?MAX_DEPTH), + StateMaps = + lists:map( + fun(EID) -> + case Data#data.events of + #{EID := #event{state_map = undefined}} -> + error({missed_state_map, EID}); + #{EID := #event{state_map = SM}} -> + SM; + _ -> + error({missed_prev_event, EID}) + end + end, PrevEvents), + StateMap = resolve_state_maps(StateMaps, Data), + AuthEvents = + lists:usort( + lists:flatmap( + fun(Key) -> + case StateMap of + #{Key := E} -> [E]; + _ -> [] + end + end, + compute_event_auth_keys(JSON))), + {JSON#{<<"auth_events">> => AuthEvents, + <<"depth">> => Depth2, + <<"origin">> => MatrixServer, + <<"origin_server_ts">> => erlang:system_time(millisecond), + <<"prev_events">> => PrevEvents, + <<"room_id">> => Data#data.room_id}, + StateMap}. + +add_event(JSON, Data) -> + Host = Data#data.host, + {Msg, StateMap} = fill_event(JSON, Data), + CHash = mod_matrix_gw:content_hash(Msg), + Msg2 = + Msg#{<<"hashes">> => + #{<<"sha256">> => + mod_matrix_gw:base64_encode(CHash)}}, + Msg3 = mod_matrix_gw:sign_event(Host, Msg2, Data#data.room_version), + Event = json_to_event(Msg3, Data#data.room_version), + StateMap2 = + case Event#event.state_key of + undefined -> + StateMap; + _ -> + StateMap#{{Event#event.type, Event#event.state_key} => Event#event.id} + end, + Event2 = Event#event{state_map = StateMap2}, + ?DEBUG("add_event ~p~n", [Event2]), + case check_event_auth(Event2, Data) of + true -> + %%TODO: soft fail + store_event(Event2, Data); + false -> + error({event_auth_error, Event2#event.id}) + end. + + +store_event(Event, Data) -> + %% TODO + Events = Data#data.events, + case maps:find(Event#event.id, Events) of + {ok, #event{state_map = undefined}} when Event#event.state_map /= undefined -> + Data#data{events = Events#{Event#event.id => Event}}; + {ok, _} -> + Data; + error -> + ?DEBUG("store ~p~n", [Event#event.id]), + Data2 = notify_event(Event, Data), + LatestEvents = + lists:foldl(fun(E, Acc) -> sets:del_element(E, Acc) end, Data2#data.latest_events, + Event#event.prev_events), + NonLatestEvents = + lists:foldl(fun(E, Acc) -> sets:add_element(E, Acc) end, Data2#data.nonlatest_events, + Event#event.prev_events), + LatestEvents2 = + case maps:is_key(Event#event.id, NonLatestEvents) of + true -> + LatestEvents; + false -> + LatestEvents#{Event#event.id => []} + end, + ?DEBUG("latest ~p~n", [{LatestEvents2, NonLatestEvents}]), + Data2#data{events = Events#{Event#event.id => Event}, + latest_events = LatestEvents2, + nonlatest_events = NonLatestEvents} + end. + +simple_toposort(Events) -> + {Res, _Used} = + lists:foldl( + fun(E, {_Res, Used} = Acc) -> + EventID = E#event.id, + case maps:is_key(EventID, Used) of + false -> + simple_toposort_dfs(EventID, Acc, Events); + true -> + Acc + end + end, {[], #{}}, maps:values(Events)), + lists:reverse(Res). + +simple_toposort_dfs(EventID, {Res, Used}, Events) -> + case maps:find(EventID, Events) of + error -> + error({unknown_event, EventID}); + {ok, Event} -> + Used2 = Used#{EventID => gray}, + {Res8, Used8} = + lists:foldl( + fun(ID, {_Res3, Used3} = Acc) -> + case maps:get(ID, Used3, white) of + white -> + simple_toposort_dfs(ID, Acc, Events); + gray -> + error(loop_in_auth_chain); + black -> + Acc + end + end, {Res, Used2}, Event#event.auth_events), + Used9 = Used8#{EventID => black}, + Res9 = [EventID | Res8], + {Res9, Used9} + end. + +check_event_sig_and_hash(Host, Event) -> + case check_event_signature(Host, Event) of + true -> + case check_event_content_hash(Event) of + true -> + {ok, Event}; + false -> + ?DEBUG("mismatched content hash: ~p", [Event#event.id]), + PrunedJSON = mod_matrix_gw:prune_event( + Event#event.json, Event#event.room_version), + {ok, Event#event{json = PrunedJSON}} + end; + false -> + {error, {invalid_signature, Event#event.id}} + end. + +get_room_version(Pid) -> + gen_statem:call(Pid, get_room_version). + +partition_missed_events(Pid, EventIDs) -> + gen_statem:call(Pid, {partition_missed_events, EventIDs}). + +partition_events_with_statemap(Pid, EventIDs) -> + gen_statem:call(Pid, {partition_events_with_statemap, EventIDs}). + +get_latest_events(Pid) -> + gen_statem:call(Pid, get_latest_events). + +check_event_signature(Host, Event) -> + PrunedEvent = mod_matrix_gw:prune_event(Event#event.json, + Event#event.room_version), + mod_matrix_gw_s2s:check_signature(Host, PrunedEvent). + +find_event(Pid, EventID) -> + gen_statem:call(Pid, {find_event, EventID}). + +resolve_auth_store_event(Pid, Event) -> + gen_statem:call(Pid, {resolve_auth_store_event, Event}). + +process_pdu(Host, Origin, PDU) -> + %% TODO: error handling + #{<<"room_id">> := RoomID} = PDU, + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + RoomVersion = get_room_version(Pid), + Event = json_to_event(PDU, RoomVersion), + case check_event_signature(Host, Event) of + true -> + {SeenEvents, MissedEvents} = + partition_missed_events(Pid, Event#event.prev_events), + ?DEBUG("seen/missed: ~p~n", [{SeenEvents, MissedEvents}]), + case MissedEvents of + [] -> + ok; + _ -> + LatestEvents = get_latest_events(Pid), + EarliestEvents = + lists:foldl( + fun(E, Acc) -> + Acc#{E => []} + end, LatestEvents, SeenEvents), + ?DEBUG("earliest ~p~n", [EarliestEvents]), + MissingEventsQuery = + #{<<"earliest_events">> => maps:keys(EarliestEvents), + <<"latest_events">> => [Event#event.id], + <<"limit">> => 10}, + MissingEventsRes = + mod_matrix_gw:send_request( + Host, post, Origin, + [<<"_matrix">>, <<"federation">>, <<"v1">>, + <<"get_missing_events">>, RoomID], + [], + MissingEventsQuery, + [{timeout, 60000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("missing res ~p~n", [MissingEventsRes]), + process_missing_events_res(Host, Origin, Pid, RoomID, RoomVersion, + MissingEventsRes), + ok + end, + resolve_auth_store_event(Pid, Event), + {ok, Event#event.id}; + false -> + {error, <<"Signature check failed">>} + end; + {error, not_found} -> + {error, <<"Room doesn't exist">>} + end. + +process_missing_events_res(Host, Origin, Pid, RoomID, RoomVersion, + {ok, {{_, 200, _}, _Headers, Body}}) -> + try + case jiffy:decode(Body, [return_maps]) of + #{<<"events">> := JSONEvents} when is_list(JSONEvents) -> + process_missing_events(Host, Origin, Pid, RoomID, RoomVersion, JSONEvents) + end + catch + Class:Reason:ST -> + ?DEBUG("failed process_missing_events_res: ~p", [{Class, Reason, ST}]), + ok + end; +process_missing_events_res(_Host, _Origin, _Pid, _RoomID, _RoomVersion, _) -> + ok. + +process_missing_events(Host, Origin, Pid, RoomID, RoomVersion, JSONEvents) -> + Events = lists:map(fun(J) -> json_to_event(J, RoomVersion) end, JSONEvents), + SortedEvents = lists:keysort(#event.depth, Events), + ?DEBUG("sevents ~p~n", [SortedEvents]), + lists:foreach( + fun(Event) -> + case check_event_sig_and_hash(Host, Event) of + {ok, _} -> + ShouldProcess = + case find_event(Pid, Event#event.id) of + {ok, #event{state_map = undefined}} -> + true; + {ok, _} -> + false; + error -> + true + end, + case ShouldProcess of + true -> + fetch_prev_statemaps(Host, Origin, Pid, + RoomID, RoomVersion, Event), + resolve_auth_store_event(Pid, Event), + ok; + false -> + ok + end; + {error, Reason} -> + error(Reason) + end + end, SortedEvents), + ok. + +fetch_prev_statemaps(Host, Origin, Pid, RoomID, RoomVersion, Event) -> + ?DEBUG("fetch_prev_statemaps ~p~n", [Event#event.id]), + {SeenEvents, MissedEvents} = + partition_events_with_statemap(Pid, Event#event.prev_events), + ?DEBUG("s/m ~p~n", [{SeenEvents, MissedEvents}]), + lists:foreach( + fun(MissedEventID) -> + case request_event(Host, Origin, Pid, RoomID, RoomVersion, MissedEventID) of + {ok, MissedEvent} -> + case request_room_state(Host, Origin, Pid, RoomID, RoomVersion, MissedEvent) of + {ok, AuthChain, State} -> + auth_and_store_external_events(Pid, AuthChain ++ State), + StateMap = + lists:foldl( + fun(E, Acc) -> + Acc#{{E#event.type, E#event.state_key} => E#event.id} + end, #{}, State), + auth_and_store_external_events( + Pid, [MissedEvent#event{state_map = StateMap}]), + ok; + {error, Reason} -> + ?INFO_MSG("failed request_room_state: ~p", [{RoomID, Event#event.id, Reason}]), + ok + end; + {error, Error} -> + error(Error) + end + end, MissedEvents). + +request_room_state(Host, Origin, _Pid, RoomID, RoomVersion, Event) -> + Res = + mod_matrix_gw:send_request( + Host, get, Origin, + [<<"_matrix">>, <<"federation">>, + <<"v1">>, <<"state">>, + RoomID], + [{<<"event_id">>, Event#event.id}], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + case Res of + {ok, {{_, 200, _}, _Headers, Body}} -> + try + case jiffy:decode(Body, [return_maps]) of + #{<<"auth_chain">> := JSONAuthChain, + <<"pdus">> := JSONState} = _JSON when is_list(JSONAuthChain), + is_list(JSONState) -> + AuthChain = + lists:map(fun(J) -> json_to_event(J, RoomVersion) end, + JSONAuthChain), + State = + lists:map(fun(J) -> json_to_event(J, RoomVersion) end, + JSONState), + lists:foreach( + fun(E) -> + case check_event_sig_and_hash(Host, E) of + {ok, _} -> + case E#event.room_id of + RoomID -> + case E#event.state_key of + undefined -> + error({missed_state_key, E#event.id}); + _ -> + ok + end; + RoomID2 -> + error({mismatched_room_id, E#event.id, RoomID, RoomID2}) + end; + {error, Error} -> error(Error) + end + end, AuthChain ++ State), + ?DEBUG("req state ~p~n", + [{[E#event.id || E <- AuthChain], + [E#event.id || E <- State]}]), + {ok, AuthChain, State} + end + catch + Class:Reason:ST -> + ?INFO_MSG("failed request_room_state: ~p", [{Class, Reason, ST}]), + {error, Reason} + end; + {ok, {{_, _Status, Reason}, _Headers, _Body}} -> + {error, Reason}; + {error, Reason} -> + {error, Reason} + end. + +request_event(Host, Origin, _Pid, RoomID, RoomVersion, EventID) -> + Res = + mod_matrix_gw:send_request( + Host, get, Origin, + [<<"_matrix">>, <<"federation">>, + <<"v1">>, <<"event">>, + EventID], + [], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + case Res of + {ok, {{_, 200, _}, _Headers, Body}} -> + try + case jiffy:decode(Body, [return_maps]) of + #{<<"pdus">> := [PDU]} -> + Event = json_to_event(PDU, RoomVersion), + case check_event_sig_and_hash(Host, Event) of + {ok, _} -> + case Event#event.room_id of + RoomID -> + ok; + RoomID2 -> + error({mismatched_room_id, Event#event.id, RoomID, RoomID2}) + end; + {error, Error} -> error(Error) + end, + {ok, Event} + end + catch + Class:Reason:ST -> + ?INFO_MSG("failed request_event: ~p", [{Class, Reason, ST}]), + {error, Reason} + end; + {ok, {{_, _Status, Reason}, _Headers, _Body}} -> + {error, Reason}; + {error, Reason} -> + {error, Reason} + end. + +get_event_prev_state_map(Event, Data) -> + StateMaps = + lists:map( + fun(EID) -> + case Data#data.events of + #{EID := #event{state_map = undefined}} -> + error({missed_state_map, EID}); + #{EID := #event{state_map = SM}} -> + SM; + _ -> + error({missed_prev_event, EID}) + end + end, Event#event.prev_events), + resolve_state_maps(StateMaps, Data). + +do_resolve_auth_store_event(Event, Data) -> + StateMap = get_event_prev_state_map(Event, Data), + StateMap2 = + case Event#event.state_key of + undefined -> + StateMap; + _ -> + StateMap#{{Event#event.type, Event#event.state_key} => Event#event.id} + end, + Event2 = Event#event{state_map = StateMap2}, + case check_event_auth(Event2, Data) of + true -> + %TODO: soft fail + store_event(Event2, Data); + false -> + error({event_auth_error, Event2#event.id}) + end. + +resolve_state_maps([], _Data) -> + #{}; +resolve_state_maps([StateMap], _Data) -> + StateMap; +resolve_state_maps(StateMaps, Data) -> + {Unconflicted, Conflicted} = calculate_conflict(StateMaps), + ?DEBUG("confl ~p~n", [{Unconflicted, Conflicted}]), + case maps:size(Conflicted) of + 0 -> + Unconflicted; + _ -> + AuthDiff = calculate_auth_diff(StateMaps, Data), + ?DEBUG("auth diff ~p~n", [AuthDiff]), + FullConflictedSet = + maps:from_list([{E, []} || E <- lists:append([AuthDiff | maps:values(Conflicted)])]), + ?DEBUG("fcs ~p~n", [FullConflictedSet]), + %% TODO: test + PowerEvents = + lists:filter( + fun(EventID) -> + Event = maps:get(EventID, Data#data.events), + is_power_event(Event) + end, maps:keys(FullConflictedSet)), + SortedPowerEvents = lexicographic_toposort(PowerEvents, FullConflictedSet, Data), + ?DEBUG("spe ~p~n", [SortedPowerEvents]), + StateMap = iterative_auth_checks(SortedPowerEvents, Unconflicted, Data), + PowerEventsSet = maps:from_list([{E, []} || E <- SortedPowerEvents]), + OtherEvents = lists:filter(fun(E) -> not maps:is_key(E, PowerEventsSet) end, + maps:keys(FullConflictedSet)), + PLID = maps:get({?ROOM_POWER_LEVELS, <<"">>}, StateMap, undefined), + SortedOtherEvents = mainline_sort(OtherEvents, PLID, Data), + ?DEBUG("mainline ~p~n", [SortedOtherEvents]), + StateMap2 = iterative_auth_checks(SortedOtherEvents, StateMap, Data), + Resolved = maps:merge(StateMap2, Unconflicted), + ?DEBUG("resolved ~p~n", [Resolved]), + Resolved + end. + +calculate_conflict(StateMaps) -> + Keys = lists:usort(lists:flatmap(fun maps:keys/1, StateMaps)), + lists:foldl( + fun(Key, {Unconflicted, Conflicted}) -> + EventIDs = + lists:usort( + lists:map(fun(StateMap) -> + maps:find(Key, StateMap) + end, StateMaps)), + case EventIDs of + [{ok, EventID}] -> + {Unconflicted#{Key => EventID}, Conflicted}; + _ -> + EventIDs2 = + lists:flatmap( + fun(error) -> []; + ({ok, EventID}) -> [EventID] + end, EventIDs), + {Unconflicted, Conflicted#{Key => EventIDs2}} + end + end, {#{}, #{}}, Keys). + +%% TODO: not optimal +calculate_auth_diff(StateMaps, Data) -> + N = length(StateMaps), + Queue = + lists:foldl( + fun({K, StateMap}, Q) -> + maps:fold( + fun(_, EID, Q2) -> + Depth = (maps:get(EID, Data#data.events))#event.depth, + Set = + case gb_trees:lookup({Depth, EID}, Q2) of + none -> + 1 bsl N - 1; + {value, S} -> + S + end, + Set2 = Set band bnot (1 bsl K), + gb_trees:enter({Depth, EID}, Set2, Q2) + end, Q, StateMap) + end, gb_trees:empty(), + lists:zip(lists:seq(0, N - 1), StateMaps)), + Count = lists:sum(gb_trees:values(Queue)), + calculate_auth_diff_bfs(Queue, Count, [], Data). + +calculate_auth_diff_bfs(_Queue, 0, Res, _Data) -> + Res; +calculate_auth_diff_bfs(Queue, Count, Res, Data) -> + %?DEBUG("authdiff bfs ~p~n", [{gb_trees:to_list(Queue), Count, Res}]), + case gb_trees:is_empty(Queue) of + true -> + error(internal_error); + false -> + {{_, EventID}, Set, Queue2} = gb_trees:take_largest(Queue), + Res2 = case Set of + 0 -> Res; + _ -> [EventID | Res] + end, + Event = maps:get(EventID, Data#data.events), + calculate_auth_diff_bfs2(Event#event.auth_events, Set, Queue2, Count - Set, Res2, Data) + end. + +calculate_auth_diff_bfs2([], _Set, Queue, Count, Res, Data) -> + calculate_auth_diff_bfs(Queue, Count, Res, Data); +calculate_auth_diff_bfs2([EID | Events], Set, Queue, Count, Res, Data) -> + Event = maps:get(EID, Data#data.events), + case gb_trees:lookup({Event#event.depth, EID}, Queue) of + none -> + Queue2 = gb_trees:insert({Event#event.depth, EID}, Set, Queue), + calculate_auth_diff_bfs2(Events, Set, Queue2, Count + Set, Res, Data); + {value, Set2} -> + Set3 = Set band Set2, + Queue2 = gb_trees:enter({Event#event.depth, EID}, Set3, Queue), + calculate_auth_diff_bfs2(Events, Set, Queue2, Count - Set2 + Set3, Res, Data) + end. + +is_power_event(#event{type = ?ROOM_POWER_LEVELS, state_key = <<"">>}) -> + true; +is_power_event(#event{type = ?ROOM_JOIN_RULES, state_key = <<"">>}) -> + true; +is_power_event(#event{type = ?ROOM_MEMBER, state_key = StateKey, sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"leave">>}}}) -> + StateKey /= Sender; +is_power_event(#event{type = ?ROOM_MEMBER, state_key = StateKey, sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"ban">>}}}) -> + StateKey /= Sender; +is_power_event(_) -> + false. + +lexicographic_toposort(EventIDs, EventSet, Data) -> + Used = + lists:foldl( + fun(EventID, Used) -> + case maps:is_key(EventID, EventSet) of + true -> + case maps:is_key(EventID, Used) of + false -> + lexicographic_toposort_prepare(EventID, Used, EventSet, Data); + true -> + Used + end; + false -> + Used + end + end, #{}, EventIDs), + IncomingCnt = + maps:fold( + fun(EventID, _, Acc) -> + Event = maps:get(EventID, Data#data.events), + lists:foldl( + fun(EID, Acc2) -> + case maps:is_key(EID, Acc2) of + true -> + C = maps:get(EID, Acc2), + maps:put(EID, C + 1, Acc2); + false -> + Acc2 + end + end, Acc, Event#event.auth_events) + end, maps:map(fun(_, _) -> 0 end, Used), Used), + Current = + maps:fold( + fun(EventID, 0, Acc) -> + Event = maps:get(EventID, Data#data.events), + PowerLevel = get_sender_power_level(EventID, Data), + gb_trees:enter({-PowerLevel, Event#event.origin_server_ts, EventID}, [], Acc); + (_, _, Acc) -> + Acc + end, gb_trees:empty(), IncomingCnt), + IncomingCnt2 = maps:filter(fun(_, 0) -> false; (_, _) -> true end, IncomingCnt), + lexicographic_toposort_loop(Current, IncomingCnt2, [], Data). + +lexicographic_toposort_prepare(EventID, Used, EventSet, Data) -> + Event = maps:get(EventID, Data#data.events), + Used2 = Used#{EventID => []}, + Used4 = + lists:foldl( + fun(EID, Used3) -> + case maps:is_key(EID, EventSet) of + true -> + case maps:is_key(EID, Used3) of + false -> + lexicographic_toposort_prepare(EID, Used3, EventSet, Data); + true -> + Used3 + end; + false -> + Used3 + end + end, Used2, Event#event.auth_events), + Used4. + +lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> + case gb_trees:is_empty(Current) of + true -> + case maps:size(IncomingCnt) of + 0 -> + Res; + _ -> + error(loop_in_auth_chain) + end; + false -> + {{_, _, EventID}, _, Current2} = gb_trees:take_smallest(Current), + Event = maps:get(EventID, Data#data.events), + IncomingCnt2 = + lists:foldl( + fun(EID, Acc) -> + case maps:is_key(EID, Acc) of + true -> + C = maps:get(EID, Acc) - 1, + case C of + 0 -> + maps:remove(EID, Acc); + _ -> + maps:put(EID, C, Acc) + end; + false -> + Acc + end + end, IncomingCnt, Event#event.auth_events), + lexicographic_toposort_loop(Current2, IncomingCnt2, [EventID | Res], Data) + end. + +get_sender_power_level(EventID, Data) -> + RoomVersion = Data#data.room_version, + Event = maps:get(EventID, Data#data.events), + PowerEventID = find_power_level_event(EventID, Data), + PowerEvent = + case PowerEventID of + undefined -> undefined; + _ -> maps:get(PowerEventID, Data#data.events) + end, + Sender = Event#event.sender, + case PowerEvent of + undefined -> + lists:foldl( + fun(EID, Acc) -> + E = maps:get(EID, Data#data.events), + case {RoomVersion#room_version.implicit_room_creator, E} of + {false, + #event{type = ?ROOM_CREATE, state_key = <<"">>, + json = #event{json = #{<<"content">> := + #{<<"creator">> := Sender}}}}} -> + 100; + {true, + #event{type = ?ROOM_CREATE, state_key = <<"">>, + json = #event{sender = Sender}}} -> + 100; + _ -> + Acc + end + end, 0, Event#event.auth_events); + #event{json = #{<<"content">> := #{<<"users">> := #{Sender := Level}}}} -> + get_int(Level); + #event{json = #{<<"content">> := #{<<"users_default">> := Level}}} -> + get_int(Level); + _ -> + 0 + end. + +iterative_auth_checks(Events, StateMap, Data) -> + lists:foldl( + fun(EventID, StateMap2) -> + Event = maps:get(EventID, Data#data.events), + StateMap3 = + lists:foldl( + fun(EID, SM) -> + E = maps:get(EID, Data#data.events), + case maps:is_key({E#event.type, E#event.state_key}, SM) of + true -> + SM; + false -> + SM#{{E#event.type, E#event.state_key} => E#event.id} + end + end, StateMap2, Event#event.auth_events), + %% TODO: not optimal + StateMap4 = + maps:map(fun(_, EID) -> maps:get(EID, Data#data.events) end, StateMap3), + case check_event_auth(Event, StateMap4, Data) of + true -> + StateMap2#{{Event#event.type, Event#event.state_key} => EventID}; + false -> + StateMap2 + end + end, StateMap, Events). + +mainline_sort(OtherEvents, PLID, Data) -> + IdxMap = mainline_sort_init(PLID, -1, #{}, Data), + {OtherEvents2, _} = + lists:foldl( + fun(EventID, {Events, IMap}) -> + Event = maps:get(EventID, Data#data.events), + {Idx, IMap2} = mainline_sort_find(EventID, IMap, Data), + {[{Idx, Event#event.origin_server_ts, EventID} | Events], IMap2} + end, {[], IdxMap}, OtherEvents), + lists:map(fun({_, _, EID}) -> EID end, lists:sort(OtherEvents2)). + +mainline_sort_init(undefined, _Idx, IdxMap, _Data) -> + IdxMap; +mainline_sort_init(PLID, Idx, IdxMap, Data) when is_binary(PLID) -> + IdxMap2 = maps:put(PLID, Idx, IdxMap), + PLID2 = find_power_level_event(PLID, Data), + mainline_sort_init(PLID2, Idx - 1, IdxMap2, Data). + +mainline_sort_find(undefined, IdxMap, _Data) -> + {0, IdxMap}; +mainline_sort_find(EventID, IdxMap, Data) -> + case maps:find(EventID, IdxMap) of + {ok, Idx} -> {Idx, IdxMap}; + error -> + PLID = find_power_level_event(EventID, Data), + {Idx, IdxMap2} = mainline_sort_find(PLID, IdxMap, Data), + IdxMap3 = maps:put(EventID, Idx, IdxMap2), + {Idx, IdxMap3} + end. + +find_power_level_event(EventID, Data) -> + Event = maps:get(EventID, Data#data.events), + lists:foldl( + fun(EID, undefined) -> + E = maps:get(EID, Data#data.events), + case E of + #event{type = ?ROOM_POWER_LEVELS, state_key = <<"">>} -> EID; + _ -> undefined + end; + (_, PLID) -> + PLID + end, undefined, Event#event.auth_events). + + +binary_to_room_version(<<"9">>) -> + #room_version{id = <<"9">>, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"10">>) -> + #room_version{id = <<"10">>, + knock_restricted_join_rule = true, + enforce_int_power_levels = true, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"11">>) -> + #room_version{id = <<"11">>, + knock_restricted_join_rule = true, + enforce_int_power_levels = true, + implicit_room_creator = true, + updated_redaction_rules = true + }; +binary_to_room_version(_) -> + false. + +json_to_event(#{<<"type">> := Type, + <<"room_id">> := RoomID, + <<"depth">> := Depth, + <<"auth_events">> := AuthEvents, + <<"sender">> := Sender, + <<"prev_events">> := PrevEvents, + <<"origin_server_ts">> := OriginServerTS} = JSON, RoomVersion) + when is_binary(Type), + is_integer(Depth), + is_list(AuthEvents) -> + StateKey = maps:get(<<"state_key">>, JSON, undefined), + EventID = mod_matrix_gw:get_event_id(JSON, RoomVersion), + #event{id = EventID, + room_version = RoomVersion, + room_id = RoomID, + type = Type, + state_key = StateKey, + depth = Depth, + auth_events = AuthEvents, + sender = Sender, + prev_events = PrevEvents, + origin_server_ts = OriginServerTS, + json = JSON}. + +check_event_content_hash(Event) -> + JSON = Event#event.json, + case JSON of + #{<<"hashes">> := #{<<"sha256">> := S}} -> + Hash = mod_matrix_gw:content_hash(JSON), + mod_matrix_gw:base64_decode(S) == Hash; + _ -> + false + end. + +notify_event(#event{sender = Sender, + json = #{<<"test">> := true}} = Event, + Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + LSenderServer = SenderJID#jid.lserver, + UserJID = Data#data.local_user, + LUserServer = UserJID#jid.lserver, + case LSenderServer of + LUserServer -> + %RemoteServers = maps:keys(Data#data.remote_servers), + RemoteServers = get_remote_servers(Data), + lists:foldl( + fun(Server, DataAcc) -> + case DataAcc#data.outgoing_txns of + #{Server := {T, Queue}} -> + Queue2 = [Event | Queue], + DataAcc#data{outgoing_txns = + maps:put(Server, {T, Queue2}, + DataAcc#data.outgoing_txns)}; + _ -> + send_new_txn([Event], Server, DataAcc) + end + end, Data, RemoteServers); + _ -> + Data + end; + error -> + Data + end; +notify_event(#event{type = ?ROOM_MESSAGE, sender = Sender, + json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, + <<"body">> := Body}}} = Event, + Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + LSenderJID = jid:tolower(SenderJID), + UserJID = Data#data.local_user, + LUserJID = jid:tolower(UserJID), + case LSenderJID of + LUserJID -> + %RemoteServers = maps:keys(Data#data.remote_servers), + RemoteServers = get_remote_servers(Data), + lists:foldl( + fun(Server, DataAcc) -> + case DataAcc#data.outgoing_txns of + #{Server := {T, Queue}} -> + Queue2 = [Event | Queue], + DataAcc#data{outgoing_txns = + maps:put(Server, {T, Queue2}, + DataAcc#data.outgoing_txns)}; + _ -> + send_new_txn([Event], Server, DataAcc) + end + end, Data, RemoteServers); + _ -> + RoomID = Data#data.room_id, + Msg = #message{from = SenderJID, + to = UserJID, + type = chat, + body = [#text{data = Body}], + sub_els = [#xmlel{name = <<"x">>, + attrs = [{<<"xmlns">>, <<"p1:matrix">>}, + {<<"room_id">>, RoomID}]}] + }, + ejabberd_router:route(Msg), + Data + end; + error -> + Data + end; +notify_event(#event{type = ?ROOM_MEMBER, + state_key = StateKey, + sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"invite">>}}} = Event, + Data) -> + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + case mod_matrix_gw:get_id_domain_exn(StateKey) of + MatrixServer -> + Data; + RemoteServer -> + StrippedState = + maps:with([{?ROOM_CREATE, <<"">>}, {?ROOM_JOIN_RULES, <<"">>}, + {?ROOM_MEMBER, Sender}], + Event#event.state_map), + StrippedState2 = + maps:map( + fun(_, EID) -> + E = maps:get(EID, Data#data.events), + maps:with([<<"sender">>, <<"type">>, <<"state_key">>, <<"content">>], + E#event.json) + end, StrippedState), + JSON = #{<<"event">> => Event#event.json, + <<"room_version">> => (Event#event.room_version)#room_version.id, + <<"invite_room_state">> => maps:values(StrippedState2)}, + InviteRes = + mod_matrix_gw:send_request( + Data#data.host, put, RemoteServer, + [<<"_matrix">>, <<"federation">>, + <<"v2">>, <<"invite">>, + Data#data.room_id, Event#event.id], + [], + JSON, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("send invite ~p~n", [InviteRes]), + Data + end; +notify_event(_Event, Data) -> + Data. + +send_new_txn(Events, Server, Data) -> + TxnID = p1_rand:get_string(), + send_txn(TxnID, Events, Server, [], Data). + +send_txn(TxnID, Events, Server, Queue, Data) -> + ?DEBUG("send txn ~p~n", [TxnID]), + Host = Data#data.host, + Origin = mod_matrix_gw_opt:matrix_domain(Host), + PDUs = + lists:map(fun(E) -> E#event.json end, Events), + Body = + #{<<"origin">> => Origin, + <<"origin_server_ts">> => + erlang:system_time(millisecond), + <<"pdus">> => PDUs}, + Self = self(), + Receiver = + fun({RequestID, Res}) -> + Self ! {send_txn_res, RequestID, TxnID, Server, Res} + end, + {ok, RequestID} = + mod_matrix_gw:send_request( + Host, put, Server, + [<<"_matrix">>, <<"federation">>, + <<"v1">>, <<"send">>, + TxnID], + [], + Body, + [{timeout, 5000}], + [{sync, false}, + {receiver, Receiver}]), + Data#data{outgoing_txns = + maps:put(Server, {{RequestID, TxnID, Events}, Queue}, + Data#data.outgoing_txns)}. + +do_get_missing_events(Origin, EarliestEvents, LatestEvents, Limit, MinDepth, Data) -> + case is_server_joined(Origin, Data) of + true -> + Visited = maps:from_list([{E, []} || E <- EarliestEvents]), + Queue = queue:from_list(LatestEvents), + Limit2 = min(max(Limit, 0), 20), + do_get_missing_events_bfs(Queue, Visited, Limit2, MinDepth, [], Data); + false -> + [] + end. + +do_get_missing_events_bfs(_Queue, _Visited, 0, _MinDepth, Res, _Data) -> + Res; +do_get_missing_events_bfs(Queue, Visited, Limit, MinDepth, Res, Data) -> + case queue:out(Queue) of + {{value, EventID}, Queue2} -> + case maps:find(EventID, Data#data.events) of + {ok, #event{prev_events = PrevEvents}} -> + do_get_missing_events_bfs2( + PrevEvents, Queue2, Visited, Limit, MinDepth, Res, Data); + _ -> + do_get_missing_events_bfs(Queue2, Visited, Limit, MinDepth, Res, Data) + end; + {empty, _} -> + Res + end. + +do_get_missing_events_bfs2(_PrevEvents, _Queue, _Visited, 0, _MinDepth, Res, _Data) -> + Res; +do_get_missing_events_bfs2([], Queue, Visited, Limit, MinDepth, Res, Data) -> + do_get_missing_events_bfs(Queue, Visited, Limit, MinDepth, Res, Data); +do_get_missing_events_bfs2([EventID | PrevEvents], Queue, Visited, Limit, MinDepth, Res, Data) -> + case maps:is_key(EventID, Visited) of + true -> + do_get_missing_events_bfs2(PrevEvents, Queue, Visited, Limit, MinDepth, Res, Data); + false -> + case maps:find(EventID, Data#data.events) of + {ok, #event{depth = Depth} = Event} when Depth >= MinDepth -> + Queue2 = queue:in(EventID, Queue), + Visited2 = Visited#{EventID => []}, + Res2 = [Event | Res], + do_get_missing_events_bfs2( + PrevEvents, Queue2, Visited2, Limit - 1, MinDepth, Res2, Data); + _ -> + do_get_missing_events_bfs2(PrevEvents, Queue, Visited, Limit, MinDepth, Res, Data) + end + end. + +do_get_state_ids(Origin, EventID, Data) -> + case is_server_joined(Origin, Data) of + true -> + case maps:find(EventID, Data#data.events) of + {ok, #event{state_map = StateMap} = Event} when is_map(StateMap) -> + PrevStateMap = get_event_prev_state_map(Event, Data), + PDUs = maps:values(PrevStateMap), + AuthChain = do_get_state_ids_dfs(PDUs, #{}, [], Data), + {ok, AuthChain, PDUs}; + error -> + {error, event_not_found} + end; + false -> + {error, not_allowed} + end. + +do_get_state_ids_dfs([], _Visited, Res, _Data) -> + Res; +do_get_state_ids_dfs([EventID | Queue], Visited, Res, Data) -> + case maps:is_key(EventID, Visited) of + true -> + do_get_state_ids_dfs(Queue, Visited, Res, Data); + false -> + case maps:find(EventID, Data#data.events) of + {ok, Event} -> + Visited2 = Visited#{EventID => []}, + do_get_state_ids_dfs( + Event#event.auth_events ++ Queue, Visited2, [EventID | Res], Data); + error -> + do_get_state_ids_dfs(Queue, Visited, Res, Data) + end + end. + + +is_server_joined(Server, Data) -> + try + sets:fold( + fun(EventID, ok) -> + case maps:find(EventID, Data#data.events) of + {ok, Event} -> + maps:fold( + fun({?ROOM_MEMBER, UserID}, EID, ok) -> + case mod_matrix_gw:get_id_domain_exn(UserID) of + Server -> + case maps:find(EID, Data#data.events) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + throw(found); + _ -> + ok + end; + _ -> + ok + end; + (_, _, ok) -> + ok + end, ok, Event#event.state_map), + ok; + _ -> + ok + end + end, ok, Data#data.latest_events), + false + catch + throw:found -> + true + end. + +get_remote_servers(Data) -> + Servers = + maps:fold( + fun(EventID, _, Acc) -> + case maps:find(EventID, Data#data.events) of + {ok, Event} -> + maps:fold( + fun({?ROOM_MEMBER, UserID}, EID, Acc2) -> + Server = mod_matrix_gw:get_id_domain_exn(UserID), + case maps:find(EID, Data#data.events) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + maps:put(Server, [], Acc2); + _ -> + Acc2 + end; + (_, _, Acc2) -> + Acc2 + end, Acc, Event#event.state_map); + _ -> + Acc + end + end, #{}, Data#data.latest_events), + MatrixServer = mod_matrix_gw_opt:matrix_domain(Data#data.host), + Servers2 = maps:remove(MatrixServer, Servers), + maps:keys(Servers2). + +get_joined_users(Data) -> + Users = + maps:fold( + fun(EventID, _, Acc) -> + case maps:find(EventID, Data#data.events) of + {ok, Event} when is_map(Event#event.state_map) -> + maps:fold( + fun({?ROOM_MEMBER, UserID}, EID, Acc2) -> + case maps:find(EID, Data#data.events) of + {ok, #event{ + json = #{<<"content">> := + #{<<"membership">> := + <<"join">>}}}} -> + maps:put(UserID, [], Acc2); + _ -> + Acc2 + end; + (_, _, Acc2) -> + Acc2 + end, Acc, Event#event.state_map); + _ -> + Acc + end + end, #{}, Data#data.latest_events), + maps:keys(Users). + +user_id_to_jid(Str, Data) -> + Host = Data#data.host, + ServerName = mod_matrix_gw_opt:matrix_domain(Host), + case parse_user_id(Str) of + {ok, U, ServerName} -> + jid:make(U, Host); + {ok, U, S} -> + ServiceHost = mod_matrix_gw_opt:host(Host), + EscU = escape(U), + EscS = escape(S), + jid:make(<>, ServiceHost); + error -> + error + end. + +new_room_id() -> + Host = ejabberd_config:get_myname(), + Letters = <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ">>, + N = size(Letters), + S = << <<(binary:at(Letters, X rem N))>> || + <> <= crypto:strong_rand_bytes(18)>>, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + <<$!, S/binary, $:, MatrixServer/binary>>. + +compute_event_auth_keys(#{<<"type">> := ?ROOM_CREATE}) -> + []; +compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, + <<"sender">> := Sender, + <<"content">> := #{<<"membership">> := Membership} = Content, + <<"state_key">> := StateKey}) -> + Common = [{?ROOM_CREATE, <<"">>}, + {?ROOM_POWER_LEVELS, <<"">>}, + {?ROOM_MEMBER, Sender}, + {?ROOM_MEMBER, StateKey}], + case Membership of + <<"join">> -> + case Content of + #{<<"join_authorised_via_users_server">> := AuthUser} -> + [{?ROOM_MEMBER, AuthUser}, {?ROOM_JOIN_RULES, <<"">>} | Common]; + _ -> + [{?ROOM_JOIN_RULES, <<"">>} | Common] + end; + <<"invite">> -> + case Content of + #{<<"third_party_invite">> := #{<<"signed">> := #{<<"token">> := Token}}} -> + [{?ROOM_3PI, Token}, {?ROOM_JOIN_RULES, <<"">>} | Common]; + _ -> + [{?ROOM_JOIN_RULES, <<"">>} | Common] + end; + <<"knock">> -> + [{?ROOM_JOIN_RULES, <<"">>} | Common]; + _ -> + Common + end; +compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}) -> + [{?ROOM_CREATE, <<"">>}, + {?ROOM_POWER_LEVELS, <<"">>}, + {?ROOM_MEMBER, Sender}]. + + +update_client(#data{client_state = undefined, + remote_user = RemoteUserID} = Data) -> + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, + LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, + Users = get_joined_users(Data), + case lists:member(LocalUserID, Users) of + true -> + case lists:delete(LocalUserID, Users) of + [RemoteUserID] -> + {ok, Data#data{client_state = established}}; + [_] -> + {leave, unknown_remote_user, Data#data{client_state = leave}}; + [] -> + {ok, Data}; + _ -> + {leave, too_many_users, Data#data{client_state = leave}} + end; + false -> + {ok, Data} + end; +update_client(#data{client_state = established, + remote_user = RemoteUserID} = Data) -> + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, + LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, + Users = get_joined_users(Data), + case lists:member(LocalUserID, Users) of + true -> + case lists:member(RemoteUserID, Users) of + true -> + {ok, Data}; + false -> + {leave, remote_user_left, Data#data{client_state = leave}} + end; + false -> + stop + end; +update_client(#data{client_state = leave}) -> + stop. + +escape(S) -> + escape(S, <<>>). + +escape(<<>>, Res) -> + Res; +escape(<>, Res) -> + Res2 = + case C of + $\s -> <>; + $" -> <>; + $% -> <>; + $& -> <>; + $' -> <>; + $/ -> <>; + $: -> <>; + $< -> <>; + $> -> <>; + $@ -> <>; + $\\ -> <>; + _ -> <> + end, + escape(S, Res2). + +unescape(S) -> + unescape(S, <<>>). + +unescape(<<>>, Res) -> Res; +unescape(<<"\\20", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\22", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\25", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\26", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\27", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\2f", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\3a", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\3c", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\3e", S/binary>>, Res) -> unescape(S, <>>); +unescape(<<"\\40", S/binary>>, Res) -> unescape(S, <>); +unescape(<<"\\5c", S/binary>>, Res) -> unescape(S, <>); +unescape(<>, Res) -> unescape(S, <>). + +-endif. diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl new file mode 100644 index 000000000..b2479c7b8 --- /dev/null +++ b/src/mod_matrix_gw_s2s.erl @@ -0,0 +1,583 @@ +%%%------------------------------------------------------------------- +%%% File : mod_matrix_gw_s2s.erl +%%% Author : Alexey Shchepin +%%% Purpose : Matrix S2S +%%% Created : 1 May 2022 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(mod_matrix_gw_s2s). +-if(?OTP_RELEASE >= 24). +-behaviour(gen_statem). + +%% API +-export([start_link/2, supervisor/1, create_db/0, + get_connection/2, check_auth/5, check_signature/2, + get_matrix_host_port/2]). + +%% gen_statem callbacks +-export([init/1, terminate/3, code_change/4, callback_mode/0]). +-export([handle_event/4]). + +-define(SERVER, ?MODULE). + +-include("logger.hrl"). +-include("ejabberd_http.hrl"). +-include_lib("kernel/include/inet.hrl"). + +-record(matrix_s2s, + {to :: binary(), + pid :: pid()}). + +-record(data, + {host :: binary(), + matrix_server :: binary(), + matrix_host_port :: {binary(), integer()} | undefined, + keys = #{}, + key_queue = #{}}). + +%%%=================================================================== +%%% API +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @doc +%% Creates a gen_statem process which calls Module:init/1 to +%% initialize. To ensure a synchronized start-up procedure, this +%% function does not return until Module:init/1 has returned. +%% +%% @end +%%-------------------------------------------------------------------- +-spec start_link(binary(), binary()) -> + {ok, Pid :: pid()} | + ignore | + {error, Error :: term()}. +start_link(Host, MatrixServer) -> + gen_statem:start_link(?MODULE, [Host, MatrixServer], + ejabberd_config:fsm_limit_opts([])). + +-spec supervisor(binary()) -> atom(). +supervisor(Host) -> + gen_mod:get_module_proc(Host, mod_matrix_gw_s2s_sup). + +create_db() -> + ejabberd_mnesia:create( + ?MODULE, matrix_s2s, + [{ram_copies, [node()]}, + {type, set}, + {attributes, record_info(fields, matrix_s2s)}]), + ok. + +get_connection(Host, MatrixServer) -> + case mnesia:dirty_read(matrix_s2s, MatrixServer) of + [] -> + case supervisor:start_child(supervisor(Host), + [Host, MatrixServer]) of + {ok, undefined} -> {error, ignored}; + Res -> Res + end; + [#matrix_s2s{pid = Pid}] -> + {ok, Pid} + end. + +get_key(Host, MatrixServer, KeyID) -> + case mod_matrix_gw_opt:matrix_domain(Host) of + MatrixServer -> + {PubKey, _PrivKey} = mod_matrix_gw_opt:key(Host), + TS = erlang:system_time(millisecond) + timer:hours(24 * 7), + {ok, PubKey, TS}; + _ -> + case get_connection(Host, MatrixServer) of + {ok, S2SPid} -> + gen_statem:call(S2SPid, {get_key, KeyID}); + Error -> Error + end + end. + +get_matrix_host_port(Host, MatrixServer) -> + case mod_matrix_gw_opt:matrix_domain(Host) of + MatrixServer -> + error; + _ -> + case get_connection(Host, MatrixServer) of + {ok, S2SPid} -> + gen_statem:call(S2SPid, get_matrix_host_port); + Error -> Error + end + end. + + +%process_query(Host, MatrixServer, AuthParams, Query, JSON, Request) -> +% case get_connection(Host, MatrixServer) of +% {ok, S2SPid} -> +% #request{sockmod = SockMod, socket = Socket} = Request, +% SockMod:controlling_process(Socket, S2SPid), +% gen_statem:cast(S2SPid, {query, AuthParams, Query, JSON, Request}), +% ok; +% {error, _} = Error -> +% Error +% end. + +check_auth(Host, MatrixServer, AuthParams, Content, Request) -> + case get_connection(Host, MatrixServer) of + {ok, S2SPid} -> + #{<<"key">> := KeyID} = AuthParams, + case catch gen_statem:call(S2SPid, {get_key, KeyID}) of + {ok, VerifyKey, _ValidUntil} -> + %% TODO: check ValidUntil + Destination = mod_matrix_gw_opt:matrix_domain(Host), + #{<<"sig">> := Sig} = AuthParams, + JSON = #{<<"method">> => atom_to_binary(Request#request.method, latin1), + <<"uri">> => Request#request.raw_path, + <<"origin">> => MatrixServer, + <<"destination">> => Destination, + <<"signatures">> => #{ + MatrixServer => #{KeyID => Sig} + } + }, + JSON2 = + case Content of + none -> JSON; + _ -> + JSON#{<<"content">> => Content} + end, + case check_signature(JSON2, MatrixServer, KeyID, VerifyKey) of + true -> + true; + false -> + ?WARNING_MSG("Failed authentication: ~p", [JSON2]), + false + end; + _ -> + false + end; + {error, _} = _Error -> + false + end. + +check_signature(Host, JSON) -> + case JSON of + #{<<"sender">> := Sender, + <<"signatures">> := Sigs} -> + MatrixServer = mod_matrix_gw:get_id_domain_exn(Sender), + case Sigs of + #{MatrixServer := #{} = KeySig} -> + case maps:next(maps:iterator(KeySig)) of + {KeyID, _Sig, _} -> + case catch get_key(Host, MatrixServer, KeyID) of + {ok, VerifyKey, _ValidUntil} -> + %% TODO: check ValidUntil + case check_signature(JSON, MatrixServer, KeyID, VerifyKey) of + true -> + true; + false -> + ?WARNING_MSG("Failed authentication: ~p", [JSON]), + false + end; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end; + _ -> + false + end. + + +%%%=================================================================== +%%% gen_statem callbacks +%%%=================================================================== + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Whenever a gen_statem is started using gen_statem:start/[3,4] or +%% gen_statem:start_link/[3,4], this function is called by the new +%% process to initialize. +%% @end +%%-------------------------------------------------------------------- +-spec init(Args :: term()) -> + {gen_statem:callback_mode(), + State :: term(), Data :: term()} | + {gen_statem:callback_mode(), + State :: term(), Data :: term(), + [gen_statem:action()] | gen_statem:action()} | + ignore | + {stop, Reason :: term()}. +init([Host, MatrixServer]) -> + mnesia:dirty_write( + #matrix_s2s{to = MatrixServer, + pid = self()}), + {ok, state_name, + #data{host = Host, + matrix_server = MatrixServer}}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% If the gen_statem runs with CallbackMode =:= handle_event_function +%% this function is called for every event a gen_statem receives. +%% @end +%%-------------------------------------------------------------------- +-spec handle_event( + gen_statem:event_type(), Msg :: term(), + State :: term(), Data :: term()) -> + gen_statem:handle_event_result(). +%handle_event({call, From}, _Msg, State, Data) -> +% {next_state, State, Data, [{reply, From, ok}]}. +handle_event({call, From}, get_matrix_host_port, _State, Data) -> + case Data#data.matrix_host_port of + undefined -> + Result = do_get_matrix_host_port(Data), + Data2 = Data#data{matrix_host_port = Result}, + {keep_state, Data2, [{reply, From, Result}]}; + Result -> + {keep_state_and_data, [{reply, From, Result}]} + end; +handle_event({call, From}, {get_key, KeyID}, State, Data) -> + case maps:find(KeyID, Data#data.keys) of + {ok, {ok, _, _} = Result} -> + {keep_state, Data, [{reply, From, Result}]}; + {ok, error = Result} -> + {keep_state, Data, [{reply, From, Result}]}; + {ok, pending} -> + KeyQueue = maps:update_with( + KeyID, + fun(Xs) -> + [From | Xs] + end, + [From], + Data#data.key_queue), + {next_state, State, + Data#data{key_queue = KeyQueue}, []}; + error -> + {MHost, MPort} = do_get_matrix_host_port(Data), + URL = <<"https://", MHost/binary, + ":", (integer_to_binary(MPort))/binary, + "/_matrix/key/v2/server/", KeyID/binary>>, + Self = self(), + httpc:request(get, {URL, []}, + [{timeout, 5000}], + [{sync, false}, + {receiver, + fun({_RequestId, Result}) -> + gen_statem:cast( + Self, {key_reply, KeyID, Result}) + end}]), + Keys = (Data#data.keys)#{KeyID => pending}, + KeyQueue = maps:update_with( + KeyID, + fun(Xs) -> + [From | Xs] + end, + [From], + Data#data.key_queue), + {next_state, State, + Data#data{keys = Keys, + key_queue = KeyQueue}, + []} + end; +handle_event(cast, {query, AuthParams, _Query, _JSON, _Request} = Msg, + State, Data) -> + #{<<"key">> := KeyID} = AuthParams, + case maps:find(KeyID, Data#data.keys) of + {ok, {ok, VerifyKey, _ValidUntil}} -> + Data2 = process_unverified_query( + KeyID, VerifyKey, Msg, Data), + {next_state, State, Data2, []}; + {ok, error} -> + %TODO + {next_state, State, Data, []}; + {ok, pending} -> + KeyQueue = maps:update_with( + KeyID, + fun(Xs) -> + [Msg | Xs] + end, + [Msg], + Data#data.key_queue), + {next_state, State, + Data#data{key_queue = KeyQueue}, []}; + error -> + {MHost, MPort} = do_get_matrix_host_port(Data), + URL = <<"https://", MHost/binary, + ":", (integer_to_binary(MPort))/binary, + "/_matrix/key/v2/server/", KeyID/binary>>, + Self = self(), + httpc:request(get, {URL, []}, + [{timeout, 5000}], + [{sync, false}, + {receiver, + fun({_RequestId, Result}) -> + gen_statem:cast( + Self, {key_reply, KeyID, Result}) + end}]), + Keys = (Data#data.keys)#{KeyID => pending}, + KeyQueue = maps:update_with( + KeyID, + fun(Xs) -> + [Msg | Xs] + end, + [Msg], + Data#data.key_queue), + {next_state, State, + Data#data{keys = Keys, + key_queue = KeyQueue}, + []} + end; +handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> + case HTTPResult of + {{_, 200, _}, _, SJSON} -> + try + JSON = jiffy:decode(SJSON, [return_maps]), + ?DEBUG("key ~p~n", [JSON]), + #{<<"verify_keys">> := VerifyKeys} = JSON, + #{KeyID := KeyData} = VerifyKeys, + #{<<"key">> := SKey} = KeyData, + VerifyKey = mod_matrix_gw:base64_decode(SKey), + ?DEBUG("key ~p~n", [VerifyKey]), + ?DEBUG("check ~p~n", + [catch check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey)]), + true = check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey), + #{<<"valid_until_ts">> := ValidUntil} = JSON, + ValidUntil2 = + min(ValidUntil, + erlang:system_time(millisecond) + timer:hours(24 * 7)), + Keys = (Data#data.keys)#{KeyID => {ok, VerifyKey, ValidUntil2}}, + Froms = maps:get(KeyID, Data#data.key_queue, []), + KeyQueue = maps:remove(KeyID, Data#data.key_queue), + Data2 = Data#data{keys = Keys, + key_queue = KeyQueue}, + Replies = + lists:map( + fun(From) -> + {reply, From, {ok, VerifyKey, ValidUntil2}} + end, Froms), + ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), + {next_state, State, Data2, Replies} + catch + _:_ -> + %% TODO + Keys2 = (Data#data.keys)#{KeyID => error}, + {next_state, State, Data#data{keys = Keys2}, []} + end; + _ -> + %% TODO + Keys = (Data#data.keys)#{KeyID => error}, + {next_state, State, Data#data{keys = Keys}, []} + end; +handle_event(cast, Msg, State, Data) -> + ?WARNING_MSG("Unexpected cast: ~p", [Msg]), + {next_state, State, Data, []}; +handle_event(info, Info, State, Data) -> + ?WARNING_MSG("Unexpected info: ~p", [Info]), + {next_state, State, Data, []}. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% This function is called by a gen_statem when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any +%% necessary cleaning up. When it returns, the gen_statem terminates with +%% Reason. The return value is ignored. +%% @end +%%-------------------------------------------------------------------- +-spec terminate(Reason :: term(), State :: term(), Data :: term()) -> + any(). +terminate(_Reason, _State, Data) -> + mnesia:dirty_delete_object( + #matrix_s2s{to = Data#data.matrix_server, + pid = self()}), + %% TODO: wait for messages + ok. + +%%-------------------------------------------------------------------- +%% @private +%% @doc +%% Convert process state when code is changed +%% @end +%%-------------------------------------------------------------------- +-spec code_change( + OldVsn :: term() | {down,term()}, + State :: term(), Data :: term(), Extra :: term()) -> + {ok, NewState :: term(), NewData :: term()}. +code_change(_OldVsn, State, Data, _Extra) -> + {ok, State, Data}. + +callback_mode() -> + handle_event_function. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +do_get_matrix_host_port(Data) -> + MatrixServer = Data#data.matrix_server, + case binary:split(MatrixServer, <<":">>) of + [Addr] -> + case inet:parse_address(binary_to_list(Addr)) of + {ok, _} -> + {Addr, 8448}; + _ -> + URL = <<"https://", Addr/binary, "/.well-known/matrix/server">>, + HTTPRes = + httpc:request(get, {URL, []}, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("HTTPRes ~p~n", [HTTPRes]), + Res = + case HTTPRes of + {ok, {{_, 200, _}, _Headers, Body}} -> + try + case jiffy:decode(Body, [return_maps]) of + #{<<"m.server">> := Server} -> + case binary:split(Server, <<":">>) of + [ServerAddr] -> + {ServerAddr, 8448}; + [ServerAddr, ServerPort] -> + {ServerAddr, binary_to_integer(ServerPort)} + end + end + catch + _:_ -> + error + end; + _ -> + error + end, + case Res of + error -> + SRVName = + "_matrix._tcp." ++ binary_to_list(MatrixServer), + case inet_res:getbyname(SRVName, srv, 5000) of + {ok, HostEntry} -> + case h_addr_list_to_host_ports( + HostEntry#hostent.h_addr_list) of + {ok, [{Host, Port} | _]} -> + {list_to_binary(Host), Port}; + _ -> + {MatrixServer, 8448} + end; + {error, _} -> + {MatrixServer, 8448} + end; + _ -> + Res + end + end; + [Addr, SPort] -> + case catch binary_to_integer(SPort) of + Port when is_integer(Port) -> + {Addr, Port}; + _ -> + error + end + end. + +%% Copied from xmpp_stream_out.erl +-type host_port() :: {inet:hostname(), inet:port_number()}. +-type h_addr_list() :: [{integer(), integer(), inet:port_number(), string()}]. +-spec h_addr_list_to_host_ports(h_addr_list()) -> {ok, [host_port(),...]} | + {error, nxdomain}. +h_addr_list_to_host_ports(AddrList) -> + PrioHostPorts = lists:flatmap( + fun({Priority, Weight, Port, Host}) -> + N = case Weight of + 0 -> 0; + _ -> (Weight + 1) * p1_rand:uniform() + end, + [{Priority * 65536 - N, Host, Port}]; + (_) -> + [] + end, AddrList), + HostPorts = [{Host, Port} + || {_Priority, Host, Port} <- lists:usort(PrioHostPorts)], + case HostPorts of + [] -> {error, nxdomain}; + _ -> {ok, HostPorts} + end. + + +check_signature(JSON, SignatureName, KeyID, VerifyKey) -> + try + #{<<"signatures">> := Signatures} = JSON, + #{SignatureName := SignatureData} = Signatures, + #{KeyID := SSignature} = SignatureData, + Signature = mod_matrix_gw:base64_decode(SSignature), + JSON2 = maps:without([<<"signatures">>, <<"unsigned">>], JSON), + Msg = mod_matrix_gw:encode_canonical_json(JSON2), + public_key:verify(Msg, ignored, Signature, {ed_pub, ed25519, VerifyKey}) + catch + _:_ -> + false + end. + +%process_unverified_queries(KeyID, Data) -> +% case maps:find(KeyID, Data#data.keys) of +% {ok, {ok, VerifyKey, _ValidUntil}} -> +% Queue = maps:get(KeyID, Data#data.key_queue, []), +% KeyQueue = maps:remove(KeyID, Data#data.key_queue), +% Data2 = Data#data{key_queue = KeyQueue}, +% lists:foldl( +% fun(Query, DataAcc) -> +% process_unverified_query(KeyID, VerifyKey, Query, DataAcc) +% end, Data2, Queue); +% _ -> +% %% TODO +% Data +% end. + +process_unverified_query( + KeyID, VerifyKey, {query, AuthParams, _Query, Content, Request} = _Msg, Data) -> + Destination = mod_matrix_gw_opt:matrix_domain(Data#data.host), + #{<<"sig">> := Sig} = AuthParams, + JSON = #{<<"method">> => atom_to_binary(Request#request.method, latin1), + <<"uri">> => Request#request.raw_path, + <<"origin">> => Data#data.matrix_server, + <<"destination">> => Destination, + <<"signatures">> => #{ + Data#data.matrix_server => #{KeyID => Sig} + } + }, + JSON2 = + case Content of + none -> JSON; + _ -> + JSON#{<<"content">> => Content} + end, + case check_signature(JSON2, Data#data.matrix_server, KeyID, VerifyKey) of + true -> + todo_remove_me; + %process_query(Msg, Data); + false -> + ?WARNING_MSG("Failed authentication: ~p", [JSON]), + %% TODO + Data + end. + +-endif. diff --git a/src/mod_matrix_gw_sup.erl b/src/mod_matrix_gw_sup.erl new file mode 100644 index 000000000..f29ea8b0c --- /dev/null +++ b/src/mod_matrix_gw_sup.erl @@ -0,0 +1,77 @@ +%%%---------------------------------------------------------------------- +%%% Created : 1 May 2022 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(mod_matrix_gw_sup). +-if(?OTP_RELEASE >= 24). +-behaviour(supervisor). + +%% API +-export([start/1, start_link/1, procname/1]). +%% Supervisor callbacks +-export([init/1]). + +%%%=================================================================== +%%% API functions +%%%=================================================================== +start(Host) -> + Spec = #{id => procname(Host), + start => {?MODULE, start_link, [Host]}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [?MODULE]}, + supervisor:start_child(ejabberd_gen_mod_sup, Spec). + +start_link(Host) -> + Proc = procname(Host), + supervisor:start_link({local, Proc}, ?MODULE, [Host]). + +-spec procname(binary()) -> atom(). +procname(Host) -> + gen_mod:get_module_proc(Host, ?MODULE). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== +init([Host]) -> + S2SName = mod_matrix_gw_s2s:supervisor(Host), + RoomName = mod_matrix_gw_room:supervisor(Host), + Specs = + [#{id => S2SName, + start => {ejabberd_tmp_sup, start_link, [S2SName, mod_matrix_gw_s2s]}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [ejabberd_tmp_sup]}, + #{id => RoomName, + start => {ejabberd_tmp_sup, start_link, [RoomName, mod_matrix_gw_room]}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [ejabberd_tmp_sup]}, + #{id => mod_matrix_gw:procname(Host), + start => {mod_matrix_gw, start_link, [Host]}, + restart => permanent, + shutdown => timer:minutes(1), + type => worker, + modules => [mod_matrix_gw]}], + {ok, {{one_for_one, 10, 1}, Specs}}. +-endif. From 3f07e232aaa795fb1bb8702cf3492a13cb4e5630 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 06:33:42 +0000 Subject: [PATCH 0411/1302] Bump actions/upload-artifact from 3 to 4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a04993d2a..c86d7d158 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -301,7 +301,7 @@ jobs: - name: Upload CT logs if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ejabberd-ct-logs-${{matrix.otp}} # From f40a036d979e65723a0e5caeda95ff1176ae6426 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Feb 2024 13:04:32 +0100 Subject: [PATCH 0412/1302] Makefile: Express rebar3 profile using "as profile" This allows to use REBAR_PROFILE to add another profile. Right now it doesn't seem specially useful, but it's possible: REBAR_PROFILE=dev make prod REBAR_PROFILE=translations make dev https://rebar3.org/docs/configuration/profiles/ --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index c287cbbd9..d73a774a9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -160,7 +160,7 @@ endif XREFOPTIONS= CLEANARG=--all REBARREL=$(REBAR) as prod tar - REBARDEV=REBAR_PROFILE=dev $(REBAR) release + REBARDEV=$(REBAR) as dev release RELIVECMD=$(REBAR) relive REL_LIB_DIR = _build/dev/rel/ejabberd/lib COPY_REL_TARGET = dev From e1f863afa438792e01668f89cd7df150e8400550 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 9 Feb 2024 22:37:23 +0100 Subject: [PATCH 0413/1302] Makefile: Group all variable definitions, and later just use them --- Makefile.in | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Makefile.in b/Makefile.in index d73a774a9..2a26e7a8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2,6 +2,7 @@ #' definitions # +ESCRIPT = @ESCRIPT@ REBAR = @ESCRIPT@ @rebar@ MIX = @rebar@ AWK = @AWK@ @@ -9,6 +10,13 @@ INSTALL = @INSTALL@ MKDIR_P = @MKDIR_P@ SED = @SED@ ERL = @ERL@ +EPMD = @EPMD@ +IEX = @IEX@ + +INSTALLUSER=@INSTALLUSER@ +INSTALLGROUP=@INSTALLGROUP@ + +REBAR_ENABLE_ELIXIR = @elixir@ prefix = @prefix@ exec_prefix = @exec_prefix@ @@ -81,7 +89,6 @@ LOGDIR = @localstatedir@/log/ejabberd #' install user # -INSTALLUSER=@INSTALLUSER@ # if no user was enabled, don't set privileges or ownership ifeq ($(INSTALLUSER),) O_USER= @@ -96,8 +103,8 @@ else CHOWN_OUTPUT=&1 INIT_USER=$(INSTALLUSER) endif + # if no group was enabled, don't set privileges or ownership -INSTALLGROUP=@INSTALLGROUP@ ifneq ($(INSTALLGROUP),) G_USER=-g $(INSTALLGROUP) endif @@ -114,8 +121,6 @@ REBAR_VER:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print $$2}') REBAR_VER_318:=$(shell $(REBAR) --version | $(AWK) -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') endif -REBAR_ENABLE_ELIXIR = @elixir@ - ifeq "$(REBAR_VER)" "6" REBAR=$(MIX) SKIPDEPS= @@ -133,7 +138,7 @@ ifeq "$(REBAR_VER)" "6" ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" REBARREL=MIX_ENV=prod $(REBAR) release --overwrite REBARDEV=MIX_ENV=dev $(REBAR) release --overwrite - RELIVECMD=escript rel/relive.escript && MIX_ENV=dev RELIVE=true iex --name ejabberd@localhost -S mix run + RELIVECMD=$(ESCRIPT) rel/relive.escript && MIX_ENV=dev RELIVE=true $(IEX) --name ejabberd@localhost -S mix run REL_LIB_DIR = _build/dev/rel/ejabberd/lib COPY_REL_TARGET = dev else @@ -362,15 +367,15 @@ LOGS_DIR = ${relivedir}/logs # ejabberdctl.relive: - $(SED) -e "s*{{installuser}}*@INSTALLUSER@*g" \ + $(SED) -e "s*{{installuser}}*${INSTALLUSER}*g" \ -e "s*{{config_dir}}*${CONFIG_DIR}*g" \ -e "s*{{logs_dir}}*${LOGS_DIR}*g" \ -e "s*{{spool_dir}}*${SPOOL_DIR}*g" \ - -e "s*{{bindir}}*@bindir@*g" \ + -e "s*{{bindir}}*${BINDIR}*g" \ -e "s*{{libdir}}*${relivelibdir}${ELIXIR_LIBDIR}*g" \ - -e "s*{{iexpath}}*@IEX@*g" \ - -e "s*{{erl}}*@ERL@*g" \ - -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ + -e "s*{{iexpath}}*${IEX}*g" \ + -e "s*{{erl}}*${ERL}*g" \ + -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ > ejabberdctl.relive ejabberd.init: @@ -386,15 +391,15 @@ ejabberd.service: chmod 644 ejabberd.service ejabberdctl.example: vars.config - $(SED) -e "s*{{installuser}}*@INSTALLUSER@*g" \ + $(SED) -e "s*{{installuser}}*${INSTALLUSER}*g" \ -e "s*{{config_dir}}*${ETCDIR}*g" \ -e "s*{{logs_dir}}*${LOGDIR}*g" \ -e "s*{{spool_dir}}*${SPOOLDIR}*g" \ - -e "s*{{bindir}}*@bindir@*g" \ - -e "s*{{libdir}}*@libdir@${ELIXIR_LIBDIR}*g" \ - -e "s*{{iexpath}}*@IEX@*g" \ - -e "s*{{erl}}*@ERL@*g" \ - -e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \ + -e "s*{{bindir}}*${BINDIR}*g" \ + -e "s*{{libdir}}*${LIBDIR}${ELIXIR_LIBDIR}*g" \ + -e "s*{{iexpath}}*${IEX}*g" \ + -e "s*{{erl}}*${ERL}*g" \ + -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ > ejabberdctl.example scripts: ejabberd.init ejabberd.service ejabberdctl.example @@ -430,12 +435,12 @@ install-main: # # Spool directory $(INSTALL) -d -m 750 $(O_USER) $(DESTDIR)$(SPOOLDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(DESTDIR)$(SPOOLDIR) >$(CHOWN_OUTPUT) + $(CHOWN_COMMAND) -R $(INSTALLUSER) $(DESTDIR)$(SPOOLDIR) >$(CHOWN_OUTPUT) chmod -R 750 $(DESTDIR)$(SPOOLDIR) # # Log directory $(INSTALL) -d -m 750 $(O_USER) $(DESTDIR)$(LOGDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(DESTDIR)$(LOGDIR) >$(CHOWN_OUTPUT) + $(CHOWN_COMMAND) -R $(INSTALLUSER) $(DESTDIR)$(LOGDIR) >$(CHOWN_OUTPUT) chmod -R 750 $(DESTDIR)$(LOGDIR) # # Documentation From 3c98ec5b4899aaae36ae70c4fba844ade8ee6018 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Feb 2024 13:05:09 +0100 Subject: [PATCH 0414/1302] Update "make translations" to reduce build requirements Previously, preparing translations required rebar3 and: ./configure --enable-tools make make translations With this change it works with rebar3 and mix, just running: ./configure make translations --- Makefile.in | 9 +++++++-- configure.ac | 2 +- mix.exs | 2 ++ rebar.config | 3 +-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index 2a26e7a8f..e105b2874 100644 --- a/Makefile.in +++ b/Makefile.in @@ -141,6 +141,8 @@ ifeq "$(REBAR_VER)" "6" RELIVECMD=$(ESCRIPT) rel/relive.escript && MIX_ENV=dev RELIVE=true $(IEX) --name ejabberd@localhost -S mix run REL_LIB_DIR = _build/dev/rel/ejabberd/lib COPY_REL_TARGET = dev + GET_DEPS_TRANSLATIONS=MIX_ENV=translations $(REBAR) $(GET_DEPS) + DEPSDIR_TRANSLATIONS=deps else ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") @@ -169,6 +171,8 @@ endif RELIVECMD=$(REBAR) relive REL_LIB_DIR = _build/dev/rel/ejabberd/lib COPY_REL_TARGET = dev + GET_DEPS_TRANSLATIONS=$(REBAR) as translations $(GET_DEPS) + DEPSDIR_TRANSLATIONS=_build/translations/lib else SKIPDEPS=skip_deps=true LISTDEPS=-q list-deps @@ -228,7 +232,8 @@ options: all tools/opt_types.sh ejabberd_option $(EBINDIR) translations: - tools/prepare-tr.sh $(DEPSDIR) + $(GET_DEPS_TRANSLATIONS) + tools/prepare-tr.sh $(DEPSDIR_TRANSLATIONS) doap: tools/generate-doap.sh @@ -647,7 +652,7 @@ help: @echo " doap Generate DOAP file" @echo " edoc Generate edoc documentation (unused)" @echo " options Generate ejabberd_option.erl" - @echo " translations Extract translation files (requires --enable-tools)" + @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" @echo "" @echo " dialyzer Run Dialyzer static analyzer" diff --git a/configure.ac b/configure.ac index 065047cb9..daa1170ea 100644 --- a/configure.ac +++ b/configure.ac @@ -256,7 +256,7 @@ AC_ARG_ENABLE(system_deps, esac],[if test "x$system_deps" = "x"; then system_deps=false; fi]) AC_ARG_ENABLE(tools, -[AS_HELP_STRING([--enable-tools],[build development tools: ejabberd-po, etop (default: no)])], +[AS_HELP_STRING([--enable-tools],[build development tools: etop (default: no)])], [case "${enableval}" in yes) tools=true ;; no) tools=false ;; diff --git a/mix.exs b/mix.exs index f7e252533..791b276c6 100644 --- a/mix.exs +++ b/mix.exs @@ -132,6 +132,8 @@ defmodule Ejabberd.MixProject do defp cond_deps do for {:true, dep} <- [{config(:pam), {:epam, "~> 1.0"}}, + {Mix.env() == :translations, + {:ejabberd_po, git: "https://github.com/processone/ejabberd-po.git"}}, {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, diff --git a/rebar.config b/rebar.config index 4242f5d0b..8f03e18cb 100644 --- a/rebar.config +++ b/rebar.config @@ -25,8 +25,6 @@ {deps, [{base64url, ".*", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, - {if_var_true, tools, - {ejabberd_po, ".*", {git, "https://github.com/processone/ejabberd-po", {branch, "main"}}}}, {if_var_true, pam, {epam, ".*", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, @@ -283,6 +281,7 @@ {copy, "ejabberd.yml.example", "conf/ejabberd.yml.example"}, {copy, "test/ejabberd_SUITE_data/ca.pem", "conf/"}, {copy, "test/ejabberd_SUITE_data/cert.pem", "conf/"}]}]}]}, + {translations, [{deps, [{ejabberd_po, ".*", {git, "https://github.com/processone/ejabberd-po", {branch, "main"}}}]}]}, {test, [{erl_opts, [nowarn_export_all]}]}]}. {alias, [{relive, [{shell, "--apps ejabberd \ From 8b84f991e1af0cf32a87f5e954c77c96e9bc215e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 21:25:51 +0100 Subject: [PATCH 0415/1302] make-binaries: Bump many dependency versions Unfortunately, newer versions of linux-pam throw compilation problems: linux-pam 1.5.3: tty_conv.c:9:10: fatal error: termio.h: No such file or directory linux-pam 1.6.0: pam_namespace.c:649:41: error: 'SIZE_MAX' undeclared (first use in this function) --- tools/make-binaries | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index c32771d28..d91cd8610 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,19 +67,19 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.26.0' termcap_vsn='1.3.1' -expat_vsn='2.5.0' -zlib_vsn='1.3' +expat_vsn='2.6.0' +zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.2.1' otp_vsn='26.2.2' elixir_vsn='1.16.1' pam_vsn='1.5.2' -png_vsn='1.6.40' -jpeg_vsn='9e' +png_vsn='1.6.42' +jpeg_vsn='9f' webp_vsn='1.3.2' gd_vsn='2.3.3' odbc_vsn='2.3.12' -sqlite_vsn='3430100' +sqlite_vsn='3450100' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From 2438fc8b31047739cb0d0843cff0ed53bf92a2e6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 22:23:14 +0100 Subject: [PATCH 0416/1302] Add OTP_BELOW_25 to mix.exs that was added to rebar.config in 4ae06f0 --- mix.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 791b276c6..b620e522a 100644 --- a/mix.exs +++ b/mix.exs @@ -81,7 +81,8 @@ defmodule Ejabberd.MixProject do if_version_below(~c"23", [{:d, :USE_OLD_CRYPTO_HMAC}]) ++ if_version_below(~c"23", [{:d, :USE_OLD_PG2}]) ++ if_version_below(~c"24", [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ - if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) + if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ + if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] end From 816900a6cf60ba77b2c7a823a4b75f45b12053c9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 22:09:53 +0100 Subject: [PATCH 0417/1302] Use uri_string module instead of the deprecated http_uri http_uri is marked for deprecation since OTP 23 Use uri_string which is available since OTP 21 No need to provide alternative, because it's used only by mod_matrix, and this runs only with Erlang/OTP 24 or higher. https://www.erlang.org/patches/otp-23.0#inets-7.2 https://www.erlang.org/docs/23/man/http_uri --- src/mod_matrix_gw.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 929cdc477..a1e3905a3 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -743,7 +743,7 @@ sign_json(Host, JSON) -> send_request(Host, Method, MatrixServer, Path, Query, JSON, HTTPOptions, Options) -> URI1 = iolist_to_binary( - lists:map(fun(P) -> [$/, http_uri:encode(P)] end, Path)), + lists:map(fun(P) -> [$/, uri_string:quote(P)] end, Path)), URI = case Query of [] -> URI1; @@ -751,7 +751,7 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, URI2 = str:join( lists:map( fun({K, V}) -> - [http_uri:encode(K), $=, http_uri:encode(V)] + [uri_string:quote(K), $=, uri_string:quote(V)] end, Query), $&), <> end, From b8181974ec4ab45b7c2a5bf245a45ce23076c6c7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 21:51:33 +0100 Subject: [PATCH 0418/1302] Replace 'if' preprocessor directive with 'ifndef', add OTP_BELOW_24 The 'if' preprocessor directive was added in Erlang/OTP 21.0, but ejabberd currently supports Erlang 20.0 and higher... Let's use 'ifndef' instead. https://www.erlang.org/doc/reference_manual/macros.html#flow-control-in-macros https://www.erlang.org/doc/apps/compiler/notes#compiler-7.2 https://www.erlang.org/patches/otp-21.0#compiler-7.2 --- mix.exs | 1 + rebar.config | 1 + src/mod_matrix_gw.erl | 2 +- src/mod_matrix_gw_room.erl | 2 +- src/mod_matrix_gw_s2s.erl | 2 +- src/mod_matrix_gw_sup.erl | 2 +- 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index b620e522a..9bcb3edff 100644 --- a/mix.exs +++ b/mix.exs @@ -82,6 +82,7 @@ defmodule Ejabberd.MixProject do if_version_below(~c"23", [{:d, :USE_OLD_PG2}]) ++ if_version_below(~c"24", [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ + if_version_below(~c"24", [{:d, :OTP_BELOW_24}]) ++ if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] diff --git a/rebar.config b/rebar.config index 8f03e18cb..f7779b1ef 100644 --- a/rebar.config +++ b/rebar.config @@ -129,6 +129,7 @@ {if_version_below, "23", {d, 'USE_OLD_PG2'}}, {if_version_below, "24", {d, 'COMPILER_REPORTS_ONLY_LINES'}}, {if_version_below, "24", {d, 'SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL'}}, + {if_version_below, "24", {d, 'OTP_BELOW_24'}}, {if_version_below, "25", {d, 'OTP_BELOW_25'}}, {if_var_false, debug, no_debug_info}, {if_var_true, debug, debug_info}, diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index a1e3905a3..476fc1ef6 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -24,7 +24,7 @@ %%%---------------------------------------------------------------------- -module(mod_matrix_gw). --if(?OTP_RELEASE >= 24). +-ifndef(OTP_BELOW_24). -author('alexey@process-one.net'). diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index a56c2496b..c5ae25f1e 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -24,7 +24,7 @@ %%%------------------------------------------------------------------- -module(mod_matrix_gw_room). --if(?OTP_RELEASE >= 24). +-ifndef(OTP_BELOW_24). -behaviour(gen_statem). %% API diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index b2479c7b8..468fbcc14 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -23,7 +23,7 @@ %%% %%%------------------------------------------------------------------- -module(mod_matrix_gw_s2s). --if(?OTP_RELEASE >= 24). +-ifndef(OTP_BELOW_24). -behaviour(gen_statem). %% API diff --git a/src/mod_matrix_gw_sup.erl b/src/mod_matrix_gw_sup.erl index f29ea8b0c..f5fa62769 100644 --- a/src/mod_matrix_gw_sup.erl +++ b/src/mod_matrix_gw_sup.erl @@ -20,7 +20,7 @@ %%% %%%---------------------------------------------------------------------- -module(mod_matrix_gw_sup). --if(?OTP_RELEASE >= 24). +-ifndef(OTP_BELOW_24). -behaviour(supervisor). %% API From 071dc0840526604773f944bb918a93ec822ce596 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 23:16:27 +0100 Subject: [PATCH 0419/1302] Container: Support OTP versions with "-rc" string --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index b9c655ffa..d9810d370 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -36,7 +36,7 @@ jobs: - name: Get erlang/OTP version for bootstrapping run: | - echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV + echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.rc-]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV - name: Install prerequisites From 18cb0bca19709d2d1282b522b07d77836da2080b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 15 Feb 2024 00:32:18 +0100 Subject: [PATCH 0420/1302] Fix compilation with Erlang/OTP 27: don't use the reserved word 'maybe' --- src/ejabberd_auth.erl | 4 ++-- src/ejabberd_auth_anonymous.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 0034e6a92..594fc1bd7 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -425,7 +425,7 @@ user_exists(User, Server) -> false end. --spec user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe. +-spec user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe_exists. user_exists_in_other_modules(Module, User, Server) -> user_exists_in_other_modules_loop( auth_modules(Server) -- [Module], User, Server). @@ -439,7 +439,7 @@ user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> {false, _} -> user_exists_in_other_modules_loop(AuthModules, User, Server); {{error, _}, _} -> - maybe + maybe_exists end. -spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}). diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 17289e6fb..9fb67047c 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -153,7 +153,7 @@ check_password(User, _AuthzId, Server, _Password) -> %% If user exists in other module, reject anonnymous authentication true -> false; %% If we are not sure whether the user exists in other module, reject anon auth - maybe -> false; + maybe_exists -> false; false -> login(User, Server) end}. From 45f24c63cb2e29d8afee6f1b76ff14e436c8f7b6 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 15 Feb 2024 09:38:14 +0300 Subject: [PATCH 0421/1302] Update mod_matrix_gw copyright dates --- src/mod_matrix_gw.erl | 2 +- src/mod_matrix_gw_room.erl | 2 +- src/mod_matrix_gw_s2s.erl | 2 +- src/mod_matrix_gw_sup.erl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 476fc1ef6..f02cfb28f 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -5,7 +5,7 @@ %%% Created : 23 Apr 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index c5ae25f1e..d3da782c2 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -5,7 +5,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 468fbcc14..3231124ab 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_sup.erl b/src/mod_matrix_gw_sup.erl index f5fa62769..b0c757a5c 100644 --- a/src/mod_matrix_gw_sup.erl +++ b/src/mod_matrix_gw_sup.erl @@ -2,7 +2,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2022 ProcessOne +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as From c10f2a22b60d7531de8727ac01666b59f4c56292 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 16 Feb 2024 06:00:57 +0300 Subject: [PATCH 0422/1302] Fix dialyzer errors --- src/mod_matrix_gw.erl | 30 +++++++++++++++--------------- src/mod_matrix_gw_room.erl | 11 ++--------- src/mod_matrix_gw_s2s.erl | 16 +++++----------- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index f02cfb28f..5981f97ca 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -362,16 +362,6 @@ process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], case get_id_domain_exn(Sender) of Origin -> case mod_matrix_gw_room:send_join(Host, Origin, RoomID, EventID, JSON) of - {error, room_not_found} -> - Res = #{<<"errcode">> => <<"M_NOT_FOUND">>, - <<"error">> => <<"Unknown room">>}, - {404, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; - {error, not_invited} -> - Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, - <<"error">> => <<"You are not invited to this room">>}, - {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; {error, Error} when is_binary(Error) -> Res = #{<<"errcode">> => <<"M_BAD_REQUEST">>, <<"error">> => Error}, @@ -733,13 +723,23 @@ sign_json(Host, JSON) -> Msg = encode_canonical_json(JSON2), SignatureName = mod_matrix_gw_opt:matrix_domain(Host), KeyName = mod_matrix_gw_opt:key_name(Host), - {PubKey, PrivKey} = mod_matrix_gw_opt:key(Host), + {_PubKey, PrivKey} = mod_matrix_gw_opt:key(Host), KeyID = <<"ed25519:", KeyName/binary>>, - Sig = public_key:sign(Msg, ignored, {ed_pri, ed25519, PubKey, PrivKey}), + Sig = crypto:sign(eddsa, none, Msg, [PrivKey, ed25519]), Sig64 = base64_encode(Sig), Signatures2 = Signatures#{SignatureName => #{KeyID => Sig64}}, JSON#{<<"signatures">> => Signatures2}. +-spec send_request( + binary(), + get | post | put, + binary(), + [binary()], + [{binary(), binary()}], + none | jiffy:json_object(), + [any()], + [any()]) -> {ok, any()} | {error, any()}. + send_request(Host, Method, MatrixServer, Path, Query, JSON, HTTPOptions, Options) -> URI1 = iolist_to_binary( @@ -751,13 +751,13 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, URI2 = str:join( lists:map( fun({K, V}) -> - [uri_string:quote(K), $=, uri_string:quote(V)] + iolist_to_binary( + [uri_string:quote(K), $=, + uri_string:quote(V)]) end, Query), $&), <> end, - % TODO {MHost, MPort} = mod_matrix_gw_s2s:get_matrix_host_port(Host, MatrixServer), - %{MHost, MPort} = {MatrixServer, 8008}, URL = <<"https://", MHost/binary, ":", (integer_to_binary(MPort))/binary, URI/binary>>, diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index d3da782c2..e4d348916 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -72,7 +72,7 @@ -record(data, {host :: binary(), - local_user :: jid(), + local_user :: jid() | undefined, remote_user :: binary() | undefined, remote_servers = #{}, room_id :: binary(), @@ -347,14 +347,7 @@ send_join(Host, Origin, RoomID, EventID, JSON) -> %% process to initialize. %% @end %%-------------------------------------------------------------------- --spec init(Args :: term()) -> - {gen_statem:callback_mode(), - State :: term(), Data :: term()} | - {gen_statem:callback_mode(), - State :: term(), Data :: term(), - [gen_statem:action()] | gen_statem:action()} | - ignore | - {stop, Reason :: term()}. +-spec init(Args :: term()) -> gen_statem:init_result(term()). init([Host, RoomID]) -> mnesia:dirty_write( #matrix_room{room_id = RoomID, diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 3231124ab..fea528eef 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -216,14 +216,7 @@ check_signature(Host, JSON) -> %% process to initialize. %% @end %%-------------------------------------------------------------------- --spec init(Args :: term()) -> - {gen_statem:callback_mode(), - State :: term(), Data :: term()} | - {gen_statem:callback_mode(), - State :: term(), Data :: term(), - [gen_statem:action()] | gen_statem:action()} | - ignore | - {stop, Reason :: term()}. +-spec init(Args :: term()) -> gen_statem:init_result(term()). init([Host, MatrixServer]) -> mnesia:dirty_write( #matrix_s2s{to = MatrixServer, @@ -476,8 +469,9 @@ do_get_matrix_host_port(Data) -> "_matrix._tcp." ++ binary_to_list(MatrixServer), case inet_res:getbyname(SRVName, srv, 5000) of {ok, HostEntry} -> - case h_addr_list_to_host_ports( - HostEntry#hostent.h_addr_list) of + {hostent, _Name, _Aliases, _AddrType, _Len, + HAddrList} = HostEntry, + case h_addr_list_to_host_ports(HAddrList) of {ok, [{Host, Port} | _]} -> {list_to_binary(Host), Port}; _ -> @@ -531,7 +525,7 @@ check_signature(JSON, SignatureName, KeyID, VerifyKey) -> Signature = mod_matrix_gw:base64_decode(SSignature), JSON2 = maps:without([<<"signatures">>, <<"unsigned">>], JSON), Msg = mod_matrix_gw:encode_canonical_json(JSON2), - public_key:verify(Msg, ignored, Signature, {ed_pub, ed25519, VerifyKey}) + crypto:verify(eddsa, none, Msg, Signature, [VerifyKey, ed25519]) catch _:_ -> false From c9deea1f6f9a0a52f3334dfc61b0f6563a82f47f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 19 Feb 2024 14:05:25 +0300 Subject: [PATCH 0423/1302] Bugfix in mod_matrix_gw_room:get_sender_power_level --- src/mod_matrix_gw_room.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index e4d348916..e5cffbae7 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -2031,12 +2031,12 @@ get_sender_power_level(EventID, Data) -> case {RoomVersion#room_version.implicit_room_creator, E} of {false, #event{type = ?ROOM_CREATE, state_key = <<"">>, - json = #event{json = #{<<"content">> := - #{<<"creator">> := Sender}}}}} -> + json = #{<<"content">> := + #{<<"creator">> := Sender}}}} -> 100; {true, #event{type = ?ROOM_CREATE, state_key = <<"">>, - json = #event{sender = Sender}}} -> + sender = Sender}} -> 100; _ -> Acc From 045170a67e2d1ed4f4ddf3360eb4503c02aa224d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 19 Feb 2024 12:08:56 +0100 Subject: [PATCH 0424/1302] Fix dialyzer warning on R26 in mod_matrix* --- src/mod_matrix_gw.erl | 2 +- src/mod_matrix_gw_room.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 5981f97ca..7545b0d94 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -736,7 +736,7 @@ sign_json(Host, JSON) -> binary(), [binary()], [{binary(), binary()}], - none | jiffy:json_object(), + none | #{atom() | binary() => jiffy:json_value()}, [any()], [any()]) -> {ok, any()} | {error, any()}. diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index e5cffbae7..18be85b15 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -67,7 +67,7 @@ sender :: binary(), prev_events :: [binary()], origin_server_ts :: integer(), - json :: jiffy:json_object(), + json :: #{atom() | binary() => jiffy:json_value()}, state_map}). -record(data, From 59ff77e171a286b396024f79f9bfe95dca009d6e Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Feb 2024 22:02:18 +0100 Subject: [PATCH 0425/1302] Matrix: Fix commit 816900a for OTP below 25.0 uri_string:quote was added in OTP 25.0 --- rebar.config | 3 ++- src/misc.erl | 10 +++++++++- src/mod_matrix_gw.erl | 6 +++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index f7779b1ef..8a520a048 100644 --- a/rebar.config +++ b/rebar.config @@ -195,7 +195,8 @@ {if_var_false, sqlite, "(\"sqlite3\":_/_)"}, {if_var_false, zlib, "(\"ezlib\":_/_)"}]}. -{xref_ignores, [{eldap_filter_yecc, return_error, 2} ]}. +{xref_ignores, [{eldap_filter_yecc, return_error, 2}, + {http_uri, encode, 1}]}. {eunit_compile_opts, [{i, "tools"}, {i, "include"}]}. diff --git a/src/misc.erl b/src/misc.erl index 6d3491a90..1800ca253 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -41,7 +41,7 @@ intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0, is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4, get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, - crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, + crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). @@ -97,6 +97,14 @@ uri_parse(URL, Protocols) -> end. -endif. +-ifdef(OTP_BELOW_25). +uri_quote(Data) -> + http_uri:encode(Data). +-else. +uri_quote(Data) -> + uri_string:quote(Data). +-endif. + -ifdef(USE_OLD_CRYPTO_HMAC). crypto_hmac(Type, Key, Data) -> crypto:hmac(Type, Key, Data). crypto_hmac(Type, Key, Data, MacL) -> crypto:hmac(Type, Key, Data, MacL). diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 7545b0d94..3591c95ae 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -743,7 +743,7 @@ sign_json(Host, JSON) -> send_request(Host, Method, MatrixServer, Path, Query, JSON, HTTPOptions, Options) -> URI1 = iolist_to_binary( - lists:map(fun(P) -> [$/, uri_string:quote(P)] end, Path)), + lists:map(fun(P) -> [$/, misc:uri_quote(P)] end, Path)), URI = case Query of [] -> URI1; @@ -752,8 +752,8 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, lists:map( fun({K, V}) -> iolist_to_binary( - [uri_string:quote(K), $=, - uri_string:quote(V)]) + [misc:uri_quote(K), $=, + misc:uri_quote(V)]) end, Query), $&), <> end, From aa21aee14bca90f7180b635aa4744d076ab4fdc8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 15 Feb 2024 00:34:08 +0100 Subject: [PATCH 0426/1302] configure: If --with-rebar=rebar3 but rebar3 not system-installed, use local one --- configure.ac | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index daa1170ea..35ee5fce8 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,7 @@ AC_PATH_TOOL(ERL, erl, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ERLC, erlc, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(EPMD, epmd, , [${extra_erl_path}$PATH]) +AC_PATH_TOOL(REBAR, rebar, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(REBAR3, rebar3, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(ELIXIR, elixir, , [${extra_erl_path}$PATH]) AC_PATH_TOOL(IEX, iex, , [${extra_erl_path}$PATH]) @@ -58,6 +59,12 @@ if test "$rebar" = unconfigured; then rebar=$MIX fi fi +if test "x$rebar" = "xrebar" -a "x$REBAR" = "x" ; then + rebar="./rebar" +fi +if test "x$rebar" = "xrebar3" -a "x$REBAR3" = "x" ; then + rebar="./rebar3" +fi AC_ERLANG_NEED_ERL AC_ERLANG_NEED_ERLC From 70e430cff1525c0a16ad484f5a48e9849e8f9115 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Feb 2024 13:00:33 +0100 Subject: [PATCH 0427/1302] Makefile: No need to use escript to run rebar|rebar3|mix Usage of escript to run rebar was added to Makefile.in when support for rebar2 was added in 4d8f770 for ejabberd 13.10. Nowadays this seems unnecessary, and configure.ac right now can determine what binary to use: the sysmte installed or local one. --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index e105b2874..72c5c91fa 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,7 +3,7 @@ # ESCRIPT = @ESCRIPT@ -REBAR = @ESCRIPT@ @rebar@ +REBAR = @rebar@ MIX = @rebar@ AWK = @AWK@ INSTALL = @INSTALL@ From 73f20a8f1e5fec48b9c6feba68bf68044fcb6002 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Feb 2024 13:24:05 +0100 Subject: [PATCH 0428/1302] Makefile: Now --with-rebar detects if it's system-installed, otherwise use local one --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 72c5c91fa..acad927fd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -188,7 +188,7 @@ else REBARREL=$(REBAR) generate REBARDEV= RELIVECMD=@echo "Rebar2 detected... relive not supported.\ - \nTry: ./configure --with-rebar=./rebar3 ; make relive" + \nTry: ./configure --with-rebar=rebar3 ; make relive" REL_LIB_DIR = rel/ejabberd/lib COPY_REL_TARGET = rel endif From 16c9b169f2a035b396c00d3a2ed8a22016588f4d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 19 Feb 2024 11:32:47 +0100 Subject: [PATCH 0429/1302] Runtime: Properly purge nginx in Rebars job, as done in commit 853f35c --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 98d81564d..4f8c4d902 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -54,7 +54,7 @@ jobs: - name: Prepare libraries run: | apt-get -qq update - apt-get purge -y libgd3 + apt-get purge -y libgd3 nginx apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ libsqlite3-dev libwebp-dev libyaml-dev From efe5c764698addb530a8541e6c2d9ab42737370f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 19 Feb 2024 12:13:50 +0100 Subject: [PATCH 0430/1302] Runtime: Test compilation with the included rebar/rebar3 binaries --- .github/workflows/runtime.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 4f8c4d902..bceab94f7 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -61,7 +61,7 @@ jobs: - name: Compile run: | ./autogen.sh - ./configure --with-rebar=${{ matrix.rebar }} \ + ./configure --with-rebar=./${{ matrix.rebar }} \ --prefix=/tmp/ejabberd \ --enable-all \ --disable-elixir \ @@ -187,7 +187,7 @@ jobs: - name: Compile run: | ./autogen.sh - ./configure --with-rebar=rebar3 \ + ./configure --with-rebar=./rebar3 \ --prefix=/tmp/ejabberd \ --enable-all \ --disable-odbc From 081cab821b9fec3433236015a01b916511925fde Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 19 Feb 2024 17:51:18 +0100 Subject: [PATCH 0431/1302] Rebar/Rebar3: Update binaries to work with Erlang/OTP 24-27 They are compiled from their git repositories, main branches, using erlang:24-slim docker image. To compile ejabberd using rebar/rebar3 and Erlang 20.0 up to 23.3, you can download the old binaries from ejabberd 21.12, available at: https://github.com/processone/ejabberd/raw/21.12/rebar https://github.com/processone/ejabberd/raw/21.12/rebar3 --- rebar | Bin 202935 -> 203608 bytes rebar3 | Bin 795852 -> 796043 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/rebar b/rebar index 0b6871e70eb20c2d9e0ab53c1c5278b243ed8927..d0f5766d62a56a3725e4d1e17fc25efe5c3f4a4a 100755 GIT binary patch delta 182418 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�cyLSHZ@V*_546o~5ET7AK&%&H13QKG1g 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

3UgASAr!Q!vZIV={puL;= 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%0AS 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 SRSWVn3z?KTfRJSz>i+<};=iu| delta 181637 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ℑ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

)n1k^%-FADVvfWy4<>LCu7wx8id@Xb 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.fFBq+J+|NkJlIuTkcZrv{{~v zz-7w#D;@lHxiEDXs}=YeB!6$*=~PYKGN#lxj;*slDFRmjt3h8M8{~krG6nrm_ls`F z3qH2XK=S6t{dohBV%>&$gfBQtq)pt0CX^4nxrd`Ca5*FBx{)drPVO;l*^B}g8i|N$ zBHHt7w$P>K`2^_Y;&qud%Y1D_-Qy-lkP~Bb`=aSjfBj*XNh&a^`Cqa@eBda#M6A&-yh7?0zbdMgTBAQ?-RM_xdaDFrzki5 zIj>kp$Fob67}e1jNb2P$Xrk)cw372b9ws_d(%y2KH~^yKtY2*1Aaf=mD`E4U9dr5Pl z4$>7SmsLf`_Uq&9dM*=@P?Kest4nfY?lpSXCob39()iU()P#1s>udY_bOj1@*L~!6 z>NB?S;<^Id&@ej@uy{E1D~0D7&&NNqvOL~_*Cnlk($e8OcZieshfC}6YItvGw~q(V zNB8PmR%dQH&)Kt^oz-e|%73k>!1qu>|L8R&DI0(LeHzAib7|ma{9Kv1|GdR{%gEQ@ zG#>R8%P8Qwec6`W_n=dHe|)uVE>4)W@>$m0lbNT{)xPt2O*@H8kkw@J8dlkM^F#7H z<)1r!-&J>?{L-Gh$x>|*#zb>t#ti9X7pi-oBcxH{u&0za_`}_ zec5|2d}(`0&O8O&j&}_SbNI!fk?=c$MnD%!-v(9^{vC4dS)CBJd2*4MkuHg&o0#Deu607 z-r3Q^5|o`P_HX_Dzn_dlt3I}^#=NjLoeiHD;Lmfow%P)u z8~FNfr%rsSZ_88e`o98#y(nLwCUM;D-BPihw|Do2R|Pm3ot?u>)|+2fHTfG~`Yl>- zTf-qn{aL-XUqda!ZZ_$L{ieE8zbzeVxR@*4qI?E@F!|J{ck!zQ9bKKj3Li0}d2e|# zp8U^29zxkDQG!2Ep2*zZ>b#D6>_y#^!<~PlrWCRqNu-R0S1OsqSBkq?@ni;G%r8R~ zL9n5+7g{U@>CDr)(q~3*%!#=2zms1G0i%h@CIqDx85XFYxi>>^hu%o&&H>?wWzE|( zL(ofb7LxyXI8k2!{@cA2CvTCm4hjTh_WuigvEbq0{$(nD*`bM}<*oPZY&%18Zi(9v zmxL=77rWP~Ac<1>q!w|gc^8O_lH7c)_grr2d!%jmTnYy5xVUG>6L>I-_{xDO-7Ipgs!& z5*L$+HsxP9!qJCvelN$B&qV(-B)QW)V&gSoa%+aunX!_@mxS11F3YLOcqbOlFc#sO z36pieCM~HOJb50bB=eg#wEFvSa;Gfx`(4fN)A>zi6(( z1+b{VsBWdfIu3$BPJ#T3Si8CLZVj~#D5MDu*sDUPYF#A`z z82x~p9Otr0J?GbAl)jm^E351!g5>d|?(He+QRl!oK&+1JoX~(p>j!y~vW!ZGeX5%V z0p=X&6IsmMv?DZ=eS?j$`^^_1HT>DB_Du&k9Lq6oq%)&nE^n~6c{?pOe0%<16DvzL z8y{0Ofxkv4{qf-iT3ZVa);#1dIr8)YrHl)>H+qjlc|Hm;-7u{Iyql|jcRNqIhw6Ss zr;w(L$8oYQqn9)Eu6<||2Hl?%k{;QsfZVz+-mk;MhVd_cf(Bc-llMl^y!h05@H+_0W;K7LjIJ1FXWykAWZ zkiz>W=KcfuSU7mG|{S8f}seGIA8XB%5HeuCb zK<0^4wldAC2Zzq#L9B)I8qRu4Y@%@Bz^^DFnDW2EVk(?7Nkv)>w{`vKOg~p{}Th1;asv(LHie|A_f8? z_!kRzPV?-6LQ7lIfkp$Qk&vgTi9*c=IT(cfMzAK@*9OZ3LV=2h8Y^0imsyZ5>pH&? zA|XN+Lv}I(8c@NT8-^T=o8c;=y&;;JY47Saa>CobAM=i@o#8sf_Ko#f;phF>uH2Lk zjA6x{x&FTV`T6Mk`PnJOpwm?zcPdg?fjC*2o#xPR$+l$~b@Bjg|K=DhaAiu_!Ml>d zoi?FEo{Ul^MNMF-8F%H78t|`hb#|e%DP{#vXhxpE`MQ?RpxYZ%V#AqYJ|f?IER>c| z2yLv%@W&>#6%AGMXIe>=DvgbqGchZu;$ELUiKWy9qsy{XElY^Q&iM4COPW67q&%E& zx57>vr%+{hPhbHQe%U)^$Wd~|y7FW&*eR6qimc~9?b*bIH)@kABgsM>HaG^#yOb$WrA*kn7}gqs?Ne(4lVG6%UQa?3?V8jn3!6MBq#p z%y_mUF%>a=NPwx98oKbTBNro}kDvgw#Za;v%m!2Bb;f|tXT;_j zM$u(L4z}BHzG@_CiMBsW{S(ZV>fz9-#dNf+&~3}+|JA2oDPdxrds59OYHF0K5J>J- zG=AfcTUl;FE!@dMsqkRjOyOjFbA%7;&pEh;A2`uQ;1{Str=W1?h-R6N&$u#ysBnR? zq8HJIl8ghuP{-}+Sxv=0o>-6}O2Dr)vrL`1>~VvxNcA#4XW&{f*3iJcB6-G5IJT|P z^rLI&&X{-xC3FIMf#P0BMMF|%`d3{s^j|`pnWoUB zJV1HM;YYx16XW&jVr+d5(L1!;79x70_!{DpI=raY*xYC{M7MU60j^fE^_Wc^D$-=Z zn#?gBxpHcQ%X-z+MGei-8))k&^fyo~xKPL!#I5VK!ih;DO7;UvBnfDTj*}0A%Es!) zfF!^~RON%`pRR_+!p81KJ&E!ldWk`)@W%1KKZ-$!D89#E-j_OJq1Z(eKO&U_F^YCn zu{hq=8h!F(a78?n`gI1zGwD?kt2Mfg@Qi&5 zvcw=u8RNUsUpGCy56Z`Yf^}L7);Qevh!p_X?eSQfFZ#Kw+Qc>VLYKAYn;V5PqnnW7 z+yU{Qrs8gorGfR{=1waX>w8nGA|7%b%2rm;$#tu~sJt8ptnt)}KTt(B_4hgs8t#9o zK+)mT(XTm?yW*sVc3u5B1tZSHvgd> zZpu1|G6fQRWQiS)iwD`bg?{QWDYgTAI~giFb?cAP6{9n&4O--BG^QWd%H$rEUyR4C z73c%qFxVB@89RO9)ymnFn07s=<;}y0QeJVlza^CKol@s_O!6i;sVL8q6mhOFs9Ux2 z^HeH?BIRMh1ZcWx(MAcC{^8!|Lx!ja z$i!t^2);w4qaLGdxxcM@Y9*E49x0R`qbOZIhehI8BXO}CIF|AIYf;zOe_Qe6{q#yh z2%kfR_AR6dPNJ(Es372_MT1=v6`Cdv6q3Z}3q#D8hb4nA7N4=YUs$?g*~ z99tiUq%e%-DV(3*^F8 zr^<$iY?t|TcV6Vtwj1}_;GQ~J7oWBBVSSUr5^UG$d-G;VprO5r)>CXtvLgB)UY_uw*NBYBHusDh(7|=^itwROmHG5h>PQyk7WJd-Q z4-OxgHc<-X2%;i9YyKJiQYb2yPcbTVa5-aWupAPdl$0v&%*2R6;vC2h_3@OEg>}{w z3DMaHV1E4~8Wm08HB11!5G9hzJCi;u1_!q4H#6zB+!si*Lr1b_c!U>_v@|323o{YIs$Eh04q+*os7glunf32>Sy@)6H1YEds6WGWZmJ zTTJTIdhJJiG%Jez$hg!OO|Z<_yLE?P%~KBKLPTXr48_Gse~OU?6FFw*N-lEkkg4Vu zVvw^5KOct_#fXiCSCzQ|)9^vvt4U#-C0a9tCjZr`&UR3eMZ2Y@$qpMPTQ$fM4;h%`VbPQV82kCP=$1@!aG(GZQ|fK_%$JPS7~-YFMQ1!_B!D_V&@6r()I zv7n}=rj937d}jX{Z!P7=k&ILkz$VcW%~H!S!*HbwL+ArkbFq-*Di{~)usU$Sr9Ig3Rd?}mcR3L#TiHtbnfRpG#zQOG{Df;o$ zZK#smgmvP)n}c{q{ngm$^B02mVUFl8Qp#vMEzX- zGpNnzb{I7XI#CE%8lHz|R0HyXaDoi0n?))$375;~S~@kAo|VVmvwDgtkViD~ zI;Vh9(;AtY^3kapl@Odaiv9L(vAN=>mHsvd@btaOxg*Y$KqCA4>jGByNa08PqBOpM#u>{++JqFLadl1|0(iK9cg5O2z5~&8WqOxsO<^Nm3he z1#!r^*gNCmn_3gdn7xvwv4E_J>$xK_ttZG5UpHe*n1O7>cXu$1*Au%GJdRgL%kYtk*9N0 ztZ1U|sKhs{;K2MD9%k^P1RB7EXQ6+uSFXUn-adi+kSaErv2}Nd?Ky5(U zkf#r#=C-|$X@>$MQ%79iu-A93xHwV4rS)x%rGPB`k{zNVM?d-6Doj^G00j=;5qAVB zjRhpbS;7WP4Y!!fks1meP!OfO|G%j;ycQjwW86E!L2_8aQR<{tv9e7fu5hL(NaONE zEV*sdCJ_F&;AZE59T?jqT8||tj(Tnm+aZvf>isuDjp zjXH8eMUCcdQCfVte5AfSHdvIg>PBs8a=?O}>PBCUEmJu?2Yc<`SCF0VN+5vF!hkYN zoJLj==n%pDcE}&{-w_pj(gy)R6!3B|={C7`gBehA5ZF?OWVDCU5aHBJ+D482dsT~M z%tky7wl1+-^iya@Hz@i^UA_+(xpo6oh2z(Sq|jz?F%HX;qjbl^pkXOCPOgg;;kUX@ z_yOJ1zuT5^oK329cPz-QU~d58`gi&8FC=a3?X4{xlSkJpdt0?K+fgInVtRcSV@T+f z#SGgpH}3DGA=CHQD?&Z+Y1rj!Fqa-nWBg1-kS%16*9)FYJ6C?LIOlZQ4VDu5{fDPR9$`#IUjdrbEaG_H@9vCA5kXfIm$PR~ zhx3t4;;H@c;cMj=nR^cpnDxs%Y~5E#HJ(#vdOib)7uqUJ@|}O~79#gwDCX&CrV|D` z0FWE6*j{_e`d^xNQy*nXnBC8Cpi$FfQ*@(yEe5)vl<;N3D!}gnGyrI7zkd@Zv(~UP zt0&P45k>exIEyFNiX%n%LyVaxj4hm^xi{Wx147RsmOsc!EJAzCy^?34=`C3}17Yy_ z&!T3-k8OGod&+)A4lDkh9wguF{-iIYKXGtHws=hVAT55U2J5FzwJw%G#PT1ze8-4= zQZ40hyh9wWGCKda`vCTsdPyUB7R-hvi_ zF<&zR8AP-kVdx`76wNWqgJ^*#<6t8zXAJTHMb2x z?_bWpuFjYJ-$Q(Eqwmo$MuHS5BXw|hEaB1k@FOJ+Z~M1A{S#xKU){#TgY7Mx;n$Ji z_vK6O`x*P~GiED7lxyF8Q@my0{hZQ$$tGIE!69Otou}_@=^lI_!E(^TR)K4!Aix;QxYyt00<*)g( zZRs!w0CQF2PJZ;*SSJ2j{aecpg}~QRZ0zI-|G%&NM)N0f^s{Dr{D?tDfQ5YC<7J|+ zM?&oOevrZXgWBhG#_rwY`1ZiXWD)M@uv_zC4#>eECwRGy=YYsvDiL_ryy%J8);S~@ zX!jWXyyWoMG`g==3>KeQ0b<)8(rbG@`#X8QQ1zNF7B`!*D9o7Ba%fmE&xbQc4B#pg#&G)|m3gy+4|KL)*mKT!Abv7_f<(!EnH=1$W>?)C(vm-{G0JOaR(?}?`3ZSM>*Dous#f5v@Mk4j}!-DbZ-HmWJHa2(YWN|RQM!9;g%WNppg!`+@nZ^R{MZL7kEetd-7*c^M4C++JRBtjWGU5kTWBh(po7`< z?oHn}3ek3e);Y9vXVOu=-CCznw6dLrydO3=k{90I>R7w$k0MrS+C^yFNpsXG-4w{63Py^eK%iC z1q_-YIrGA;N^jBCr8R0=aKuxDh5@LM$(b*U`AtA8M?GUl8Qx-sg%DCu9$Gk(6p|)h z(y@X`BrRDWY&R^pP#)AEW50<0b6+bv1#9M9ie8h;u<_a~CrV5V-Th09ymzw-8Mmhn z%4JBx^V{3Pmd0!IY<+wW-*VASCx5i^Ot=5@oV}F#($peEEd@!Yb`AXF_YdTV#3J+~rEtn5sC7v3p6-Zr7z!#cwO4F|9BE za;br;sV|BTN5kP+lg0N!I_I=ckOv9IJ%q%UqAUzZ%sRk5x={{9$o>f{3~G6ZT&UDn?2ZGNu#5m(V38Tk6!U@>9Pw?5r+5@|9=V)I*M_>sKqoyMb3#95d+$Q{~!?(o9*Yk01Ygn+TwSTr_V*`}WO`Fco1C z93!EfU$bI82BX3+>!q>_p|8Nh*EJCFJi)D?5 zO4b>5>>{DJBMs7hV4F$_W=|OSMy41pRywnShe;RQ^*fP%4y&m!jr0BUw+yc+{eG#i zUh3-QV?<3*T(9o!1EG`enkIBhj6JzR9LfeDY#Td4UmJlzHb#Nut55h_sz7A>*| zZ-RFZAvA*jKq0s(;Xc$z=TM76Xaa0(?GFf|I1;FW6$l~uDB6}nwhY(Y#XZRNgKp$M z{GFqlDTs4q{l3J|y%>)cPyPr=gL~ojon-qLkFTEHu3(9n-TL^~zw(H`QGRFLSEcy| z@E*sNg*h=Ag=~sCkr2xiPW(*`9*ToZKB|9uEwWXV5*?Has}#p4v8FtJ1V#RXsCLCT zIuH{VlUExWDN0NlK4|hCDjqr-K`Q zk7E`a9ElGe@UwXG&^aZH&?GE_d(l+@cwN)#IyWwExv3NtA3FlGn;1~L?axexUfCX4ot?xJ2$ z^4T(=)~`_gt;-UHE*thUYvbJgS^ejCBmEo#8Qtrisgz`y{pOZyF0S;ytDFi7pt^%N z7|$C?gAWzrcyg^~S@Ok6X0W}-N;?H1(?iw&gpnD|xCNr%+)CP5~aOoC=aFKoP`;XahFy z*#7ZZh9*)Na)_>w{H9YNHEe(FZKN6x#QNX+Ut{8!?IloLW7e(>36$KSXazPG^pn{O zf#}oNyQiuD)0~PtFN$#Lhg9kWM4|+^ugYB8!MOjxhW?dedbY~f7L$4{#ar{8>THpg zVJE8&3!$~bqtSucSEjBUs9K<5YD6?21`WEY)D)_#&&sI%QMn5eU~UH41|n-CtPJM~ zvQqGI&nc69VbwR-36+AR!5qFhc=F7$E>+_%LXvSn&4rs9Dv4&HVd*zj{cPO9y_S3J zmtluy-Gy)C?8nHWx=X}%27%yuG3>!Dmgka%SSqfatCk6|D%=8qcd0; z^rLPd^4A`6P}%CMHgqzG(xqnv3pe1Yg7SW*S?k_feGlLJq2SPkV0b5k zT8LzREe*V!oYipyg<%UP0aLKE@uXfZD9gXLB4F4-E|vGa1Q^}MSX~giu}voYtn>M> z{ji7_PgebebEgjx0UCPH6?phMLq}B3{tQ*hPG{)&NypU^My`$#zYBPTsiFEGhWMZG z!DCDgW6tog*~`}oVzmCu5I02>11-j;2#_?1Q4`8qv(d^xBk9DgHJNSYpkY>%eB=ZS zGok$+5tu!~sY%mif$NGGmt>0?mt>G@oGF+ma+;)SresZ&frIF|HxmnTF7!Yu5_K1- z`Oi~O?nuLvq}sa5fT>vY37R`YQ*13N#9QG6G4d!GWH}7T7JozBb^q95vieTN>`co@ z84=ur#J%}sL`U|SxwH+KOQ+#)K!sp z5^Oj)x(fMS#pyb=(bnQ;KTSkLOa|knT$?~aYrsc;qpsrVx)?J5NH8*Ga#UerLC+0g zvE=qQA4yA}!{owtU}m<&wJ`)k{|zF9kEf3tgFvwg!1uwYH}jSc|7IGT75!*(7oRkUsoAyL)BR}@h*lojyCOgOxn(% z3H&S_5+gHS6&9h~0wy0{_CH$;N(AN)_3`8tZ7h-Qyd%&^J<8Bx6sxIyiKxY|$tI){ zWyX0wAjXLX*IaLPCgn`!?Zq>@VjkJNd{O&8xlNB@Q9Ew|>LBj#Lt61 z3neDwfO+Pz;$jA~Nzb;qJ4y~y&oVZ?%I)F({`|4;$;D}7jWS@nY-U1rh~G=m?!k$m zh*iFsazo3(aEr`#LRvI`w^<66jLbEK&}henY|s3gtYv}A&K!=zS_#_z4Q5}sSozun z4XB$mR;f1g@W&Tt3+fxw0k!->kT`Cy4&;r=Fl{%5c$p>Mp${`cU zZdLY)!n94lG4l@pMpc{TJrDBSV&$xJ!PKP0Bz`OzEMU5!Wl;@|7s~^!I5tEhH_wAQ zUQ$*B&AVD)=c|y%;Yz?k&7;_c-o=Hv<4=n^O1I_Q9X3?*qnr-jRrv|_P!qx1GYMqL z1#|aw@q$6uqAth4B$|>+$AtPw514RORfZnRUHjd>sSlC#xGQK{k`b!M z$Qy~h|EVTi8jt|qxP%D$f)>!1&jx&F)eUFZg#8e=j4d{70B1MyS561-c0jxp0c}p! zqzp#v!d$ZEs{Bpt2~PyM{mXQmq+}c>9hx^ox2Q}|wuxD0+8+*G#Jj@i@7|GucN|Ld ztZ|;m+Q7a}A05aanV>zAHV_qr>+e7MxGkLQ!zu^&dB3d_v9e)Xyl`HpA=CiapzHnp zR@3J`pp4ZJvLS*zrm{iIeG)m7K`xC0CO^^;`%l&evz|XP5laaUI+zA*dM77&f<5+rz^`p3H}Wh?A{pyo2IG)5rO&jLCV_H zZsrEuAVQLc0fh>U!;Vtq)P1Pb{`tyHd`Zb?6 zMakkw0i77xw;BE&FF;*ro)RJti?#ha=X*G_Agtfh_lp*Hx@lpk-7&LOccJ_0A_V-_ z$Q=v+wO~ZE)qH}G$cS{as^gTKXeTRc^hy|Tyl&bo?v-^HtRbh!F4N` zds@^avh#Cu+n9 z14VcWR(_IhNDF~hL6A7pS`j!+%uw!;6nFmd@i#m7+^))`kHI0iT}hmTNY|iGc`?v- z&Ot7-uy7M{dGuiBFgawmYo{PL!X$yRR{@MK5tst-rTM6%P>Yg4KB$6Mr0#Aj^Mt}| zikSA)rh>IPlTrW#y9O5MsXu5=dQg0Sm>HjiaNgL^?*qdIIMye>5_Mbcr|-%XxG~># zR0)!>XTK0tpPqNISD-{-AmN4m3*;KnwI3J`h%jEx!hk{qZdG8$s>hw>)P{Ymr5(NoQc-0PhwuvZ@e8k1@8_Py z?>eQhYQeD-7%jX*3&fwB$DjG*;KGbe2`V_aN+@$Cl3d`%HHwFR*G9z?sDZIa)sso9 z{*GMsE+5*%7u&9^8oX}fZ2IyPBT%v~L;c7rK%iVH`;;S#u|2?E+OFBrk(w@eRHt^u zix5zJw~5#SY9g}o%Y1{4TE)i17v*{j#9w|M(aOz1p!%Rdq7BcuLiD>0KviJ8w$xg^ z)_CaZR=IQ<#ONw3t{V3gQ)vpKSKN~&f`9q zW##aXz{SqtRxepr#jEXhI-U40`f2OWzgN=m5UC}+>kWoubA@ZFqk48+9fubj`p+iY zE$`|Kv21U@IykykJ^$_}Lfk{7^zga;vv>GBO`jsbipYE8e+@4jtz>UHxX$&fe>uOm zn>)S%y$yV;i}xqTV;JnNx?T5bT%Xmpzs=qr(z5_oft(kedX;o%qTS}ZtpI_ZyL~q( zd0?~iLM!hT;_kq|GX}T@LPSE%ZLDg(&PILam0QjC@$tCt7HxVMIQV^0zus0irAHOD z^*@%7s(#IcNH1eh@K!KyGRZwmwNnUObkzZ>o?ofvIlx~3tBtW)_npVh5(l_iZCbBz zlkelO`1<$V{jSi~S}G*9k>2aRie{=at;NNCI6?kQu_GKPt!5lb`MK&G0p1Ul%z1gdC zO{IbpmNmTYxBk_ZUh?zW%z7{-**+ilcf)H09;XTNEKUSH3Bdi|3zx`qAlp~!twD4RcmC=tzKw0K(YWhJT+z9TmgsOedLuxx6!W3L2R^(ZGua+$Uf14B z#n2*o#Wz#bFEwyOT69f9cy5}RVDW`xS?ONsGY&T=JD)4whG?9K$@F5 ze7g^4N4Q3E z(Gzt1Imh*dQ|*l$Q6uq{OS4Z%CaUpa0&$OawoQ8b`4R1zKA9Wz6PLw`I<@HZriESI>Wt4igT{ER)A({fBHhm1K=g^npCjkR!pZ98&dxZ}w(2AJ z;Dst1KueXse7XB0-{vwQg<#EIGzw>9HC1TF$G76+;VI}eQ$W{x&Ud^1)A6N39N_xY zx?GPMsdeS6n>%X#>r}bS+f4kG7~b+-d3nw5u0+N6X(Vc8Ya)Bjc`vl-(Tm|<;Z`#i z+PP%|#wB{}3UOS(89l2EKA{{{N<6w?L}9NSP#G6ARnSynH!YG`B&CjRP*D5DtSYfq z_NyYQBF{{*dC~b7zp6Z&B21Z>3eJ*nvl3oX=B>g+8UHV^<^=4#Wd%2dv$D=|xW(>8 z&r|h9wNqbenTsM<#m;i^GJWN*@Bb~xwfoy6obZmor)9G;xe)B}_jw0AqV714$fhTN zEOoA0r>(=TQIFg^kRzvQjI3Nv0jJNy_rVwSM9D&Wr;+w#N<1&BbsTHEDA0+ zN85uxBbI%Zzw^p-b2JO|lQNPtOsaA<6ck_$&oD5^L^N*1%DK=y%f+~0VZZ(F7T6Rx zHhTJ(P_O#O*!&kyO?B;tL{7a7f(ZX8I~6;&CYJ;Kd<>eNA^O{_*b@dYdjoO(Esr{sW2 zD-V*Sg@zF3(~Tu6V+kC#@Q$pBVzvn>%abYwPTMVK>nWv6#oeqz4BA54Dpp?UMhGkL z=&svun+M0>-|C{PG9e~bzybhDA;?>@9Hiyp{GrMlEESH~fT1+P0e4BGAUUYNVWfuf z!;}$XDiP{8J0$3;H@q<;jK~h=-y1DN6LtilSO!2tev!sF1PmLY7J}o76oXKk8MhG$ zJ)r1vM$j(~uUQ1*2&fAxAz{QLV&qE`h)YqEWToybiimBBN9O2Lz4`(tkR+3m_Ur@F znDr{WVngoqA({?BN4vww20_xi?}uQ{?eKut+e)TA5{pDo?;gIOOjv1vz&F?>Banw1 zWLPNEkWixd6yhRO@gha9yDk3pwrsA{bvjU5Auu5_5ZL#MzEYnWpCR*((TR5M3mzd^ zM2vr`?GA~)T5`yRu0Eg!+ME8jdEmNrU#&)m4!Px_Wwl;F&P}7Gs;Y}>wo6UF(L%=l zR7p#3@9W7Fsc(V#%C1Q51D@Q4R%+Vo?PB`L37zn01ow%L5Z-M3jMq>XtL5A;ktle?wYGb(bzaCl9H%R~Elxm}XJ-oVK7Ktgq=w84 zY}vyaZ!u4t)+%m-k8hGOsJ68 z;_%>hmfVC)l@XxRB|qtqxu0k{_c}p)xQkWKbX})De(KTxT8(l{3}7LKVdpEOC=CMu zq}xIB88haG!42g=a!V0QM?c;#Yyq$HiVUo>SeiV^%)R(8xmuAns-!t-ZVK$Xl zEh6k*oBGe^tp)3BUq5)=kB&RTUzYPPr1fXsR{?2Wc3_@K+q%r0UapdP<;}9U{l0*J z%lBGkDpX&G4+-06FECO5Yt6aI;p@W5T#oY+eX-a0HgP4>&gR&1t?9F~)6wcl0Omp+ zu>2)pf3)44-kLJ7Ezp??FErmY#sv!R_ohy|I-{_2+Wzpz=?}Oq$;tp(TeZ&Fmo><@ zum2+KXrTW>>)#;1k=83{q^f*1y8nawqr#;!PC#P(_dY_~maV}}g&fe2ogNDvF-`X>e)l`rP@F^A}pf@I+-#Xbb6Jnt0 z*sy#qkJc$SGyD7MS6N;T`rRCUr>I_k2J@y!eWl5SUh6lxe{ATUo(jyN2sB zS3;X44CobBO zf15QN(CD@mBZu~fZ3q|rX=MD1Luir1iYiy=T!`inc{_D&Pw%H%v7Z)=re*rT;qae^lFNZQVW}GPkpP&L5LqOIdlYF)_1tB5=)R= z7691lg1#VoN#`Y zu-KR3$GBaqhw5EGSg%3c@P~}5$!rW=zNPLs1wg3amlRWxtHKTK;wNT7BEV1xOz=c< zl>A0Rn}eV!h~3pUz+YC0P1vihXm*<5$CLYrLV`3+Cgz=h3MIARqq^bazak97;P;I! z?$=xt^6Hl))vuDGvZiZc`}lq@rhVSCRIZE$T@7LvKOuG40K#lxw8&IJF4`KU9}3gN z+5kB`Hg7a3jCz0+oNPc8Z}UB@;tgS3Fou((iB4HCiF#693#H*VQfuu6@Y$pFcw^d{+~Fju#%Ib-FMpzqsABY* zV3~(J&B4htmyp#f^qRBynfhlU7zn?GZ&@R*M52{YF9_qpjl`^#!pbrlBY#cfsO+_% z{j7^45X~G=z>YhaajfYBOyDtzM=w(6P(<&E*S8Df_*O;8HM;%`Jj8V)1wnh676Z@s zOW%02i?2~5;HVR*#`AK8<(PJ=-2<}hY^%U!$n!qYVLq8WQv7SGt>R|?s`JSXK+8AI zj{Id`3ML$aZsz$}i?A_?IQTcYBFyu*DS9on@J?3-KBwTpKaBxI`Zsuy`xpnq=nohn z1iQ^6K{dj8C`4!RDx zPKgl%6X6iVxW5ZNr0>u_naApsgd4XbCF{Yv4lHJdxK0W}AGT$v|DN)rmRTDl>!~7!2o`pE8W&-z8x3v~a!u zBc?s;i)-ix4L5V%PKsq^BtY&=bJw7Hx_O~I*z}9V6|cq+;>!N-+H;CZ&T9`$>)B7e z&%=6{yPsa&#v{AA)^42woUeNb!}xZip*5K^I(%iepI$FBBO~tuT|Q37Yb$!d$MRu5 zNhb{o4DvVuPmJhRu6L==)hIx5mUu9Ohc*8GSLv>iqo?_KN*aS+7XhEf4zUZ^xS!wi z(!1;Rx}6C zu&c-L&ADdOjsm?nxih$gxKFDVDl8-z#>UMudYH~Ig6Zcx!gYk7uAVuPZL<|f%Kx25 zGe(FReGFHLa<~QD>>qA_-M{LSS07$wsV~DR3TyrnzgTaI*ldceH%Sr-m_P>NfUNtz+a=(k&Y3&66D*s~|kOrop-Bblp`P%-5PrFTf>OIL6^%g!(NUtUPszq1IllhpWTV zGq(?`_u|5TJ~OcOmYdn9oEQyDJ32>Oc8&bl$cIGFE&qw>~mHF)oun%ue86 zk?kZ;7^RsRm|1em8%HC&Noud@ym5LX^hYbbIleG^!?!2x4!mA2-r;>IdLzHS6CAuf zgVx@Bct^A5#i)#k$tQG8Okgrk^LL_1+oDE`?+eM8|)=8R8xnkgeK z$ePI962kdum1&M(T3YxkL6tUy!3yJZ=u&xLo*d`4vBkO>^Or5Ashf34_#2z9U)!1S zRh-XHIA>FC@7r$OSKS;G*&eR31+<;bfWZZp70uBDmDGe25K-`lsd7K2NNUWHOx2;e zt5a;oWFc8CH*G9M*w~gVeJjDzLAqVVmsZD>#&$Z}{w_IAMN&<8SjUAnlc5z|I@pL_ zyCz9n@+d;3<`nb+F?X-iBB zG>T*Z%o}ttU{s8oV=rx7gUjAotdwtJ^2dmaO3Vk6J!7i7&zg6uH%Z)YDjfLpFgm8m5V?23NjY=5B(*9?tXi0gUX5iD*fmHr} z%S#El56uplKgC^e1Zz8)cHfmmB*P+q4mE{xyAHD|P+=mjYzbP%Z{&zSq!}m}CYXmY zosvlAR+S;0oWXJ_;X*4RUc*?j3snk(#i1#qIx=ywWE7IXC~N6|Y38<3rBZ)<$TE+e z{fBXWNeN0*8oVXNB|IAYO88iSl%NkJ-kWZdkcIE^}4H9b#M^&w)BHNn#=8-V95QC@+mx3GE<$TE&<5&%L$YK| zKS+ruYFR4e`A61{UWF#X5mNEpG5N|V5OIbOAea{{d0O+eT-Fm|waZtrtTrOlte*Eb zqP`%`JP>Y9a-G0^NUG{>R&=+r<1S9>q})!394bX5=F6P<=X0%maVFFLO|e-mCbMIv zAxn1M`?%giraDf6^k^wl&_rA>R#c>7D_&s|zex2{fo&m3%iDBOka27TYsl=ysz{n1 z04utLe+Z;$C}KrWq_UP=-bw@`?Fov%a)_=nr-*1z2^TKOaB5cZ-3TxcA|KtX7NkQ@ zSW>E~+1TZYbhnj@Za*;LPDJ3=Y)+U;)}~rm^R8&Ju{FRzmt)t>t@@KeUp&5-ekxku z&a6jm4W$b0SpSbHGbamfC!3t*YT6P8Aiha^q}b(D)ryo&uyxHuuN0*`4LO~xj-Wj^ z?|G9ZOUAT&v?dAT;gc=~Zlm1<{$}^}`s?)99}iB2$97m?z4(CUFC{$WWHiS}vz%=p z2nyQ_eB{iey6$uofs8h)fJHv8!2)elHa3S0zVVX!A0<-$ZdalsUlqlvh?!~M^Z*|Pbilmg5fT3C zNMsDZrKTjO9D(0cY+S%rxd&k}-T<13!yNKPl0v0n{+l|%K`RJWOks^TLQwRMBogx60h}%lVbegS!VAQ$q2v2kjA2Jq1z`@9jjzS!iD}2cb znXL8^=RtI+$z24`mjvjZ9~Pg*o)|pKWLlpz~LZq)m45^vU3#-}5=UZkL-1*8j7`x3oj#wOuCj}V_)slp0os4&R1BTq6H(nKp3l^v9 z-E(*o>_P~A`ECm!4j`qG>0>N9VB|pm5evYB(^27W=VGIM%|)cFE#SudFmtqKBLI{zPp>>s#m{%1HJG?4~U!_!64 zbuBE`o1uN_ceqY!JB$Xj8mV&}0Fdj8E4whvLZ;w(Gy}mueKnCSC zW=E_u9KtZUKl&GPK}z(i4hjWog@%{?7bPhCKVjCYHtW!+)33klg1@DK3SjdF{%ycz zQ5>@A_cW9R5UNgY+H`BdHspqt_bF*0lJttDA*d zV$iJN?$rdZIAaND9&lCUhnVV_k!MGY#7X+!31Bp<0x#Yk=89_xkmd8SZLE*(ACM~j^8uqh@SE&S#e>|4s3k;9q z6Q3^tpghT(h}@fd=B%q}0gskSZ&4l5IG{Xu^Ob&1C8HeAoEuQ)5_K$LKW-X%eO-S{ zi}bA#I!xE>Ws$6=$LYakIq~_%#5M-nPor3eaUu$A?_sd+@WN#^l1_uUWWj*(;LKYo zC}6C;3pb|fW7_xIEkUXKQxVPugZQNgx8KTWUw!0LH)BfIx*M?YXl_% zShh6K3YDV^(1jg6oNw{RBS$Wt@}oXS{^~Lu!;ceofK8J%iyUT9T?;cv9q9LM%eGan zQ=k_)J8{k^Q*VzLXAZCd1^B-2Jl-%xdnRLPS1Q(icSN?GK07RQJfgUhgmb`QqctU* zm%thf7&+eyfctSpI{DAc)0S($7RwF=q!7cKt)Jh>`ZCm)W&gDv{%NqB4}pKGAy>!B zDmP+Zkb1-x5%E`in2`rko-!H z$Sxg3_rQ{S&kVtUa20zVU)oNOixC|+cnD$Ji`CYxS_fs28@|gPYxhrIFWsm|rIEw= z?XzvP7M62P&o}v`)zrg)b~b-1^j-Cv#}Y`XR6~(O%I^bWxO!~3a!Xyh)q^x!$U%Ac z_g_HAhsp1MlJKw}BIJC&vLe@70GwZH@zn~nn*Xe(Lhtn6%JQN%?5F>D4Mr98;hMqL z&dr(6A^$*+VjWG`k05!^_Yv^(Cy;J2;ce1YwclBRyGf&V88u`Bh}Ij^TS3 zyDoP3i=cDwae=JouFTE`=o_dX!_c*B?DpXszhuug(JI?ef6@k@uXx~;Ol z+f0SN7KbBmyX+jlpC4)rkS3(!^+f=w{ri+wd86@pQnIZ={6Rh-Jer@Wpc9#O{d3_Y zLTUhYz`!sOP_VsyGEVGOH+@oi$qx?Vs7dNRJS3J2dRl@fX2pd@AebRgSo1;NS3XYV zp-c(nIbKI)o!9=hNmhKBs?_RRv}i+G+yG%8bQ)U^kHUe+)fzPcTu8uhuLjm7PQ&CF zxb>|efx3%-{BFi^Eg|Z}1F=+J6A9RaN{2PUe|#1ZLN>|u60Pg(b402X=lhf}E*28} z`LF=Dw2Y5BY#iVN{;N>ot!tDZGoO5QlOX>Nte;<6P4FlZe|s(M%C*c}?$FDeZ#x#o z6=7&J??Sa2YQ%fRNN3I82R)O z7GMJSzTe=+^_<>dd@q6|@{z3*4+ndx^?D!vS;QsiDMQ??A?_(zHzVTxeaO!Jy*NWw zF+jWBj0}(jw?+8=)G)5cPTT34m+0yvRB&O5)9@i}`e7u`_9jNU2YD(4)pb53t+I7< zbIYk`gi5&qY*XV^FO>YlDuP?>Lfw}QZ@#0gFRWPMiCmwdtxu!Wmo@UId0{ZK6*Z`X zy&>ba;pRV+U(^M9)eomA^vfGXqMcKj)sd~5_L8mIhN&7}TG{-|Mh!(pQ#_x+c|9UN zT2t%ez8m}fD_k?LiG~Kg=x--Mx{j5FSOEi=AzYLIqApB?Np9B-XQn%(GGc2 zMiAEl;`j(>U)+`t)d*)wqw(5)F zw{aYuYt*iBT&e8)7Y1MU8H~i z1((Ht6-UuGedqSP&JKLM zHcmZH2O-Jr^|=<|r$;H$!3xwTj_}JAY!V<=|NQ_w>8V#yp53KiPc)lOe|TF=wu=YQ zMX{3kq+fOp`lA0c3{*C36Q3Zzx>Y`375;k;J`}~yVzJAP(t3w`R9H_tVOb8?7goUm z1ZsMC72fSQ{k``qDG2Cpn7}giPaZW6+U_~UHNbm_La1NwaU&!qSe~yiv$wSHa%+KD zIfZT&f7TlG=cfXWyI72oXxQ9r8Y{3*k&ve2()9ZKnCmd9`uXfm)l!x7Ip!LFJ#*XO z6U7HRWHSiy6QgTnQ?$%lU>bu^&i~mr`*}OS2W2QTz5(I&IrrGf)*iT)l>lJXxG#jeVV1;9 zwt7aiad`lQp5hQ0r`=-Qw9eSEWL5_W0Z#&I>$}4pKfzsoe;G!6xs?TKo+^pefu&u* zi|AH74zt-*oBeA1D{Czl3t(NuaEPh)(jKJFoG{Yb`~nfutswi$ZLM8Fuv1?`InI?l z-+sUQkrYnsn@NpHevMd3^-Ny7X9+(-=PnE)5M{2$NY0JmY_`V*8jF3s9_Rpp&k{%U zA|aR?S=uP%rWaU)HfxCs=bO62#hjP;r@&9n6(4UlWj7pR@4)?B5D3XhhD&49A8TRj ze|G1k0A*&?+Yr>pFqn5IjQcms;2f8N-Y?u_%?WNJu1f6P;aoF7UNa0kX%focNP^$1 z1a@bB5&U<&PfqbrL0Q2cr2wVDs3Xo(Lqb&CDXoggQ{z*ELLLvS*g>g)BZ5LqA(asZ z)uAx}(=;g$oA00jDBA>e5#1`q>%xc&T-5)(UR(0f-`^DVc$%I` z{?qPsrNbHXuRuqUH>7-sWx;kzg$g$uR#1FA`;>P)_Z8NF=u@H$=bRywEtZC%qy)<> z{Z$ZMhwu~pLEVTvEWsvjW;lv$IARIC*h1KHPqvxB6v|8+P-rY%+xa}A8q?ULC4>54 zNfNz}RYq1=$u~>a3iQPnoq8vrbU0R5Ho8f#q-084lZ`hn$wH3ygb?%h#4*$Gg*lME zm!{e+lcq%acfd7&5%$iV)HlAiy`wZdK2i`?Y-Gv~^h7gdKTR%MYsN~_EK^tL>6>(1 z{O%199Ww|6Q{LAZ2S_*GK%5Fu;9s(%A7#sdvQQz?}@6`Q4vgu zp)0Pt9{#f?pqa5;?lu%HxUZFU%M)7}$3S4OzCpL10Ls-DtTxdrRvI94vXF_ObZW63 zb=)0YT9QOI{Svx9KJmINWWUm zpCn*#G$&e1tpy0wE(>-%ON?=nmEBv{Veqbn1S(;qb=p1T)n|nW?zB=Px?4){6_vuL zp8EyhycyE6dfy$HuG(zRP3|hH`ii)ssMj?b@%cJJD{1e5MH=df5*KdR}~h=lc>VaXk_qFYbz@@ zSD9<`H_&c*#T+io{r+M%a_nTv)n7YMZ!TQbn)T)d~qi6 zil!J21;%PR0hONH$<5%Q2+5C6OqVKAWBjEP=;CXIcPM&Ena z+G{PYjcLZlvG>tOoAY{)B~U7BGV+0chIb}{UH$yiCwqZq0fvk2GIkm^7BgG<^lc9M z%>C^w$3bdz+^Km!Ap$RRlXqMiSk_rT>SllHBeF&&OWG62oaG_ z(=P?x2Q?M}2|AczGRC+BV7n0?w0nDPtJ%0suR)>rb;6TLha}`|z@DyXuNk2&=Zay$ zjZ(j`M@TcIBe?<7J%8x7qQPGVN;3g*O{-9EP!gqB?h#BHtS;&X zk#31!eTY)b9UgGB)TJ_u#~M#l3{`Fk{TZ7SE=8A5rAhn7Br!r$#2rj3gjbj($=an= zn?&PYy+|2z*~clt{HR9I!D>^1a3;m4bC}bALf^9yS zI$S2ZOp%<@T}`6|nEj=xL73{KHn%9N@KdrNnYvxHQtr?+;?il{-8M-)O6jPdrl9DZ z6G2j?fm!HMh#}Q$);snh|G>1{^7*YviA-7|YTq7&P;ZDW#5Y}>Xu!Tl~? z{`=Tf-BtbA*n6!R9(Q$=oyQyP9gjwV;n$2>n z>Hi*jJ{*NMGAxS>n_B<8)Y^_zG;rg~JuKll8b<(p6=h;WQcn!Tz_sK78vNpUH z-7mHPGp{f6)tl2gIVW0k4v@6!ZZ~jsPQ-DWDL!0c4CDN03S?to515Gw(qxn_X!0h~ z49ZF3N#b5b3iiF!#=D+ypK(othNDx$LEJ`aTNc~>NS zGLV%N>SoOAQvX$TjzD;SUyQLnkn_snjZ3%yGC7j57Gwjc+Q;l&()mg3k5Ij314yrr zc8@e4l6uG-WuV`Yy`{RweD{PwL|H)L8$yHbV0i{Gt$}qLajS_T5`^hggd947DGXY_pL=G?q9HLY5OgE7wLr$Tfx90| zIG3OQuV~%70XxH)E?5Ig{hzqn@IUk`&hGm9<+Xw1x!Y^p!h+DI1AOVKi>^+C1f?&~ zM9B^x6rJeFr+3@7y?0ZSUSyQl+FyEX#{N3l7_4bii}5F(jMPwxg0A-* zOE`&BEA^pwUhL8$@6i{m=m59*wud-l+4s(z?8$#+m*)P|a^UwUB$vmo9=W#+8EvDu zarTnGSKakiv{BA%cmbzA7Bvqu!Pc(>YRu+uidmK|)0c{pc zh;vyBn@~~jH|?!Ydx2aeGeQ~OpuE#^K%npoX=6eIQ;CbM6*S!MKV(bDbH>oG9{6N(QDk23tC}S zB>7=ynio9b4)CGH$u47Ua;YXF(u3tdY$MX8{!+~Bk>;TN=M$_GrSl`x;w73_s8Adq zH&!0}RU~&Aq7Kmbh$ZQxok`epNXaRGZlGI2ng446b&nqm{Rd=r+F7D|N1R7r{!AKi zrD&s6xe>oVboho!AC-@@5%QkVG$V1eVX*YC18*A65JY?w9c5)oiPeGRU&orHh>xP* z`&uej5w+O5sJn3QQ2dcesmhJ;w-CxSm{$6jI(%~*D7yej3{ipj!_X>Mmkj!+5`C3| zGNdAFP3L4ZxvEs}VJTU)W3A>qke^t7ZAceO%rFja70X|5QJj;7M>rNdz8OC zpl#jGU~x2}T19oa&e8HnJW=~Q;Y8qltuH~;iAVzE3P3iH@;)U0V#Lu$F;TN=osfi7 zh`K2(qc;KK&@Ewtvd+sPIAdLL1Nx#EOexQx7>-_QFtNf%EwLWQLBce6FZx)wJm+4Q%6zJKNJ zGU;mqI{AhAee?ALTl?UMRRdCpOwvr5S>_XKL+&;WL~@&u3#6=3L#*3!YcyvFTbsSh z)!G12W!a3~3Oi5yvXWr~W01npE5F-})+JY5v9^1(wVSar@}6pr%jwHMm45LxTVcj> zWZ+xxY3{e2p5!;R^XJ>IzaJFc*Z<6%>P)HH=yO&>QmIdd%Z>P^iFds)ZNN$W@DE*O zX0{d}4vf=!{div;oWJ(#c&bLn_b=BSWa0u$eLfh@S$yI?w;t;F=>*q-9l-n^KEv-W z{@#Bh$Y*zls>BG}Ypm`6Ju_$ma{OrW1(US3Z*I&qh4K{O>-|}+1$@?{^94SQhg$0$ z^AFSG3_Isuq<$*%#QFgb^StH!G1RYlpI5m9;r{h@_-mhE$xm_j{Wn)#{{4W}?0C(e z8=9Wiitn8=a5vSfcDoLt46w&5Yq(dKBnYxG(f?d-9I*JI7KIX4m>Z!Ug}P^`T=2c3 z{VX(3U-e}yQ$qhUx||>2=;DPB0@A^dp1=u>oxaoviv}QAPQ6*6ugstLDyXs1HE>>E zZey|EZ|b(w83`*dMYX_`zRJ*$(@)xPaOdvz3c`t*CiBSd)iPT*iVblQ@Uh^4ryA<};i4al6XF{xEc&xAyg7xp#N_*0;W{>;6Q(a4u|7pz`Lj zySkl1tPpT#S*Ub&3_p1zU6xbVm0apqc9Et%u)Uh&E@g9AJCNQCs}(RNtv%V2Cz&gRFxZlrW<{MvHLaUU z3IV@!$yCQyw80!#t2+HkE7|7Nwu<90s?igTI#n1?;STA(6}eLNHR>?43ir=K1@oBW zg#TGDUWiPdOnkH~f@MYhN}KGiEp=%;3U5j!xa6xCX2jSQRX4kjN?2GwuqrigxeCri zN6$osDiKoXN|m+P(r3%$WjQFLHWJ>Ep9M5=Bjm?nH9NSuk*Utakd@LMH6`kistOj6 zvYBJs#CFMn2cjW}q3zoX>W4c_U}8fuznV_~-7HG9sz2uFzKRP@n&^6U0t~92tOQjv zt=wP9Jur{Zh{x5y&@Yn6MLe(;Gi{t*2FzG>Lm)c`wUpoHj@Wz_xmH0;Y=}Zsn*bYq zE_Dj%Gnjrv+uOHsD-ArkkHzR9C$UoQo~)WmG9zFGSR>mUJ6+RCU3%tsc58(4$>l@I zzIQ21j83oVG@P=T7k`7hHZbrqgx@c?h7Ylo2xn5Zf+dUgNL83A^8F(f)7PU3iV-sR zBL<%(w350VSBhA(aC4m&5kVFz$pB^fQSWvEjq$5mSr&RX%%{;bwR)gBN002cS^nE< z6L3g{zO(|I`8%WBpVnNO>+C~lndc~-Y!7`WlKyllp?r(AbOW4iMuK7&ci3A~3ojTcTw-0_0xnD&%;hb@=O--N=FWe}k{BKWwOH3P;@ z-3E?{iyAELYC0Ek?(la+I>J6RdO0;A6+n}LWqia^5ogln=bDlgKJa~fZckC2^6m8< zUi*g;`P}#UH32Tk?!^oKdI0$)2a;s@V=&1iTd9f?3k$Qr4b~Q0RN?0ak-jhM z6sSiATYQ$3|H1<50L`kjD|U*@?1j>n3{O$cA35R`5iXSIDkR(FFqb91?WLFeE*CPi z1>+pEv`_lSnLllHx6StbW>hU^_M~FGjIBm4CwOH!k29RD)&O|<S889GpMvuyxax%Weg~R>Ka^j z{eAe}S<>WoZvxOI1l4^>6$bO|DOtBjF57O(jg3-TtaY-gJFCB;ja9$JeQQvXp@36| z@I!69hEQa=Dga#DMMNNTl(f@3t}&018o1=y-hEqxy=NrlRfpDyojof8Ti0K<(AkzY zd=uPAhxTC;AhTo#8cZrW_9Pk%F&pTCiE7ui^H4fZ4Y}S3pJX>7rwJ|ipgoKubUr9i z@c_Denceo`{JRblC@LRA?z(vN2|A;@IR2GI22;qx5uo#!$Pi(+H5{Xlj1A@2vS5Lv zsY3qf=Keo*X_RB)0mV1oY->8#$OfX z5f#?AHpB4dw6N(QmMi|^R~~6+sdgU%_C#3@N)?SkS=_Wl6-08(VP*@GshJe($pG}6 zNwAsZBf!J4ac{|EsgEK84(oQ&)QUzU#%23DAJ$3Pf0z0Ya(Vk!k+`RF+okhL%v_IK zSuIb_9;d`n%|Gz0UdHNw5g-{03`OrjBlK{&kliDZcnZj!K-_~S5$ETbW}`TD!i6_7 zLEl-%m>35S}_xRCI)`t+owa}nqQj+z>O zwE#v}!=BmoO~5fWS*~H>C+ta%50q}ufQF)x+ZY_seQ}$oC?Q^Yb`2a-T<`ioM-Vib z^CvIp61$)jxnvV`YkT)d1@O`pnNo{Qlj1>cNjF`UUXWXpr$o7D&=f4QYH6&Hn1><7 zfELTdx>p%X%t&kgR&waWB2~zNVntYS3(=t95XlsB+dDK zZGsErMIk*?8>@V}4)nW|E`^!TC!3X9j^0#O!jUBo_q(Y0KbLsw0m`5b!mf8;sOK10 zlvPA$eKS-KsXj!(i`Zzw-^xB-_n)W;?m}{DbeR_ zDN#kOl`%XU7oy#yOBx+pgpZuXI|~8=m-k-CYMoG3Pan(k@Mpt)v0MN9>$aU3oA!#B zumwONF>xlz?8ZV?suV|iLTyEykwzgw>>J&PsAYGpnkvgiH;6eV- zDn#obk&4j7Q*#izYZC#D+|vfT`WL{ z%fh!A8hlV#>uIr+er0EwRH9W)NyLb?MLDFmJk*zy5$kiQ&9PS5+Feg6VV(RNfGsQEbLC@$tVPBAhzmmR(D&yIhZ8)i zJ7g{g*QmlU0+MmK0O8|pI~l)uc>H8dVLFSUfTQV0rx4J^o6^b@S5ZvP>}oy9 zYAMa?2s0C3e!LNm;f#}>5hB1{0%%%VULJoH$z8E6nQ>zva;uTr8>5LZlIqCQ48hX5 z^ro z7K=IdwE$Mqm4nOjRqQE|)Ru%i`}Ms(NFTbtIS=gu1mxcDqGux&v%4j}i1MA45Y)dG zN~Ppgpxd`WfK}%jnSa~xk`jmjv4-HQzioTm$;Lv7pUE5#hh%PLF%A=IgDhazlKgo= zSNdM8I=6W#$wb5={u(rxCc?PGLjh6X0%oMn>Uw7X70oLGg|CD64AwC=N%er1<>{jh zIj{R8tn_qS65mDA!Qdn_WNOr^zuID8dx(+SV)kZ+0nBisDPj>9tMr@~`SK(DefV_9 zV#oI%TnG08d$YOmLW-nyj)v$&`4E;+IlRAMc}=);lXTsjmG-;Ioe+4&+aU`GpA-uv z26Ou&`C(=Rh(-zJ@dr4VpC;|PEV8=34W#Xny*~|HkbuPFGTHoT6UN!uqr(&)TX7@u zQHl%JfR?>`xPqoYI?+f)OoIn6OnN4bo7m8VY}2_ysHx=!tJI(m?ZujC#fHo@fj;b% zZV~OuUAlKc8r{d`9b~MlDsOtDX6Kei`#a2EAQ{mhkNAJo1OkYSs6bVVFNqa!R5nBU zT!k`LMurWdT)w>8Jn`lZ4fp>*{0PPqLjX`e0sk&f5&QrAp5qrrQ92hhEQKyy_|-{zXhjonYSPfaRq>yruUbVGvoK z7{Ksm<5qw}*g)eb*El9Q$Q z#)3LR(64MFLHC)sm-b47P)78eF-jcn`%;+tdth(WFC;%`Kyhe~+UV{O4#1Dr^tE#b zLFRJre1Dbm*N@CfQ*aZQ0Uf^8BqT=(8WoNXE?fc0l&m{sgqout4L4^9xB=531rG9P%L#xGS!oau+) z_4G+_X6?6gI6DXly%&s4*7S`D$;f^}JQok-~PSQUmtOp^A_DVgkK?f2V5?-H`uvI z?eE^bIoBu}e^Ko9o!_H-`4Pn5QV!FGeN|_(Zq17-cSR1VUWORlQT?^&m4WpJyFL^6 z_%?6ZY^?b8<-dm!uqzr5wuSDr+@5^E#}G`%Q6wD7>NxPHcHm_&SfFhk>o(+pFOEn5 zXThZ_z;iXP$N^f6i{_K~4*+hee81y0`M~$N_NyCKe)Q#E-(Wv}BjxiHiAZJZf|T?( z7pG4pRq(C$pHbsSH8X4T9F}? zgh0^?;s%+CDTi@?Ld~M{b)<0tma!k|UrhFvkN)0R{6au?V%pE&4U~Y2yZj&=uTi7V z^^!Mlepx3}3x&OAv>velr^0v%y>+eT&`!#{uU+0Vb zUkPp3_b2ok!)SJh9ndcmwF|Waj^nWkwN{4Pu>0%4^QdQcI{e)-9ze*#404iO^7h4$ zp@_$_jGM8`cR(Cq>;Tw~H*HM*kX!3sS z%x0G#5j~r+!uw^9!ISW1E#_e_`C{4fx`%o29KJX8ASLw~321p=^t1n7KHLrEVjh_P zku)^J^-p|O-Rrd#gSZBq$u?TuO`q=y1i&3JlWPGKqWTMRu1T62f?vpSo7K7{onvv` z?4xv4%i{iXI~@D1G=4Bc;n$>vpAG%YjH8W|X|nWQ0YLw64#A&4MH_z#p z7f0+cgM1bF-d72EK2I(A(nne7+%->6-mz-D!_SJa$p83)q0D)~5@gvdCPX1 zq6=X8xUMzpBA-QQ!M4=Yj6>l`XJn3*KYZxb=S?sv>#}ct=yfdlqGrF*mm+Gcr;8;l zZ}Oev7G(K|rm#1mhir=GLb;69#Sv4W6ij6_ZREkKCwIlDg6KLsw0~uBE9A+S4RrDg+hsr3Looxf_T@&&-` z>$%m&RN(s(9Z+X%WB3tkKPgLw#67hrQ@V}W!%MWRE-&v{M^bTu)#LNh+@2C|qoM`uiYKR1+As9ebQWw{G z)E;p2)#tG8UiRD_{35r(1-98}uNhiv|M^sa{JWh{ca~4kf0%=pF!el%YEPDVitJ3!(UDmd2;vBVgr}bRJPP2TfQtLJN@<~0Mh@NQA-2e`RDRx3(3vO%j z_%=s;#ppLdoXhEXI@HB)FzNC)09~JfF4n8cLv;nSsh;o7Rf5e=7yw-+=qheT%&&K` zDG2(`x0mZN>cs~QS^3`4P9Ja0&};{+@8j1qJ@;S}Bj@#7-3!BfXRDil zWiC1P?n~#-Kg2KHnZ~Nk4t4zt&p;*tg9wcH?+SFOz3x9r|#v>ZGZ5Q zi<+OQEZhX^RldJmCMv3ZwW+0c09#WUeEkFdre%FUE&ip?)Es%z)B*?|y7<|rsoPtp z914vI+HaMs>lKPvBg!r#)wgsf7X- zEwM3905;~hfpO5f!fHXB$9J1ioAP$(x~w}mRrtpxtFBe9&Xh%$m|eAs~VIWnqbR+C(GQ&@aPLWD>Jl4RylQ~IkksO zE7DAgDyY2mssJ)X;WkOwvYhz_(N;~Kp-S>E*+y04L@hZlk~7frJ7amrQmYzGj!XsL zoj9IV<9(N+oa^bi)^*?~zIgeOjm7SEXO56Qii{OhzG1AUXDowf%JSd!bA_Da?Oe;n zv=r%Apf)W4_0q%flvMS{xz#!rMtaGIc|R$Q;@Uav8f8bh32 z?V7RXes^l1LJ1>01kFVCgyjiRG)?@k0!qVRC@UFJKD|Zc-$9l-anUI_0wghN0;%^N zB@G_MKLBHyP!(q!&pQIyd^wGZl+jbI1GhNJ7i1tKU&P1Jd3bVrLD5uVQuDjvBL;O zJY`N5Bp|WL4J*+m8<2NSomAUB-DAO0zD>~3^7yuNwdJ+CWTM4j>m!QI7&`LC31LW}Y!Y3u!fKLY8pHG^V?GM;QwD((7omZWC|3F_vQL8s zn}8Cj#DGRlD=R)u1Njx9QQT@JFd^Y>#Wx`ZwBn`{^_>?~3w|jEKhtzWArv(~gQ1fe zou_q!%9S`(!mJX}oKs&4tSh{%Ky5AD1e_sVKrqgQu@-=Lv0y!k;Hn8@N`irLK5^rL zKz+dXiU=4*bp3%S3&Q;k?P@fL&;Y920PdTCsK5ff^#`1P03IulnITl70Rn5t#YhCk ziIjjW5ZW&yNGm~;!sIKvXyAE?Ne}jz)?b5XzYVRoH+QJU|SVF!b|}Wk35K`G5ZA;nbBG()`C5 z0ZBKQ{x2!9+q?dFO~B34C*V3V@{-lvjBMv>>la8E%-7aPh^CVeR3%+|v*PL7*E;Jt zdP4{xBh3`8qKQ?JEtQNWAmMn552jn-P~T`tO_o(xj|{Y0(749Vs!~zwE6}j6rcwWA z;Rw3Oe6H>IGVR;%efys%_mI04V4;Mv@@ebma@7nlZ5Gm;E^z&J*!NoozPqG!cfUkv!~KcRby7KEP?D-saK}` z?zR9N@)oD@mZe`vCL5TiT??ID_H9uoSP-MF(ILXt5|@O}X@0Ys_A*=*=y} z`x--Bm?s0sJBu97JF|?}w?=BTS89{@rGQ%)3v!f|7R!jv!dK|+Em`If1EICP+3XWW zh&kcE`BXU+%pOj1v>&mb(2kHi5_HFwY=MCN0_~GXT3k+Yw_ULGdhME{OG`!e$RwF z8J2PehIg6@HGO3XkThc?uM`YlN`bRr9z?xT(h&3t69Wt5o-JF%SRJ*VPpyq+p(iMbSv*T=w1Vswq31Q(vZ9lgby7=p$ngQsK|xL=_4y z;0;vSFc zb98FZ@dJsNV>0pNQebZ5F?Z}KzNh5=6p;>wWHJ)j`=djpGOMR2j>!-zrtVFQWw~X8 zbDk#XHSq{Y`{KC>BS?W~B$iEoj^e;o5j~h-Jt7!cbxk^9_;4&`vjBwRaa&}{O~vt6 zG-Hd8PDvE!JS$0N@I;Y(4Cj#eXcv_icx1`Z$}dVxsf zOkN;gg2E~P{zG!INXW2Z>O8Wmkp(JB);{nzcI&wJo7ouKXa(e~~HUAqAL zGl25{R;lKQ!2zzAJ}`f*gP2Mok0Xd(f-*w)*j`waR|q=Y=j&NTgbhoAt7E%OA?lL3 z&|^q5>p&dtx*H=8DNs%ML!3tNJ0#jlys*Nx5I7ZlHf4lioqjuMdPF{;P}tYgln1r( zx!^Ae6VRDTCB=}`JSQ3RgKh3cz7a_}fi7?{Oz`Pn;{eisi{yoAH$qNBk>jdkx$?KRY%O z%0xg)7za4N*+I%C$|&a@)l&vTBh82b!J-YxRPRuNRXc1acvMgUe=s~#wpc4V^KrH2 zwqYD5V(qeoV5NDqnvn+nF;E$;b!#1eW(!{iq9nv$27(DUQ6GkCj*Af~fJS+JU==(Q zQNb-;jBf^U`MAt@dhw4|=Mm0$)*o=!9*om{rU8ET{gbJ;-um~3Th|xkeffV_zx&4y zy?bo=5q8CrPoaGc6&9)p-pVeH#i|c(AKiTUeS?3}8Rz{it`pkR&mxcYxUzF0b^O{B z{FxiBU$L(iohe#!p6$0Z$7|!UQJ-}zg9*SDHG-mU=wCXSsTT?W|kJc3RLU1-fJ z1}I=dy3qTM-SogP*if$U_s7977fMsj=fh(JNA9U4WAlQb3EZ~n0TV%H8pwpSsdWU$ zcs7qxB*w66cI;4bIG^;CO;Ds#wr2O{6oBHugCE=-qVq(X%jbUR1>&Xe2cQdsVtN_d1&-?sYOX{5fL;i>R^?JnK-r|nan|j0dYrr>h zs%2c9z7X;4ORe5m*`2D;hU3n*eRv+P1eXY{bzxR;%%Mv|YsO#wfKGg>A zI{(+>$6^SvL*@?G+`nu^6+WsTf7hoG;(t;?KlgwxIK$rVlis|L9-5a-^Rv;=#la!N z!ERj>oc2S7EnxXQ`kTxrhQVLOLz(6`Z$BacI-eIVfVe-*a8&58<6isS|3-hneM5d@ z_66+@x8K?8ji}pw{hWh^|3LoF(}YSfo-mLA1mubN|F~@c?ylx8OeW^W_Ay$QzWoaqboCe=q#+qGbg&t%a@pjlYp|?kNHtR7469rR?bXWzPhyWvdMK(LG@oabZKxn z&dKYaW`)b!r$;Weyfn_6uYAz@)atUgw!K2B)jak97N>qymC4emMFXbND4A2?ww?w* zw)89&*Y(fu7t#4~xsVdI@oG^`-*Fl9q(xwiSX8QuE|dDq3AZwJK9gC~JMFT&OLAeQ zN}A{TVU&PZpTDqC$81%Z5r_ zq0*r*!GV?+B?HR=rJThZ6^nwJeVn$O1eW4w2txgsC?od6UIK(B=Bgb z1_XODIl0oVZkaX}jxvpw;?hCsodzvq22&4Eu5VR;G`X&aPvx@SdmTuZxsy)UIUp+t zUy;+`C0Cj#-tHh;mu$kIKX#nsb{!k#d^BYJcr5zZY{Ec6b$VF98NO9;nF2rDw5hU2 z9O`EN>$WOz7*XN?qLV*o% z`@2+pyVD+1O_!;-{FV3oOJ+%vD;a}oN!Hs%?$V6z^cUV-6)%Npio}}1xMhxvcmgT- z7kJ@24kQ-GB~|XZ$$LQ-v~;q#E6}oiUT@SQ;v36omI~_y#%ck^(NS zYNDp*xixlmvcn5#IO86JNq@yR)iXDsXHveh%jt9QW6!Pds09@54cjQW$gYw4xH?Or z^EmEg3T}((X_4!v$`XFMwADn?ZlILz0L-pMb|w*D=VSS&NhX+&!%I91?TG9*Z?FF}Y$zoH}K;;9^f6$U**JqrBwT7`*L%MzU zdSO{4T8lbze_e#Hz;UFXh{gRY7vy{)ax|oCCUPQj;1!J-l|#NR329piZAV;glpNGN zIR0Wmc%Ht^jlZ5=T|<*>slON?C$UT=;p#5oy3?2zx9WDRMN{y&80JZ5wNdXUKD8(& z-u}iG0+8%s7s-xhl^-QRRm|TXSbIt=%>IC}CM5(F;~oC`hkL6h7vA<;bH@H7G}NzJ zCwzc2#LEzB8sf^w5b_yzOV=H$lUblD8nf&{U7EX78M}P4q=&T{`mF*G`5ITCjS&q7 zw=7^|q@G?kXHKdbHX)N?JK5+4DLy39JkhG|r5oF>(9=4Lt4GtzV?}hhfyeI-`K;+uOK?LpPtjcIFCV@N+rB$2ygtbQ}Vgv3P zVZDvHOJI=cByE3QPAwTwt9&-hN9J$Gxh}azslR1bZMRZ7S4WD*(zQ+*W&vyUZ%wwC z_~#@6wV`WWR*sO(ZDBeMov~;$=!Ms1>x^x@qK0Jc^{jLsv`w^d65rC>o~vdKGxiXc z^<KcI)2dFJ)vR&dP59<%p(fjMZhQ2QcVp z=AulB89vK!8Xs;I0f>(P1ezh9$_36j6wS#AbYYPgL>6+AI`nOtR~b8q^F-1vU72i` z#M`5s*`H;xtRHjfPOh87_5S9ev}wz>XK6vqUW0-!@Ps=JkC6AGm@%E##y2;ug!O1$ zgIRdM!u_&`{& zR-!atJ{5WAxMhZcY^^Y$?K5Jn)`_lNR>-G|96e;oi@Q>KL!xG8G5#K9!Fu5wN_~dN z0#yl>US&qkYAGIne)e-4On_`4at&LvGai(R4)eOS(5wLHU}zRe;J4^G=cUniwC!j) z-A`miYYq|@4VP%ju~pMt;B*+u^=rYb z*%Fv|%ic!P%U>z>(J117movntkdQ0O94S^B5?eq)J>*Xo8nmwF@bq`yty0p;`d{C`n0UZD$yda*bTy$K&#J&6?C`RI> zy05JK5X=*?j>ltp+DAIt2*)>!TQJuZF*m5O#6TXXAnajWJZ&+;NJb?&XFf1D`Z<}P zHCXEqn`+^<@tUf)R60q2Epg>>b=cmsilYq6l+4-lk!7!g;2LdW{+gTgAW6F}VrX4% z;D-TVm{x5!7o~4QK+J(5azI0i+*tV>k{U?Bk#X{|a;+JaYi{6fp>9rv8By2G6&kdm zdmK1fc@3=4t$lFOLFv1ANtL9>a^Q$}#**06KjyBU+0_!8>)YBZ^--M$L-*aW5SXNp`cE zDKeC>&Ie{KH4j5Gkp}9fRAy&kY%2LR>-B>9TgF)NdGo_z+UF3epW!%alikhh>>UDp zOu(`N^ZrbPU!-}nP@s&q=wBg$IP(Rr73&W9!M$@bzJlo{pD8c zQ9Fz%*FwXau5y<5&YOs|J9zylMh-9LxdhS|w_U0~d!u%iOQMZNAUt1wQBxkjE$_zU z9pe~P;i9{{OtQ91fn$Z$(;c^wBjf@=ZeyHx_@j~oYft4XUMTaG!KjnsL7vziU??Tj zNFua9UwZ}vTSC$byoMxY@C_wI{stIDARTXwYq1Hd<_&ajkg#g4;%RIafx~yTy!ge!@vq zOFTJx+Z%kV+<0qIgb?_}AykUi|8-)@Ku^Zx#er1ay|040Gs)!$o3%G@+$g!n=>v5Z zyVsz5GoK4%4LtHG&hY47_ZJ6TqC`j{5HE$yxfzN%46$`2NqG)T)xykd58k8?JU8Mx zHRI-I+qbSEkZq6CZP;jNSmE+d3%{|i{i|l-iTK&nk|<2&%1}T1V&xKZN{hm==xCGL zLiT#X!|5a>B0$JwLpN$Fk<|Aj4@}jCt@cnaycfAv@PN@F4qSo8_y`2p;i%l`5KMw! zC5RH&Ebd&gl+B4r6N7;9?nE|T+I|oPZ~Crzwjg}Ssa&*o8>9Fz|eI+%E=(FAXnMJe@Ka80w`8ykj& z#J~qB@4skYj0jOycg71@M$HAt@ypfwIm~bR@o_`ZsJ~~zs`jwj@Z#>gVx+=5IcH)K zsLdS)P8bKNSOP>$KKWS!^a|rW8&8$^U4y}r9PU0Y%znw{S%$sHx`u<<)o-nDqA`xP)1tSQvkmus$lNJ4m( z#ddcHMIGDFPqZKtrp|%{$A>7~UfNX>#sbTMb6!%IFar9P`w>RDBPVB+Y4Zxqso-f2 zm%&4B6D@`cM@az73-LGYrvcu4m|5IR1!teTHLc`**cSnu04%ViOgPqo-P*!=y zhE$J_Sh~NvtC<$?dF`Q1Dc`WW7^d0?h%dP2ku-d;LMtqpQ|LjzqEs5^E)FF&6N&&U zuCU?}s%RjVcpvg134lOec6b?jtM*aP(^_G>7hryD){OX`D@G63<<&Q31oy-WZ!Di9 z?!}~Z6b77v_{tRiT+CxknJZZ^heyHwxY{f4fi?BE3fw1dq1PKL_7u%&e&$9612Fm6 zN8*O@JWidYP>Bc*8Oh89b24-P7$-szl=0*{@o*DP)qmuB`K=us76nMeg2k?aluEH{ zIcJ_#MGYHD)H^wq`S@SylL@v#BThy%661?$fB=e;!*$)e!Wh`w8rn@lSm9-O9byB3 z^vVhiuZ=-S4X=^Ph*Ntbt(7(;i_-?PF)F$73+7fW9(Mffwp2&!y?eE9m_scBzkmYI zLTe!h;OpW%|5!RbNHKn>aX);oZ&d}6AJ7T*4~`)G`@_N-WF*A0;VwV|E#4C5kVOuG zbf8MX{zrl4DHi=oZHCs(7QdolL*8_J@vFmBQN@w^+y{n)02crLM}SkvtQ=! z)~CgU@E7`X-m1}?M$ekCAga+gQS^2ePhBwrd~iQKCqYNQlt(oD@}CRTiKWDIbo zDcQ2Ze2QF_KF_*S&H0NI(4WE${8TBr^>wq*)G8$QdJ62we>f9y{+TOAj`(LCFGzDE zG;)JQ_(e@5K(QkvdWm}bb22}4rt16@njaP=v0k)jEV$wn2<~y=2U4!`qtw`5@irn-$*~0!mjy3?OKObLpb=vUN{9m?_@00WAX1?kUVRU|0%yk*B z*2&b*E@O1m8{?~aYFznU;MJ)%I)JSE>~>B9P&jAk*0_7{#!sIytUbUmvv#$b^QBVQ z&!4ZIlR9;vDcH=j`4^6;vN`x3(jsU!l{^VEbo7F-4J+f~!5J4PZrV`Bejb3`;U0?K zo{SuSf8Sw*eGGZOcKJzTEifD)R6b|8nmMRFjxn>6Ik^6qIY{92OVtvtA5@j+k{#GQ z(qRO5P8G6{HMt6R`VC{-ACyCZAgY5F)`@4#vmuQ$!-Vbh?E&ogzVQQE7dDK2BLMSB zFpH|bhl=CLJUdn4zWl9jFaQAkW9HEJM9PKJ`B8|TZ|ZR5db^N)>EDCC7{zy|-2um6 zA_;YrBoGHulm~bF$g!my45S~5@iT+D)+r0bl(C?VZ^@u|$rUb|$M-Dpk{y&Z&2NN~ z7)ysq27$WbMf_okH>|tAb?o>c^FGjtY7I;`J!IlSH(i6$d7L9lVpdR>{rZeHRZ=a z^>|H?2jE!d)z=Zx*NnoDHy>W82p7zJDB@-D{)y{p#)c8QU_TDcu5bd}DX>QMej+V{ zN|MCH6B#^5KXksVS%A|HMTtD^s(;_h7WpDCj<}2~Bp+8QMft-dD;3Cf;INHrum~E_m0TJZq9WG4FH-a*92PNw-}y3kwhi7`@L`3O8v*N z+1o^4LfL2XGo-cu{$WvCqZXs-kNC(p^8X3xX!xqBE#u__hKP)AgNn;}LjsX;Anxs+ z$4FvkLx<$Ht!+#)OCNAZgb5^}?o#6^t#+FzxsB+Uj%)YOgVF9@MSM2;6#dF3- zr8u~mk}iFKAS!$yP2j2|UB@X51#vv5o#*7JoyU7aDDviDqsgTB*KzTfM}4TS=`p!g z##Wevgo|#?eoDW`xS`BjwVs)>Yhu!tp@XWuznu&IO==BV4hlpPW_nU|4vhtSl>3HC zVy{-}-D(lUQlALE$h@_yvx_W3AkmPrRml-|ZqHC*oZK zkN!`S4z>Xbt~2($V+n(RGlLZA8o2Cye-%->wvhWc${geFmy?C9G_bp=9OJq8N}ozV~^ z$dJNs0OM^Kqr(dBa~q>$43eqPNn*9|1WlSp_`q8AV0BksA6hk?((k|7pwrc8eI@`K z15VC*P-u>PR?d3o>D}L|{Xr%OG!AT-_1*69ew+LT16d{7!OLA3e`W`(MJeSc+Wr?; z?-<=l_e2ZF_{FwuO>A2e+qRufCU!FM#I`-*#O6$F+nVss|GD?e`>xe%^0E^dlf$d%r=1N>9Mtj(dcB0Z|9)e{vOuuys3cD;lk#W`|?6B)OL znUIFS@7QApF0&coL(PV)sZn}u1xdas#Vg+!fQ3SZVCz{WE=j{!_Z@B7m_zbLI`SBw zZu4mOzZ-Fo>+Y$|wT=ehfAlC7*)*KkG%oTp?5FLkDr}D`D=R9aGrR38`PO|AjlRa} ztT6={v6cOaTN?bq*F?t?;$>4>;stIYjo5-#O>`~v zwOzf9m>24+w zXX8*ZQ5b`DL2XaMl|W-HE-AKZGk!-Tu}?#}tuII{6(xnM^1g*T5=fF33yl)c-8Ufp z8GcPfmYtEf>PY7Ol-QL%^tR{otVAK9R_>hfz`79CgiTtODWwJ%NCXP_`TB@b=CO4;BraZKgW; ztK@WVXB_u&0q$P|$QC*1%=0r$f8RJ(};IcFlu&S%B^?wSJDl zZ?lmMNRQP@k+JJLxy!xh6J_pm{<#M8p$UW4=BF8-#w~{B+szW3h1rVL@`zSx$1bOx zC}gJAow~bAk=lkTwKOBZ-sk@Ed7g77z}<9eZt$qq5O-o%^D*BeudAo%Y5Ks-ti0|1 zd!E4aTjGw5kh0}CUn=vf{r!cWXV1gs^tGAA-@j9;OmVlHkCAxe;i>X7f)Cf%H&k?r zb^;v}W<7cq3jALJE;mD5nsis!J4ZYTZYJ|pJ7+c*PCq6-^6-^_wKk%mGJ5Vw2DwV2 z()8bqZASie0UP5k=Y8FF!`U|c&To_1qZW1k*N<6IcgoW`tflYfifX!Nt?L0qTB{Mw z%4_RT1X^c1cfLr6>8=aa;zyk_`+Nd8k@_J=`ge>nH`moTuijtx{jZcnCb9o|p$f(L zB>2PcVEq-1)Y;Vs&Lhf$Bfoi*kSlNdNlgEB5=qgS?6iTDh~h~Ak2H0uT@=HFn+8Rm z^y_g9dhb}#4XUe7%$yqR65c!=as|8fCPPhcafolf5#`XjZuaIK48OC9-5KJbS{P7)s4^&DUA7`|M&gax9k;Ky2Y$OL9#B<67 zp7s7H$mcZ!ua&0@r{nVSr|k35as-vOWar&+QbMA7lD(3DHXpvE4;jSBB=5A;2HTyiAp?S+^ z&dO;D@R0kuyh#(iFuJ@%qwW!VFD=aDp}1tV^>S^p-tN3W(H)34yM8o{KtKYtFQ!LP z$J+XO1it)Zs6m;34fN%7aQ@Yj&?sb2l<2W^a2AEdz+WgN&!pbB*iO%qEoswvFuZky z+WAZd)F%g1=9r^%<0*tEB~iwhCquAD$C4$>kYy^ACU%agv&SjWaLAzPC>$n37pHj3 z@K)rXnA6!%rWR)kh%6>gnN#UWYbHsL>Nci!DYPaTm~#_JZBk!-hukN%p%@wM9U}vM zCKN0EkZ>x2AyOQfTRZmlh3)>wI3s({%ompiEP!-nt18l8%JZg+VvbE$CA8)vj?LVe z0=@~!YAqa{$h#A4h(67B-xIyCEarTq2Z|-kPTYgKr2sRNY6i88oQiur-?ZE^;f0xF zvQ;@2_WtVTkU-^L<8Ez;v#FORaC=s_CQ-%&ta?>@h^)cV`U-o#tY6VOcnlU;)!#LY znsg>7@K^L(6FHk;mqD$;4wg*k2#?IKi2;*BCZFa`P2Kh+YeSpndMga}uzsokSc2*Y zs#9AUJUvr#EVmj_mVu9ifrtMe0fPV56b2#}7?_IQe>w+%hr5lv+kdy5{#s{l1R7~t zXRXy|X>|*p6FexSa+EUPC<-M&%b_B#SmLC=#L?lDJD6H(;`MCjHX6-{IZR+OCeNo8 z!tpFyGgrRJO=(_W{>GO4qkd7wWcvy6D7-xpxT5@9^Wy0}XN^f%jJ+cIb<=HH@F{!C zXT8Xcr3>(8PxZaX%=~+Tu&|RlrY`rHpf2abd9*f>W^@%6Pk;!MOR|{d$vxVxgbJgx zW!U!KH(k4uHMpy|b@o#e6?wVG&O>vdfWLjWz2Ks)rWXH2kiW^aBhl;I&jW5YOK#tCM6t@U=4xC~>p zs?`Xkn-4F&Azeq5+>7La|+OU)wl_LDggC^dGo#Ke@g}3PO z7nsidME91RmQGCyb5E;)#OYc+$C>Hcc1_$>qB$yK2!6zzXQN{IyCRNb zj|)I4Xa9Gfr+k?m24ti(W1x>^(Xlh%I+65bg)@v6Q zYb56$Ke&+BrYOO`q3XlT%*S)r?~)Z3fHeZ(J4Cv_bXgUAL_00Y%>HJD=vCR*?S1Jg z6|@gC^wT5Vvepqom4tA>Xp+4Te`##S+hCHV{7!+^f>W!2JYsbQ?+^*~lJ%>8NphIW z&%nV>V`jB>qm`eG<}0cAH{q}OOReu3{B7OUP4V4qjbP@vsp|Br26w`IIip-%Yb=r3`+8YJhtOCy_utTP`CC7`J!* zTkq7?{lHWUPRmH&I?V^#@+x1~E;=ZZEu`VHPYSJ!pLDqpe5X`gU7U@t&r`3^Z@=;( z`_tk(<;F+<@6ZdCstM^besq134uujhA$nL-ILGJEE#KW7f5rut)rMBSV~KNtV{?j3 zlU7Jq2-F~t(24&lzD#!fa>srJoOYh1NXYC{|CrwlMIq|6czovi>275w3^90iVH|Pn z?SBvJE}Bxak;~64DRFmh!kw0jcnsX4eYKk+RuVY#vyxg>SXo>cqUJ%XV}^T`_brpkjtZ#@|4U@MZ~Jc^AO z7K#@6=0!}sX4%?~Z&S-8f9fBKHXd5H+_L>jKH)P#zLF=JA)~55TVmUE{<}W5bQkz6 zSJZEtW2jy^jj968rZx**lc-yK01LTXz&ccU#-!)?byU_(i7ToJXzY^uw(neY=}|EQ z&ASS1&Aj=bs92dg5&M=ki6Xc)mj4Ocy4ruAZ+Zx?E~ln+kzSaUMDJECpc->CfIOzo z*U%*!#l@(xm~$EZ$8%kAvt#ogi@G~I<%@RO$70q5zQ1xx?C|X1$gVhi6!Yr*g3|R`~O#Jx?*-8=s1Zl#MwV4TqcD4SDWFg)r&9k2cpItq5dgz)(e%y%D-m0%@A-ioy zUSbP}(zhi2?p@xSPh-q;xn$nURW=HC^`S~a6Ad5~-Ks;FVySdolw#j>94 z<|^ri?JcP@e35YqHZ6t~bX>Q1TnC*|0nBP62@GFDa*I1ZATMthYaFtoBvfPW=b-!0%~11lMGK`os5 zXN54T4c_=3Fl4Jx!gqwF}GG7TM8SRLh&!V{z)Uou|_rMG1 zalJ(k&V%uYm1s?{Jl=ZuhTf9%iLLd&SNiC=jmP8zR9DFI*1)emZl~FU%+^d{Cxnlq zJ8KUV*HV9n9CWrWLPaNA{G%|jpIH3K=_^_$-7enU#wEPHozM5;x@<9 z(x+Dt3Sm**RkT2}!>d7DJQnp^_w{ny$;g#ki7?flH&DiN<%{9wLw#;Jwa{jcAsvcS zoCJFeaLKIMJ#wRVAb#LCNde!UdK-M(?c+DmNmCK3+KE$?C)a67WX#T7;Wa6cza_5N z($(vK)cK&9i<0a#Q37Un`Ow=~L&`ZhBeNDw@NuX0Qb3 z7m6Z0N?23oi^MdQGbdS5*bHaYSGO;)=}bvteb-`^Stt%kE!>Khr{xCDOy zq!PdIcZc=2m*h1PdT#WU?opmfbjzi;J{Qqw`X&^XKk&x2=H`7${V_J= z5>`p`56Kwo$mS!I_ zS8*kz$)bCsNHrVI1OBD>6QtMspkEn4Tm0sl=Csj13$$dUXfsx7-1k%gJtGx7u zsf-GE`D%OZM-5Facb~*gWitSPlN!0^x94HLeN<0-h%knY?F7 z44{G^X-6X(p@qc2Qbl;c`%Pra@j_)unoS{A34^(oV3BC@Wgs-a2W zd?$M6>5@+_2Z|VkiYx;X(<7*dOt{o527P#^RDQhiATngya^46Tb?MAb(L+8Y(w)Kn z2_Y2L#~6hU!=W%NWyr577B(!<)SP87Dz4n4;}9gck!E4ORSgryyum=_k4r{TTiV6K zwNZ$qI6s@i{7o43EUR*R*ncF|tB+FDhI(Z^sJwHYQgLmT5F-sli(@TRlQ+=5PZpxx z(3A6Vkx?&SH0`$cldMpIWyMVXp6;8~ZSB9xLLJHUl-j{4)if!>Iu>@Ul%y|2pHLqx zaxNykS#T#yOE}HbI*d8l6=1+ViX*ouesf~>4@(sGZ0H-o3MAi65Z?@S!{Ts2#2kmk z3}oL;CRGaYw*3kS43}p9Djl@hlNHY<=0+&gMAHbW*3VH5#P|5Tp^z4 zR7cW@aM|^BcZ2expI2h|w#u$)$UiX6r$rAHBn8GqHtK+D8s(U#_HCQRoe2_{&}KM_`z;FteqzF}5oTt$!(bF11@CpIc%t*k9gE z);m^zwxSp}>*YH%Vc0`S))~cUgir^UuL4@x%#F56jMuQzu`ACL%tSad)Bm_J#FPXm zPMSyTZWREfY?_brP#&jA`Zpa?PAByhFh`Z=zwkE#9O3Q@U(_jqK^*7Tj+}ZyFMWN+ zQml`+L_;#NQ}UU0I`;zVLzg8(<*{C)uQ=7{u> zoc>V~lpBmIteiWNq!%mCs)m|gaQt* zBiRtzJXnw(ypj5&9SW`gxg@eT=uXS|<3RQX#3w4+8r&7_rPKO6)ZSRzxb_=)e~6`s zzIJ@XhUOskhd15ai|rr)Rl_2BW|zFHXohdWL1xH;e)T|i0|UAl(uY~bslwQeqvxPH ziUWebc`NQXEe=+}i|7-l>5i5No>U+X2WO zQ+4AHol6Z5mF}O&Ui3ZaBE!_GN*9G{cE}TW0{YcIrdW~H#8|`}@o-zlRzrFqC&{;# z{dKmT`cp@nh~g?~36;2#I>jh1M}3A|tk(V|@(*8sjOm|}t*_NFO_H7xq1CiAtw2JQ z(hc%^4n?7!M97_Ia;LOJov!+`Ucl`E{ghnb;Fh=Mj_BalAWf_{QJ&#o`n66%JG8Z- zWWQ5BMh3zbSgQr)VL>GK=F~Ca6%-O1W}MucseXbthVI+R zaObuQ_YX1&{dT_z?iW7RtNJePPkD39ZU$kRN||mnSXIm&)Zk zsfhU7b@PvAz9m*a+3o|Y{inj)H!=mZtGT{-Rk@ElG-oprQxZin`4r&I-u_g z_aIJnZ(fuaTqj8KA(sd0pG}0(}y# z5ZrhLix*#S@qeM7aM@3`qbeui*mwSkv)CPhn(y&ekhlLj%20b}M*6|FD=dWK_er-a z432HEPJ*QIbxfkB8+wi)ql)-Mc`$VJbxcBlOi0mHe)F{=I}uVJU+kY$fvf-o^YyF9 zdGK=+F3pdxbi#|GF96uJl0uYl!_1&?Q*^5m5^7w+k2ReetFTY_9rl)x$eldlDG{{_ z)1Ge{HAWk!D*kN_aFRpT$t<)_R4$>=BWOCbn}o)fPssn>UWV#;8DnaEY>nxS znoS-j>L6;sTKyVfMuBO}Fd%1z4^_b|GIjdJ%ysU2TH)n847hq_d!?0`vVuc>lS6}o z5>;34GZaxD{6;jrDR^}k;KU98eCGw6UrzdMZEopp3O9;t;@>d}Dc;zPu*GZ@JwHg6 zJYv-j5x6OFtmW}eS*RX=k(_pqQLeLOW4ZX^_n1o6qc^oK!Fkthi#=TAjY^fVi!?-* z9yi@R+16>A2V{;^9ka!aNMJr;_6EQD2RYsNar%**XRX3P{uVHRTotjk^t6y=#9mnn zdW#!kCRwI;=MdcS3g6`xJr<6@&eK5;S4BbSo+7}NEm{Ya8A9(cdpe&wEBUV3OyoA84|o7hVl z2989&u|LrXVTrv#r)44SO)|Lcq21RU2u7)~z}+V7vQF|c)sG1r0Wk6*Y zSg+enR^Juj+i*AHViA~tWAJLo5oN|l)EP-6Z%ArbR6E;tw&pU>8o~s|CJ#A9l@B9a zy7Tb|Kn~JL%!WU6Z_>e8zi4y%@scuG8pTm?U|-#q(}yK#HWN))5)MJGQPrnSyNm>BX<4=q+fY zi*z?^a1*w>4OAC=Gmb9yEmPl5$740{9qf_@r_R;CY(r(i%3PHOL=unc2@wLY8pQpU z6$m;wA&e(IP`9&J1JPTML{7mvX=%@a`$=_JPNCG|3)AeH6HhJ-{o0dpe;un!;!}s* zfp3X7Sb6b;6<^pe>rH!^=?mdx)Ap)R`u7b%w5&$q*Yl2gwcZ#R8=y>*G2W_zC08@J zW1>FXiHXv+Hr}f9wH)aS{_IrGAftoo`se88CIM}kZ=q6&zHku0>!XNVRw`A4N`#o( zx<_t!!TlmwvR(rA<}3PfCB!jeQ0)dZ;O;>cf~X}PPIJzmGjh?H&AI)VF+^fF;ybQX zz0|zc@w1*I$dlNqjl-iXhi3IcVaw)7cF3w<#A`sr4t|u1w9P_@!OiBCZpg~S55G0f zv8|i0_dRy^$>f#z+0|g$XP6*5;cD^qk0E3Eqki46I47LD%=d|xeozw)DHUT6V0gpZ zXAgB4p4~*8aVEGG^`2M0zuRMIz62TI=)%LOzRBjCIjcyn}zGmLROz>u*QDx~BK_S!?o*VzlwJ}_K zgw!oZ%$|~|(3xg<449t426!(3`^WDFn0Y9Ga6};y6-3JDjgTNa{R5HMk>v5a9Jq5Z zOtryr9%TUC(NqkRZ;>S5|%7s}~=Q*O=kS}Am$w-)vz51Dp&bVu8T{3OG3-qIv{ zbIsM2eX0%CMac-!jM=b&BAptq2YUJA?%L90r_<9e%bTUnxu?V0X?bh_C?W{xt#~HS zaAmH3(UhGG%X*YV7YZe3Cmg zpiKKFfkj#gep9UCwIUa;T3bFn%Q2@bD`PtUN@lD#P^h zTKTbJlJxRLHk|f7B(AMZoL)#ympzJ4(>$6UnZ*DKI-y_g19}gj%~iWB>{R|8^$#fh zUCqUDL(J(STWtN+X|ez2zRU8j&u}t(0kA#~PKm-E;(%08A~?Wf41R}8|3I5@z;Us6 z5nYFeqwNoiC}#1p6)lY1DTo*}!;sA);Q#GA?eA`083{vnQ#@9AKlN`HCHYTKv9-_1 z<$Ii}?2+umIxB|ZIW6U@MM)WP(_V0cvv8Ql}`XH z(MX^5miSlpO_XS1guR;Zn?SZ0e}R=I5_$e!?-u`{At3&o(@^@@ z>c+^G{Sav=V%*rrq9c|JQH!T{m7D*w;Te$Aw@r^0mUc}Zn+>2-!vu2q0*V-^@4n_T zd_ZUp7uKs|?;4jxg&3Kb&7e1^=*l$X(1iMDQ&nd(LTr%_aHmKfME9DvM$?;1qr^KY zW-oAj2ZF_A5Wwk&{!Uwg;)L<{KfKI(OHaI(5k7=glbP ztb>rPpr0>sSE^*{O=%(3K()_RtUJ0(=s7BjbpL9$6#1*uP(qIhWZeudxJ@2{nwxdh zOh|o*7+|@^H*OC}%fb@9IaK)62qJa*sRMr#1A~r1H1Sl?z2NaP&uPxt+BwQ@2?6~0 zs!SwMfld%UVnyrS#=HGpMbCPc`)Hkzk#H9U&)BHCuyY1M}L=^apc%R?BJ zz`L4u^}t)fjZ0LMmgdsoW1_eCo8idNYmkuz`O~4TyKAV`0btst&#-+y#0ds#hxS9l64S&A7=94F^P0?)&gY+u07smVb4$;P9&o}4vrfxKFK3D0uX0nzD%S4d2+pT;kVzYsw#-d2 zBok_hA`iDzSA{p0;d7>C4a2Ag98h7bHqB~x>fF@DMNm~K4e$PuT?)AvZ(=tv)W{7* zQ5%a*IjAKu?O%xN3&Y%c>%pw>+>Qq1gVff4m=9BI^AtIYR--$cM>WIb76$7*b<-9h z1-*aZtz=Mf@Eyi4rsej3mmvuL!o$E}e5%`~Po8zvrQGW*G-NHvD zB}u%?zeZuOVkXj6m;ChpTMFLb^|ui+%H6=|PYB1XzM8@S&rjHre14$_jZsE%O_XF_ zdGn&eb}HBC&**hwefZc!;4RXhE<&Y$cgMe6SUrk-)*nGPGb@y7*$i$0BxvVg;$nmx zbKcf~?_{tcmsX13*^5pDM-UorvF|*y&KtQyRp8_rMwNODhrX9e8xAHtUy+v?wJVso zUq7i@%6!t|pnX=nR1c=2`wC{qRnu{2F#h7*zb_O(^Fa4)7*!$@2>4O<+EX{^NNXdl zW`Rky?Hfy%oB>BFDp>-OA6LZ0 z#>H~ZLxtY=0@B{oOW1)MC-a4?I(P*yk$1r@JqYGGDLVj%_e1aq(=`OodE5^qVntzP z(5X(zOohDLOV7xc(3<_kw}YjdLz+xzlgL5eXn1C_cXp-)Pzt8YB~YT3m*0$!UE#_P zbQA8y$;4;idFJ(YRj7Qy@A!?C*5OOhS^e-b22z#@ShpY#uXBtaVPl<)<5dc|jRS+U zrFUoZ2Zfwp&ih+3c;uqM)`Y(e&OwuWgf7O3bmGZTqgfYzAKM!>8{rx=V|0m)FA_Qi zFgZp}+j_i#s*yF9zWhl6!>9n!2P?*$#H0ttt8XuF`fAp31Ei&_L&oT*a)f`3zw;Vw zr~iyPhE-E^t((@OyVx~St)@i@l=RFQW{t{*V@?){q)x&}mBC6aWz>G8%;|xVi~3@E zT%0YC$vLhDf{f$p8?#|tPE}3v5eyp?)`s+bxP@L8B#^>&3~zKx z!G+5j*uI>Q^sg!4i#e*tv_54un%W$YAk6`Llp@LI6 zwHw)|3doB+N~vG>M62 z0E)z))StU>A{WJud*W`D9mEoX)0V`q$Ewp3*wfzC3ZOrAq8dn>xJUJ?_N=p`if+Pq z#4d~dQF-%uvh&iGOoZegxXPC#E8-7NSmX%d$X`WH&Zej&k)U$GM13iW9le6Th27yCG~|ywIBpAbag|*u zRUdsb!dtyro~C97r^S4C7l?Loz^{bqqjFG!Hgyo}k(&y6z~@>){>@-WWqkVEv|7R(QdcG8Mm(GRvwMjUL&5GUe;eO+k0&0Um5DrQA)QO%N~D3rd_jvI*`x*8jmDqLx2 zm^zUo1pfgkdbb&0+w~j0oKD&lpc<5O)13a@WLA{D6ILNQEVMm~=yPIScal8G5wvC> zm5Ik2vpl()Iu&qiLdu7mmX(w0h8JEnDsOvYlgJP1_mZ99eRhvlygcVLMm zgJhUN;@5n=l^3Q zJs{oMt=2cZ2Fhkmgyf=tWYS+@HXTrZf}!~EikZh_f<|H@#!Aw(ccs|+7J7yM@}IFC zPU~hDDn>`Bf&0S`M_7CjVs!Y9qPM%_Y0lX2TibzcvZvbFX#t4_Z4-rIg5ws=b&%qM zNY#n3LF~qh?9(}@hA~RF>}+&mU(x>u+s#Q0wU8MNhO#5)4Sj|;@SsF3q}G{y>7^fg zX0>xMPb*GDiBS>`{$wA`3R-FQ$5cH`g0XX+_WdnTJG58%#kwC)b`iAzCa=f_)3WxD zv9gf2LX=y9I21|s*!YN^n@W18{OSrFyB1o;(y#IWU*X&&<;1YuWlrf_byJXxR`C65 zwZE;^JkC4vNy~*SAe@?{jB!%tMfLBe)hWcCQ>JcW{Nc+y7Y9Ao8C-W-f`!z?171fk zoc(fYUz1!mEChw*#3qyV^iBrD|hf&QVSFJpM0iwPKhj>qfWFpu%rp%p*<%rLP&}#hfGw=Bd6Zg zj&%CpJs;|CTG6$P_rUD!FOK=o7;oaywM-90s=H!RkFI1RNSRqScT3$}@u50MW1`6$ zRp}+0=E0HZhd+vo-Z^s3Ow-jD|OG(}k>E0z_D2qBt4zah42vToA?23J-mQ>I zDp>ku84FqFeQ2pQgL07Lar!o;sb?XdXCAd)TrLc~jK6L8BID`tjND zai94z{v{gvo=jNVxhH$QzhAHF)Fzgx9H0Kk--jCr=xNbcf>kjp8l6vXnX z4nDeypL$-HKVYhBerXXsa{k86QnlJE=b+#Q_OPvXt*k76XD0p5Y>4UY%Z4}N+k;e7 zH?z2pprKi)`h_J26mHYq)A4M>VEST8VGifF6<8xYWjccR_K2t+bEwu4?I-Dejy3lZ zV+#J7X~XR93wL3d#jB?BC5cj|+~L{=l3mv7-dG8&Ksd(<43fh2C(1k2Cv~vPD8;x? ztH#k}->rPWNu+j|PF|h26`oR$1HZRYP`%L3u5k3EI|pp<(e+b0(1_@TJ0G*`p?z;v z?E{KQ`}TUGg;m)-4f?F3PhuJ3DEA2w^EUq)b|5aQS z;e$j~04B={b0+xAUGRDVEc>BKc7DXGPguo2B_KC1+tD6xDLEi_hOk;ITVzg-fZa-L z4&QzFpzlO?=wy-hG=?Mn`DBiLG28Lo{%J(Z4|{ zBuM!$KQ>AW0oZfPuT6BeN z1;%wRGy<6?@hdn$ed5`9@A!e~{uS0Y0MeJWy9Z`^GgxZdKF$yN(f*$J_{*oKex6*zY(NI}Nn>H!E@$;G6`O#0LL41)ty$!sskQQQkcfO`> zk_*G%u{aVoFD977uT^R=C;(fM@)!^E6XMl>YReoDbiA`40(C zl^|~W_kLKOi2@pDuC{OpF9`l&BJ)+BzL$9%o-CH?_|sDpCy+Ey-|+46=gT=N~sF;i!8U=B#&+0`Y7Y_&%moJmu9heaHdOuot}hotX1 zT|Cftu~TtRvr1*K!5^1R$~DMmY)>$|e^$hEJ9Hz(%ftf?D=k(7LX=3rUmPNh%3y=0 z7bPj8kRS3o85S;q0{5T7$4_K5d;yflz?>aJR6JQ=t5lZE%K^em%Z$(Bso`uLXtY!5 zU%^rph65Gfxe<>h%Brk;h8bZMo@I6zU`(C+sIH35z36Z$2=X{}LA!QW5+bIx0V(mEh(dPYxVJ0@}q;byh zDa;u#U)OMYxw%xUX>+;*cnXZg5V0-0kN$wlOiIC!;8$0Owwb}I($^UEO zL&KhBuGJh77cNVN2ohX(^Y+w+H)`9Ha6$^b=coCWi*kA1XP+JX`^}aI!$9sZ$cgleTZ9IWZw!k*Sdbm3IyILkw(%1A zvo`ykYgT=;f0rcp73TA?-bW1ksrCC?yXT$v9cNZb?|z$)wsAm>hKys?m2LRiG(EQX za3y~Qz-<9-&#%gTq~5wsqODL#6cmq;6 zv!9Q&h*kP+Tn`mzTnV{cr+=~WmqaOB0?t=yfFZm;8+#1*95m;$j^28m-0lI^x#N`% zM~cwBT(;|=*Nuh%x~FCJT}$L^a9?CtN=kUQ)= z$7TgeOnmk_Zi97_=eG#kCu!b00xbTe^da&-51TJl@6ls;k^XL3X0NCFRM2_-o?G9% z0swn^eRTFqh56b%>ZyC&&!e0TFE1_sC*$qZPEKf{bR(pn#3=#=jqC+30Rt_Q^TVfK zQ(q0cSDz7=bnIe~S8%8FMmAWMm$!fZ>b5gc9WKABekVK~5GMBd`!UzDW%cOe;;*5s z7N*mw$C16ahcTg~-jT=m?(Sc$?65A31{|fO4jG|O3*F|dSmack5Og~nApPPn6((H% zR6Zh`N#4q6zUb&M|CkOWBI*vH&Xd0(oTG4kU5SP?a|+bQZE}@ZG+RijYSmb$Q<+3e zHb5p|y%~?)W&F%sb5g4BUikDEZ98t}zEArBbUgg4WKm zP_ApQWgM{l87m6dyW_`NJ`2$PqZZ+SI=@BTwW_D?hA6M)l# z5Ua@&-IZaG_L(P;j3A`N&!Q|l3B--&ZL#?H8&pas<~+2^O1524R7Pj1drZV{I=H<@ z&27k*kcFjkZ!HN5wR!hFY?qu|&CeTta+&+j+NNK_dmt%)^sEHv)|5qbI`{<2ZcXoi zoA5LFArhYNKRr!f55WRkNo{Wtf3P;+`Tf~Py}tNiHC`(HRFqDRfkv4T0{j-sFO|Qf zgQCpFx;Zje3{6r;u;V$jCUenk(VC0i_wRLBthhG#{HXo_W6pj7I5R^}m5s>b^tl#` z%?KMi+@-$x5v!jUG<{S;JruODk3U?HF0cV|l?5!gQ?mv23=k1StrF%YxDUI|Dd5T~mwv(o=@4Z1V!uMks%J({`y>$!UERoQ+^e^Arq zDDn8JOVAf|mbFXzpAjGS zl{EemjdvN=1^7xEocGq~>2jHW_5SZQ-FM=X=mzmRg^8T+^xRnL5)fQhS`S|PxAA}D ztl+uql~J~YczmS}eYuvT@T6zv2>ls$EY;k@T7}j6o-pGt89KQ77HC($DOM_Urd#PN znm`49KL&t$qupiVC#vOoHXrcpgC1g@2^S*gv|j$c6`y^WXOpA-vp${NT5zk54$xyY zkRIG5xTmwt_$d4<_aZpnZlwEftSVe~j^2`;!;hI(yB3G68A!XJrn^4ddK0UC|LsXH zW@k^B8Sqy~_6x(D6Z<@N5+0(h!z*J#0UDhwZUB!+TIWl%#Zj=zb^|8a>XRf_62xYO zB@-%-UmHYU+01MT=3~ymtAZ!Q!mBEa?r>QpMplAl)IUbFAmC?rZjx4xVIv2_P)$T2 zAqOj2GJ3K)+m20T#!>jzps3@eKtwDeMf9j9ReooiyzU(#!k~nn;HFWL9!}OajI1H**=m= zrdwlQGVLY(a?*sI=5|_Xh;;pwk4?s;rKFUzGnlUM90U(6mv~Dr9X3%8@AvAeCQyV% zGkMMjHJI-=)=Q>o^nv?Iel2eY^_r6|I`J5{>YhLRJ+6h_+igym6$HhOIYck=oj{zl zTsm&q^m=`lU(tpBS$=rSr{VXM^Zta}vA2M5vzjSqnogngt+h^HfR;qxcf!;8x>H%u z3}-P@|Ac6iYn?)tT+rw?xN%1KGSYJ+Kf;7eEQ1o2Oi2aa*5n7PMh4v^dLxwTC^j}o zKFmTaK9S*#z8dpOKfOtf3TZVC2nd@JORCpera8w!Mvx9O>v!1+b(W(=n}$0IW8W!s zmTYHU$8?1E3ZviYxo%To62LNqN0JG@PXuQdgJ2h}QPum6Jf`Yr9?eJ2@f+GhaT94(%zJWCgUr>C0(J>TX)O0?&JF$UCNjafLVb(GB&lp=3qDtw6IlFt9?jiO`6XLAx z#db)pp&Vx|`>>iDuXdK!k@tEA&jDg@QsUSMQ|0rxcNZ|^UuZla48jPSJvwjv+!(7p zjdn1cotk#stA4+0p)L}VVPNE%&L1Lg*rJ`#=wEdemOs{3U+ObgPsnHglQUB7fbtFG zdXUi`_cN1YKZy)OKUywsVrwzRiNfS4_h7a=jbIc~1JxE9?5;$16#oq=^YO9O)@ZJly|b&Ix?Dx{*B?)G?M19E0T|i`mTwDH-w8Z@oPCI2UT9=$gNzuP+q<v{%bJrO=Nh-_KiC*mSpF_ z8*(%B?#8x@P38sgtkzCiaLPNV4c7}r(&vfp7RvHtM02?}-e1L6O}!r3-V1Ij>w zq-;=WGvNj$L;eq?Kk>t{3Z=V$PSpRm3k1x6yTB9&z5h1{ zrUXd)Kj0+s|7KN`1daS>!I1)0{13DZNP)h9|A$_rK}`P_(aVBpApQ$emj@aB=WLY+ z1^r(quObNQKWCeWGHCHXTRNU9$nd`~UsX`-|24s@fz1CiIje#Az=8jLiF%Otq!$Pb zOaKiG4CCLXT`larSu9L#99jN{{%ZlO(E^+?{}63;w~bx!I}RK^_9sD&_7jmMlC+IU z-o}t*@tZxPkwL_dYO|zwQV@2m=XQF*AIA;V-B9)gA`I-`N}brNX%Gxk^H}!d6Qxz7Lq2hvlWMSvoI>W5s~oe z3b?amhWoz^7tpkY73tc#6(-DBn^0hk4LaxwQ(M{ryYYjOZj_nrLPBT<)1~*`>Syu? z1PHr7YfpI6g)rvIp1!eFTX3A%QxnP`FUTmJPGC4g(Y9&G7oVF$lRpQ`0NJz))U1{Z z8Q~TStr>^w)hCci3d{7IW~5n@l=JoSKR6ZG@h|@m0Ej?$zeH*f;7;HQCsLcW=_zfB zSKizRL(<}jMJ;~=b6~76p@ox)-ojkPl6?O}Dr{^wGVy_;DO`qU^u;rj%&b01Oi^So zl1N5+0ca$hHYg8&eLWLOl*gs5{e%X#cpg^uL7+ zZXl92>=^>w$P|!=!5NyKDI{4^eeV4DFr+pTTFN;-CyswSDVohBk~kgFiI0&gva}dc z!^up1+JF|zCMl_8zja6y}Bf^iYeP%J7rdN zin5A0RYHHJVi%eFu0-Fuyp2PmM)0wX<+HBd6vW|?70>} zM6H60?nrP2oQnFeN9KSt-vm>twJBtt%RzLjK4kKuJy**Oda)h$K%vWINmj|!?ak2& zc9Va1*_CclVXO^tE>U86aa1Tq_d;=xs)vGB^(Mg;aw^I=0+W~iza@*Spq)C`Tx?vJx z076SiTM3?AUj^v9<3k0iP@=H>45zB9fpKC{qZ)w3Ws|#_DK$cvr2z5(A-55dmh);4 zRP;c40%9h_kg?R{X91Wpk>$%;1Rq+0gi?z=IrAmC09;aiPgC1Vq(vV2cE?Ng>~Q_=98?3S+n1o;w$~8`i^nIg*$e*167YvC|q`@xo#Sd|a3^|5*nRh|e zMsdFkNEAmolBVJctN~mse~HNenkHzk#i|Q|4ffNNv%zXJNd{(_ce8(di)C&B;H`zS z5McQ>;1evZU|G^EEJN+ci&})Gu#2Tulfy2S24E79g8?Q`4T_ZeYiAt-v^R%x1TKYq zOIUsxsLQP)ZIDtWMG(WR&*&S-KMX4e(lD|_EdjwG4kl-Cf(Rfssuxqc3xuP(1mdPX zBz^`6WM^iAlu|z#WNv?69v}4~`h04y1X)1!DjG}lg&>RaVryvKT97)b`#}`y19t;v zI<+@~T#~0R&hu%e_Ho_Tv5oM+q$vkYbiJAC42>_Nb~1YC{z~ZoUx?Ng^YHT2Zl}79 z#@MLcc3C#Hf#M^!Hae=bCc}2F_$v%8)ZR+#TlTI*hMZjm$hm)QqqmRLuYFo|?pk|D ze&(fAWz~w=BciSH8K>l3_vqp^(YwAl&=!8R?&|Z;U$=Yl!SV(BV!!|Rh8urd|NhCD z#-DE~eS7Hh=#y_nMZV-$Rd4Kkk58`V?h%eXZyQiW`yWu3gdSKcrxXE0z>*bPrk41kres=9YpMCqa$Bq>L z*SzV#+T|ag-}Aov_|UbtnkVu+Cq3z^kSZ%UHYEw&g#u9wy%B{KU*E?jS3C~0TkaeR8X z{2x_Yw}yXKrZ&UcLdzc!e>?r>zn`%;*VV)CC6-l7XTWF>x55}x3O6q!9T`f&&1xVG z*G4^@*0pHdFt(r=!^R+BUDw8N*V@h)gY|fCL=Pvmz8F)ONG0LUOER*Fe#YnTOJw}k zdcYsH?rna^6Z69z&rfdAenXFrJ6Cz6LKKHfXR6n=De&_ZR*9<>C|97wd_z~NQAD!(f z=&yeWFMg%df9j6o_x$mgEBWk(5BJu*SM-b1r^M0qpH=phbu8?vE4aJy#V?D_e7bho z%cpv;?)>tpiVt?aVZZRj>fKBiFbjz8XC{AE)4pL{xEZ(OAYO@A;mh$FydH1BSK*B~ zjC*h&9>NFk1NcFF9o~Zb@eTM!{6oA8593f<@NM{Zyce7JF1!ct!#~G|@GtRU{4jn9 z{~AAnkKsr06ZklO5}(8`;8XbY_ahMi{TEP60|XQR000O8B(YFf`(0Y|^aTI_6bS$T z8vp>89~lfBf1OoZY!p=(o|&_|oq=7L&TLuQav8b{mP@-^TC~3C)OJ}erIhZHP(s|> zOv|*>opE+rN(`|U&?-b41aV-~kVZ5{Ob{b65@Kn<009Asm&i*sA}>5NQKRsp|Jj{l zF(yQ(`ObXveg8TCfBxC&S-r8Xh9DUCbK!;!4d&*0e}W)O2!beg8)|<%sq1ki)1je^ zyaP$HPGiB`smpF4s*+Bcik8VDNij;4Oj_-gWGOz~ai~hB+u4y+v$7+T?2$_}9qUQ; zkto!c&A3uYsVAwsWJ676I-Rm%XokB(GkTI{JpbTQw79CITm_BB)%BW+>QXY2oH5-= zQ|pPFe>fy-lFMiIXi~2#=WCzcDVyF*+5HutE@^lloC-gh0BFW}i#9)u#MLf8*f6QFJ;w&hzAeB~sidOAhDuqLIDG zCyeqUk9hL9)k<;WEO{z-0*#yieY~KLSqX|uvE+^1MKp2|^i)Bguyl%RW68jTqBH~37xdhJwlAdqTEY=~xf}CeQKLYTLjb}&GgHfX~RomuwA0IgB3pu<2 z(O)OlHi?ZfF)E2^Q_SuW(IJa2E@FlEe@#@xhAf)fL^Hw89XZQtB{Cz+hsc!{7x-2yw&E$%PgHJ@7#b1(8E7Y%57GDoD_-3fwMGe}x1| z`=JPsOQBnVde36ifc$8eGbHkhYTY4G^gHtV3y8J(EjsYtMXz5B#9_^^ZL!#tEBKpBhcJWKE^Ut+)58D%K{UY~$ALDq7}T3PQ|`oFVQ{%h8yb)IE7 z>#|3(B7%sTMht(LYkPEj-vTHCf6M~R29SVa{JjDw01xomI*uus#^=~En9rT2V@`$p z^Xp%*{}=ddzryDD5~k%@U~?QZ&z^&M8GyE9d^RWTI%e9OxA!nryVK@)KQ5bl0B)P( zc{6Oz*lQQrpzJvgo8z^pX2ryN=>)vipGzka6H+3P=&nu<5NCc?UGIKIe+lc$E^)tK zUH|3M4Qn@S(pOYoy4>8d;_}t&%g_8st(!@nxF2e2e|dDj;0g|(FWqJxd(%47bAqUy zdzJd&rgP<|$Fc(f|C-1g;@flcRuj!yI%f9)*HCWXD*>f84>XvjfwkQ#-pO`KBJeSh zf5@kZKeafntVLM@kM>Ik%jZ6n<%QUu7ST*d9`o^ zWpS8GB1$t_JgX;nWa4S9M^{wYi0g)ymb2OINFqoHCbTtgi8pVINCacZsiYBCwN8nE za|g~g8Lvnx-9#YRshGjMUmJ|4dKFa)!a7nAjx3PX{$SQfPX$*#e>T`YOs6IYkDJhi zBVAdo*GS9V#ER+ReR4+9jE6M@ryuLpaN2f3ADU8~3fvCtZiVrO{@mXEXJ?Iv+~I54 zbV7ok!@kyMFRe@6D%&4@x@z)%Y|W{@b9ZyMKcxFEDC(wrTPJre`tIZ9U&M+I>>S%R zbawIi?_d9j8hd3Tf8p$27=Q9+BzWbGb4RXz!l@Ty_l~XjY1Vr;uCTf2uS*hT;ib*h z&ck)r9?bgsmyOR{zmjT?JotR!-ND;Mzx}cPAkhpRhKe-e@2kxIE=i7QVcM8>rjzMr zG$zaJU|wWiVs8aIn2Dp9AVyOEavEcYYFie7f?$B1QY-O00;miu~1k( zoq*FDHvj<0m#bw8B!69O8&`JbXhz}{#gRrD+OlNJrcFADO*5uV`bYlDl|+S()q{-yZzZtx9A$^7KOV(;TBk+kM2X?g97{Rmj(J# zATNFIckT=+O7iN*IG7R!**6>{0527^G)9xSq7`& zwEb4wH)?g4t<-IcPAd1Edb7V~Ew@*PiJRy~+ppVRqUF|`zR@!MwS?F=NI}2nyY-gR zaB7xZvo;1S*MByft9`lL@MhhvRZX`hPIzqDG8=|nUslC|jpE_qvp!mNE!fp# z(m>s#NB@dx<5s@gw&IO-5RBI0cGJYg4c{?dICZq@w6+bi)uM$}^&PivdBhpA40+q0 zZwW)ZB)IH))%5F5(_@`;p>J`XZkIHDr$MLfy64N}Cx6-{cMO8KTyL80wo=92;}9sU z^&rwugnovMxlT)%G`MQ{#)fIPEiDq#HDjz{ zZZBI#uYa7rd_J*mZM#;>HmlZwy$q4;c=VhKFC`FEUojxvx0*f$b!Ejuc*!x#11pZ( zF#W_X?_t+!I2)GXZ8yj{hU0caxt#zeBKkncHY|9$3?n_K$jJwHq0%tdyF!OW0J^7! zB7Ym6TODjSYlu-t6&)TYrP9&9>B&GC&e@w(*5p}yrdhQn8& zH3ZkH)wEnv6pHhz$VY!XWa=e(2Cgt%HJeTokg^Rb4+pF*1d)*0%X#>vRAbF?*1h!0 z_kRS{69Bec-_tq~Xd;<>$FUPFXVY@MHOsaqy3yHhT--rwZ?`O^-K=l*Z`PZ|^J6dG zV*toQN(@!k;2?og3%SuUo3+rw{uRfr*(!3NF8ohKsQ3I(irlK#v`w?_lSTu1Q?)#A zv`g4%glq%VwufRdK;^aVjgSzOkPRnH#(yD~eBcV%!ze&Vp9t<=+zQPcv>mf1NCP2r z0uE}s7C~gVWswXy?>o@{?Loi=Zf&6&% zPD$okt8LpvxwE3UM~7<;s%ewpwuR!MY?^L!BHDt4ecS05OhEOeIms4cc(6xA%XE=gB0CSh$VnO|c3=02KHRajQ$Tvm91t8{vLllK8#k9d za?D5+B!V4PnklbkRqHEt;GNj^Y0*RxS|;a*K_8~-OARFKk^>&KVSjMLbn7I|8$g*r zm=xMginvf`F+Dqu+Ry;&u}(dS@_#CFH6Rx{A`_PA>Y7zu7kKTKH)EyUtcn&g^&5Ay zzl8)t*+OIM7cd;8_Ca1iW~1h49&U)j=%Q7MOCEahyn*s!!=>nL+n-xHe2%(hkVu0N zhVq&W+eB1)k!YlRD+%W65sQyG&8fJ@{Wq-5A$UrI^ z@#xqHiRkH^dZet^02#;iu!V$<4x6zz7VUeO$3pA8K|+^r0Wtj+i5OT$MRr$5!_z&5 ziP12ETIUV*2v!9UMPC7DDs+B0dUaF{NA^YQII15(Xbc6;HLGx)*BM!u@4^S*!TeNj zOzuaY(ZyMx<0<5lRMf5ZG=CL!&+1@j=JMANBq5EGBnc)mNTBmbuafs@{ zufRyaVLyE&()9lP?fIFz^LOJB)(2bYYGxDY*J_Rt7W~fi>$I$<()L#_%T}v3W;Nj$ zT;J&S>At$tU$q_2!VrmjdtqLBkrV*a!~`_j>6P7~Wr!$fFg0h>lYj9(+_OeeW+K-y zbd!nnoa}bJ7euA@Y(+AXy~OdCaX<@(wg^Bhd~;fZQJ->JI+5y4Y}>ZcZmqgz%}R;g zeZ+&^){E;_QRKrpGO^o{hr%8vn$_gXM1*~F;w2ixosq6p;d;1ZZn)HTn`?)^bFpZ> zuTKoX1#sP1yXjh@Q#XOBS+YP7f>px|zI?Yc(P(?NF$L*+2%(0P`F~$CIKr@AoA=J3do~Z{ zJA?Mw;-1Se`becFdvKTI!XBx6$M`)`&*XpemmqX?n9u!xmS<61VJ=31KAGIX$_>HNRsV1f9uaE7Vn_QUyY|r!E_*CQ?M(`C3Y~m#6z=+S)PG+%UoXM87Mmn&7=5@5vItL zV}DraWC|^SZ;w)RIHiuKWMR${%o)$Ahq8$& z0Unl#$H*IOoWP#~(8ve0>fkG$W)nCdCysUn9O9`AFX_BoLw`X%hCm+SN|DsU$4ZJgNz`i!SNVR!w@aA6O<}f;+$okWMyC{U{`y&OM*L_Apr9PC4bH};-&M5 z51^LTM6zA)J#;}F0-Ep^up0~qG>N?yus2}v3<7kjq@LDc?dfDX&2@zmG<0-g76-WS zGQk-2XQo&1Y*#Sx%|?QA0h@#1IS4+JETlyU^pwEQW3ZJWA={}y^C5r=08k+W5WrIq z;5iHM6V0u;Q10><|huzz>BV z%HvD8*(`20lenYKNNk2zp2^qMIf>2kK1i8EV$D~+mgV`%bNaU<1D$iCIZ(;$U&#Lx z6d)(xC!O;Fy9=EQNPmVmlM8z@05xe&7a(24go09!;Dy!x9@Yem6!ty{J$_qw{4Lm3 zkyu5<;zQBd-lon*g+jwEzD33E2}Tk2(F;{VfH#Fa`dCTl|ABylkxBFyc=;2|IPXrY zH{s=5`@Q^L_60A08(zkhZsx!ZFJl~FZ;3$D+bxKryT1(+DSyznaS2+#6R_`)F96GT zOX@-<8N(p-4Qj`yFfBK&-huM>NVx#cog5Ysh!&FR0?$zL1kAw2&WKW6VH&y_CW9rA z^4g_BOw(d%P5XH)g*$cf)TBHmN07^wkruBb2i}8HGU@$*RbkSHFzIgc5jx0{=pg9R zHLm+yeh{!4&VQe!HbAx4!thD)hwf2XG~-2Cj#|TFg^lSHRO%NiyF_XtB|%&jEjx>r z8n87HybmdO|5Plx;tl-Xtwz!4EzK}kj>u?*!N(w3DS3xJDA@%;ei+T%@kh%2H^k~ zP7=REA0lD2Q{9o_ccW+%!w$GFg%*;jxCo7YVeq z{_X4Pvc!z;5b=GSxd!Dmq1=RWOnpm2#Scii#7tZUyckD*DC9#pB+EtmP6`v`j}e=) z#FlARRWL`TbWvvTY2oW)8V=Z>D205GQ>%VrjYc|6{V^tps;C|`k3jhMDK#y4gPtn^ z`+rk}q>ijz?R2g`gLYK-`*Pw=cV5Sr8M`s9uA_mjcN^$BuClJy6Slf0u{8kZVAzZ5 zcpVR4?~DKIq{px)u0ho961Fk9(ux>>OfKplA020;`kvwteihW=DY#E>uO74 z4mKvDjlYOCB-RRRk;tUK1odQ2bqVaJ{(q<2HvbA_o>M>TRJ_j!jV{>f=>_bsZ-Iyc z@nhK6f_?3hx`EnGlhp>#6j9{|xVj~=&4B$4u>inZm(^{3MxVPot!~a;!7r^Yphgy` zMlSKYo$nI4JUbUtey(A_lZ(pioR;BzNU{Jmc?RFQl=0yN88w-Q8p_4r;&X)Js(*;D zB9TU0P`O=FHwo0R>hpm81ctci#ox`TU&sk>TK%4WMg2lg&+4(v)r|TDV&nfG00030 z|8!Y*U=&5$zs)RWTyAyEopgn$m=&O<~95)pgEC{>LbyFgUb*s;9t z_wM_lLBtY4MaAB*cSY=7v3Kl>?SK0{GkbRsKmSN}_IYNW-e%98>m29KwfLzGzB{{C zIqmLT=Xe6l?sDh&fpda#>X>iwIcc|pIVW0tZUpF1PJeoy7V-sEHO@(%#ZRwy&ti=% z<~K5rf!>*#-aIfk!{TSeV_wKFul9J2r@R8IVud2_$CxVe&Ha#H!2{kHvw!%!3UZ4w zvTGcW7C#g0&T3%pIht-K6P)f85p2||8Vb&QYdPGHw!(n2|>`U+bT zY-z2RsLt@bs)DRnR|mWRa(`X!d=@zUl#Tt@VE=Au_aYX(UyJvFjS0_JcB0esH0(N@ z+dA!DNP7l-1q|!-Vj=GaSf$+y0IrEa-U~)Ei(iQC^t=e9={X;4>3MO;uZNaX;fqV+ z8EuZnu1MI z+LTKHBptg`OsfeC-e7e67nEtVp?!jN2GH?F1HP=J0mjOl3f8iuO{~hw1pk4>m z`=2<>#%#!?oYTN;nH|VUB!XS!Tl<6|5pISE%BI(l-(r}AJ?wj{vG0FMy#GUtLTsn! ze?z_;3{MX%{=elo34i%*a9@KCgxjk++TWsx|sA|D8a@>;)p zBD?2{T$F+LM!@DG9|T}d1MjN%-euw{kBtvv;~3y?(D*BzI(MPP7phcJ<$X}kQrQ#j%O+%Bg?>NE77rCIb@!z0JkrP%=1ymv|_yy4E8|CK+}7*rnewI zZn5|+8v6pUV|5HIhtcpKkwp2S<7QQ4p$GK2<*?AL7Qa=Yc(fvP$U;xFS$uhe?cSw# z+zHCJS%3VtrL`G5ili4K5-aqEJ3!)gi{GxAHHjRNco-z^P|dBKEeNBR?3kRmUZ~uu zL!}L&au>*w;iwG19Fcuclf4II@3#2e5m~!L_MVdOF6tR8ufTWMa4$$AIJ6~RB};(h zeVXL`Q00D$--j^DP#Br;c}<7U{gDL{B?~+d@_*OK)hnPcJs*UC^n57fZ@~5QQ7|5k zXEG{@dS@#An_yN~?>@$c;V+$b44`M%Wu&O%yboS`4tAeoPJivT)zncf{+vot5r!}yRC{e{cO`Rt z%5%!f4^eps)w|ELE_W6BS6^>gNo(bdP1rrv{>Vu4d>Xq^t22m`l|-t@cQ8F4H8A8K zqdTVS-4{UgMNPD&L^N1kq4kNO^+Fn(B7a(|AP{N!B52U_rI3FLZU3z-{&IZ2DkpQc zStVPQ->%rakbed($HBs{Y74&(3%_FVS3>?ddiHoE^9c7fi@zH3FR-r(?-qY8?Y@zB z`dj>U)tsf!YWLMV)=Rmc&$sv+)WY*sH0A8*t5p#6ON62B0AEpyqe{R?<18RwqkpUE z$*9N^tdM6cssy>xhHuu}&>C$>fL+MffZQ%92KDZz2+U7e06&HN+iGtGwE0A9^B%N$ z)8cQ!uuz*E_Fc8NGVOj$4I8DixF%V4?)PN%wEK~sBVr6RJ_L=K?pw|*_g#y>MS3NQ zJV)En`hSQj89+OgYu=AqyqqpaKYvA$`+=6^T}bk_#oyLt=4YTLq}|md1ys#5Enmog zK{V}E@4f@azyHsU{}qmhq2Ebklhe=Q??#gC%sQJ?$k##!3S4Dpqcq8B-yjIy^A^T* zLWcQVkI4MZ5Pd)7zfv z@;?!A{{^$p<86vcYa*qxIhBlB2+X9CnEU>MsuRh6IV${Unk*DCbl!lxivlMs+mldZ z6R+SbU~I7uIWJEX{AhVauDt9@VVTlhfQVE@t-VAC5yP7)NHM;l;3Ch3Z3pfeG7mau z&L?+LXoz06k~M?fFJRPq_kSza+x?R7y$SQv?w3B9`HRk=DHIz(to{hjA-c>WyhSyI zx2B}*T&JhV!-CrxxdTBFWVtLMFzFGK4Z&nuq`&NPzh)VDTvtupbplg4 z7x_I(h;U3V_$rM96d~I6n=pO3$T8O077Imhip}|Ev{}@XKpX^cCx7}H3X|u)355%f zT?HZ5RK`)U5zICz?S9LgI#cU<1@8CQ^Q~@h0gBCcq1YG)uLkh@cvC>Gra2)Ijq1XK zBVhDtdH0uTIrnEMTA5cVHyZaz*B#v7e0`t>EGKIt`B?2sRU$?0(ayMk zDyJLc+2Oz?Kwv-0v43bX7Mo(T2d|uAqKW*Oqw+2KVCVkIS>o%rTD%=l@2*9uSZjJ1 znCH;P*6Kd?Bl_467XP7C4#Z|4dmzc66FwJ;zI%9~=m$nHP;P|#lf{3e5fqrsvGpK5 z6zQ@z%-8K#Y(as+au)w-I*kD;m+w>{(I2}HPP@M{XBnV19)C>v^NgIyi&;KA^2-bi z>98Sbp;GxyToX%&e#fC%3FO(Yuq5T*S|~}6ALB~)C9`Hj! za)VF|0`{j@-zplddIPm}P|A*b*B72vs-s z76!U_-hb;C{3?HO63ct+LelrUnU7I+cE%E(moYQd{cwfU!-m)-6x%_!2b3jh8nUuh z^GJXPX!>4|DHJ(Xcd`ArfKRnV0wY!_&}ha2CWE+|3MB{2O{vbxKCHLwLv%kLi^82XFi!Rxv&lrFQDvGfp-%Z;@ zYo6@P0J|%g^(mu^6*L4<5bzlU$<}TVWPeOcDLBt2sq2kShGHbT<3Q5{M@LOiLY-|w z(TIc3AEQEz)by$Un-)Hb!f3D*I)=7I3=J~9S`MOKEd=#NyWAIb9OR?9 zC_?RCM$zXYQ2>?$3qud;l4;$Gm#y22m9x_RTQlAbX#v5J)oE(R(IsSMkNcY_ntyF+ z$W2i)I25Cy=L^tNu6GG3@-are-AiMyE_J3Jo5@&z{~4o8YAD9gwwBVT%{`b_M}Igp zQ*z4Jk+z44@$C@f+v^x_s$5c5rKsVKHR-)w8XL@ALTrz7By1)WdqUV3f!-k=M8bw* z90DvYcciOQk&o5Y(R_BKk!U=X$bUeac~28cO%?O*NF~~i?mA)u(tD?RnPrIkDDf!v z$LXts*sGLqlw{fR3O#os$poA36jy;Mv3@H0$RL{683Cz+?p;_*Br~W4E(cgwzVyJRn1N;5Keik_|QKf5ef%&BP1Yb|Gl#K_V z92}9B4dw%{@%Vu_b!1u&)0MJb8)F#yy=cI8dJcy)V|BKWt;*`w^FgwZ%qDe?cfuG+ ze}h0yvc&G8m_ppfgyK+e`>@3ld&F0?EUj(R*VyJ(HL+cdny0dtsej~R96GCwm1;<{YXhU%Pgvv589*b3eYnb%^|9{rdC7Yv})^w|8BDQxZ zjs}rANS8_Le3SpjA7@Oh(}eZ+_cHB$)}y@-(ViTNV?g^x%mn+^%gJmwlap&(0y#A! z4_0zNOY9f9B#BOwZDg5Is2G!LJDipBAZL|4(3v0)aK_60EwO(*(?s<*n^2=tY5?Nk zScr9$u4e66wSRdX<(5I600RuQ*nNOL4#Q~*CHw%drKvCqTe3hWHF`48$75bgS>nLI zWA_F21cN=u%>+%l@@WAI$tv2gA2yr_lPocZR zDUm47pRnjMOPm^tOCif&u-xpSaxP>^vc_aA+nDr{X*nn9)KTbWZ47C%wSLU#Fb55K zwk77mdU)%=<+rM4+ZZ%DtzLGpVgCmJ0Du4gl~;FI6iL(HVV2MeM+o%n>Er$j(|N`J)m zo)VeFQ!eoGl#9oD%4KpNxrW?ltX%Hng+(Hl_}EJ%KD0#64A+U5FrpQ-TCsYxos3c} zRx5gF7+O(cO0mRAxs=uwi-*#*P^~D?7VAiv;pq}>8B-y|_C17R>?5RUL$QQX zEOk(3(D<1lwi9omjbx&)^l0%ON`J9TtyqI8HT)^Xa$SFa#!JZ1QbuCN*gG(CZ-$mI zUYM>>DASu~tz1KkesWf>kh&-fr7}+`o{dy2aix`oMq#2frAm~n%oe36Gbu$T4&8)y zSLRX*6(i{cD_o_Vp%tyn=`c)HW>JbPY}mK+_iPONbiy1AO=(JzD`vz#w0{Qe<4w~$ zjbuV^wPLNdxW!qSC&k_+t`g-cBLaK+Bi1_jDx&_mh3bVJ7kI(wL&el)5~InG$VD$v=gfp>x5#x z#avQp4GC83$kOpbtPwK;AAcc^QA%?QmLOrwyfYawBQnN$Td!>=!wV zae$!|MNZ0MT9;uMdnqF~V>CgglTm#ZK;!T3!bU0W13P-kCU_+)Az5C zVpNKeAx`2fT*v7@I)9XPShg@iIU|+eBE=yvavXY)P^K*9m--e5cyDZow~Bkv%$$A9Hvj~Axr4-2y$58C0N z+{wkg^M|-A)jGW~uK_N$T~5l~UAvwU{e;t`lY;j<-t4>h8QATh+`}>L`GIM#T1SkT z#F%NHld`Idi959LSw-X8S1ZaeC&L9LR_?=gRhVXf=dkYh^?;M|;QtzS0NWkJ!w#u0 z;8BM7t=dUhgMU|DjCYoBC~YQX%@84yzRv+)uOdPdJq{=T+>_?JJp?kSiBTaRir#rc8MpZMNIh#H_Z2E)WF8-f+G0O=y=go!VwBYGAdDxO*1r|uK6&7nj=O5>* zKhZ0(*nbdgi^Yz_%<&B5aq?qu;9*CC2Voh^i{F`tT?lr?;zs7g#Xp4j;`}mXZr)gY zh@Rsd#>?#pUf$RraF@*}f;qpVc{YK>FPD?CM9M3eWFU-qwT|YkX&f(y@w`0GB7B@pSDkZX z=LtIhI6R-1s|Cb{%hf`j-=#d9!NbdVxn0iF=kRbYFFyr5{TjjdT3t%w;cB&zXTSD` zHSLOjJulB21>g6`CZ2u^(U)N<7kuBFU3JdQc^fYVYhuIMZ|C8iJkClU=Pn-J&70Rg zf`6;9?C0?uB0jlwIn3j%;f<~3;iCkfz*0x->#;NlzOVZ!!g&VES&~Dp2CfnN8(3}< z`&)v}y+#w8+k)@4^j`3NUw6&Rh=;knef|M!bgmcGP4NAE7%2F+4k^iZd#dZq{(!zVh;L7Zx0~3Rjq(0KAX5DoL2Qb0{ zEzN!>`w#BcZR;5URjRR$3Gnc1KKFLItLrWI)c28-&)zgDncueILyN&%SNA*iRe$R@ z_8foA%>D7}_QUPYGhB;vUnINC2-S0)F(Lb2=~SDC7MI-~tM0`Fg%qz-tdPB?yhrbx zcf=|-AZQI}F4Jnt3M(=29a{zmmxccIajAI^#rYTG@^$k~<0dIrY@Q#m(o%la$k|dY zUp2Ya#x|``zS(tT?(uociZgor(tqCb+;Tc9-ezvF{m%h`QtulEYc=mm3?JJ&9Y21q zF=*0}HQu*V?pj&8C^wrI{aKmbpzSbbRP67A{!A2j-^u!%J$gs*^5S}jhKchhZmJ)2 zPJ7l}%gA+w#*L3t%0Eu%7N2f##I5JP-By2{91-=O+OYb}_*iG%m(3-QoPQsS_bLnZ z$2dpFY>qAZId`DldEs9JD(?HfYC{?Rj!91VP!nVMFl)BAlvZnw&9cCrIH74FCHC1@;< zsJfpaYZ`OH>9a}E^MGeX%YUv{T2te<$@IdE%VRnETOEjBLbc*3?vG-L>QDe{|y|kCmHSCnufG6gAyry`qa| zUQGQoYwwY#Lu&uNTz~!k;kv)|#s%-VX>#>i^tghnRDzN7%!xBz=lPkL&wclFuIGb? z-fcz91BVxdFDBPa?d`I2htY%{Mxz|Z?6m))h}1tAl-XPBz+hX)myPE#=o7w`{+9|e z<~`aGWZm3WYj)UkbLQ0iF$?sjv?uHBUsyCVf8c}q1^K#IuJ87@j6a4kk+zY|}HSc0h5{m6N{({mc^*Gzhy`T9}I{NCqNsh#W1EUkBhoVc=Nd}f3?+i9P%$(4(%%Imk4 zuf8LSIQOdW>3^IR(;l}bsl%q`zpt>e$et88?&B&4k)S2$ z$0lBhtb1kK=Ud2vz5BjKK0jR=_0Id_CHan8&E5q`?tdi~<^@lGpX3qVYxj8VJpVaU z411jk(;wC=VIkDKYc3nmKlM7&Q++rFVCzTwkb-}KF9Tc3Tay6IPz{_O6;D@%K#=bI`ch7?w8E??#x_c`=x z)x(pTgMSWaJ(`v^)5^0f=P*ZuSTzDiV|U$qRJ zi=u7p$F_}oo3UHqnt*o6#*Y6>tsS*J9U3q3&6_!P_n9%&KVOFB>}}rbwz;mtqqKX# zlsq`~MnkA5!uZj!d0r#8Tf{tZJ1}yL)v3cpGY^U1Oz&H=a@Xf6{_Rhud;+Dr-e`L7 zI)5aQ`KfOWGP}__I&wy6UBlUNmEEO<-~0yjia7l#e$%V;pYPoCvAVc0^R3M^7ww(e zCq9*Bw3j&J)>nbT$>588O5+%iGvzf#|jR zEX_4g=W&1S>$x5nHfSlg5_w-V*0qs3?33<3I<|L*XkC4Fjzn4PP}>C;wk=a(NuwRb8wj?oN1>GAk1tFRJKk>gTP`mzaAi{sZjX z%=ezQw5MFe#mG*)JjY4{fPb(cm~rJ`hm6twC763~87{*Bo^x_dY?Z_Ald>0d0I{^k;b8Mr%^< zk?a~$(6$5hF^@de0n_+t1M0O^ zR=)O&>)yL5vS-bJ=?*5*VX}m}&1^#dLFP<@X+Cgi+P=+(3xs|Ld6MT%Jjx`48ud(x z%Qlxs3U`*i&8_5)lQ$$UhzSW~P&a*Cxz{$m`N-3@j|+u>Htrh8y}j$f=b+pUZA8K> za*au-na#3N-}0f93}x$auQ2o45~uByFlJek-`q+00}rDfRWl;%g9{jGfKiAJt9)j zK@pcE*cT~nZCYTg8Lww!BP66WFHJ&w62$^1u)mJWtl37%NF=pxAAnuh8kUwc3RpA! zu@YeUQ=W{Pn}CV*6q(1r!^2Abn~Cpk2h$qG*ep~|S}Gy}J~q5QTq%1L>Bfiy$#W6DkB)Pt62+4n zD#|TwnT=e}h3Yc=L8JOKCsKl@v7VdLkX|&(gjPQbfDs4 zS^oAWH$?DcEsXuWq{{{4rVGT#0IDbtg8`z;pQ;vw&4ADz+_o3gJpjoMoTQI(7Z#*f zV;7R#e~b_cEbtctIB9@M9Eeh%H3MQ%pr`?(21rW45g|&M0N*GmxB;s|pivwsTL6;+ zOmm zW*5yI@}?J3LJ%G*4`DHsm@;H5pD0Oy>Jr#Xh{PPuYk;qo{u4Sw2rEgL*&KmLMM#_D z*Sav|DO7eIv$KEAEPVR_(JV$xi0>=`L_ZJ*0M)Y($(|z%YG9WI6WG|0NhYYA0e-lT z)1FTU?7@&|6Bw~C&>nFU91!?Kt(+{ggvi#fe9OWEnz`%Y4y+fHx=T>OcT3#~@ppjx z_V*`BbFbGe(kComaP=*xH}J+T%`29#Avm?M+TeM;SQYd}D68?5ChT&8YHeJT(MJO) zKttUX>SD~Zp}UITYC_I{qK?R#5@$H1VeXW;y??mY!J0U;f7%eE{_Il86)SV#xSnQB z)fzu@rp6Ffgz1iQ!M82+c?ZFyl zV^YtEzJYyB`_krF{u%fq$i`T|?&{JD@C^D9|JCm+hAn||MyOj9Dk2d87Y}_N=_^z+ zimOQsUN5FLiU%*MXO4L%ZUOxwu z6na1R*PK-=GD3lI-A`&xO~wB?ugjdELMU|BP$e>^0OgdULOA*q)lz6>4&E6Nz$+-P zNb`=vD@?C|;~D)exHC)RtiL4ulsK-$s>CWaRPkPkTER#SNKPJ?J*v1@h*pPJmsJWb z3s41BZKk$c(ooH_EVHZ_mpm%F7q*+@UX0l;L^S6QkQnX`irhnPg{v@Nn+(?81v3YI z%KLo@vF+Egi_i)W;pe;i#{j~F?59T<)UNjj^nZQakJp7fkpI$$tcihuu>SeD>#*Rk z0nVE6&ZsLcIdf5s@_{*6EHpu{cpCn{5?0YV^7wrrst1XG5jO728>m%o%~{S}Pct>p zfDIEq{0T4?E<4B}vWUXzplhUqR;&AYix!heiU<;3pmCPz}F>>zpc(L3|G4&k@!ILQ>1Jo{&~|-d2PiMroBw$^#>-^((xR$xjw_&yRz^X|4@u=mNh#*X?PM+s34xi!xD;m(h{5&{4sk9u~S=3=1f@cc&~DIfC&ZP8bzCQsBs5 zHap?6C(Dd$Qt}W=7O#-8H3-nGNRLBTJUENuC}cNrXJuLsNCz&AU@B1^I!vk(8o#em z9aKKPB?a}aNd!s88eOZ(5>_T*1t>Su>5x2#g}>TUVym;JOvWhJr#rI=<06>qsG;l~ zpe_F~=c%#`=mG^GYiiO^@Uh}2vldZDAZa5AhcGc!VU*&nR6X)D*dR+b9lzRa~jn02R^HmJsF!=!4;5*u+JftHRXQ7_J&)QI5xQOz*esnk8eB z8nEOEQ7PtyF;Sjb_S}9rO7mPbOsa^GC_3JqJasW44^;`KB2@}gRx|5y5gugnjup6c z(~{N3DP7CUlQk0#?Vg%5Sg1{_jD9LI7oKvHCUSWc?8N*kRMi@b3@skF2Kf4tv&b42 zz#f`>VncYAIk@o@z-k;X9o#(Pen79dY+QaX>WJ!L2X?kbpt8Uto4#CUc)=)mv`1m8 z0r_B26jD?Yj)oJwJa>*4AwCsT&?r5U=jNAa_mHrMyVT*^2~~cEXG%Hx3z<$$jlZ{XKb;{K1jUczBFud_ada2s|lVCShN8}0_guM#g~bu4UK z4&w-b{n53Lxc8SZr#$YRl%K{UJ3)blv0Dyf=2VzA_QTPV$8()XDw~#G=TJ0sb>-|4 z>b*!9_dMuVxJ!WESA?((3RqdksPel@D94zWyRtJG*{rarCmdDONY6HSKiOSn4sG_e zbGYCyG}N|nz%(HNSTR!Efh9MAH9r9=cVLp4Bz{H`iy919u|)OZljJ{ZGG?4klyBte zwevm5p*k|=7i%GZuQ=&2Y#T}W^ILIEp2|d0$z2_h=REPH^uM83Bn}qO=E~hU^`5V(ZIxb7ZP)1fC!;m>4AY z=hqaK%-$3Ip?LBeC?T*IgS#jmr$0NpnCdrmep|5dW`;w071?|5!^@*ptw@pQr%Cu{ zCx5D6Gr`9O;1YxTOT~n%5J~NxyEv~-#7s=lr4JMu3=T=Qk(U)`f&K7|))lv$p)JT~6TVk$1z*Ivet-y;^ zG!D-Watuam2D&xTX@5!wZF_WT2&dJuGbb~YyA^wLdTPXG6J&FoZjf%1xHCpOYQ<5y zHWsBWRXbwEe!?E*q(54R&UQEDKy?#FXJ!eom%J&ZGqQAmwn^O?Wz`$mCdQL%)z7+V zsWa4em*qRT5Y|#6E}0E7BcUfK9y!)#h!J`0>|WQPwa=rmo8dqfP3@#ufHcG^yowy^ zPR1`oDbN=pZiF;$k?$^vOxPE?_zplg(@7K=c$ZvCh<0cTxG1DZR zc!iVQ8&?MRO2#wMO|H&3F<8lX;j?fBJ(@@f^gH#afgCG`gjlKJ(?$?=-u;_!7A-hQ z9n0JBxIr=&Ehb@Pbxx7N6O>41le&BWW^*HDbTqgHZIlK?KnhNPrC?m7SPd2!tKeqV zXX`o^SoQA^I(p>8->53T>I&Qq_pT$T9~50gJEwv>g?tCcjka0+9FGgn=};H_SD*#Z zm$k(8AY&^G_V$i%7UL27kF_X%Q3W>5{+%w>WF?v6%1d_}fL4RWH5!2qPGsZ+Xv1=B z{vfV4352iIjVy+cuJe6L7a0$c8z?Yc3n#8y%M-`fTil64k|_ zU$hd%^jt;85{X)ij5^Q4Y(>2UwoS|cw zvBh?I5Eg4vuUCfrwteW`G3rJ+EhA|5reE75`yKwkjDUDano@o!DCkFk9Bpn$&DEON zBVJf-kGMWOTNa7|%{y}rF!y_G@rS!_(-rrUqHd$Bt@hlwbuGce?j%GC9nb7=o9VEK44?j`z2!# zCe|^AP&$fGnuC#USrIkD6#c$&OBltQ#{Ntg)rEI+(!56)9dO$T5XJ3sr*8p8oHfr2 zC>7J}6o)A?+ysrwbkAZmG6M*Fiu@K^Sf+qH6(0{i5$=rAu|7$hFJ#iLN{cHZHtFvmK-}DUaFyAZD}syCF@l_M zqH{FS!-ZL4-Jcy?X`3q{8ivABLI4D0lqG=+8<-c83cHw^lHIJpU*ZdgtKyB zxM0TstNsN9V2&Vzt_niB7dOlG3$dRFBdl|dl!(9t9PTGvF#~h7w!zdx$5ku}*G4L$rl7)6>@vt}e835#1&*z30!l(GE z7Ch|o_zNknWRCG-i_yGuzrUlfQd*pX4qi7eU;5r3Vn_786&M?zjWuiBqINjwWp{xS zt0^iUpvhvw;iRvXC+SYmT<%#Uu@n?D?N^~#_H|JGN_32fl@Ff#Yj-@H?UUindo)m< z3I5eE#3}?70++&P)_*2c`xeYt4dN_keooUMV!*=}JF@Ld))s!|rEZDgl{2o#-`rSd zIC0y+IwDhlQ{?(_@nn^Yv*Zs26soAXZ= z{qRLyWIR|Z3ncYLzZ|qAOS?fFrNE~-RMY|LJAp!Dp~W zn(}!q`W>7HuV`k2G+7%=)PzwtBxdu5n_pt(*nC9c8kJEqK+?P_;-6i4g^JbU6>te_ z+%=jtLVKw>LH>j9-#m2xB9v+BaQF;*mufv$0yUGE)RxO~v2Gd}_^&C=1Wix@I09@& zOuWTv)s9qMa01rv0b?0X(|1v(Y~ORmK5FJo7XGKc9Pz% zg1X9I731woD`!sBF1KHOI59PU$k$S`K~snM-yeFyzLx@GziA7;7w%<$9BWNkJ-a@8 zuYTV9++w`=HCLQiXPur7v+HI6+wZ%lb^PZ($J0yaFBRK9#rJqOS1mQ;kX!QG5{;k7 zPdHY8zyBI9Rl4avb=B7ZR-Rw5PY6Djn0qcbN;eU=0Q+uqxd8Xq$A6h?-_-=ZpOv}# z?<|jzTRj{e9*$yi?+Rw-1%UqUj_a~f{ZC9Nu-iJ%+m1+8;7N1)fPwusc z`;UX29q(A)hpE1KC&tE$=a(&Z0%>)b*LL2m9Q|+7JA%iK$xpzN9Mj4lz~?rRC-264 z*~3w;*vPg%{U(C$8_xqEu$U%r=kjNKDwp?Ze**Mq#^=NC`(}r}(^H*$&-V^`^Tv8N z=YI38V_y1OV`pq=Lcde<=Ig0xBRrO8FLb=?;iL-Rf&XdQKlp{lSLG$SZ{zxJt_4@^ z{@P8>o6pteF+sNH(96mWx7W``>QDX2vFx9ZVeU)mpSPx)?AAv>gMLX*9sAZZ#EKU4 z`%bR6>&w%7^&Leg0iy54;Em6fJXgwNP6hw#CQk0sKNNzt$P+b^lHE@6B=d@%-7|K> zN^)1q<&*K>^j6c{5H`!DQwMV<(`}L%O}6qo+QwSdRFg?zbPBAKJkvzp=AC__%@5j4 z`$rAeonx7h!M{8JtINf?1j?Z(E2_f8{qpv%XtZ4Ki-+Es&~?6^yVoVeSlqjpqFuPW zEr=W<_mS4Bgxt^(O7V+xv)7*GTWX5~3`-E2NF`P(vZCUF5v6~+fy@X>J+@S(l*n?U zBDvI@BGl>47Qy)zTA0#8kDOR}y*o9M{8&(YOsrzlwFVF1fUU-Bi|F8lm;H@(F*^(~ zkehLDc~JtPa~|Ov@c*v7w(eOqW z*AjeQYe#2e8e*x&$P~tQMd7e8%3tlX(o8HJzexVsy6=esr2=>{sgng8PxJ9T&jN3} z-hR{P>H_X8+o{>H@Ku*tMcH)P^_Ms0wkq4t4^+mASf#KWvzL*bl6Z$<*1ltf2L74| zHH=ts=tj@a_Z;x0YCmE2{K*c-)U8%!^f^Ina(T{x zyibgP%*;kuIbQN)#An1VDY9o$)~HM znbmhHVt=^KG-ls_-w?;Ui#~f+osl{m4ZmF*X;FTJY(5P(y_r%EAs!>TFi9CuqNTpy zm&bsU%hZ=qsR3Xsx+crK6vyIWOJ==m;U`hEv`8n&pv@Ef8(@!}OS(@tQIa$UCi{ol zWde+APH@4j^@>GMn&GHfTmOLsuQFlHToWQwy&UPYMXgwqgnz?5ro_V)F9l|ADqD2& zBL|~%zuTke)q8e828D%{qzH7Pr4Ye6fp!d0#wAT0I!q8pSZj2Qm)0rC%+x6kwb~?- zApqSZ=|>51AEOEJn9>$|>lNoiLW3rbhZZ4E--liBJ_(Hz$x5S27^F2sZxF4QKyn(? ziKm_)HPZlO9ll){Z|xFK>ru&{Z`B1kNi(hb57-}g&_xcx&PDA za@RWM^{QQl@{pjwLj2KYM9qtJvBE>KEHu?Rg9Lb{jGJQP8|N+E(znpcT*E$l$%kVc za948HYigsz899J^0uK1Xa8H~r*a+k@hQlvH$n}+f=1PioR($olOJrx#+i1*8A=SOz zUN)U(-m;x$IA1nt>Ks0%$prx#apzD?c2+YtwrD~1Js4nyMN;y3k&k4`T19d4C5|8sLjIWcNbH2$nqA1JFzuIIL zo}#WNFTkrW%MhBkj!UU}$2uWCWQdijI%iChiOi>$QX`K0DBPGZ#-b5Q6V8ZWaj8%` zjpm~wVPy_>;-m1x|I`BtlLzLAs>^h1(tWue3N`J?)1TQ>Ei@UfqyCV*<0c|89znvM zx$~tCauTB@;O&Q=_ND)M57QXWR! zIhPi`9fqYFgsw;ntcKhARl5t$^ht@T&6rFPstT+Vlgyz8VTb~VTheoV6rp5{u>XQa zS+JzcOVAeXKrGLHX_;Ws&Sce3FICWn8JS<$l22wil|v<+M9u49Ovx!yPT<&(WtE9U+Sr)7vliPxW;g53_#>z2+ zMlWSv4$xQO>s|opiwd}y*;8l+XPa<`Uh{)sT?<4HK#$tlvasgHL!0)}N>YWn_CzGm znVGQOsi${YkEYM<*`^Nm`c(#F$txU?&lZ^Nx91(`AoY@u2(>-l8eroq+*z{yg~Cc` z;dF8Qg=VSmZp9yv9v+jl6E_Hj!0{I>%qQ6XTGi69u(1M)_Mbw1Zm+Bi7)KR*AOs@= z`aIy3*%w!>QPlYCuy}FtrcoW?W#OH$r2lnBWJ{6n<= ze)Jf0y}|y=Rx{XIVQ9&3yhs*#K%`~sb5jl^1j`QtH+*+Ey-6_m(h0glfa{^eN+|Tq zC!|hJu&)ECIaY++p6zszLJunFBYxD^`~qjJ|6RUP8DCov;xmZpB&liV z-&(m;D^&rmd8sFbXhruUqi~TSjZKs2OB=SL-X%_5ji=`(Sql0iNh<0mn~}5Ai%5;dF3_fi zeCDiDoFL7$Fl;$$i75q%bjZq7S+2U}B9mjVL|m=5 zA@K)*-PkVOMmnz*INqBH*{LbRbbytBSC2i79Jt(?RQdZXL&^k-%v|9J*`0ACk6B;{ z)Fk_CRwmWiCu@pmTm&*6N@-;)k}FK8Q=lC~zMc8ce0Cc|HFy}A!PB!;t`WIzjiO|i z;Gec692*(nG;k4z72tw}iDS;KiN#yY@@Id*Jx}1YE3^vsp9jJI3)6?}Yc$K~s^vtc zV7mKo=kLQwQ05_61(cF=klqmqS{8G>RXd;$d{*yuFy8mNt==wMAXVzLZGkwp0&Z)^o6O zHG@b!Wh4ZIufuiWdOQ2ex5_4sW zplU3h{@XYBcA)2VkV=25Gi|64f5!kow;(SEqdBkl6vqweYjMcT=gMF8I9Fm}YpuhGDY6EkFbJn zXXrx{c=ML>y-JPw3JG-_XDUZQ1KHx4Qcw2+6~t$V*mFHt*OWGI=Vk2v>!5-Pvx`lq zRjv3MaPz?5Zc>-O>9s?fVt;!W`bxNXwptKeKpwidhQLH|1EXA+bP1~aJw5d*T`j|} z1GQyu4F@Q!_KlIFvqZKV2Q)Momd@DYKk7+1Y0-S46VGugj2eVN_W^20R9>wGa4(@;L_pBzXif;m#rT zg?`0Nk)8GOAu)V-Vgwac9#*-f8?LeSHHSIFl24y8%HtlF4xJfq0XHY3MjFus4Z@8K zXS^raTPF^C9}BgXY#ZyJf=rwqxCU+mce@1GZOv2X3RsZ|o;~aS`fTb4r-CAoLiB#! zZ|Yy;D6@+Q-H&-)!zW}1yaajf^Zp4Gu!Rl@;O%ShC2V;Y{?7XefSDt%*LN9((EX>F zwM06H3TpSMU=-P(aBTSZrP@;d0=!k}z@of7Xa1Oc4CdMB!emp-;|JD21lbC#vQB%F zBdN%TZt4c?QvufsENc*Uz_+6dxB`vlI5c}9AD*AID(=S#u4qC5yx=`42rt2cd=9K{ zNc)?#iGe}{SG1u%;446cNEWAbk4@KYQ;z+|CLVjICLVztUS#x2Aj#<$0wJz|!gPTg z$~1NbFE;UqowyCV$xaQ+P7)JtA@ONhjAr5e>Twl3BPof@1s?Jl&CQI%1dYQYSSs58 zXofL9RIEXjoOjX!VoEk6tCKnGjv?T+V@M~L@U2+$+gr4gAlPc3-{IDwhhGG)o|wTS zqqi3dofJ2;BR{kXzZ7>k_e45vthitU&Yph(EdFz1|V$r{RD_D zn9&a+;XwAkrVUp%feT%;6Csx`??`btLP|yh0wr+k!RtT(4js60SF0WiWq5Gd+6=a6 zH|@+R4QYriU_9XMa2Dff*M2wxSpa-ZeEb4TY(+Ib^c+7tw->gx8G1LJ}W`zwd7;3M7uXDjQpO38Zc79}d zBH4S-ks@CAd4&yB#d*DwYV?jid z{O}0v2vI5?ax=ad?;fVYSNJDmYG7HJzVyRi-L=#MJ`Pm=G}=3E&3%dc%Rdek~G!J>e8d z2do2XlX<~Pczo%(&!CkhpC$~>f+PrFY7^1#fm%Dk)OH7RtWEWa=#k}VynvEE1?jz3 z2}%|KON7pXMrS@lTEe}dysgJ0m>O1y16BknhjF3y(^P@veeCf2w-PwqqRYsx6>3j) zBdX735(IR5lCx2tc!L;0m1in8f3;0Zw+x@tyhuJOBzm;Uoe!#y|0)Ll`VcHTE4?ij z$0bz(MdYREzBFg0h3^v_Qhziq#->``sh_a~?0#`fvdX1e7V+yBhS=YYMEZKIQLT05 zEA?Ap;8X}QmsD7mYgsb;WsAx}^{>DI_s=2b`wCTuz$v<01Ge`2&J#5_dezF*ivVz5 z;J(PO@=f)nck1n~LRn#mqUK`205|xBbVmttHBo*%1BjHMKAIukFt+1}NBLa?(A8f6 zjvpBsuaq>|0to!bRj6Axy^A){+AqP1I!FUyH^YN*0` z@?qyyq3rm$7!R19wc33*M!Ped?sPx^k!J0poiddUB#;~_U-veM-S=C8WE_^z*nYH2 z9tS)1i>XpWpc=elK7-j$^y26)xR(=}nXXYbJ;Oci0TB5sxl`=zf6!_5qyg z%PAvHj$(oVS}sc8y~!Yo}&% zJHW=tHuTkO{2SJ3m7D;)C~l9eUw`0J1>X?lNchbURvdI60hzKbi4%Fb9X8!pO=C9= zS6{zzzJW8ngKjWRx|(rCZ8)IijCnq+IWlZ|K$TC4_&%=GpoayWYqIX^-A~jJER5GR(eGswd?&~{XI%d0 z%S7%K_^yjYR|1`fF4Iul0Wbc}&9BKjh=DGfv&(Vt>7}C^AKCA-Ba!{9zEZW%=hTaJ zS8FYWhWEk)@t?26j@V~D`|9F@h1G55ZURfY9M0FV%-DKgUeNSEC(M#9P<^IOU@@ecbt!R;tM~1v^D-FpjGqwk!S03YYO0U{rBgJ_0C^N zi-U5StK+nb1Q7uE<;CuNLw|z*s(L@M@^in`As%C!%gM}ilXyl$qsNPN;eO&quqC~; zUbp@Q%xLd@)3GZv?uk)UXioX`K->87vc>tB;{WXs1?YTP|Ng=Hv3LhyG+_K_ZNG7!v;g9=+jSnL`*7|0jiLxVc!a#q$&m!sS zm}T760oxS7&52O+_oNY~Fn9vY;FTkq*PE;q1A5sfh$UxlpSSO|H@E)T8~>pB(KpJE zV^;#ogfx|n!a}ml(_DUM{pg(wRJALM*m~$DH=d?j<=R2OE`b@Vzbv zzk>k4{3bt=!+ST%t%}k;KD%?g-xcm|cT;}-bYmCuRdyYp(;R}+v$f=5aX9mHv=*yh zOtK?g;`(`^%PxlD{n-BU(V-I?t;g$rpDnucwKkB7r%uqht5fB#<{P;^9^asU`; zt%)BR+C#aM19HCxW$O6*~BHOuIf0j=kfq znoA}Fo8{myO?eW;x?G~KDuY^XGC_s#e!zBEHk~~YxX~!aS zdI(&qu8WG|Gq|jpjxC4B%!C-B>6Zo_wwCwIKwOuu zL!+?RKs?qUv<10b&M~)Z+WavD z+xC<8igbu^hnHqzjhP;vvJ_Np22N>4FGWiQkd>aBI@N2N{|73l#9;0-06EWqQ36XF zCeTFB*V{qR$V1QD(brbcOwLl#TTs#0(bB;I!U+b{7)C)jPF|!lw#N$w^pB#Yk`WT6 zqY~yS(}N?^gOVUQlbIQF*=gyYl+(bY=%<53DP||7NB`rZ(-RYN;9w`}$H>S8!w6O= z0QK~w^fZ;Me`bw)>@%T1t2W>EWkJVxd3V>sj5XRgC-%Kr z^E68L5r!P}B=mPd3gJ=3S>XuR(%~h2cV+I^)pV{@g!506{ylmml9f%n7udwm80&d=J?--l;!soI8_yD6y9a!4L%F zQ3PDBWe$#{sQaM+;r!W;i_Uwew{y)E8VvMzo)#tNF1?k93O#~&>V~Z66P)7v_YiJ% z8Vqb(-x3wq3*IH>x%3Klb@M~CkM=W58=`+qVm$}lHWkhV9lNx@sB+0$-h~;te7x#( zwpt!JG+7Q0Pr;n~*EE7o#rcVs-Ab(bzq8L_2VrOEP#@?3N7~$jswb9YT5K)*G}^YN z?K;(|)D3S&V>lYcNJR*0WM9^Om5Il6+0Fq4be#jyPI~2gLS-k*l}8P>NL8jS3T_`K zFs4jcN#r>fnSe}%FuBfi&tfeH_UU%C6%qyQDif)h-Q8u)mO0Uh8TTbl@Gn`^cu&yK zf^4|2@kBX5#G1>ubw1C{^1(vs))})+GqPXC-_!~;xdfVEPIW+uRt4N-+qQL!>}I7J zPLQr$n~L3{*Pn_bqc!V-#j)1z<78@haCC>~Y1DBSbvyTm8pR6aUt_b zpMYWmz(q1`yihyGGvG^4bzHg?Gu5wmM7lkT3$#y06$=&z{+!%{+co2t(=Wy+C#TO_ zH!4&n8ybUpp2r^}k-8TFi>X6LRE+WCL68U98ZWbr{APN1Jd0*}PA*TXwo2w;fU*w~LcX2?1#;VnB2tJ&TCUbm^{86?Y1y|EAX&^=9u0=ms6)U)P+zQ)ULH{r<_TKP zvEd05Vc)fHnGmt`M02WOTLWtOAWMZqsem_gXR=VJdq!Hk+yC!ka-0+XoexwWwG(uq zZq_%NCaC^G-N7?IBwLIpemb`?=)2j;*I=FMdU3@dQmn|5a4Hm_9^r7-v3ro{c-aEN zXncfq%5nCw+lgwn*aweDTgW=3%vKcYd9vNt*P1ec#Gk1g4^`p>;8!*>bjmwb=QeV5 z3b9zguS0sb#wd^Y&vnQk3nPg8rj%80{z601uE%$FPhCs7v_%^U?Iq(FhN*A8>GW0R zfytqDdbe>++`<+n9NW9xGhYkgd2RuEsFo$S!M5{2TFANS$r82Yr&h8inias6#$=BexBA}=hqNEof7I&6MC(JbBK<*P(Eg}2BSK`9 zulVTxFzfdoiBAAY=8yQtdYmKA(gG?;`VV~gTS;Vw@pp^JS92uSkhkrxbR6AB?9;D^ z?Hy5lFdu2hnAk|y#n_CspUYM)uTeH+Xfh@wl~dQ!8c~>%Q>>#L{p{cq1oUc6CzCuc z3y6HWtIaUgqoNQP+YJFU4K^ZkgY?zNgPrD@_oFKu0H@UiX-0CFhXTXbPxPWs^Z}+_ zavTPkXgj>1`_U=I^h8bFb;fili4?U8JF+Fl;tV-%z8-kbQZ-~G1zncB6C4KZEzjn^ z%MDlbuPw%OKh~$3*1?4vrFIVFbJZ8mOYOFDky9m?h8o-Z58QxXq1yjeAqOI?MwDgl zPPj5;s&m*zILjJ6l4~}Vhg)#9&h4rdG%S^U=FCk<+y>#GCG6ds!D<>d65B^y`hZb~ zhr;FT>$76K-#T7xG`3jO6W6|6R>J~_+_`mczpatR=Jcq+vhZw;{nj_(!YdwM59GQl z2X+thIY&}q1;F@?2aC-zDA`tt;Nh*yeoqgpVXnW7e(ly2W38qLIr2Izf5M2Ofrpd< z6frFBfCVp#(e^0M?8FbW^7{VG_EeV8>jod`Wi|A&ikB9mAqiVhn@QC&O~%ZBM`2aV z`fWBUiv$IZ8n2uNS-8cdOKB4x?Vm@;(MEmNO-9w73Q&LOdF?g7{~HIcmip|0rHa`3 zI=O_-Ym(1=_Vst}4G~w09J`d)z%ZHdDzqlVH+zb(`B4xS)!iF|Tg~pMZEo!DBVmd# zi4oXQI+OU2y(NLK8adjynbPoJ?J6_Ul&qqz4CRSUauZ6e7mKyd*32L!wvbX0$xB4i zu(IQ162QXxI#5|+rq+QD2rF;oSNYk55k0ZtAsh_E76TWkr!xfZStL5lKk$`sv0Olc z5fns;wj&~oF%EOG2--#u)!i;O^2;UCfR)E^0Pg#=ep9Z>+b7?p(2W+?ibAU!VWE**4k;s&wGi^JDBG6 zgUsqH_So5jHOC|Tyg-cLYZ~$Jq9>2Fx3r7U#eiWTj&vhefND>loob9o$Qa6VeE88Q zri*eD?$XADPu;U4prDHG#GMksPB|`IGU>7-dZU`bPSskFP9%E88kt5 z8~}ljucDUjn9!S9Bx@D_9I?uc%@9L19GVejJjfxIIml6h8S6hMg80FzN`VxE;xCXF zisBxI1oId-Fd!D-kB>h*P(r4~8<3z$%!)!dL}WR(%ME|DIW4v^BU9JK(-;u0(|6d7 zCNn&Lb@ckr`a*%G6e)1d&6f+NBtC0%4TzMYQa-;|#2Y@LFdRN^jGMFqlgn`F$B|d<@Gk?8~RWwSJ%MSsMNQ|CNSwrP6`^t~-^6emEg4G@r z@xEM>F0V_tmZGQ23vxq2cxEnla8S4}@+_d16(%i>>o#Z@O)zaDjFMi(m_4TT1o%Q( z)r#UFjS-6pha?#p`ie){4;zkcl-1X1zT|+DT{>c66>Mz4VDSYwTPn(3)YtqrNGc3> z6cO^qkl2mz^%KRnm4>3@j$`0V2~#{OBla?sIzYr^dQ@b)7Ku4yOqUpIl#Mn{csd^B zXePL<6{s=nn=`K&07)FDrozvF0OUXpF-G%)>9O)EZ}=i#?b6b2l-wDUE<+n7G#pH8 zWGJ#DR)H9d_{Z>4%B{wSyDCr9$*(y?rUTiC{y-ln$qgAxH#6h=@t-{00Dntz+}Ss> z6<4Jx2r~^sdC6oXCZGj&BgXoCCsjlfj^vUX*<%I3B-+_t9hP^dkk@4h0c_?p60aFY z(#M7=G#jwPk5be)sU;IhE$Znkkagwp_aGGGo52rvs{{9u&^=L25 zIWpr|9ApTZ5F!$>0V0xWAq0D0vJ0u}>k-ZctIKM*=qSe7vOwBtifgNajOBOG12EAS zyl^wvxhfU9SsU9i^5$*BfPJ|F4yqCBGr`70Oi*iP!;^?%`IrdtHPbuHE+}0aT?(cW zc~=Z^_&s;$U)DKVe8&%(mXom`pDe_WED>+e^BBZFjvkCSh+{A0?rs00hlf^&+~wB=0L&y)^JO5&T0CR~ zXbJH&a7NH@X~5n137YNQ22nGRqRAaqu6q6$xJ5=rV0mSJZHTd3R0LKtB7Y?L&sRxW z_yp0OXF=Ir!8zn__#I#oIg>12vmMl-_G!G`go%ytBxYRhEYyk9ASHS9)OIJ4%d3)Ca2;F`&5Z&2ghZ zwRQH`!I|A2W1l~7xm>!Hp2e4Br2V6gti2dVFyaF^|>679vb@2D? zlZSF9;GU}hwZW&`eRJ={@{?E##Fgjqnjj{EeR5)7b^%*xUg!ZQ(Eaw5TU1SzeEU#? zVYEjmIuhmz1b1!8GreAgg6#|~c>XlGFvW3KR#@u*P;%Z}5;eQY2+Hh#73=-&bDAR_ z>mC^bQliIg?Shwnm6LsXy5`yNheD%4PGv?mGd|0V#0wN-ChHCTdipzcdB<9Esjmvd~@lZP8e|STz6HTq!>E3Xf zSg~)(bghscpYi#I(9j3F*qQTk|JC)JXW0BZe>u|h0s`+NlKoVJ8~ZHAd@y6K7q(+D z@FkWIKRku7v*bLSkTIz&X6}c@@$Sawn;gyu0Nr+rxX7$&MXeL=YaM+$qER`dQyNoW zV8ecYAnr^N6QNJ|nX2AYud)BG_8j@Iz$`t|`vKsfq;TF?;2}i!#^_B^!n7ro+{bib z_(JMm5-v;n1(m*KcZk~hXp5U`Z^`?qEENF|E&NPb2I$2%+I9|LN(TgiN4X3uLK`%I zunm6p!dzLpI%@(kuIL}wW0y-kKRRiWeRbXDPIzslbF!GyCGi+&)}@n01p?)T4(cKO z;vpv0XUB;E^*M=IMZP{OaFZU0wH|z=9@9{uhqBk`HpG7n`-aD1V8AI&60$hoR|%p> z^bZ?dO~PVjWzjc{UN6y;i^)Ujz&;mxZZ)vZMw(fG{@r4Z8j zg3WNk8m;7o5rK%t=iAu!X!q;JqN3z-70Lv~;;H7&-?mk%la9oWLC137(KHnB`7?R) zZ-R9~{5^h=Hh5Ytm-c|Gz?l$qB!L}Qpm=KVPssFFS#Z|sQ-zl z&f@jB>S9F}MzIZU)3k`R$I!q)!f+q&Qhms;FFh4w26+{N(|lkOfa%~3fj+qv%Te-F z4re-6TGo8~=2w_V)rg!5z32VhC#>KtA?_85?u*;ls87D+_bsRy_U>ZQzJ6 zO6U3#$;N^&(ilLB&5>j#b{oac?~OM`J&zu9eoS-e1pAcJB)yLIh$31YaEL&kn<*K6 zYx+oYwpno7#rkG@XmZ2ljtkG?%$Tyk^jhqA9(l25$a~bSVTpIhAK!Q03}@E*&^7 zt4mw61yS#Rg_Wm=h4kp{#IG4)v<1U)mEjWz5bMX)b2xruhb+fuy^qdoI9XpjOJNGqcY%f0+x{~IHdT8s{wlS$Y2K*nj z%;jgh@zPoDCj`~p`aXP8y6A)irMAQh=59XG);)39|<_8}=n#!qnwrM(#?~xt#T7ibwhk|k9U8@ZJz12URH0~az21`au!7uE` z9e|<>x<$=o3fmduce2+|lQi}+rI#x6*r%mcx}MQ?g@E0F#mu*>uS;ER`v|9CeqUQ! z&Q7OunKDP|@h<-cqd|K)%PiDd-$Sy1z~0ls@F&^9+xC6OFC6vWyQ3uR7-*3v`)KM( zZ(G2=XyR$|rtvH*_xX!;+UKW%cUA4mA<)C^B&2E=^%%5SCX?q)PJNLx(&WLN$M|{C zF+O)Hv=c8@W_Vp9;py#nWT;3y(rfym6y`B`9o2G5T_7de+ciX_74i0j#6SIv+~@J) zckqm~llQT`_YB{M{ql9Rm_biEtP3zXiB60AKtz&INmPG5^M;~dmqm*ab_xnS@0IV7Xs z`5nknYCzAqwyIQl+LVyUa9D4C&p)9gE6-_@iFZ!AJQjRP3A_1<3 z+9jz&6=|30k*>ww-4+RwZK&sL17hN2P3u2H{IqkX%j5J)Q>a~KAFX31NtW5;?$mFQ~g(>3L$Jsi$N(_tsvtHK5jIu1zCq=*-tB~J+I{7lr;FGRV zjx#wc_55^<&Xw#!@kgZj-G2hPnp1<^J`O_t1oniB8&d=QiIUL99s7kkz{ zERN;*Lr%BnY&BE@XlPlzKW;Dm*Zx9;Q$j3V>4zBtjbikzg9a z8~3Lh58z8g(#gko66RLxH{t?IQG&GCJvzLH*kHGYn`S-;W77&`@{R8KGZ82eUeUj-^A=C)U-0Y!3N@A(ymxgGlJGR4A0)e0r zd2>Q>0mY<*v6qHRpc#3E&5^c59Bxr^f{NVQ57BOmNdAG zJS8a#0&5&a%%95y9HzYddaNSMrw6iR>yzTt39z)L#-;Ua4kBqhGU~+H;(cuno&3}w zeX}VE6)zEw;!*RlA3PTFe^@k?D$n2%T`8JSyK1s?BLRw+W~db08y+gF;l~IFHRTP# zSZ68f(3Hg8AHF;(pkSWmo8y!}s#&Z|3nocV>8id41BDJ6nr)5=k;mX@&HnSGVJ@)I z;BS&_(JemoH8Oz}a*-wbJM-XaO}bgm=IozFkH5-Ngx8*eHs4*T9Jl({E&!w?;n#0A z(J3KX)+pdR?gYV9mP{})zGheq6arKmFM)8_I8EuHBeHCD=1zn{or<`Yc;TCyDGfN> z5K^TP6e0{5KD&C@dvt&4J)S3Ig1^{PJ@=`j0UEG|{`yXr1JSu;zP#rrY|MCUkSly4 zWS&{5M_s7LiX4>Izdimv7^y~bSNeN!&rjaCLFtDS&Au9HMj3a&TWm#-*|p(wb?BhHXYi4p z9R>gwq8U2pV|l4faM&sGMdYVfN~ky8WjEp6={A*OtH=<_S};*e9IT@Wktvjt&=E@a zpaH~CirQgxGk?jAp^)l5$Y3|3?{ z=iFrxX3R*(0a_;_l3pa5)D;?Kx0x`ZI#uO6Q)(66sC+|Y`n5)Ss7<~p4U0{#4SV!C z=+g5L+CX&!y>QW=hr;Db7QavubOJ;!NZRomJgLd^=gcVeK_KY^0?&1%!O#71TM_`X zeXj(%dkKn0ksksnbQZUFaKZt8mg|KTv z&^Ue{91pGxW_L$U66f9#!SWwM{6hth-VQ}m9ss9(Csu$719piSX}qspmalyuKOBFG zB5EX#Au1*I3?HqEGc1Ck@kxU|3DdK?+n;xi0BnI(t?p{uOX9@7(4HB?Ilw-5BTGPNCI!8cI2!`ww+- z5G*LieSFIwUxm)iu4S5dO@DcDP+9x>54D>raKRKz`R>G|asGPwzzE@Hq=Xn(U0TON*XK$WZF_#<0~i?I`<)(#-H0l9QB?(=O7ZCFrEo0a#F@j1>>pc)jLX_ zC<5o@C?Uuha8@rc80@5&7%!(l;h@nAPGCzSlsQLcdk3~7XT~p3%2aUCbLvJ|ez+mp z98_Wo!kg}3jX~C*<%uaO5O70OhG6vIi$+);Fo|OL<)Ml}_9-RsnQO?Nv>N9N&*Ym)Aa^Gg={9N`*<0S_nA#A4`XH6nF%5kx&ir%y=;CuVYgu1pn{N{$lLkrw35 z_uUkZjO!JSY;}=~wdSY+Q<0h`jR;97&NwfKW&CWoC9Qw7D+4N`yR#G~MgUPP1F$Ey zVZ0xsBq3yUD-=?xeh=}b2AT+VhoitIm6LN*pjp* zMbj)Fk3VkD3Ktt7?i|oyq^eW|^jECv+Q6U`t|v78E!*KLNpKk9o{u zdiLLhY1(t74gLCAW#|H&34b)AsYU#s(fypjLJVWP@xP5#E0G^6;Oi97T0Z#yM5qZ6 ze1#=D6bLF7?7IoT2-@?e6|c4-T+Xk7z{&kBCj8F-p3~c-auqy41Rp*X(8K(<jsVlLAl8X35r)vY^RzNA|-bhIao0wO0-qMkzsrt(QK9MYkd2(L3bnL6N{rRGMIl z3Oi%(Og$CKBppJpRgKUu>wIRD<401B8(KbKdgPMhpWWvIm~=rjdcySV?r;ySpAFD{ z2pdC1QV&M|V#4dU1StZIhGN2tV#g=-l3*B(6RPejcW-%}8&CCWUQ@A@S`6HTc6I^6H;zUyUv)G@Ck=|k8Rhi9oa4@G-}Eeo+?}j6=sH01Tjsb zS18o!SXe&~1fAX+z;@Pv`~ETYzg6HmL|j3%;B#-{QlAfCm&Zr2h6P^)Jw-ni&7bQZ zWw`7oYNsiROQhqU%6u_lNP&T%^!Z@b|glJ?bY^#jYxtW{*?E5aEVxm)in?Id%eb0 z97@W8F2oK+hYAg_%?KM9yy;IjnSwW@qr0tWc~nc4J?u17eQvThU=*6+YvQk9>bacvCUo<{u83o&A7(b=#KCAj#p1XXw zZag*9FA0K=QtI?!kHYC(HJ`{FY2e7({f?nu%J#!yr{Jd0Dg9Q@vY1L%ZKG!lP&S*2 zy8Ir(^k&%{Ep|$2mGhRcC0+Ga<8Ha~n$5FE9Fj>@W*wIbM`iP8_I@_T&UJ#K7lP%v%*Izt0- z+rQ=BdR(lpJ2Z2@HU{4^bEgzPcC+%n&abm_7if0`Tpt~d9Y&!uuRa4Bjz=&Pic%E+ zIN6;f+O+7=ZpHm6{a&xv>GrUEy*S|qc79(=#t}!p%zkb>?mdys@%Hk*Z-Mg?d49dz zn~i;N?&vB#$y|WXWK{J%e(HUuyWEwf3FdnH_!;NEe^#B;emw&p$J^JRn+HI;@7Vyc z{$truYMx8t0X!;1~C^q-J zTmHEbAiO^HevLaxujBLanc%d~`@WZrllzj=$%?Gael!Bn{Ji}a;Q92n^!TP@!P9qN zSC`Y?d}0H9GQKv|eXXp6ZAt^RmC3uyS0s%~zM+_q_x zcz(g_%pKYFk*>i0P5>MF^-m5P)%bgg;4v$Jjz?JwW(f%?e! zWy?0orIV$-yJIbzC8N`Kq11K6W(~Ek<*%g*ZbY7{+0R>kF*jSfZ7C zp01Jj^zyr%_a3Ct8YlrYB7zg6MG>BCZ)HJQet&pBkl|ymEp57A4MexFsq%cUm+^N- zk{Br-?T9aXx%KYjb{t+df1HReX;KS8<6SkmsZO8nt7$^^e=p40AvRK8DVpV|({L zP6gkI%-;g^%1?P%@7p%@C9AU(?+&^Lhalv6AM{oj& zn!As?1!e^Yz9GNKfq-wKZ-j5(FX}IrZ@O=SZ_;nHZ<8;~FB%}`n;Xahg1#xf6+f~* zHb3fK+Fo{Ul&|lJ?>JxMUUqK3XMsWB+~sA`XI%5@z;~GHrP8OcZlrCyt-o!e?5)#R z%OH&L^x(zg%V3#x`Wx*3er|(J(P8l5|0V6EMO?!D01zF~&G5Eo(m1*rbb8H~uw)8! z5>r#>%{Plnj-G9P6qA4%&)y5kFI}|;mrAe6zhrPriV3Un(Ly_Ob0OpMAqxxh;{dT8 zle@@ZD&G>&ihlzpnG2t0iIJ`({=MY%$GhePNxz$}dw1U7|4oE7^LPprcS8tD(J+{D zqmRgA2biPJctefMZ?sL-l~l5Faa#%^Dop>S#+JL(zoQmyETQHx%o2Vk)(NqQ;7|?la>yY*&OFL{w?qKdyL@ zVnv;1(A{|rbrQw8LqEn7?O9??h;b$2hYlA;0Dm0ma9}+S%>&^Ge}v#cqEC2=(Z`Mk zPI#taU6in-P;OxcdBN?*mJ&pdqTxo=a4r`iU6?mcs<%k}+{nR}@&73tbPBsU8IKhr z_h2YBS6q4G8baD>tzap=LV!$>dJIMf5&;=bI;dR!b0`R&zaYRVw$Pot>$m)07^?xP zY5*VfNVs&_qjY%huI8-zA3yz=KD1Dqo?(4dEDArLBG=H2l{95mpxHl&SK9AJMfPLn zzZ%LwS~dByHTQCm#K{^2$>iDi(!=>k*CwJRG#@aq8eOKi!#?fj_yKN&p*{-WI;>Ir}5KWv2H=c&%wO>(<4r)X$14 zL2C_7vQHpIs_b54T~Cv2_2!JuD)9`I>6p4ynqBjGDecg7G&Rl8p#c0l z2y4cie!fILA68lY1V?OrV%30h9u@jNGx~x;e3GYvwfmqm7o)-x)|5GK{uxEAmKXv`v63k?h!$L9pvpf>iYMX|BuOU_1EfQ3oQQ>s z#9R(Xsfc2AqsTTR`hxI3K$mbKOsP%}o*EkyQm<7P1%sv1l6(>Fvk9AW2F~qWw)o+ zRfSV~l{ZKTOOz^{`9H2P&cqu_fEslaSB$Xt13FDUik$iu862}36p6+^Qrg8};!uX7 zuiSLz7TV5k)5@OEJ!zcO0kovivtmC%9gj&<@pJZNPBOJ>*+yalI;vB(?4#+D3#3Id z(j`rW7Uc-sNqJ!JfYSB`rWn`J?r@fQt|>_L8x2X^&03e2oLNd-1I1E47<}{ zSVqZO&Pk@(13UjA+z z@JLJ^B(ONBILg+TXUfC=UMoHk-~c8yR5LzKY85#L8?g&h=K(9rw6nVFvM`-f2%RS4 zYk&`$XYM>5(B9ufo3zHxt&4hTb#pv9O+9VN%1&e65n*MJG*C*ZiIz>L3I8*|6wcFw z6P9H%i7BKCasf$W$Ufx1vUXUIK`AarVeXiH5jjPKE?lH&1f)jI@*>oW`sgJTIngu9 zm7!43=cOO{Wzsi+-msYv+cU#FL#SiJmr~!}YkMeE01{hw+jIta_PtjK)OqV3@TU+T zP22y=YUV6vC?zD7rO1C;h+BwGi6MSu7c$=`h}h`kB*PyrAQFMQg?_e?erA6R=WR$p zK6I`jG!{Y)DcGMtNMVTh(ZK9f$uH380xZ*VoZ7%tUT8gwwHX)^Wh^9t3xRYrU+&Km}9=IsI_uu;jxu9ONz0HF&aC>Y8gO> zVx5RrWb&z36JX5F|MWu7r_pNR?>OC9+hoy4wWhYU+l5T>4pihI!6I#JO;5p@vlOuQQ>8Q2lP7HmlPLeI*(P=pchr=F6Cz=rZ+g$mO^^YWo=4~9Ta8`I{oLj1 zj+e2^H6eT}@E&)6fz0=C)4<{L@{$BR8)yR!u5bU1_N8r)6WYETYOF9?*jjn|-U!S^ zFF62CMicCMJ7J1?8)jK(F7yVCCpq+pjP@0RUGE}=H}^Vvl3{-LtKxH2eLZG(;2t}z zUULO=9KhQIs1vBS)9ZhsGCe;Z9QHna8HsJYRP;P9{|?M|jX?`;X?(lQPQwH5xdvK( zw!0lpvdCKv4GzKYPes0#pUc-&&|kmzmXi&DEWW!j7gqaj96mE{m#c;`-XK~=_N~u^ zqm}F3GbjD(q}dST=^59eZ=!taQH1uo^t4OdTb%BtU!tSAl^mX((0@k}_EW_~itfEE zg1z*uRd65_N6xZ-nh&c&M^OS-IR>|xUA)K8*zmmeQr zb}LVA_y*&GbSJM0r#XLREWF=+;Vrs(+74sDX5%dt6{~9=*UOqkN?P?IfO4bVY zd0M)?de61%BmE-!wFLn8q1ZI~d~VmOrC*Ohf4TY121YqQu2%irEH7@sTgPPs?l&p@ zvcK2*Z9Lx*4K!QRUMf#kj%{yq+mpX%HQ`J1d}?p<8WIH|Mk=UGJztobo;P~)g%|0{Zjjryd zc@!}M*}<(#2<$ls@>wA08%3atVDtgS=u%c?NoTt%rx@D0qDDWJq)wSicH{-qTQMx3 zb7u1o^A!GSZEg4ZzTa+cc~JdbYw{o)8vl|4s=qV8?%Z^6pth!&%~OS+kB zyhrawD%7|uu%liJ<7qGwJAAmgQ|k^{aHNQ*jvwUoIP%yUi`>-p92oKl=E|Z~8r9~P z)*GsGZ?W7&6;oW9R2kM=>F%Yy2i&c0bs5-8iq2GKGZrLi58BJ)N+YGo1Y`Y~)agHrLChslv$rV4me>8!R2&S^T!jW<&%pL1KAUPjCTg1rms88P%<%_0msnbQR|7o9^ zM;2nEh{b!HA31E+rxR{Bq_Yqok}rXyh}~XEdr)KUK`x|U3>ksPo3M=qn^J88Qk?($ zB?g~RO~YHJDk1JrtuBA^BSNIIJFX~uiUD5XdTA+1ZA|p9n%mf&rT5%Zr}H*kFy&#O za~h4rZ)L>BcBN_T>iq*JklDM_%w=SiG6p8QM@(IAvaGC!=+eprC7Y|H^?x^gUgo2sFf5W3uPh%T?gQz zXPoa?KLx_q=U%FTW#{#Pk(719|^2bz? zqR|(!{49(_YatXx`)M^Ety&^_96sb-Hb``jk-XH)MFl;mOiBSWnGTuu6Agm`*ObUP z!cZbusQ8zf3M^cbFlhpI!td!4EAoKpB!&bOjQmLpJ!9SFVQ7lH{nAu5797drYFYdr zQoY}15Gs`%*J$t%PJ3bT0M`^>xQ`5*oM2SApI41y8*&Er?^~q+e%yqSQ>#4EFlmg8 zG~N9M)!k%ly?tqz(iaHQ2qq^+MOz@0pO!|B?h>SqOe5F_JlJvqEgb6ViE2&DI^?-? zxQU!3pZi?em0fzip#`j26KfbIACo*U=bx@@jz zHiBcZD5bT(l#&1BA->);T>`XJe$eq>97^5HV2T@eu31OOf(jiJW#o7;NHKj0Z?Ily|xNIZ1|)k{q0=v8`H_ zT{oyC&g6wkMwK(l4NW^_J4~}eiVuy@6q0Zwl7)F0s94Oxt0E{R=jGaVgFbxXccOol zWAW?H_T-UB1)1Hk?)T?O2c%~ttm5(M{e zT}Y|%{KelJTo5xKovi7wc7pw7`YNH^lsT^4D#wVbxTs^ND%9JZ^O7>JIQDBBni+Yf z{zIkTl4|U3N=`ojt~v~kEkX~^MamST;pQjV>%PiZzvG)QrfpT3+8Kt5yx)qwUGPy_ zmJj^pdVWLq4j{@_Wo={$m8AKjIrKe5y?M1pW@4Aya7Gp9C9V{9WYColEKzdb$%*OuALD4RL_xG7pO)S?sFtw7&@7Orp9FWn> zK3eUM7XhjXaO0)J_)FqbCc;Qu(??riqMcl)YpB_OBl>TlL2x%y3*kV!*hKshh8|pW zL^I-F)3dn49}q`AY}^(NKLSGnqXvQGc$GIKhhT?;!_Rlp3DLlj^eLkbIhIy57RZGbpZhLS|e8x`o4R0L>1*N~X@L6Tmo{_<;*R1O~& zo&(Z-INS9XXj}^iqV|6~A9kvB# z!32Z6xXGf%m4oj7=hT8o$Ij?{N1MP8zD1Nym{J91R$*I4BU}J&zc-x*9)_8or;kF@ z^6s^(r3`6UHSrwKh10%yOx<6M{+3T(`3aOa+oG-Yp8FNd<^IMg7O;sIDz?5VVEQAW zMj^Ke6S^Va_Rz)F8!~D-r7eV2F?k4yoAU}Pi`AOjg^2bWPD~l4VoQWkPOOd{Rky01 z9~Bm^2bOE5RkRF^LHO|0h{FDk8HQfyjOb*Sg8Yr4Q+?JK(YX!cPuJGhSkVoECk5WfF0f~pF=m&$9TDPzZoKl`^ai@!p)(W~FSc$c^z`;=rQOZp+Gm#P1+ zz14a?KC&*MUhMd2b;Tu~a+axUe^JcCAw#=*9h^ije~h?a;Zg|L31Vl-5Mx&)d}iGb zD<>GxvtgBrkuX#;D+_Ir9AXCPSo(m=p9cyfOi%qN9P@Tvxq0lSs!}*dvmGdR!g%y@ zq;b$gC~b)0jqb7IivTR}AMc;F1rlR6_bf_9HKIqdwy!EAc{58rQ`Rq~czz)>br33- z9#WHajV49z8k>A6bq&AIJ?!K^2Jm`oNBwLNcg!!E!9#5I!8a@~lwK*bu0a5})wMsi z_cu9-XT5_+D?3olTdC;zemVk5LG&{e))xrNyWQZH^T%gh_X_xmnFlKzioSm<8(jWk zet>CUxJX`t)U;pwfaK+kI&=QSpPx%YjS_<>0sIUOohDD^+r`z*Y{Lo z2hFRqpqCPgoa<})I91ihQcL)$cs@dO+}(6P_;}pt@NvBNs>t@?eFn_S_Z^5BUqF60 z`Swxm@Oo@!J==Tdt?;M(xuX7F-)4QhCb)V6E>b!a6|;fe&fe!dfMxT2yrTB-;^g5# zc9rNkPGD9JcpiG=jWwT?4Q<5k-Rtgp9{l>Oa7c{2&3LNY@-UB5@mSjVPWJ2j`WXI; z-e=p}<@*(NKdAru2Z&>+y^;K$%-;M?bRFh>{wsKHA>ebkG7`t(b3T{1{c#yox8-^B zx1-m9{b}o0M~l6tfcz~#EV*ug1AQrTR+%qm!|x4mUw1uALHm}YW@ztA?KhJbkkrs}Nh@js7~bzR3I zGxy*N9y?iG^?%2Qhx^q#n13zN`HDU2Jz-x-kG7Qfl6^R|7O&)?$f(sY4AD&;C6$O+ z3w}QFsrx(Bj*$DD$zPN#3M3n?A=s{UUA!IE@(1A_9}p|B{OgyY4ZPf}6J``x0_Hti z2^#<76`5F&`41Qm?pL6MFc=iJC4foUmA+O(VJo#XCp9Cjd=p{+ zt5m$`kn4R>MkvV4E8KW2WJV<z zon6S|B*eY4?|P^ko?kz^?sM6HWc_$p(mL1>{Ns7HTL~Cs!+-tURNmCzGq%fhS7%r1J*?hoxZBA-+Uh* z-j9smc8b??x%fRsH$7CEy)G9QwI1KUA9JP|gs@37O$5G*e0jz2``=soc0;j=hR{-- z$n*n!+W}a{F~2|2m<0X81cP|n(Y#KaR|7gu#Jy1019`)o2Y!6Ps-74Yw-6jR7_)m| z3P7tn&yTP# zU}vCimyPJXZ`VBi|FatvuvB7!`Tw+gYq)8=knlu6N;T1SVosZdt?hP!q^k_E% z3rlcN1HH*TlA|5dv5|f#3JG~5ogTiC7TUASD7m~$rV#9*Fj0JV@B_#X>3b52Lp_lL za`Kt_ndC)tj!k!S>MVN?3szp!%^Z2&t&Gp!s-SlHrcJE@D&YD1bMBh2PG5G_m4jw7 z9li#zqp#9-=A2c=cmx+$*09mAX0kpp=2n%@;_cy|NItt@cUgFu6_YNF+jZml&%vwB zBGD>v+lL_oFSohKeOf`>+og2QieSd-)phVvx^yPEZH}#}gufx#&dWt(`@y_MPGkxp zJvBux-95vq#Q1F6s5OIC)x0lbHbsQOBqa+7x>XY}k}z4X=HEXT2PhL1GoI>$*K@98 zD3|y%P`8*jqs{L+Yz468$>|ei+z$-8_)?|B>hzOK6WwPSf^JXbs)}3x5K#iO*ewSdELlJg1^PH;$D+=a|?jxLdqq>CA zI?YA=xK)L#a#>gkt9p3)4?4Gd^?BS)#Bo7-sdG=iGQL+3myBWtnP^~U-ezuN569B8wGJl{pK z3;z(8)f<`*F4V34x_u0Uxb{$8t zxz)2F2Ke!~&>?DQ97%!kjB#m5wNVf%@>8+uc@*2y*eqNYFnv+PK=zx$T8(p11&LP? zn+EKJWo0Y3#6wWad!p_|(7Stkf z$cn+)+!+nZgWb^_mZzzIA%Qh339lN&LV>%w`i%!DRB0Yw6%VD#kt~yH2D;& z_?{~R%x)-t78`KY50I%u^#-R}HG!>%htNC4%jJ-EgKDeiY8TIzgLFXjL=?s@XyZfk zs!$0lfIw^+#YMS4>vD1w9xr=+2&!RE1Cz9r6^aYqRY3l|LL~#fr(`CDh{q0kAZrVH z@K0)AY_hft5{(zEpHk8-=FTHzp@}mCJ?6bmhy!r_lb=8kGiDJ2&K+g4BDOS&G0YJD5I#n>HKyX2F(+ z2L25Sq^*^Q!U=h;J~V}T&_w=pgXz4>dbe?=H*jf~QvcRWlMi9g^!5GJaA84zv+>WU zLv?jCItFZfF!*kztS`Wzhw2ArIOSbMV|w}5kjHluPn$K(1It}q=gEg0||P?vV~ zd7WA$j1!b2Q`A&LB)!1VKjfEdj#3Sz6^&x0-RQK@u@t;y`cE- zDnMqE*)K2^iTUa*HYlknLX}pjje!Zc`Z=&fic=(MyeRUlq?CUJ3iI7rY)DdsNYn=8 zh>neLmrVi9s`H@IcwxoD3K}M`Qe6-|GxB)I#q3CDt)yq}3O6-=Nb&!&IR7T9g%tC0 zX@yacY*C-KZ56``ox4iyFeJLWG!k9900FLP`CSxNV2Qm$6Hs)ZXZFI^PDzwtXA)tq ziV4kONil|b(8WE0R|&JU>r^64_9tk)533Q~e+6^W)m30Z-zs z^LQfaM4}^dxF<*<9`e(o#KUex!eay_b|RV2MZZ`V5JF{Sk;y93(kt573EhaME>- zi9=}d3h-~0S!%Gw-J)JCQeH7hp!JN9ix+{tl+r0uUO`DYzrYr=0(*}rW1trEMZKz| zyt0z?3>}~sks*C`60PH1flyi{ty0f%-caH{*nEe!!eIAt@i5k>iDBLFSrB({r_Pp0 zzKV9rL+6Ag2?atXs=tSs2K6mAZK5gvn3Lg*a_V}fJwZJWjMdejUpl01AKT4AZ3G4g z;rpQ7h8cofvoAe6kz8M(Zv7V3*TT7nzB$8ryYT;-b#mT3pT|Ug1>%!7g>?``-+$)Q zT)6xP^^w@pEf@vNOfdFpJHW zUS!2Jj->6=KR(SnKCc=nY3od%(tC5RFzKsea=;(%D1Rq0fNhZT0?1u_y1^G19*ITh zQldG{V&5)M>{JBl0heV(VY-Aj>5QT;(qZEYz~H!C5Z*}YGk98A5UD}x9L%_2I&PeR zUr~rI{>_9%@)KnkQ3d#QTuvOE&iMHLZz0g|-zqy)F3Zm%(5xmEg9kIMmg z5z>Y3(urb92DHI3!jrczg_OZc~@K}-Tqqoge1rn-!8_B8(@ zugrOoDUeSTT&`5RWr3+e{KKX^?cc79WcD7x-14TC8B_2CWKxfp@RM!cd$hd-65YfJ zot}0M{R}^|tG$)3rHWjy6FfCnOBjys1j{Co0Jjh915w3XpLZg~i4LkPB0<4t z)bF>?oVbrKp_5mDtA4Ss@jg$R;cGp=FLx>p046*)!|=oG*ef}SR)*dyQ-)LKhKY`&w0ykK{(WL8{T(`U!-^Lode=i%lnMrLRIJJN2a*YkHLwDn`Gfp{O_ z(BFKcIV>>3S=NjCHRHN0_up#;lARf>nH&##OH)J6nI+#+o_y)Ql$jP5r=`}m3fPe( zf2bV7tU^MY4fII!j4fu`%*hAJL zq6`0~tsiPL(tus@Gt2xn=x7D1pl<~*Y-1$s)WS6&gTMI#u1a5=v+gF1V+kS$yKpKrs_WbOH!e$A(4(Di_xsLpdv#>Y0}bDs)J^RtSk)y^<*TAkOz z`44EgG!gv0mG|a2gsrIX{n2^Cki8mL{XLXi+hA8+)cc@xPX%;dE$!54y)4~tWo>>x zTn+E|o@n-de^?(4$v{^#T%OP!ZGanSI*ftuuEtu8wV-dfY)JoYeyV&5b(ip#^w#7p z^DXzS{uFx`>&pKWc~^Uvdv5`r;x%--&s}D!+zr$;n7-< z{ql#@g2bO0t-qxkwq7&k2$45wvE{Kbl&{X1F~462Gp`q~PHAa4vW3(c`L992oosWu zwO;2}v|AIzh1uqs`+swbw$M{epsi6`16QUV4g9u@U1?i$M25%>qL)l+2ba`O{O-~G zL%C*iP1tMFPjKEbJppM+zYm6o&-oyQ?}g{!|9#kj##4Dnwed!6-`mg0Y1Q&gY zZE$mS1slqsv@NIG{@Q=*^k+INR+fPg@<}wN3(yc*D9BPK<%&yUVIT#AhT!rdmu5*q z-s_^MBmlM)Nk^&P&VpIj?K z`p}$oK0NsQN+RyCqN{*rx=jB!IGlg83k|I@^jK?GGI9^FcJ;lMY2b5PDyZhIEs_}! z=PZR6>6jCWHTix%tnV1pZorMm) zk~#bN$U!m}s!xh>R9C{0c1qV}97_g!5nf_eb<(zefEVg!tRSivJE<+q(Gic&fL6U8gC#Ujl<6C&c-hHV8Uf(vm>oV$PHC(@4 z1A84f(&BR3=f8ea3V5w6%T%h+^swJfuZGbGy62?(Gv zrl&p2)xx>gyBfMtRKaOHx^09s&}Qsq^zZAgcgNuW(haWv!WIOgfPfr)a}6*-Qk<{> zIKXd*Y*qDa;NPp+4ZPRu*_5V+qU*Jw>3cE?|E8qabq-6oBo&Kii3B!A+xcQ?DSecM zeU~Du;a_>Qz8xq<7X0wwF)d?li2W!8f@U9Fq-T`{^)Vxr74@g^3~dF3=5Ak4hmc^- zG4&31&R18TUte!uU7WUO>AxG{Rd4Mlvw_8n@~5w!A-mim-@sGCte!7>s#svJKU0=m zu@DPB0<`Kc0Ze>&XI@q21+?&xe*ssql18O?kN4{2OK8KR4sJ~UVu!rAv8fw*i-z`R z)fBvmIWwc6j5{fjIk02OQe_m%gxNE(q-V(tG33Ol9v})FlG5?v?(S1JOtf9O%7Dei zN|Rt(~ORD@t94!eBXTYKxhA0q`_n+nW24JJ$d=N zP`9$t+{>ATgUZ%gSy3#)!q*Z?^GW&ZZ|fA^z)`81jN7@I0k^s9O9m!BeBcy_FR^S| zR4{B(o@6RQi9e%UAUIr@f>0*3e*|DNH`PyLd+s+}S!rb+&E` z3mV+z5F>&VnL%>F2PfhSYQS`Ft!TVB5a#Ivb-te}$yKJ40rb*lGN~G7W{U=pxkJg~ z+8V#r%X{>hFk>$WqmYWqGq>B@cf8!JyWN(R2OEU`FioH{-h;(UfAJz3gGivzSs5w# z1_vhJ4dD+*f^`fYImIGZ#7e>Ks>ySN!|!g09kP9w@CW@)b~~aDZh#R92ad9+ySie# zY(@#DeX9)$i6_XWxudnHf9d1fxf27CbCRi1F;I30L=coK#ULdBH{6X7ib1G-H$LCG zZT(ThS^XZm_{TRqJE47!YcVx8(yq)Z z;keW7qOJ`WWpqSzuH|CjaL!+t7{$l`$CM&_mq zi(|^$B8%C9mDg>Xiek{947RSyJGFkDNePDB2Bc(<6~! zF-vT!EnFk;<$6<%%0kJ(OH0za?ePRZh>HH&XDAo#uq72J46JzXU1`8 zAU>kjXqH}K=msN!>lf|^YwjBRDbC;I^M&{+hW~6rr2^&-;=K7=Ws2VZ3=y9%)pHe2 zqAj%wH5B<^9G^_EIB^Bzl6CPI#HEEqnxv$sMFC^s)+fKi+-pFoh?KJHd*Rc#!pY56 zK*P7{g}P%&bnl8sGxKY|4gO~)P0c=yQCwe|Roq&u zCIFgNOO3?!G}wxvLCHw3bdh1jy`JsVkd1A8z)w8OC_QA}5<9n6?`WU#ZZd$il%$@W z4G;b&M99QwVAPKLW&pP*G|JK0@O0q*ah(N2(rqcs@x-BcZw`0K z!*kANXaMDURwJwEf^|dMrY#ko+a+NLOU{vHGK(>jZ-9(m*$##&#ioQ_35`6F4%O>) z+FKrM=pTqr+y(N8S8awnn}(^%M9_`0`A;?&lD-^Lu-*bRNV3rKN<5B1r47R7&jfJd zcx(c(<(Gs#dOjgGL7>2j6k1waN+<&;%_J$+iR4{RQ62>{7pmcGUq@}v4Jq{Bm^cQ2 zrN5^m3f(j0C84;eED2G>gRD~NnQE~3&_(~xa0)RSy;pV!e?Vb>b)KoWhy}=07%mQI zVC2cF7*5_YiZ%`7DX9%bFp9 zGSNLm{I|oI775wHIXGIKqu~OWZJEC4qe!4tAl*mlxCL$1y}3c}K4)+h^E!TBGH#wm;L zfDv1ry65}+3!D%oUa9V^ym;~1MgoH~f4|Iwjgc}LU!)%#=~}g0|C9?9kBw)9$E*w8 zZt|=GPBsy)wyjg{Ki6NdZZSXsYx;YA2~)#DQ*;@FCpBnU-z0 z&hWX+nXIe1tgBkbSM~i}Zo33(n~={AUD1qQX4@Ga7(Op2f`C|YdE7Oy9XRU4BdEM^ z0lV~*{h>2tZLNg_-;EIs_Z7BY`A!vw3{BXoBiFoS@k`IV#Wj=UOxHa1zQucPV=O)e z#`2k&B}9~%o0cFA3uNH>y*S+hMbK!5uO^%H(P9#K90!6P60M6L935^DVz~&kdfp~U zrOB+!t3?HthP1px`+Bgt#)i)#tv6N9R+He@a2C?)EL%~D0>COd;(=x zq-@t<3^eZl)Di;W0|=X?MK^Pwp_nY3J018_s3O%4%_3p?M2CFp<-6^h`h4nx#dYq7 zK^r#hm<(BM72|8Sm=poPjl^wUEkU*DAn9kvbFe}52(v)&_je}^g@ zWIEterbM;p<4*UG;?{3a>971}j>AtD2Uw?vYYVcoQg{Ng9|hfC&#-Aag)5Eb*C5(} z=0f{%)1NN-SApDvf>?>~gu{$9DPTSvGiOOw%_)~WSVZ1YFXxk(V)#SD4!e{|6Mdkz z8Zm+9oMX$4|C-rmzhTlc78p%E?qlj5VLlq^nPj5GYrw&cIL2vkk@I)VVlLU%E5T*M z#D;`YcmV(rI$(y_j1@d0qgxS%D(ShM;k^l?UVgTaMK&+>VD&iQwg84i_IcTg>__^r z@SahzW$R5CSS?C34}W@c;M}`x+03;6a<4n83h`0PnWEq1LBRD~;xXQW3>*r6IL~*S z{hfcKWqe)5dKA^mi1)leFL=~|I7t21=-Q5TZ^a&XeN%Zg8hG8hxN^m6!UWYd*y9DC zhT~oDPdCKPvqh-9TbaqT0(Z{y9B2Pqw;W@eXdOA+n^o{QvRjaTohQfRpwD42<|d-a z%bwuHaWFDI)^&3Ab)l=y4T$A)as9D<;ofqo_A(#7+e9Rh^X5D~o}&M`OXd^pCZKZK zaS;eaU=3;8#^-xZ`TOManUnn1%x0&3I~Uw+M`IueJUJZmzG;;xl;EUgsy;WU;@ofhPh*#7E$NcHOy~pz6rDTZqRn>ayb<5AK9^zOzB+P9~#P0Rro8P{2GkZDoaaEOhyPNX&$Ewrw_4+ihz0vS^c6tq1 z?(J6P<#r4upj2%8e2f?QS>SaWIQ%&9#P`}xh9=16cyjdmYRicDr;+}{Vpi;MXqnT8 zPVi~!>Pa5f7guH5Ggw z(`?NZKkic$qqbZ2s}(sX-(+oLPjv1@wx@G`{C&H>c(bR|S8esVH5P0`PW9Zr0mAE; zSJiEKJH5}P{EY%$4{R>JjZ?1kwTEQ$ery9vb8g?hJb!TWKRy!hLUq6JJ#hk+DbX45 zc`3+)tNUh%Isr|HU$3p2pDjaWzu}oKZjdJz6%D1953F2$&5li63GG;%mXVADnM>ZR zqw$OCc;UT+nY9Wu*5Pr%`mGOMS&Zf1w7zG3q8}r90=u9jI`c%o8^4CUV`_)-esiy< zru0ycFZd*uQ|>xnW>bs5Iswr`KDYD@2E>j=tV_EDw%*j#JgrV#kA3dm-gGnudd!+s zN{*>JEzP;oA_k1XqsqG4Fh3a=X59lmZ~4SWoiK{IFrP|Ld-04umAsiy!b1{2#aIa8 zN>qF@`)c5rDy_E<><#wSQ|W-%N~s%C^tj={WnvsGJu6n8WLD8#SHQ{}Hze_V8H5pK ztNi#&+}9NU^zRNtlI%Cm@aXLzbL@le!5KvwXVsfL6KcMmiBZaJoEVy$15*O4D8X2@ z(8O6^H#d8TjT#`*MI6y3kNf(Ts;a)jfyb6qFqjNA^0V^stwGiKXG%h+9)dbn_f2Eg zipT4IV7Cwcg*P**9`J4TPh|Tz@Gj}YG{^Gml674|ankd%lQ(CA+xhLKr_ev`Q{3Le zT7T2+^>bPANYdce=h5)6Tanx8QT@hnIi(()BHQC)G4b+u=gmcufq{1Y`J`q$r&qU+ z-O+IK=hFS+a>b?DT5r8X#pJtcG@ses70>&@oxP90cxEiX0Z78o%j&@W=f=jS|IPuU zdI;b^VAm7vPNE-?)q>N!GhKuAMfi7!z@6Z-PoM)5+5wr4E8xu$y}})4+s|!x^O@3{ z^%InE2pDQV=yeU#MX>E}-}`p$>y70LLe%rL8+zUUj7&HjdPDZi{Eqgn>J3jcjP~sK zPVI~O1?~$c2#i+T1NKetQL1-vQpZ%^%(1_rU)PS=uo=*|;10cCoZ!Ff#pR8?6O!QCn;kFn@%te`4Y# zJd%S^P0tgy|z?zehJirga2N{TN3oT0s_Cfzm|O+ zH0)Q@=wRlXn;SU)+E3Y)VL|*u6h?-(p>zv%UKMk4B1M}LV;OY-YNTkHI|0eF6ex}2 zZAeX}M%BJ`uUSIs4W>gv@0BI3EE4P}$s`0>R95r5q*w|PT?QsjehG2Q5u|Y!Sa2mo zBj_f}nbhst&}K5#d3M`juGet&2Pf8OEKU5gFx{8*rSp7;|{wCBl1Fp6m6U_e@i z7ynBSSB@fTbizhe@iac_k^#uXw!b^!K${|eT(|4qM-HDcDpB{UK09tsUTm+@7j`Dm z6qc4IjPnsP+SH4~Z!`oIAwfEtsnGLk(HE9l;6%#4tB_4{Sm*OF7;j_sj2hd^`jhq7 zJ=QUfO+uLwKMj8&4)Ul@U14 zuThwK2d-aRa)tG%c*rvYt8uvk*X3LlNF|9o&6AT{v~(Clh$WK)Qtn0!WWockvh*`6 z*?uitI09*5dwSs}e3;Dp->;?EySAfl)VqiDBub36vNK4EVqdzXi`QDh=qFXAv5{Cx zzNd-13S}~@F;5$FJOgm7Kqbw=m945BO@clx(B#aFV_(XYe5epPRu7h}X5dH$wtx{* zUxLhxEA3QLC}zTMexWxu_3|9tzOGFk*{hh~IBB1pu96>C#w=FGg1jX;w&>zL{U*f}NcqLFzvUTEMSB)=ap?@O3 zLMsb1%3RX`GbCN;_ka{mL4TQUF^|&6lUa%ymchiXW_kHXd-;2CJ-q?@=eQa3IxM|; z*C``SI9-ow#oeJ+{Q*l;Gf%g(l~qMzZQa?&%xi@uI{wKr!e)>*r@6X8SGaILMQO%` z>`Hr6MU@{)&?u11fpS#j^9p_jG}j~FW)yLnPgt!n&X02RB7a-OpCmF}2f2Y_HdwGhzM@f$xboP*oeIG%c~G%lw%2C2!C1fllw=UHX{etLey@kV($ zO+rY=5;rhR^)O@P@TN54dF>$I?r)IRQ@{hRVP`ql7E=iBa2y=-4SUoJ?y$F2f9f1p zap{i;d<$#p~Hp9BpDtInjfHfje}J85M>*ka_UEN~KOo3CwdOS?D9jg2im0O=n7N;=QGZz7lY@Ubx6y(s`9x{>ld10n&k0o|Nf2vL!#Az; zIfH5-)TA3POdIkwe|FS1VrE184elH!n6mk4X=9A`36w z2r5oX4iBn{C<)lFpzna0dGNC{8qTc%tt^MAkTJ!OVRyk+h;?Sl9LguHDIF;>EmGqO zChk|yHQ|x6!mNg(ke{)2t$0iT*I#o|7UQatDGCV2;)Ub`Pnul7j|lNL6yE~2;f^Y< z$IfaH1_z~NVYZmgRB)dMf;WSOyeygh5~N=MW43jxa7~I^Ml`8X9jwQ-pb{%%C87xP zpc?3isOA;YjC5zG%kf+*hSNUb!fq@SZC4|pZ?uLZZ?_%YVoZV8 zrD@f8tf)l&Wz2Ki&KLIK&XM%M!z)67S*>%7=13J3a54RWsSg*g4WPLUa31IzC%)9o zZ~@nrZWr(9?R!X&<5Zus<0@&KS$(J7#I)-H%LWhMu-KJQsv$(T$qxYP0geq94?FM! z8lZ3Rke@8_mV4;GnJQodaHX2bwjy43^}(6}>Cw4nkvf5Rb4Z-z8xolMkb_Jm33lRn z_1a3ce=BDoRY;9fzhI`9#%jNdBf?wYFGk4Af`bJEJSBc9*5c5BLqlTESB>8Vwb-rlQBr zPSx)(CV?Of9=x=7==2k=04JQ=127H&C71c=0>uHiftDDcIWAo+pUbWtFM`EQBkTZ8Kl4LU)@|$7NJchY<)uprvelZ< zxVcV*1VC{mH_C0s`{|(F>g(}>VrTUn9OO&=%GX93>a2m92^gPEMh}euu@#c0^}YI=$j4}h_I?ML5UyFA;7YN7pApcwn!-wL2B%L1Tb*cPvE#{~szQo03|U{Z1)Sq45rH>;!tCIQ2& zcY^}jrr|(-*B(swij|miq`8bV3Qhw%Sk$2G%GNagwq?YUyx(fVZp?8IY7peNU1%eq zVt%@rivAJ)$j9TE)2}k1T|rN>{>3++F1a}Pf&dGSE7s3N$PeibL6Y%T+L;Zok2}DU zo>tFa>K(>C9|_Zr2WNw{571mg|IaqC?mv!M(L%s+ZZpW4F|c>qn4e&UH($d{85!Im zH#*#u5Dg(`=3RgHQxS6&7}`NgGmNz|IdAxC8a?6O*Hm_{mcV>5t_%PT^*vK(0H%1l zS46^4ci{;*1vzuR9x3!y+_G=Bk{a!#{?x*2ppvz-FcqnkAZvOpmYyVT0;1>dg z&#ui$Ezxhmm+7^?z`2M1RSnsGsysY~4s9Ksi2y-g>LoFXWy57l8WBb zmdn6wFWfG1j*p}4C)bV!Rhn^u6=7Tf+%{(&t2I6U#FE%|nX9&~=TB!w-IbbU-Jig( z_fa?B=Bpy;&zVQx>qm1=g4g=5XUVJR%O9<-2N}SsHy4A;JW-&><@Os9;MXGl$p zF+gtmb9<}7>#L3G((|C|dUId)-VIC8$9=eWDo`ghhnc9m{Vw_jP@z$289x1XRZEsL zUH$UXdBuP4Z|*$|FgS?8f6`F5!F^r*LBxl<%IdJ6H-dH0+QYx?EBml5=nK45v__3} z!PkAiUihe$*bZE#=Xsj+l6^JH=PjT@+|=)~d;Z6(@OVkjxisy2>UCtG|Fta*jmech z<=t#|#49Vfag8*lzUi-gfBGiKwVD=Di|eI7l9Sipq45ZcaPxlIn*RuQR#h_ucUD-# z@n6(*)3}0Dn|-(XbG{FFR!2}8p2EXBe(I5P)1muFyS3&f?#wa1#LyjVi3N@C=nf_B0#l#;5MRb!7*nKs$>b_Vhyf z&G})ZcOQ-8cFzV>iv>1$Os>Y8ZwbCm6}TU~h&kOuArTyR`Q|lSSSYyvJIhPQy7E3+XB6C(?y7m|f2;7-d7rUn9Fz3=Y{+$!@De}X z%YO^_>#hHOM8sL?`7n6Go;RJ7b@%D-mYL(d<`_EzI|~Vn914;Jc7k=+^O-QiCh*iH zEhJLr$0Prg}bqLOQPv}tCBDGIA9=SW9xn<)PkNN_V_LO~r{~rvZ zS&;`o4gpSHtJLf&0N4TlFZwA0fd4N_u@DDb{BI~lO9J5h-`tc000H#Bp)E;3-G4!% z6u=JQ`yegJS|0PJZyW<8#i0R!mqH^A82j&>M`-};50w8M0Xwd}X7H_(p+O4*LY;Hkfn^a08oHBa`@`l0`>Lhm-I>@X~93{ z(m~e>>wfSi>HASm*60Xe`Um3t6WAtHOEUon+mf$!4xi_>RwlCM!`hNV?^_9VT{O&_$z7Bf*W5Cl( z;fZTM3j`U63{um))x^#W_`%LjR;gu6YC)t_^Uz_MGZ1Y`i37rxoxSdp~Te{ zaRe+Z6SAv&CC2+K685FQC*2VF=M3Mpgki{@VR#h1Qq8h=+KAWnhMkx(T{2Hf0f101 zV@X(G_R)*xD3e=LFhvlyY(d^lO`J;=w^7m&H{U({IBGZ=y%GIX=N@YDOy6tEa@Z=W zGYT`-ZDy!P3fZ3uE(OC*FdS671dpw0|B<%?MmD5~aQzVDa5snq3=tDZw@5O9Q2-&O zl=DD}p4;kWJALRs6BzD$QaTgg1;zRJb(1(C#w%#}Xh!O}ZLfD%+aEIDF)Bi{aF8s{ zT0}(%s|qfCiyJr_7;`AmA0jm|6VtCA)g$DZeGQ!5JIBvb?4lvr$3niViU2l6EQADj zA*-kvMXUshXU#(~(s(&?OqR3orTZSkylW-*PGlVVO2o<7N(?AT#74sB4rG!o?Gs?) z<!+oaQYim&7%t(sv+dKWn?TqJ-1EjO@GL#st4lFc1%su(c&IJ* z+5Upzx`;N&!!|=0uc@(Hgj>jt&onu*;lT=U!wUD)bbdZt+ zOAPtRy+LJ{&Xo4HhOMqMN&|MPcty5z0fSxZEp$RtfwrXv`+w^*iN{pwb&SBLVZR&s zi>#MtB=gGQiNy14=W~s{Ta6N+%XPh?fv*0hy_Z>z&S$>S>vD9gCPUUQQbsv8gI3q} zE6MpbX10yw_E|mlDes!@e?>l7SN!WYO=z90$vz*)jZJ`etGBA?Gg%~k_x;zK7~_3X z5Yf^yQcaxeW#!;yIDK^ZLFLd=j#KPKX@U~ZQf!Qp*UzsgKf(XVyRhHNKp-$6|I4Ln zoq*^6QdMC~lYJH>2nZhx2ngH%QWa&&DJeY4x6+-p4Z1pdcG~*z)Ev!x+C?H}$tGsp z+U2AFW;)3Hk0Q)9l~)kanbnIa+ca0+_KArl)F?Id=s1`>bZqKX8KoQHIYW468S)b% zCa~GK=i!yo_ZcM`Xrdn*g;8c%O@ zpmkeEI2&{luEeR$QnsPf@PY0NcG`u!mToRG9 zc@bfNd2ikNHv4q{kPF6>C6C5@4GQAvN7)J!X^67|O(7%G_jB9F{^Ae1YAXnAC0@ih zBj_QiAX~y)5;?nx!R%zLLl&vAvqGT;l2oQ?NX#r$Bt0Tz;A(rNyQ78r=WayDf2!2Z znTv%a8Lp)36<1v7T^Jtl&v2-rDYJKO>y{eTTYTkrOBsNS<*&|PwO*wR#twkQ6ukcSIt=pr2Wtu zmY9jaj#h#fXa3cq$;a;139AL=FxN53Dgvu0dqYUB`Gyj^CvP#9BYfk_7H&fmd|YQj zJ~oM7*GYqVOl#oJV+sYEB<1xn5@{kKm)xQo@#oNt5axnU+-+nMwa(NC>KsvGWpaM@ z`8o5GaOSB1B%(W{FmNDo$T|2PXc0h2oCyx=nUtF7y5lIOxLOe9kWj+-Egp;38jl>F z|NQ%a|4b4U^`Es-9grX<2VHr*OJf@3nZUcE5DS5@HrcVyp!xZkPmrxA=<@BZv~1B8=CT!BWuh zkztH*gnXSSXK}l~^M`JC?1#F_^7Xm^JYP<>L*zA&85)_1>Drb*IuOBb$ep1!a0gDg zrqY;7yjMj8gY^s}N+Oq3KzE8X*OrTK5kh}Bmo|voHswq@=kclcRc}-FNj0dehl;aP4KEE3VEJiXnVIHAvzj$ITle1%ld2*R=AX_ahQH<%&EnG<^hO2H?#PDW zJc@Iu6uCR2m1)>)PQ!zzRBO#E(ug_|n-vA>Z4Xz1X&eo6QNBMjYqL0XQtF8$+v6ewc7K>m0#sBggV;( zalQOJonh1z^fv#J$a_!zK`}hNq4lP^z_q=mFX;4H8%4PLQMX4{)a7PS>al)10T18p zdp19&wxN)-zCdtTg4Xn6!;Z~J`+|1S0O}jX2s*<(7QHnG=ESyn>NuBC; z1{-KZTRe+w6cpsg=O+~|jpoh7LEe?8TtIo7e|$N$XT>%KXggv=7Et~1$^enDMbgx$6e6*)K3azqLoSg;Wder-5p(LSLLN7Eyy!T&DqH9h57!SRl>_S4gEfp}X6 zX@L?84_bp7fe+AzxCddR!;*Q-F$v4f$=3A=gvZmxAB>WO+YlT=kh758l9hs}Y{AeK z!X~aaO*{bUomB)w2Yh23)&~zyI(!aWn-wQJJr4WPCTu83Zz*PFAq(}$rMLF-=dgky zr~(hn&AJkh(*W+RAiKej^&7ScVvy28#H96F)KEKn{3RnBg@q%P?)8<|WWTzeSTlp* zqc8YgWFwep4q-Gs?kbTxWiQQbgl!UCM#=JjD?6J(b?iz_wUz99%}pv67XnDWZBjps`m9a;@hGJ!JF2Dp zV{!!Hl`e>Bvi9G-t_n|B)g9n^u42A9$f4ElS793b!QXBXC6 zhEEVm9DvLe2!{<12rYvyqMq}o7 zJeiQH`p;)h1y_@Mr#9Qw`q%i3~rD@ke2 zV{?R3C_x_Eljr6boMU$-E#bod%W!@&Dt_9E!g{^CSwTs0ruNoVB17!saL96ULi=&s zCH1)k7_7j>N#$BR{97wsby#xeb9@5$uN6?%>_EQ_Qf~B&iB*zMUBubpI@JM5oI}l zrn}rhsB?!t=ecHbb}#v1Fdg(HUct&StE4HGqX1msmbAIsl06ln*V2tK!PUxH zVT|ZZqi95ch?FQeb!p{rd;Q}|ef{`EY>TI#hXM+Yw0~zTxQZBX%4PXAXg}7Qo zlFAyc5_wS2b=&=F1h?rGTc7&hj#p_%5rxA9(}vpvW#b~ z_8cCTPGouk7W{agSldt?;Z7m~ByY8?)*WBZ}D!t`x zI|aEw3^`i`PG!el2|ufVy`AK>Ng&>g$>~x@dk>*uJwGVYr3^a=FJ#sE>0?Y8e&tnA z08_EPD_xHz$cn+d4YhE+3?y)zUDLjX;ol$YB!tb&PsPZSlZDxZZhNf-jw7rMYKq(? z-LgNYlttybK4FjY{n+V`*w4&mP@8?RkMbc4L)I`Aq zdrPwYVROW~^3rkx$#1V3aij3kF}0jT^2d7=Sh;gLgPGOUpi0zWGHQABaK2DJZc5fw z9#3^O94k9#nx9725Ypt)`L~=Vk4`5GP$kycxY8fmX3J>h-u3baVM&zK`|+vNr~~o9 zhJ&o5gE#Vm=V!j%nMsbcarb(Bl!VoFY6iPd=@PZ@PKxsU9J2g^cKsg-5mctG&bNQ5 zNyc})aY?4yCr10k9tl9^7n+FSo}o1qNDYrI+L3JY7L_DHi7 z<>63Q3dyz63FAq{UR$^_HR_=svx0&mP{ba zVd6AUx>rR>O6AW!gxg@k=!Qj9!Z4%GV*bQ90hAW_36g}{aCvnBnNUN>6n{)8tIP9; zn+FfZd*#$HdBp#uNQczLQKI=-K~uKXi!d_n-Vk8rPvC|!hz&MIxiHiIC4=of!;~Q< zrSV6Ly()YO$o9`hW+mK)WZ-EA>ba<5p}|!lkEssMxKg?QH9+%Ef93OKTwnu9lo7%F zV1PLCUV9Y&F%wEA0#!@6?aWLog4hsmxz#{eb31+^Nk4Ax>V6t3)K#~=Z|Wcb)6Rl? zuD{cQr9{?8Yy=%dGEn=Ti&XT+CH6M49Kzp)R3qv)d3i}q2-UI&P2f)e+=$@<`yn1D zGM!2r1$N*!Y!1R-S@&K#{A3VTZk-^OLoG;rQ0TpK)EZR&N{L!;7q=Eh9 zf!~5{YFCt)WLmns5A7fL;G(iRLcFtNo*dXnTty;SC~H9*}^chORg}KT;vu zhb`mf&9R~vA2vDLK4a_vG*oBU&(ZRgBbQ@B6yEm@RvY(vhe2lpYm^5eST_LhmB&4U z!uS#w+s$Ear(p+&?~v8`8>Rx*_6^<7UjmbA4XOC`g9p!l?INcOM?p}jr3tfS&b^0# z8B)kVcvd4`)O^$o3(<0i98yWcWuZ1}eL65f>amIAA($OPhrJX6tpz0nTc~K+UwwbP zLk$b33zHIiow#8KAv`Q8U4i{WeCQ;86Y=6<Y_F|HsUS#cjB)YrmSbeC_^i)jH`B=3bfZ|1wxC_Jc^ zS2U|jM7;|{qKzZ^rcj!nQA`G2+m#+@7#`&?`Mhy8(}~6TrQz`z>~-W7 z89A(=Wva!yw@zk76nF;L31DEZ9i3%2&<4 zs^$M%9!`e~^z1132)9t(#(YsbrDA+AtNs%Bc%hnlVCcRM?anFv zY8s@;WXgY(c0avL96aTW0f#3$+gsiz1-qF&rG0#W=b!6t)mQEJds(4(p;MUHN8}rW zBo#zV?cFyQ&Ix^;?#LDH-(77Gly4Z#`r;5Zx+a)z;PDBjt+ z0&@D6uUXv@fUw&=27}vu+e6!f+x^Wq1n;8n#_ufO_B4>b;9scS;lKwV%Nx@FxA#H* z57W{rHbFbo{D!j5p;HVx0Te(7>;I^WS?W69n;`7$)z!->dV_kB@B`la_z`#ne~m5!mtA+q+wo(wf(W8 zkWi)Lo&%tiy@_!@rkR2go^vmcGVZ3ozP5qf#|Jq-kZiAVORtf@ff;W^=WfGZG!ecz z`K9U|`zL9ZHr8nMI2Fh@Md1m(M<(nAhUWVex#@mV|I!ca_>Bj20y?GH};aasQVw1sOGEFo7RC8DN%NucmH=rKL zghWuWjnur`X=t%_09!iLM=XYd(j4VyLY(RX+n3d~+A4)T+_Y(1)2EJaqW0GYSW9q7 zFzAXSL|2wfjadqfjprs=IT)&SMFKzVDJG0J`>?{aa4ccJu<9odZGbVbiBOlz9~Pl#_}aXdoitX$J*0{@k&qSW7^qChroSewpwv zB&3b>KXHuL2bQ9X!$ML}Qf8Tr{i~6p5HzUNafJV`df%0 z|GV?yoO#8QB6p#L!hp&{Cx=YC)lxdBNYG4`N@kN{Chws@B~B$~p7ZXMGN3bWCQZi} z!yJf+VAE;z-?g?Xg4r{U$0jOTwZMvv3&Or8mG!Vw10q{+EDdUU{SB4K18Zsv{VVMC zg3BD8O7$x~)t7Q~nTgHu=e4d?+q1Ty!jDti$LV=RDg+)K1Y;-p~k ztN`}a0+_#AOun5d^jS91Usf~uiE|#xYN$gCnb0+%5iCwO8Yy=@H3TrkDiZd;DRfT@ zz_&%8Y?4tnQ%fya9r%H9KQD0#RplPGy1T|ZYKlv%ZL$dn199(h5&}^|C}{iAR)1l~ny_VWlK&m66sz2-42ss;4AcaBm}+I7 z{S#b&YiRIr#RUh za@bw~b1g&iQ`EdO+FrL+OJJxDQYmzLfsp0qG>*7<<(6Nf!Da1TY8Oo1C5DU~hm`DE zM@Vtu&u8UZ^TA-&ip-vkd4Pc?I*|A2U;DIuBxTO}=IhujkWR<9 z={%9Fz=xG;Su;jbP#*(WR`nEE!n5tDs##LJ{eD>S=<|=20Bb6XYop_;3XMN&b04Qg z^7E$Un#_7if?xjF=VgsEJ_LBDhO8&Q0P1f6^{ymf$haMiZc=Z5DM$Vf;T`m zI>7sX%DNJ8DBeH5iyS*6_kBlE5+y|>$&peKIz%YBI?$=ms>qc}=+z<0c1fj3cTqY- zNl{UetAvmvA^g8PGyML;Jm&E{v!CyMy>slW{LDsyM~y?i{_ia2`8@dk@kh_U)}fBz zGgaLc-SrM02WzVLZ2HkI@${JxJHjJ*4Fjdyy?p-NKs{lv+6pcC6JKV!m91YraMbJQ z4)Mir3gYBnzkI&!eWu7nZ=CO}OR1J`$gJbDbGCcspE>M}IWIYz7`EnCl3H<}&Ij*{ zw>n+Sb>9T6)^W{IIPvquI@i^4_8to&jyDKz+mn%4zV3vJbM?%tQtO>Mt%mMhac6%@ zU9#s4mM`0pU|}M4>ciHPV!BNSUG?#=?p9ri&-EHvnGWt#K2FXP&C6$9aF;M1_hD|9 zQn{do$AUqARf$Z$dz;s#<{j=u@w?<|Jgy(qjF)rH!SE+Ap&T67b{=ov_0mxVemc?9%7lRIS!O^4a3)>y9UR{z>u{p1ULOvizbNEF)$nHft437GA7gXIiTm zU3SHx$?&4p=_wtTwmMoKn{&80`}C=qy;0$ZGwd}o#ETYxuRNFHac;cEorsjBOP9xI z?qMHQ+H+}Y+sx|&4NsMyd3-GN8Z`WynWThJ7@Fswu*vnc9+VnD>Ys)eAE3-rRFUB%7GuJ6+Y20`;^H) zpC9fIf9<`wx`Rj2%u{v78r^Wm6Z*RarY>7yRM^U%v}Iqz?c@hO61ww!Z=26~rF`E; zKwGEMY5vxQ-TPUqza2}j$=Wks%|5J3iPsZbzPZfmV&URhzXi0^`lBN6b=p;=A6>d# zTrFYOw92nJtDbE@g|j~uxaBu|P>bGfu%ukbb4|*Dpk|AAuRFeG*zz8j+ICe>rO5wE za^&5@+508j*w5Ia|9X-`5|(5pq}^YpHrT-v!dZDt+ZZc+xc8m)&t`TC5uE=wCmA77@aSi-(p!g!Ij zlh`k@wCX=Ge69UI^#w8%cCOE>Uo(FT&uaUZsu?oj9^r=UrOAfR)W)yPZ<=(#{^o%d z)oS74BK8UQugNXC5+$cA-`^N(G6!8uXN75^n#@wKz~#>rn46^BKDs(q-?h7zz|$Va zubo?6+ZDqziz(`^?SA0dXPu?Zw|M+?mJV(7o4VFJF6YAL+h;frI~cgn&Fy_|EjhLO z8HKf`c~-GSVSCuI_pgSWa!UWuq3#?SFBO~F_orrEX;^9S)Wu11YhvbiM!4)gCXtM- zUnBBh=i`A-g}E~R(^3WlH}Bl(ITIZWKF|yJ~rAk@wAjfO_G%m!()UlV2-(^5p-to$lkqj=m!%^j!SB?JTMA zqmFu~Z1}E>Kev6d%v9OJ?wm(MM~@dy%s!u(d#19p&B5Du>5}=nayGwI^&L|TuQx2e z_giHA0k-j6-T>P-PxhLeNq&aNIzz_4!we&Yj?rZ%>{nbxYL#a8+sxN?hg^6S5qyN=X@N zMB4k*&lc4*uPnKqqtg5LpBaZe;kn*@eIrAe6DyM=KYBIGd0Bqhf7^X_t7U=mm)HTr zu>KqS1%8X}<+~Cj+4fW-Xx^CufdKJ~zwt9qja>4s4sCoq$Tz&MCYROikEXB(;YCmF z(A2faetuO07h<1xboEh$n+w^yyxEm4-@UfP2IWa<7(x|W+PB}2sC3*<*mk&Y1w zEe)P6;SwtnlYRQ3_oL3xAE%Ag~S!J)=_9Ztf5M zt(w1IK7aad8?6j~v#EFH$J=c1n0_1Y%$j#2ojEpEqwBGyy_KM{@B2oDQ1?cnS8AS zc0uzPV!qhMUI(ZBk@4Cg51FbSt{XPUPSRyP9yjBuhK7dSwDNHd3$1p4@;hj--#x9M z;r&K?*_R=FQ&&ZqYpG4uxW~3~5D&f(9xmB6>49a{yVri1d66M~dMe*Nzcw^?7U#}5 zP<*bK&uc{r%vB5QQI--`5NrIvAQ}hqeR{ zJy>f}kbd?|fBfY4KPrt+6n1(SJzICfaq6{_wHcBhZ>Arp6~JOYTz)vvKZ$*_|J$!L zJLw+%+C+!%CsRC>Cl^ONkyl(?ADYUJ*>yT6X#1>nj89!HAL8Qj$~H%~SX7yI{%Wdw z-TY+VF27sRHus;bTd!kU|Ie^m@51_$eL-7-!UuNVwtLYn*BIzr#+aL%zWdr>;?<;- zdmEj@{if}BRnTd;TJJ|*zy-fPxkZcEb6$JFm#}jV;pwt+xpVb53(P<7;cenvmH4$& z#q)w|t~`H5VpVtBd4p@wyO$m~Ua0u#{j^Qup931_ZBM@YvrFFVhm)|PmeZ11$MiLO zufdP6wsf42@A6*s;@J4q#)`czhq|xAt7cSEo0V1#p6xHJvkpFWeemxKPG{)Ha+yhc z*+%9nJ9_F;j?^eTLtE;*?mzzfuOTV+{)3mb+qB)HyPMIx-EGg*-+jBB9(p~c|L3>s z(;n*W2rBs(@lZ+so%Q`aZgMX}|8Dx@I&a&p4En>*=-VYnHngRb ze(G6Q*vgF!IIzKFdO1Q zyl>4P9KI^TGjPbd1~cP6Du-ffFa;hH<%FZk@>56At+oLrc8hi2Gvchg|9yPz=MEX1BN#aQwRO2#ncJTiglO*@BE)k^sR%bf>zgI zqH0_LffB#Vz2Htn5WL)d`Y<8<=P=`%2&^i~sKb;A?Am6P)$Otj#=^;P*Mf#M?;x-` z+M~T;mEK@t49_SYf|qM)(?~+l{CaE-!B;3w_}yYK(gp5@&~;$Q@o@E3MGxvJ9oDZ| zxH|}(mIgY5xg#5gc~XtSDl}j+gkKJAH;>l`oIBVCDvxX&rZci{zzhlDg;Sz#Cc;z* zfT^G}0^vHKOow8)M3-kg`qY5o1Oa2YT&Warjbbqv3N(W3={%@?29Ki7XfL>YY|It} z{>eb5Nh4tz@u2*A9y#Rw9aA9KKi_EPyclo~;KzhDXxOEu6gG7p500GWaZ;aiys~14 z;Q35R&_|U<@Y#|g82=(e&nV+|%ur5C^Sppj&(qHsOnZXD*WiZre zd=ENM*b+w`A!LYCiik|~czqEVQm{J zkDSWC^neY?-vZ@C=ldjt!cN-@iexlW5pvAlGOh&_IRwln(lie{L=m(%VseBcmy*AP z9R`nV7aHx6>+uwJ!B5H~Etfte$3d$61>O^AeA&qqHt8oOP4JcU%k8}aI1SK2gNC&| zO<@~``L5BQq1grn5~jyssMD}UXDRILCQOFl+jFNpSRGO$TzoXv_W^~C0<0>xw%=QP z>@@~#-Wcrb7f7@jQ$^KHm>5wikBf#L=|Kz0Q5s!VHb|ryGa_VL9V_2$0QOh_PkJEo z>j>EsP;4_5gZW}QuG7I+hrkD_G@A^XdC>G09yL@kjExUM2LmopS98yY_@z zfY=JM>DVo^2&^o!ZN~!O9ux%5-)kdu2I`MebFkFE^);&Bl;S_{l5Lzc)hgWr^4$C@+b$EWm z_X8|1qTEI^JvT@~Pj^xiXK#N=emSh9ilK9>(&XRP<3p?90P31E zI(Dxt`Ovo^UTI{~MGYJIxf?9KAO?yse(BoEdGR3`Uq00_ONWk3)q#9S^GlHCbk9B5 z#D^aG^C_b0E-D}_d7NnQH$Q2Fy_;eQV z;Q)$m$<=&5B>W64S@?%?YG|b5>*pYs0|`KH6N=@0=&0~GNfiBuYFkD7TK$6nl9iQeFBWbuA!u>CpL)s(?NZ|L=c;0!v-+5^Q8qgp;pUa=2MNt-nq z)f#|mhEXR4y$z}$Q#Bw{C(?|$eiTjb#qIW_=XbQelS|)u2Er?7D}xNw>8FxJ zhevGO9jMVeFx2RC5dTHu?b^rTC}L&5G`zZs{Ltp0mK@ntu z+vvq=#g9t=fhvRj7}*M4thYs}ffik`I-_%K6(*s!|0u4hdZuPK!MGfU(Wz=BO+u69 z`2R0e^-s-|vjEl>klystHL~R5$@!7?`|V2>n3OeTw63{JxF|_9ZGfs3c^jJ}ui;E` zmDcDG4||l`L7?q0>F{<812d>p{s_d7=2AY5s(79 zhJ#1pZ)5{qACu73Au6?(|Er8G1~-&}8|eA>>op0jD&@z=ENvd?{62OWwD5#MKyN@@ zpGmT>)%?Oq^Kf$9Z(iAyaTPq01|FfqE4m4I(q5cAr!rXQP#FTKsQ{zKx1+Es5 zNGig~syy(Dt@8j>=YwfWuk~GG$C)_U*do&uPaTCOoCz_b+qX>x*)ef#V!q5Oz`R+& z>Km}4Cxht>LaH>nVj#dm517>XYJz_Cq!x%<5ClYX#J}8CM#MadV2Fv6mp){xZY#C} z&I|<58%?eyg>~e?B?v`k*BtfofSMHqXTh8q**MH^ONaT|3y7iXJd`5l?bA*_hSn4Y z;ihXIv4S8_MgNAeotx}8S{pJLdM=}T(>`|sD}&5r92ySF6#v(bYr|6!B3hx5oai zO*_ntzu-oCd^}_XxhW@&6vk2Uu@2p}K?ZQP5KFq!vC0%SRZUO`Z5c-?Qt5j{(-2z5 zTUZXl-^d2bZB2@xaf+Y_${QxAOOaGQ1jtLqqy4yQ9!210F33W{{FH6E=OMIg??$qQ1R5AMFa1}XCe)KaCX^rnHry0LKbB2(xr zi#>k8R~6EX&i7s`g}pk=w;|-#d~a>IW@0%S>(MEQrenAa>Ss~iKKRtab#cJgb`0Nf zDu^iz&{S0+7CJqi8aZWhdZk?ZGQmE2<1bMs@wO9%1kq`X!k6ACo~Z*it{P*bk`{@t z27QR3UFC1n{2AD~0G#fQC_NIYgHuq&ojL)15XdY@6M9(A&LW|~I8L7Z%Q}83F#_R# zp(N;BD{Z(?IRsytA}8PBC0;=?jLQ&0MGix;_luz;0aE1G2e(rE~p$fP}V&3kf6k@VW^(4rOzv zq$Jb}U01oN5xgLp8Z9*T0?HAi&qBtaPa<@3`eNYiQIhssJQZ_dz4x$E=#2roF&>xV zvA9A4K98p&k-FD^YY;HG1H9;YnVm&KCyL~Tf5im5mbVobxMHvabD?<4=r24f44%5_b&1hr7f0NJ|1DG^%zeQdYSKuksMD`Mp z)=Ncka;<2x~Ih%x@5~b9-vfbtvh+>$)P7hMw91^Nz%o0LvqBu?n z9OMezWK4c^#F!<3jKrwAh+i6++6ZiB@r;h&3KJ3^B1UEK(SvIXz&M6K>=R)h-Z;$A zxg<1gE=v@JNKjIn#6YToDfyAII4)1?CkQLV;KbkYZ#`s`Xfc zlCtWU`qEAy6&>S;1`3KvQhs>*RDzG2>fD{appda3xjIYY5`?S(NszUbI+|p`5*f47 z)`;Ha8wHFiVAp_sc;hhbEJ8z?#cI3H_3ylF{iyOhhxlE<=mu+Bv+zeM1{AR2CUa z=#R3Y5n6h{*I`>ic2_{B$9~>7ws)LGq|-F$>mr!F%Pagx#OPhD@A(}I?tZP8WRnt zpqcn$kZUzYZYV{TX3rA)PfwSO${9K!Vh-%|RKOibF}e;c0eFa;egip77FS?yL#JhN zHNrcNVL`>aV1Z%+D;l^&G_rA+S?G%_K7n+ht8-`9IOw#{AL!k36;hJJrw~gQ)0D9H z3bPrEmt(q6#0D0+v4N$Ce12d!are5bHQhu2#BD+;a`+q~#o%e&ed2IvI|P#6gJiN$ zj~uQ|Xp)~(>aYUp#1p(EJ)$gk3FC?8%i|LXO|D2ueTahvlp`zv=rgDF84HO#XDN)i z1CaVBG=h6xn*&VgB-!Y-JU*G=8}2C~C=YXV6Um{Bb(bfkqPhx8P1oKTdd z(0_jg3?>a2lXQlk&>97b;pLIvFCWd&YaE8m#$j^k5ilf#kNhAU#DP9KOe8VnE{VyH zF^c{hQX}euvz8$}MSLRR_t9@C9)@w4{8P{lKoO;#q6n$(I|mIZ;&Nl;ROd&QU4VeR zf;^&U{82Qhh)*Lp=qdq+ZRT7KYG|_(EM=6d5qak6UA+V>FMwyf1145bS|{#t0!do&KV;%J_UD zlv}Wl*>G;luz;PO{Z|oF1)o9~GED{4RJP%&DUCc-aB|%4;T{!TL>|4WLUnQ4E0SnZ z6?ct5apH|hNJN#IP|B*nDnFe5Ik{?liCH9^gXBF3)dwj*vT>Ljl1O|&5+*aovv0W^ z?KmWYR}GgW43SrZqhGI*N0+GK;)D-A=xj6A*#i@+%~31%NjDEDD*+aN zbIBdVlqTO3HM(qB{C88NsXR4_6B1j^?3;3#DpNv%}-LV%>|>y@prxN?y4U z9~ruaqm4!D!)N z^+=&U+|fRV(YZ#0Q<*`bwhOx2u*7Q!R+Oai^*RjJ| zLhIIvUrVmhR<5YIfM6TT6^qw?$bq?uR$!SPiXz#BZLw0Ru3Qie0wP=jV3X+K$x`*& z;5F|R3x6fxqZY2i8;y4LOPe}^x4>56@j|WMV5LHtm2$^8jzo@~Vtw2DA_$3cK3A-i zy=cv=dWesFacW`T(ZjHr{)jhK?^uYXVT`T`mWK&4D`5P}5xAzB8-u56fZ}1;xT`r` z03^zdrTMFnH*$qyo3fTyUw!pug~#?q9JdCzxPPVz4}v;D3USyV^%_%EueIK&0T3L+ zVH)ccB+Ht5H)BPgY>fAGEzNnyFi9vP+D-y%KG(8HDVoKTSRdN*(O3t;ita1Z55$oo$*WJh-9~p%4Ynkk z3b!?rRBOpAje50yWWi8q?Te@(Mkx5Z5Pu439eCcX8NSiddA+4svc+z71~kXFFm^QA z;W*OPr5TefJ`s|-8P$EK%?hPh$V@RqmvJWb_^e_1Qn#LxF&iXdN0^qD^|LLV2MlfG zrhAxdFw%`vEz}g10U4Mo) z=TEnEjvCq-#pzetMSl)sOS`m8f>T^F*FV!{7+`WP);011%NQA}WnQ|>zu0D2r~ksN zcZs`Kq*H|EoB^{XnPOIdipjxqNr-9bw+As}z@WCzJ9R1WFx!j~ z{Br1`@tDtJV6?)}`0{SCf^~!-Z+V+BjbCoefnvpG(ZzheVxMtF<6Uov>wj3ESgG;r zdc`WH@s);U;F!>OHHfZn zB?wrHrBC^MCDd4lLw?%ltDX+G?+IDcX2?7^7Ydx85EjcwsYCiYja!B^2J{wlfSC*k z) zw>j@i76ZI}ZUGuk6IK=m|DF&x=<{+YDf6~$m@>`6@WiynZ%Db8nST&BvUwR(izbtC ztmkL5j?dSTza&G5BxyFLcERH}-XhjRFq<$4Klw%a>)V_Uu0p=PEUHPY35T&%Ty9Kv{~GM(-LAUi)>1z z9kNecM!K=K5o=L^dMDJsK{)ebi^jJYBO!|Nh!IndEui}hH-C6%RpX9KC*_cGjjI&E z+*ZS)@HSIvNjZflz|$lZq5ppD-2?!U)#JfFmZXEaL$eYG-Si0u-L_!frfIhdH?mD6 zHJ%fSNNJ$z7LB|A53RN?X{ER? zDYi{1rbE`Yy+%UZ3|Sh#Nj7=kOhjs5Gifid=(p`w3^l&P71!gSlp}WnZFeBA_W9gJ0_%a7cOji8d&EGpqbgxOrG{kr z-AOl|GOZXqKt+k)1VD8Gq6Lz`4AgrKH`)YK?NG?5)x-qDP^+tiWL^P*ql{jfFy&WRW< z9J{j_ap~ZoP5J7ieG36~6}h`qwkEO7@(Zy8IDal2a2v#u12Q-_J@>$}^t|2Yx56Pi zL3zhA%TfhZ>W9YsaV!lErv-g28A%2H4s%P6k)?r zeS*$94G7-bEAG_MP%xRvnzL5O=lw9^+aOcCt6~f$&725__;Mo7 z(Bx{S6I#^n48XC5(1VoiQe^sk4;ZdZi2dv=S$N)#rd2t-oeWHdPZObLsu{i}A?~IW z5l74-C*O?)B62BRxJ?ojr*Q;DmVlim?|YFn62% z&F7%$;@MFhQAQn9y+KerSYD-pJ5i#(1%sRyd4OP2eZ)uwh}+++ErG$aq6mXOq<@s3R=QuIv&Ijd!WpNjI*~i1&+i7K^Ta5Ivdfwt zMV@;U`FZ##86>i2I^-$?5{j~*|AEiA5NVh78NXX_G4ANlqPy%lZ636StKxixQ*{l)`KTo;YaCJ%12Z8Ys)$ zz7$6Xn#p5Soyg~dO%AENWvY?Imz(rP`mI8znaVp?U2qp{I;3oRk!)&0Og7D8x~b&d z5rA$?iK3#A9$Iow0ZQZ-9KYc9dzx+!s@Xk(+r0(1k89jpbo=;%+qZq(?GrLn1-Eku+2 z6`DNO4pKfnEh-H5qP*0LSE?W&O(#Sdt+?9fxTuJW)#;%o!#Jpr&3_zpaHW~ZMhX`< znualBGqYCJ=Z7fcs$}X!HV(m1t}2cCf}u)2A65X!GxY{=68m8VPB>q^Qa(gA*Hp1n z?4vVSPYfYDoI1w4Y9lum%c(=ne?YjKZi`8`-DuNYi9ZiQKEfU#G^!v zM>BpZVKZt#(a}=a9DlKJV=~SJcn{dzWiE0XZE}NpWAM*D#OI^SEJ>GjG+9abKfmaI z*x8i!K+INc7}Zmc>gkAq3T38r$3|6mA{%0;>$aWJErk!{;F{0J!5gWa*E^&nVA-+Y z#;p!K4NWJg2qnZMa<$CNp}ji;$f9@f`C%9mSHZ&o`~UQa27j(?aTYvH=He*P<0(W&%8^Q zEY!6M9QIY8SAU!EVT-S{_~=PsZdKZ)t-uq|^e9yLbgwuC6;7!d4JC~DTc|%h;z^iEhqg}??%n71EsL-tNn@ot8lk>}@69nld@vPl4Z?l`kv+PY|1^Vh1>l0tp__Op~FC(GJ7gY=PID+(I zLVQUzbPMA?@nwyFDJbDzJ}+KC5`T-C#-wTe-h7W{J9xWOkX@o zbbl&i;l@B2(?uiNDWU7!Coet!HZ!NN8bW6mEM#P`@&dJrs*5nl{(12_>g8L^wkE}E%)yzHr|>eJ5Pz?k$yT6T?x3i0^ht&*241*pFasM z4}i;85%e|{z=ZgkJs*7k^7*N~23^~bOW^XaVaI8o&tOLyo;JYL>N1}}K>lTucoRoVh<|e| zgHrxx@TD)ftejIx8%4?Ivy`*LhWKwV`?}9R555QSdR~3#`vP^xSo{IX&Rafz7K;zz zfN#6v0*Zmc-@*Ui`20C+9LC0r=f(G%8z;s0 zOa;tSc$o$|zt<(77vF8S#CJ9R-GJ9cjel>6*Z1-Nw?3Z(uMzP2K|=hHaG>!YHVui2 z=JV%~6>jkP3+hwr3#x|Dmx~v0${#gT5+%tOIo)_P{&zlq2@FSJnjiIucYlz;Z8Sgc z*xT`MrR-2LWgGTM@lN2zgm}lKW(sink;dN%?yW!e`O9E225vvON&J+ietL`eF@(Ib z&zRHr-~0Sktmm=*4(;+MF#{GxrQ_&M7DpK1JO zGIN=j|6FF?r{^zZ<^y{EQh#Q+K`+1ZxnBw!AtPkoAEv8Npx;0F{59|%#}WUT?$1n5 znGWJ~!j_T4(DHTq@BpE|M+z&5-$rg0zlltUUu*o=%2IEjg5QX}zrjf5(%&AAiQmb{ zBMXTADlERf5L0WvdpHI+1a!X#Yf6{@;`6UTehFQ`A3B_c#)Q7?Ab%`QBV+%$SNxH> zP1fmP+aJ;6m{C3W{&%MN9pYW}9pX>(;|l?0Z_sc?<_8tGcgckBb!3B1 zed%^DsV{v~ePaG!0Dk}g|NoU%dw3Mp6`#r7O=dzyCz&V)1*UAoJw}qHSxN8{n1pQU zMZm~IAqXMKZXvHtvUy9u1j0iKSkd}*F;*HNNC1H?43B_4 zH+xs-$`OWW~d(J&~W~1yUjYh0ySV*MBFDJ+5|?(tnE;#3Y2 zBRq;krE8*eFoig67&_mfcZIFQFvDVs(naYTr?QWz4t)jbuJ6?_=v%+k6cQC)jL!}= zD2Fw_v}%+t*?&dpaFTLVix70b^lq3g!9e8*c}h7(bk(7z5|wl%AW;IQI1-0?cv)8+ z?EWZYcs+)p!-*&z_e-mBiE>qxP9!TQNh8q)Dkn9gk(o|5D5pq(p29s~*VBFh?G=J3 zoiU<_Ul^rIkBicG0Tq*+HHa2?pi6P0bj~j|a^-@}QKt$dLU$N8 zi=joM#iA;o;-$u_N>30?WsNn@vubLzpk-LYe_bmOoRL*m&SRbnhQJ-&w3DI29n%iPACXvQxRjg0HB$>LMYU5k8&SN9j1T zHor6<6MxoV6Ya^$Rgyw95~qBx4KqSjrCpT1k5F+1JSX$9+IE(}f>~9}cGao0v8>np z(mc#C153Kzq+IK2wj+G!{h$gCG|#ivshXfourkX}4?6-4-tbFn zFxyNl?Pi3Y&i0B#+m%~GiIHeqP4FA%5{COearyS&LefhIde=}xf8Y1m|^xyiduTmAMSBQl*(Ta?Py3@X3l$404~TZFU+RO(Y(un%D)1@>yZ$6k$S&4{%bB$3VCP;3s-9_E*9n4%u3lxlO>LLDyAF8dv*&B2Aj z2lnfR!RxzOrGAlwi1u#AFewsuX+56Acp{7`^Nke*L5VLcFD=W;ndTXk5U!&@3yV>Ag7b zjnGYN^)Y$+@^u&<3;&?36pX8nah$POLXP~LCK_*X%DSxVo z59OSB(s&%>E|Z>aT6gq4dXSGX)jZaef1HWGVDe{~^gL5+0oO|qK2z_dCV!>LUw>)R zt4;b`(>}~&+*QTSkMM>NI3E??bSAos8{q48WAM{vzh&LcQy{&0kq z>k{Xp$!k8lUHDbGm({jb2%Mcc0lyf|>k>k>JsEhJ9f%Wy>x6KH{L8;9-q`rFnw zOU@BXt2`n8^K)tYk}FCg57o-vrlY$XTKa!Aeb<_|do3CIL3KWTruxM(AAfzb(wDUQ zkyj2(YF&x$R9SghQtlu27Fnxc{m&Eyg_K=o-hlhVSe!#Aob-T9Rsm>g? zd`U)V*&p+7oY@f?zgE_jR|_*b21(awTgl`$UrKF@ICk)srd9o#`_|mGxyqjTV6D*+ zlT`01f8>cxN6*E%YR-RVm46rXUXglP%`0m^Sa)sby5lcT>i6-LnZnHXDklmfigIut z#7=%r&9gJl#XLLnT+FjD@Bhnu#+~=<{|r9k>xdfeX-B-o^-G8<&d(#tx#t9;nCpiS zMf^;(A`1CA*p2uN_p~59JoZzBoAYgm9L~2OvbldVVhYzcAu>7NfPa|GIXmBzxc^s|1L6n9Cw8et#bqd# zGMm*c#CVG`ON+BAy_q>hg~hoA9$#j$uPDb;TFT6#+byWxtU?c3ay;(Lvg%?_;6ZM_ z&^N*7$3L?bHN4wB^ICwKt|mxmwddDP4JBz2(EkR`1#EPoB8iXTyPudq%H5 z=yNS@yfb=A&wuRn`-O+=hRz?qWX7D5o=dJS?SFb_#;(}zeKvHf`1flY!uMD1tuDSl z*}v9ZI`wx;uV(KUwXo7Xf9~_mrzem6qVx23LpFE2_-&1E_~K7qza7?IIOZS1h+Hqe zGvc3wpZ-Ml|HyhqqU87G&GJX`$MR?L4!K4Cr@TkrD}V2kzmoUM2joNYarv@*MQ)R? z$=BtZ@@@G?utF$=fdo;|6M8{!=mW732l3D!ehGg+ z3F+`0jDd0R0*r?Ym4NFTzCWK!z-{5UV|m@ zCivkUSbqk~p&8b|T6hos3>#o0Y=XbQR`?jU!RN38cET613%-OSa1>6$DL4n`;R0NQ zE6@h*@I73G>u?KhLnkFvqLDO)_M(qbJB_CaG?6-K5`CHuqA4_ux+tZi=s5ZU9Zx6F zNz_fJ(tKJ>%V-s?rnPiBeT6Qdi|Atdd%Bc1&?aTHiLRon=~h|*r{Oy|3w`A+|KD-K zf&2$hO9KQH000080IZ=Rvwt2l0ST<3B3MKg(FMnoyB{cjoK3W?nPFgX26q?;?!leG z2_7sE2<~pd-3buf-Gh5@5AMO;Ed(dH+~l0^R-Ik-^j@`g_3Ezv^}f=|s&C-{W!~aa zVv1re8hmhYa87?g2OFC@xmcQ6q8JVO zOJfV9e`7m;Lt9hu-@(5Qva+|d1OM+;3H3K+^RECpbPf#3l!m<$9b%>W61$_@djq&Wbw^WgvnFc~O34!|J| z4v1X`2k`$F1E&Om0b>8hfdF!0@Hqc#LRH|^T%6SZUFw~!!@sWZ7sS87|GVE`0RL&k z|1|h7NdHG8{{{FLw102=pa1_gP|E#(GP(ZCLj7-!@}GwLFAL>Ajrd>IzXB>)x~Rhe zN{ZlrfYM*wGsoHyv~-8R^F-k=!yN2a^GMwO)xu^~Paw`Ju!jb+$}I*@$rrpyyy}-~ za%!?aw#t9|xWD?k|=Qu4Cwx|=29X7kihVzC;NTcsbGL4YN78FAvJMCU1@E3LQy0gY;k>les1FD;#B|7bqpvYGnz_WlN%O32!LFF z8w<06eGemG3QvlVKt+v6gGT~7KC!>@vi6{d1K}S>?L*{Jx9y!ZikzF$QY?8X*cImSRC8(n$6)YM*GtaidiLj=(#hjb->># z6)dUdJe$teRLmN5t{zxRLgr`J_IFx;wd4!?JhCHJeyz%n?8q^wAD{vd{6Uy0@6K^i za8QJC{dT@~sxbZ*kHu;0Cl!7?OYH;FflutYSAEE{fJc zUJ@0BLlQtDL^=VZ1#`XP!I;*61Do94_F>e7&0+E=!ZJXxOfVw+uNR8XP@32$FEI9t z6xjDnXg}w?577u*`y85cw(r+}dJjX2&+%jD<03SHf;bi>_=O~)Ail*EF}h*rO{z?{ z2$=tX3E6^p0_CMVf}lM4e3g&w0iTdF(e@{wc{3%EkM2Q1W;)AL)`RT2FS8SNdnTRrm9vaC(!<)(+CiUpd}<+KQY3{0 zuN2BUB1Z;X^xzE(v(R^c%O&LYYb(SRC5+j`J=OlGi)Hh{5wUP;WsKY}cwFC)*I3^X zC%TW_(oQLZt0Yg&9>H;yu<(QV8tq{@#sOn~?t>WWTrA;wO*!=wer1SA;6>1ulj@2} z$HEtaNPgh7&JSOEanilKZ7T*ef8thnHd`o}6D?vubJtym%(9w)N|CVsZ@q6cc?7O} zo{9^KP|iLk%Li+)-Jt46Hk1+L)v9~zih=%<)dQE9+74zf*OUq+il!r5;(T}i8D?yMZ{3II37|kM~3hF3wG^+QO zvJ?rgLQYp{0MWF6GGZg_x+GoXoPLGu>+uLL1CE|%fpcWW>&l0_179Rr2ljK(C>I=; zGZJ*VF)DW}d^E50t>lc61^XA@!dG7{F_A!P^1$oiUx2qCL{;JYgQrKg#Z~FFV7Wcm zH*`r(@|VFClZOSCXNf>*CL5}jMn14jcSxJ84JAgj$Pf{Ks$1@4HX(&w4&Ui$h>s&P zKau+DNN&v8lEOz~+WgMB5k{s(X2qnOk3qLY0Mwh~d)NKam6Dt6CIIm*{4H&=boQRB za>UaOGIJLXAOHM1USK_K*_12&(m>e_ni3ZVX06bBv|)bR(!QUIp$i`CR;z>k?m5g6 zIPJC~@EW#%?!aP}x$}`YGP)o2!ntJ>C#4y0C{jcf5YWsnA|I-Si2nol%OX~GSQBV7|W_i>*j~qyCqOKrX1 zCCEpAA{g8zK{P>qPx!Ws*I_CVM9P^C~PDRkcLizHUj2S;u<4p2Bs z1U!f+kq8EYwGT3axUTifJFiM=O0yzMal(PJH(A`szLoe*wY(e+_aQ8R-oUPJRV!?n z;~)@+Xe<{UD#>xdetnHds)Fd)D*@{Tn|ENU`mY;bT=9_>-q^F!L$hmY2bFIdnAV7Y z?J;%=-CvOM-|-!@LSx^ik7Q3^WenOo9zRiu6i4&4z1Om)trg2;rJ+DMGp=VD-Nn?u zB4zDU72q~VG9>7wOgp?$w=uC0Vc#IMxT}`PIjr@^MIi}4it(jDa@ubD{kERrV2HR{ z6^$**%kKfF>B;gBMa!OfNe?!f#my#vutLIGi`ch_LY!En*W;&vPkE~Uv2_Iyk057) zcrwiuLC5H(&a!*knEG`tn~WA@+SkW``cbRF4pb~YEM?jnmufK$CiD9W#~s-LSGZ3Dy&TyMb1f z@Ov#^Gl5_*c>7hEi=F}X1yBQXg*+2u|nSlm#_ z9~HuMX@=CqRWSgGzSf_g2WH=Yez?IU09O~{+Q{V?dYoN#pH@njO?Gv3LTEoM7Nfb7 zEn}BcCyklY{N{;S-=oeNltc1BG zLJmH@aYBLm4XIy5bKBTsRdxn`JnihXtCGk#{T2}Dez-F;X)kZ+M6zC?VUJUA48mJo z?b1k%@|BL;->;bq(A{;K5ehwcyPa4g_ovL%2h}drn!~6gy2l5ZbY0~B_c1EDHVv z#W0?AJ12 z{dvPG&+(}Sdt8HhQVDCblTZEyY)QNa?}`ZklA&yuM3=&;+cx`uqp8307L+_vnH)8Rq}HEp z@7y*SaAMw(K1s6qeSDVjynnuX?pKR&KiMVd;OzBZ3))tW33T_1egRk!Wy!EU3uLrr zQOiGn%Dv(={_=2t>!^dnHTzh7xG_`I7@=CFA$VHpNF4y`x)ZyK_Bq!b`I4QC@>nJObpnr*#6xfi>?sOg zsq$kV=wuV%vWI`UR8L2@$lN&k8|n1hCiY3_=RX8|Ibo)MO;-h&c9@30Y;lwd(eMIq zU1{hRmYL^?I{hK)5C@!Mc=PrBw98bnPh}I!x|4GS8N;-Qj($+){K^(*0wkaMT9Bx_ z*LO&9sIZz;iB+ukpC2C>PSsn(DaX7B*};7&1*k`-34=kQGFf58>3xw(s+Jpk$zKl| z;Lu;Fu6xRV-)>a1NfO`c3>I;79EaL255;vK9Ij(`u->#S1=VzuIr;7Dfr{lqItABW znK38TEtq_rQ`wlsrEZ-~%|@6;(eJ!WSYqgniHRsKBwwVCSoYp-zCY7W-oh3}!e~ z1&DGrOlmHwx{0Bu5;;b^z1hGstc*NO^tLNRRW##Y{+dGHKh->_PDDMWq{eNUffX+# zP!XNXJi-&|6$_xlOD!sKEG=vMOySCNFXf43-U ziX3Sh7TNBhA#GVPq}CPdF>adT%%bm)G$iu-g6;!?o*A#jI+7 zV~j+(80YSq6a2VlX4F8rz^rGZ0PVLkc|YYpBWCu1mv=2KjGqc_{pV5+_{L!%;59HJyXeY9Zgp8Vid-QHNU+9}6e13z@v zf$hyNvmQa85snix^_ginGwvaxjF-rqj)JI@?^E5M69cYT8RxawH*@iPIBjTuvNR;L znH*FG1lftzr>Nfpz4T~gy1E15=09nAZb~oI=H+&a3r1!6Z&TrUNiYamtMvWx#N5&l zk;QM6XitYQtUqo$LW|9h`A(1g*@~;1xraom&#aGVntxEWj*)rClORtf_rI*mTG9X` zeqHiga$~dEKiHae13u}7rG)E$*6&`bF*F7zZ#^I=SA^e5^&zDMFX*R=FYk(@6mo}I zdI=kmY79UZXY#w`?AXfr@N#?EsU+R3n&u8P8CE*1xWegvHgpz>z6(|^-8lIiq~61+ zVHN*QMa^U!D>W~>T*Yap7>r^f`4zczjypJrAlQ+K&TnWiN|=YKzwDHMSL+lvRC;xC zil-)Q8hNI;9Tzt4*}uAUo}@vx9ODE+g+%)~y9lG^C$9=hA+aW(2_+oodwy>0(na;FtD_52_^wLJjA#Wo4 zN=+*JSzn(f2z)dT(ogXSX$zLKr%ugQrW596QDxFyvc^JSerWHt-s?h!S3otqmUX(d zr%5!_5>W#Zgfq`RVeLG;ce17Qq1xX(sB<>E`bC@ey+@FKn8*>0Lf#uFtN&S= z5ZSSlV@_PM+ES}nA(b;wtDU1PN&iGI)0MTVPZO|C^f+&D@jF4Y5Nf9{p7-}F@0<4{ zN{S$Ifl}LI5xxmhdhE+o{v}<(1C{1C){Do6l}(zgI2K{QGVt04G%6#`Z3lH%td$|S z{j&F&S7u#spYG}I`ceUh@I4*eQOx$R&=BRrl*=N)xYZqQUD)o9mx`gQQL{1oE5 zIMG|jq9xw6_+*2wP0wUq1azmk`sxf>9y?xqWht9`b=<)93wG~h^Vm%CxWjL^)I;}AmM+^JrM%5Qd710 zwfP%gj`m-^+F1`OAqv0ew6`Av@~kUYHvUFD*RCCZVCS-7*3|1A`3mH0+JgN2Xgh0O zTSP28(#)q%@GkK!pO5VtfA4e6dpPahn2)@DgaIB|NE0h#?g5Bd#{m=)jm)j2F8L8D z>N;Df3=fT!@`rY>B$F$U2K8cvv4z=BNOz=HOHj8g*O4cZkvha>%@j1|2YWz$s|Hh{ zmiLx_>ZI)8cNtCs%G$D!LML`p6dQfJ3+EGO4|6M%?4ka3hXjm8o+|Ol&W)uL#4K;d zV$BN*yy@gxuo+pGK2#2OBJq#21MFdld&1WuA#FO~MRjL02pVRMN^BC}v_X(n$xv9T zVUJs&dMu&WWc>0pUN*53r22WOZp!Glz%QkL){+xi+u3PO$TfuvVb?vGK_=8FvF8Js zBGs_YaBGSG0cjdHR}^W~?^FC%j3giJw<7jqA;cCJ+Y3d5)bCdkV%wR_u0iqUT-q~N z=1#W;wh(q*KN6PK`#`0XOgYj|1YIG4=L4y?ktFJ%|eSy z2&hCkb|9zRK)syI%cG!4+f69~?E?s3$x~#zk_#aJgPB^mgVD^!zyQtaY;$nfMsI-= zk5s`@_pEvJ>63Q8MjVk}jAAafb^KF*P*H!A9KUMk#~#rd$HT593vT+d+4&DS(WwcV z^hcDYG@TTjAjCo5^Ma`OLA39Ywce-H#ocb*j#Oh5Xl+bbJn#02RYC*(m=;&xVBGMN zkYu#&AH5yrS-Kae>CUAJkjt@^aI>w=F;@zxZ%<2+<5^m%RDLXU#^^Xl|BA(b{N3MI z?&+!gh4;@F+2iDqvNL`kGq3j=S)MHgRIFNus5PBNl_mFZeGyBdaJB9vU5)Lo<5<}T zk+EOPYeIAfCMVy8THg%Y=Zay);Rl(%U5D=oTArMgt=0m2WZEPUIdx65x7cG8a8Y4u zrB9j)tuS@^k(Up_wbR4pAZ^Wmf|cdJFZ^`v#%e?5-ekI!3DDW3HlIDW&UWAzB6x$n zlbHUVT+nROmHElS&)KFEk)#b4P=6`@$oNWQH82nNp{Y#Dv-e9p$>6@6+HvF4tk_AY zB;=4i_HK0L`!??lcuAh4U^7};v#_1Sq3*?9uRHJ*K}$6GJMc?7XOAL(Um%>xO*V@6uF2z)sAKBdYNBPvowodfDr~6z zsvkHF`LYl^-8#Dr|C957NHf+GV?%t`wvdG|OnoBXJngi#GU$%~!J7i75`?K9bOq@& zdCk>n-b*PqJ;(vCUu?aBO|z4}vU)6H zDfq^Kauit@P*FoM5L(Mg_w{L2d#{B6J1g;~wVBPey1jxMuFVpEFNhbvvFdDp(-l0W zwD+)yJm$|aF1dX!6iP2XyB?yP&6;4NSobl?gwPzkP%><_`y;c=h8q3F@X3|&3!_En z)=KV7D==0l=nv+tbmPP0#UmX+DR>U`B6^KPP-AFvp9j}T%%>@6p}~R$eX`(KfsUCa zdOE6w0hh4bS=exYo0Dd_Z5isny+)`v$A4iN&!&InZvUl6a@q$zfX0j%U9w>tRA7&% z7sN}|f-J*HkLkrmX+*;$L}hXWpmfG$3!*@p4ud>h3ytX@a&$4@_y2ibJcCeOsKrEt z2eSKHYrg;9}mi8D# z800p`Ro#kLDLE}CK{E`#bJ(28c4vm4clUbQ5U(k%N?CWYML(#$BhI~#7{2GKdbDx- z@Fg!c#l*vZ7s~guuRG}RjMQ8d*FnLx3RuP+5*c{)$-217P3QgVwmbM*7#|{r&;Wo> z5Afy};@8b1#b;&3(2a^3P~t(Oy=IN!4eptk-J3RhGCa;`JrWdNMfNe4@QHod$ue;N zhTys1e9#;!u#!qBl5KBvWMcK(=xq1C%^oXx=h;zza5&9B-wN}U-2SLfOzV7$BK+xM zG}YhbvR(9HJ8!d=_b}K)O7fl}#ZD_-c!DoOAgy)DFURA1t&8h@J?I6ZOc8CJl3k8P zN0J(#seUlpaGP@gYH8{FS<_syK8hKad2olRKd-SWp}Y*F{-gtz2TMd&XL4begt z=^_j)8BZ|=y0S-XZ!vaF44=scw6$LB*EM57%c7HzWiq3!yN|IZ}#| zN9^z4-#PHLS20sYdLfEDhkp)~JNw;viux=_==N2{!@2rn`5wBAh{nt{_22_~+=*zY zz;wZY?twpEKt7 zEvYLq-)i8_P)D<4pkjtqbB%>pngbQ81GIHM11dzA(2p8*r?Yps`fD1HHL~J&awk2j zNjFa3e9oj(sRiMGX^+)}=0Fjuxr3q6Vg|(CNj0?&0%t z11?LASy$LPAH3O5Cq#7=4u5uQE7_f%Fl@27yz4A)kD|9#>{U&>fbfgE49aRL;W4tN zvLaA>AC8qOR&Al+dv0+iHSh>Gna&oxKVBjc9v&f0JV`H?v>b9Q940BKS^YI!Z@ahTWz_($-%ilAD&uY1Sg_n62GwAu3|o(D*7`=?TIZ4PKIL!whC{M; zdzAoybIx5?VU^T+My6fai1h1So@CXC)UEEA*g$323eaQTvhY zK4t%JEqx_nWg}hCn8hRQfS5M7<)ye-n_1m~u<7R75wi+PD@px}1wQ9S(;tPe{?Z+| zMg2N#QzhRl&Pkrz)R;#uFG{c&8{T~&+XIyzl8=e!ozqPGSzwh#cZX$?SC-r*qSr4P z-^RgDDasVxdHhB{#zruIa*0@}UW@uspps}vGi$4fW3r(M!<=-JJL*rq*dF?PLe%_o z;xh)a;xmu@^;Kc4+7fZSd8x(Xk}Z+gyo;#uAsW$va{G13+-t%g<}R~Fr)KWRE$sWn zKss+Ce$)iDDwR?Du^6(g&N)d?Mx=VD;G+Dxk159>sDW3R?P{)n+5>)KpTwaNzk>Ju zz;_$l+2c3QTvM*S;}%b>dsHNc(LUNGyX3N)JiPBzoEFnC=Bf(D$C;s#*5n(w&(Rl;$!pxmi`XB#LuC+ z226K{cg6od0001kr9Zc&KLHtv5WCag%Pvy}iGIJaq2y@yiI>jF0Url9E;cSXE;E<( z$pM=I4406~0U{2pp(0pzv0R@51ONc+1((3e0VIFVZ`?!_p7A*Q!^tKSd$UPe(mLH9 z5NJ~;L?MXF?k3w32+bx&3oTB?~V0tZ|y81H1b!yQTwS)v3$rZ#}u06A1J|~?YXvj zz#b|A>zfXa(olhGLg{zRM?u zp|NkaU2a-YoMEA7@$NX7R`^gH2>47Chc18bj*1}Wu|DH*)?poM;Kc-dqkZ2Qc+7Np z$Dio3*t}rRRxTBKYq0{QRu(I8Hy+LX&#`CUyl=p6R7Tg%`e$1)o2y*v;H{+#kqE6p zrO@}kq3;x(t*0O}AbCs^DycVgtY>JUgpEX()Fq0mCwrt>uM?`(iC%ttue?(yhS+~` zom9)Kd*vU?<>kHdxUN^VVq(bXtHj7sbi#=KC1jDLnx!;p%Cw+XiQGk1xm>-2w6DS0 z${IS&QQ9C9`H4(7f0reRoEvp{2NRWQxT)b*VthbwkOY$YC&I^L2vt>*9A~K^KGozg ze5ejMcYyCnjy4f~oII0MrAYCUF+_ipms#2pOaN>s(|Aar%wVF-Dzhn}pdR+ z6T+@joKsf|MN%UgIVotGq3CJWus3l1x=gi(MutV_GNbG>y+UAajU+>Bx zxyuIOH-zm+Yu}oTH*?-YgjQ`?hgU>z`bWyskJ0f$N3eeFup^a~?-BHD&k>Om)_p-g z^}$Na@W{qY)CA$>Wk|zRSoh8b#4S|p??F=-U9>?5e*P7p6P6bCmcDB-sOZ^kHRSqH z@^eTSJgfzBdw-4z5fh&cx@fF3dD52^rkWI?>>FrA+XAA% zzKIH_aG36@6K@1lMsRE>Ht1}{Zc&G2YGd2ANp1I|cwRJ%!Tg*zw~qGIx7_z-xJ{66 z7W1*>OT`bJLNKlOW%8x>|KyF*FlK>&0sm_k6-3mNbWlJ*;fO#${~aJ>Ge%P*OFKqm zGX^75L))4(TN~w7?9tn2*MraCt)r1uPe-C76i6sA6iSXG_z71$)EyKhlvdReSG-;_ zh|$%i9H5+u1#srpfYgH8Mf~zfgEH`jV8^N_&77h3q0& zR_p0bw(;mnsno5lIo3%UmYXt@Qv4r@ zLrY~9`sniUd!Y`d$`9lD1ae{aQaQ5ka|b6MDWZgM@t^<~l^AV_K}!IafTa>*pBNun ztk~gxyp$~Z0T+UHAhIz`G&n%X@0&I;b!h<1nTaKrAzWPfJeeE?wpdX&AMqWQBtU~k z2TN`QNnCjiJtJgQ zJh~f8yt+Js!a8SaVz{vSkC`k-6hLVzk>Zhsv{7!_ve9j_TBKa0d;l<6IEG*W%B5}+ zhMuHCRcC^_!Bi>kHt}|4Y~CC^&NEQoL{3r^{UwoT0^Xi!x|Yqg!K}YD@@=ta+o_m9sXR_)(UcTXW1^ZT0Ff^r-7l?btP3I%O8p{xdi&4Y%o=2m0V#MVQK2h zv3%gbR7I5x;X$xv1(g(G=)hEEli7UgK&y%p*lQxopkmnsw+)LBLsN;2T6HBm^0lHr zq6Br*SGa7B0+@hNYNEe5`nCzEA3g1|iFJ|#8zLD26I(dF$wIgTUicl<%_+;)Uo1nI z-b|A?QCzO`F*(Oxl7tn*u`K@$ti;sOgzDClM_(Zdd>Q0+cZzLxW141_Ano< z;J)5azMIiKwj#S7b8T`@HK4xMlDivoZ82w?5Kq%FJ?(RCYEL!bzRrzZtgD>jzou1s z3`^aqIY&D!_4P#NEE=AQLgwF3Td{d@^vehIW3h$^nw3*pns6glRkmYVnur79 zN{bnF735)zN{18LE3pCvap|7IOe#K=`OG?vzi8Vk`xEMPq%e%C|C;=ygP)H&ESWBf z7*_$3L5h%VCp;ER7rOSf?hr+_wv(>Y21|9Q0yx4%h`T2R89W3yst>j+zG~(-CRISltg82uBzviTw&8n!h;YJj*i#vcu}Kl-c5iMLdP^@;aR(J(CP zx9SWyZKuz&J)#l@uGi7s)UV^0u-je0 z>(4V~(z2v|EIMx#UX3+==j(EH&-dYGUHi|=6SMiC9nDI@Oh$Iq9#r0Co7_hL%S)vtF_Furc5r>z* z=1w+T99Uwl+3%Pb8-|qxp4G^uhBMPb_FssK>Kouj3qGtYsE~^0kED`MS6M@tI?w() zU^8rVxzIymb}2vPEQ+qoYxDHngn254!?h=%p5Lpv3`)Sf(nCj+hG)9>lpZ=(E~{!+ zmCwWn>FZqv?|i$rdZ#?sZ#UpWKR{c3K>x#I^!CQJ_qb!Z*M3@_7tuaxrg!Hfu)E1o z6dR@vK`s`zKT9=Q(nOD;+pWs=hnJyf&e!|zmf7K{JWbo?-uKQ@0jwY`{8oK zd`0qhx7_D|(6@5qx7-{p3d=q>ecY7dbK%xA>q)jL5LGK5exm@EV-SEPW{7u?F9Nf` z1P!^*wB>Jo2lEA_7jiqO`VV0@2>T5uJFsm3@(#`iG&jh8P}%{3VbJr9K_}RLpy`e; zJB0QQ-vgMTFUkShY#$CUh;hL2jl~CCu21T1JV@8%2lW3;92&rw>6+U}|6!qMfPns& zdp0&p3t9sv0gx>j2=mzdr0We?P!*7d=Ld>3uG_#A3%Q{NvSVCjn zo+_wnvTdo@TLmUGDj4e;FUrlAp4|0T1-d5O0WNWA0a=#=rY-QQF~889%iy?`zv}q0 zskX1D^D!y=#l^y?>FR$If3#DQ!MMKwEX8#4@9J$o)N^%nlYa;@vNQQemM70buuGS4 z%>woGmG_^Si0vtrrSdYFd<`nk;gv-hGqt>yrivt|d^#qV=HW|o8Q9@nvNeUGrbMhb zWL3;b0BqA@6+>)`ex)HV98pw@%_qX8ePvrQnL?IERpxRi#4gtx zqcmf3t3BOUcv4yfHk&yHK`w3D$3Oa1=nZog0R_`uN=7j1nWd)nDnq41L#^64r`lsp z3Q>bJ<~8HpsCJHG=8cBjU*o3x*4e1*4d!)yA3&X^I=$&nF$m%muB@87gBYi??9H%{ zOyEr7XtUAy!@5V_5k9vRyJh3AD(`v>yPdqzB1=KvtswPbNc2dBxv|iO7t=kLwm21- z00Ty18`OcF2j2>x+e4mI4u|9gpvB;F-SCf*jJB%rGI;+^tz1`BhR(q-&#!TF!2`Qm zQcHK`GkD0Y?SrH=uGE~?y3?+8PzS{MJB<1IJlEoN5m+CEvA@?Ss0IMfX3D+X)Ft-p z;X#dU;j6rW(r^fe?owyrXUHo?&3cJu02UEWP1-FeYNv#PbvEIK3_>?37uC$Xs0;x- z1Rh;BC(J1^Ajnw&o*jG%f=dO<1{q?IuFx$tvH!)h8sd}}{0%A0_~}k8pb}_f_+N28qW+UDn;CLOhKDBltGB3C}61iV21?hCwPh#VGM)5eNrQTHYow z5w0lPgzB8KP;Q_e$OHNlhQgd_KlvsY`dTX?R<}SO8#*E^LQn`Rz80#T5UdoFZ`*s|gFZJ*;H| z&iVYM(Gq;ebzP`Yo~x6^pKOLhpiyqbK(>fyLPV||BQ@)S(0`(+?YiQXiu2+y+yhVvd43GOBt0aputv|s9i|-LxBFQ z@o|nE^Ce(^NUCR{g4~<({du_9qX2(Mnq{JbOk~Vk*#>+Nhf*bYjLIOqN{P(T1}?0- z{|t@bZ6K}-Fj)wE5RdTg5tmbO`k1sv8Uzn+Mk7Q=o6`prjxg}hwWUFi5bmYs1-d@X zUp}D-x73*dXr|9~|E6V7(IL>V4M1b0CGei%UnUi(leZ!2CB$wC~tYpWeY8kyj%=|YXB3J3Ea{g+NSa~hi{uYaT$ zDHj3pUY_Y5?MnR)z=q(!z^X6{;3}IrIQ1jumhkTxH;>ih?rSOvwo~-}EdW1;`Owpd zg~w{ieg?>@74%o~=aX)3z!=gGwr0O3K&=d#M**5PbYi6W};Cg!h5{ zbo_&*hgkOjS3a9n;MR&@kOYV7KoQ_k?gpp@a+?iJzPwozM0`Y#F95;@Du@fj=Pg(t z>QzMgf~FR@3s00_jQ6Be^+2MAZW9b82*bTCa097`TOp%nz|b{YU(!M1rHWi_60rOW zHF~GA+D8wE$s+Z|v+~ls_`#Vn`W5{i8#pOgcCG2ZIDghFhJv`ra7Zjr1r zotFWnz7o5<=j+_lAwaDR(5%|ZC-kK=+L`P?y8vtrzr|KswU*Y5^sA@;X?w`|W#^-3 zj5fr-sS{lpOrYG`nQqwZMc6YV3{T>{5UYVd%!cjq;^H}#& zDNS;;Z97!JwJO^qk-=4+EZw~N0d>dk0Qf!j%0sSwhnhGwH`wt;m?n{oXiSPK8`0-^ zFi)49Sp>x-E4i0aukY);4GA~ zcelFxcNf;ZX{%!sZsXkoHyNnzhwdQGvrX<6`=pS90K4NQi9T)BBQ$QmtiW&ZtsB;O zTW?;y*|LcSUbHj#i=2r&AYgZH(56^|6De0j%AxJbNx-)5tPDDf?%Ph1hx(s6#4oqE z-hZy=7KqxEw%|1hZShe2W8c35a!204w=Gzz+zAr}%X~{7=h`(hTnfoOvu(CRcIr;& zetn<*2dPPas*ZTxBC#H-2%z z0;Nk~Fo0(*@GxkPEnC!;0mA&#^|MnC9R6wQk7Vj%%Jz3b;J4t1^#5WZ&60_S{xUng zjYg}|mq*chU3?{{r!XEoH$zQ62iSe)$(1tYxvKf@Z?n`;FdhOk$5}=t2>a^3ZBV}|onbXw=eeQ_z3!sP;rb5wiEWju}Nhz!@a zZ|wWp1hjcso=@1lJ@<+C8OJQ2x=ZiC;z8BD+`a^9R{9MO`c6uIz;EK}Cr*LBBuHsC zy$&{QKz`>*bSZX?WJc)*UmO^Voc{X55r8i(aXUqO1~ltlw~C`xc7dN5d29Dt^CfTD zfm>};0^~bgoztIy#V7B+Ti;YpJ9wbCAi@xPeDwu;yedLqz!z`BLp$nNsXnZf2PIV)yC`>`NDaorLlyd{aJg4FCZ70bt)| z{DS@uJCLISt$?bIwIl2Q4+;wfOq4v_sX7>wXp1WvSRO4wbi-;`C^3cM$O%=>z9Bap zfd?dT3A8^_H7Z)GN@+-Ixaql8wE~iB1(seJhQN0{WJQ*K(b7zB$M5#{=T>m17k3)n ziCl3_Z69x!Pav&fXfV*uDBjm`2r}fJNMhYyi3ZUT-6b)@JPFP~u!~{thml0gYfo|6zl4Nf%tw3v~he@zkKR7k1!-!H+bI zpTV0e4~edURtq5_W9^2@QbC`uDF>(VC#at;jV&6eHXZq2pR|jFqj@N{G^?&g!7^Pj z0AReHBu*M69!Wzq0A~ONh(t6rqAxU#NUy|L>`Q5&E9i&BNRt4;OFaX+Ibs~`*Zq) zbSZ9SMYIIQ9uNAbu=Qzzc+!~|5f|q5X_a1AS&S$Asu%Av=EMEj3md{5_RV-wUu+@@~NL1KQ)u$t=-O!x1zbZW7>sjPh3gaUW}myeRr)%DV+|)#)p4qNyv+FmfjrXG}gMuS=8xhWOH*&#kZ8^e_Ys$ zZ{FFZISO`lIbZL)srxSVR1GoWhwKU_Y4XqOVQ`*w|IKm)+|CWQP7B27)|_^m2ziz` zFF(WVXx%5&A1u@nqVqdiO;{_o(vT+2(BC=m%!|@+wTyEUb}H`9=sPbkyU$C;>AM&4 zo@CqeTQ$Azq)#7=1@pR@EAg~R+Ha^ixvpgS_Hd8QB2r*EiA#@7wCi$aTAPR9Vfm~v zYHqAO_oY_>uD5*MyNz6yA8=9P^xJ0Ab>5e5!2!G9m#{cJeAsn_>?OuS9hpPc2raIo z<*jcU1G?VBqaDGdN%)P%VK;z%Je>`-ZY8E}i?Vwkli}-s=zF#vdPj<+L*DMtmh>x@5?D;h5taSP_LB%U>tn2*D?6Avd({<#!83OA{ZI9 z4xPwlngko@s72^JyB<P$R3D}s%|zM_?cU{cD(owjI=fP1Ym?j17~9|%-*>3iUp&UGdNX;G+HL$V$MruX~Si%=XEh>Q&&rtjP+_4x(1XlyRssMWkt1yrZalhOE`kA zeI-M8q?cWj4l3K$-tH6rWb2vca??J#h-b+Fl0AQ5*iWE8lZzz&aZBLCyZD$4PQS#a zQVp+^y`-OoP`N0Wouq4Q+gSKWJhqz&Foxwv7we^k+zk{LEI?Tz`BrkK?-k6;G%1mi zUXU4G!P&llK;sWE*APuriLblZMo;bjKKfjishu3WZi%S z0wO|8lfwofPrU?20hFlw+AN)5zp8WbI_x#wCQcqFV);M^ zG0+m|00qLitznvaN@JG1ve0cv+@rKm-eZmlI`z`YcM&3b`{>}tg>;Gb8;i*bv!o9} zvM3^s5aYsZR1r`IB2(OWk1rGHBT6z-WcEDFm_(&wem45Rwf-WFU6dfW^}ApiLS(d4 zpP-J(&yPrd%^(gU(~HHihsBrVHwoX+hUqay0kC{@XxCZwOi(_;jQV3gSbIo!>{Fkj zeUM_!(+|(C;ia7S={U*@EM}(fs?9MRxIlptd+^?#SvG1wd~ibv41gvdx1C+Et90ZG zmh$lO9%v}>Jhtew5}1Ak;FjQ~xah&%t6EVipa`%8JW&^rfEgerF+-`jvf|D!MK7cR zK)+$%1`w>F8daCGY9NG^6N)TlJOA70kYE|{i9yGK77i56fl(n#`U$ciQZkkW2G6+v zRWQ~cuJ#ZjPBdqRH}335ei1{D`lE#hlspq5EP9a(X*e=mSMZTQ5Rd?Rj!mHds?xv4 z_lLs^F`ez!KB2(^+&w;=9B~XK*%qe|aDawWrHH$Vx^@PG_XmlEAxEdbUM@~zh-^mK zx%WmUaE2%*eUDpjKvezu;-6|uOzlQ~(Te6cqjP)CLThfhLkeG4Zh5`?>Ofuzg-#X4 zm8vpseI-R#YG%D8yaRiGYl|qr{`%1#_0_+>RVqc#_10IC3@6*!n_MXdQ&XM@;D%*j zuxjM7YBWz1*y_G{&(R*#>{ps*%@=f79yb(>9(m z0=Fe?pv`WA8ylDkE^-$VDN3FMXy)BpoFN6f;re&!!A-5~ghCK0B~i_$(!#xImDMy| z)y!d|YPqR3y?$aj^D?VOrdm<5tgTkL&Qd|L?ny#X?)@j;iu{irbc9|CB6 zj!txrN>GCIP2?YWb6#5z|7)=E8zB%|)VK#&vd$)uKy}O$Cx>$wS)@2^zzFdd3p1PQ zz$s?m9(kZYcA(=fD~A`8jUUrOHwM2)P?Tg}W`R#=nsFnbE$ z{?QAu-zxCt&5V2d>f0Ld%KibCwfpMmmi0&sWhd7R_0<8FUs04X-O+)D{M>Mmoy`b8 zq)xVbjN@hF@UY`FM<>@Q0B3~Lf4RK_`!(9R^u-!uqqZ$mIjCK+55WI&@DR5oW6Pfwz#(I zj;Ghc-pwWT=H|W&SlwR5cUR~(0k$yWq>yJre@@n%@M$^onXv%(2w=qal4F;>lCeq> zGmk4UW0mNlOrf_9@j;Y1^@h=E9sM-hHO6T#0q+!81*u7=O@6#sC|o<$bb<4>XL@c- zP^OH4OijfLlSkavNRgl2(6`>ct8j}ZBAAM`RtPmW58HnQ6rnN-dViN??W%E*BQ8j0 z4fICRDm`qa=52p0ZavM-T(ffeVNLv8p5yNNJ`C>ghB5A*b^9LG00bY^&mjTYUAK#? zEIFOIUh?l}?_?QrVbg6dU#AV~zs&9Sr<{yF@*keB-jH(L-?99!QH0mupL2P;ZTja- zdeRg(JJ=lno{oE&^YM;^X#lr#X}_0IEBo@pCFq-Q17Q!^zDtCLfXb@?DfkWSmfelt zjr3L;_YnUW{}jCA=ReRbVY^`5V+3dY{a7b#hRKR8uvav52h$t0SN0pr8#edQtmB@3 z-zeVSU$xjb!v8xi#7Lwrk7EG=fxJSiV;)rs&A~KftONY0yz>w z!N?8n=A_{Ah34J{B+C|br8r$Wviy(~ki`^LlEVTq#DZqA%_W8%U9wm48D3)_-MTWo zdz)^`KIdJ313x!am!%*zFU@L_cv@x!ynkOFvoAW^cH3x>G=6*w0f#FwDUd4=ApPe; z0HVA1Ko#;Nx^=Y;obmN>McORdBj-=t#&e3!pREnsAVZbulP2^Uoo0%MaUG7{ zthB2Jn|7l#4GzJ|812u*7d>8m>RH-Um|;A%iN}$vzfN^2RU0uyYPB|1DN4%-C$Ptn zstrMw-bb1?fX-yR=@X*1VTKf#lH#@*nv`#=b(+)68j|9& z>c6e=>dxS__#{@1xtxEzuD^Cd2X0Py8e(d9JeT>{%pcuw?j8tE4AwW17*emuGBW5u z#M?7#)+CS}>heRvr)SE{sb$w2dC&1F0I}7P zy0yjCjok(a#xa%X595C)V$2UMYPDwm6NqkX90{^{bEgNfAEjSB4C{0s=K|a7{P1w3 zIldNErjy#v1NI=1kCJs6AV*>Ql&S8WO0}dgDIO}j8MPHZTLqhI9Oaj5H|Xr;9U@2n z6vQ98QJg~*3@`3^=9N|B?P-(K1JwK2G{<8%3o~bLTBJ>BCGDpLcu{H;vVvGwlLKk1 zb&O|3me~E9onN@fI6Bt*nvRS4^~82mCfzECwbRwB)en(qkJS7v${L>TljB+(w<%bk?nu<7OQxYmMJE^A06?#r z8=+`sO|f}j)*eY<&ikaPKLZm1h^rE()-f6l2ISQKYVuCkOF+pK})31z{F2DejgG>+l|) zvJpdLadxJ`#fhlH0C3F(yh3#MM?~v3HXWPA9i)~k=GOZhH&J-k)s8|Nr>Ym_$fo;-l&X)qrO*75E3@`vL_r$mq_$O4oZi<}-@&A{dZ%LT>CjWww2Q_V4P zX)}}z4{-kObQEeWF;i}<)!vMiY~+R4=`17PDp;OpW>iWa)47y{;6J-IvgnQOZZI#F z+zUwO?Fv%@iHWKJMl0Lv?qh}WG2=l4!1ecRRzY`mN3nh*8nou|yNSER)C=jke)^GC z1^*+?F~LH@k>7~@2N4rT=LT@m#0ZRsj)2JVm0u>hN|}u@Ih!!yr1*yN*@S)^QJ=s; z96FAFWE-**RVKFZip%7t(lz#c_i7!Jdw&3%I>=Dcn^`9VzT4n%9{?H7R9I>Js-_xx zMl%ul0#q$f&NGnDh9ze@{{HS{$|WmS|DK}yEgUb{qO5G8OPkg#OR*bBuNTd6NzL+A zb1xQ27Rl7Ed10vh-*lP${`4aGYl_<{cD8b{ASslM|EY-yISh6B!?2db!`q33rvRfb z$8@O~tOM=F57Yusm0yPi);hn5*l55A+dc`6kDObzirQ}uGprTAy35jcsx!T0DcF(zYNvEqH5%R1#J z3ex*7rb({@%&iFA#Sug#o_)L2Y;qk8EC2XGP=3u9zXNZ;`zavxSzq? zrlZheHAv2LkU(=(*QyU|J%KbhX>FC>ZEkr1{;`+q-(>;Effr$VgBjGwFh#tgS2Fad zdhj7fG}<*p%0}=id^@W2axbEJKwdORR;#^LVx_hW!{~R5xKu6@tI8R_bGV>~xO;Wc z27S0k{K0GD5(A`%!%2|MtAjsJnv11H3z<>ws6!U*DxXF$Rn#!!?O;bceap_5+|{Z9 z9C;n84TCoD`wYo6mSpNZD1l=m*h^O;er2?SenTSxISJs27l}$X)2?LuFB~6$u))q& zrjR<N2%({LG6xf;Oq3ibi`p_TXRi%0^)U+mDdvbFLnF?tyOHU4%`iD863a zGFyYC$1cT^HmYy@jXCp~40^GE2=8^5LdHnCrEWB{ieu%AQDtqO(7qMM~Iu%~ip=4c%7yJ}1N{))41LiP$H+WU}#Pal7 z->f<-LUvHRQ|&(t>tQ5Y1(Oyr0VM8ugKb1kulakouihOi60*Ky`>zTB)Bt&c{0rc7 z;jmrUg>E8#HgWkp2OQW8GN!yRPtGP zPTw{IeDP8|q%Yju=$4l-?c%Z>vz1H)Gil|uzc+mK;ZeW!02C8|gvkAB<*~e?83TiQ zX&DM9JOqNJ`ffkHezUA(pGTn+@s0qf3)d^S#z7hfZv5yDQc**&h_*QDmw19J&2Wg6 zk5BZ^Jc2E1D<8;43LT$GQ9YjcDAOQfNQi)8$4wX1}8@IYN{QC=k2J!x4>;ZGxPpBkub(W6eK+=TNGF_)&3TZ;>PP+%&C) zs@}kARU>9e*A_@8H&N&D{lPiDXd@c=$i#aD2uluXrsUO?Rx16%yU0PwK28(`4^=Db z3rnTNhTIK|)6#)g9=;e?+)LEA$oP$NVo`x8_&$n-V<{{4&$3~GkXvWOcSSNiZ)Z~d z35d|{){b4k_piyc>=KKO6*kgm^5AGbanSkV+}<6Wub$8qF^ac?7M{Wza2yXZD&#z_ zlz6I&OoX|09?bRucJVu_UkCzPe`A#7`*i#VT*j4>+h=l+(_Vbor2>CpbljO{c$o{& z)g4~^HN-hL<~kS8Aq(GJ>~G-c;u*yxc2GeZ({K{twQKNkO{p)Hw6-(yXIw8I5!bO9 z0`sIhe9<$`L-=x(DgqPjCy;hu{5v?TeC%s+-8Y%I2+F5TX7i;eE9Hf#2&t1tt0nj$dw@lOi@lD`L!A zcq1GD%dH3@H@E2>%4o+}YsiqRUjHY=bKIL>qLky{3gk>3I5ztub8yn1FJOpIfLxFD z>;(%b*pO#Rzbrm48-o(f52WNjK&3K_U<4wm$pQ6~+o5r&NJY8heN1)ES{-j!3B3wZE5898LGJ+BYQ9%8@z?Nl6ztrzl*@0&y}it?WfKgPi_gQa9#)xLCq z5$I`2>)Iu!vXb|T123iRR$Rx`T#*+5D<=v==OXE!nDjN%-~GVZSG4>21o7?qM-E>Fckd%)bbgdo6xIFP4jdKze%9Iv zOeWs&E}ipT>Sj_RJQ7vfA@9moE{Yw1`|Z=MaHI9~r$Iqc&Ws}FY7^9(yw!jB9Ih`W zA6K-1A0`R`lKh?T{UVHydy$;O$Wt9uNRYA2Q|QLdv9IA%=-i7AQ;8#i3n%7aJeNEqBb`Y9;->bVKp?4Gp0l|!KE^^5_;LS-u{2NOC zuq7&fiI@j=TDmG#R36V4x{WXVVxDZ|tiyk@2aCiaYU${U<@L)mkAP*It!R<*oB7THcvvrtuf5Rj z>0=ZZeArQ+JCG+%x*q7*^7a9M)?`QB?&O&mz0)L3suvbMV4q9mvBl&%qxr3UP(Gja zM<5FUb3Zxn_V4E^6<+Q})*e4JrhB1WPchB!aPynUzP0DTOTTPY zlSJ>l!JNI+M*EB3;$3NyDC=Tdr&;N(NAK-kic$mNg~o56Da=zcvXUquLBYG6b_o;b z0iiaj-YRAQKn&Sq&alELTy*l3GN@nX{F#OT0A=vRod4`F<&s0`l1?%4%^<#~t$=s) zv*}AJi<-Yu@W3MRYq36qah`oU`H7wSo^F0?830^oe3qKqxx}{LPG|D2b6Ou(AK$v` z4olR|;+$i8LmOU>ml(vWPO+aG+PDh`Jta> zXn*FAb@C5zB7dJtwYOeR6`No8U(6982I5P!AIK+{>MyjLA2UGB8>w}~X!quw1Vox! zRh|CLP!4#TP?$Ufx&G~Lh$>C1ef@cy?t%Og@Q;{moL2G+G@fF>9WVNidG-?05B1WI zY0VpMkc`jvFM65o5c>fCw54&lr`uNxG+4-Qjr3PW@Xz)>ZT&Cn)vsI48{~fr2Myap zWCZQIuF+ZWU3LM1=Y+Zkx6QY_r%0{3LaN_kO9Z47&8U#a&K}2kwg-814uBFiF~K!E z0>*=N2kb=xxX^-+-s6QXXwmyq;b;gr_@ec)`P&|10Ko{8&ks1(>dJK8<15OqNZ#*9 z2lxG!0_7tIfN@&=!#l!U6+d^`^^qYj?4<4Uu?1_phaM2@xsCpY>yyQ~wC=6G-8BnX zID2(XnCkp~r*P8GsjYFvYF(ahTfIm8xO_E+dw+yvB6xZqr;J;gCaii)QKb~rJ*}GO z5sZH4(~Q=2^_yH?XV)9e>m9IX|M;Oj3Fx!Ivlj5|vAc~D5Zw1U{{7Fq`u#0Q_STWH z#2S_7^aO}OSHwpu^4ro7%T6vl8+TNrh?rWH7E~~^nw7$C42(^%XHiW|)C9AYYe0)T zjg!Cr$(mNX^wVspe&K(4p1=7yd|ZEg_c{H}+5P03_1tBh^~4UHH^eeSH)MZo!$9Rw z*v^WVoh#)D%NIXwT}UvcjG-n%bBkEt7RUUSZUWq61b2h!UVPT~$S3;1KoGbnnBST4-F@LDW2PcxaXgHY5L{f(y~UB;a#wlsz|-NHs~2j_ zP;RPQvo;&59ZbXhTbH%6gQKi`%nR_e2p9xeC5x)rT0lL2y|^M|e73Gx1F*J!>y5YI z_X_fjozA`;Ti%y;0T*OAOY_@oyS9gp(jcl`Gs(`kbl>;bM3lCwPC3JAo~%`UX6+kZ zKOUh!3o^>UI(0i-pTtDA#z;>}dTj8)!j%(jR8hFh)HhzSRr0 zN!K`+`PLgL8ROjNW{KR7&kAQuZeC`pGSs!ZlqBKnrDjghve_B_@EPLO?mUs|0PMBd ze?)V?E~PKb^iG%EM@HR!(mt(xj)ew8hZ+iF(Sn3$7hj64F3}-yz{i5G+^r?N+pP0k zuWP=Wdl^t7D+DChL_h!i_bn*uV-)&*Nl->(whk zX_ka+VXFlU{aGE!giRm@NwOsqNz|1^Sd3j;RLGTYpY~(>zQU|`*g*uySFJqkZwXOR zP9}dzvr%&qb`Ds{(_u_Ar!s!(uJv-Y!t5%ydo237bKdBP*L`u*_!gi(?w-e{%P;Ux z0or7-WAqP}q`GA7;aFU)Of5-iBK%^E*;ENjj}k!&)iJE8^RZFHpZ+vj6Oja^aR}>( zLo8m(kUvNzSm#5SLs8`Oawa?*QY08$(5lQnAyhOxe~FN}snG}Ip%L_xiBe(6@8=8U zrLq~NB29}rsZH-;3a9|F`+k3=kfe%CBY&$;2_XP($r>a{e@}QL(GoQp-R=AFUZ(VG z5J-CIrP`|)t>CJ%y+6P%#nQ9z@y~6Jo3_sSzO}KVJYHI(vS4Ju

iraO2K=A#En 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%0D?UX4diz*>}q)&RRZR-DJXxyCx@8=C+% z%H{}RzGI2&xwwXl>$bR7i|ezvCX4H^xb}+ct+>XD>#DexlK4JO!8uKKz!*5v4P$hc zdSRRB?8Rpph0Z1ye9l7sCfi`WE!H3m=LU(t2`KP`#NP_&_#s$qeYOCsMdU>?nL9`oov{eKvvai_87JjRGecl1-3KZP;kVc&_97$1fH1jdI6%l!}K z=0S{q(O7g3JZ6alxd&s!?}Gjhtlt6s5zM3e;bE*txlxHR%7+T9LEl3M@w_M>_G65` zyMD(u=R*Gr=Fzv@Zaj7t^gA&|-+S9Jo=#ZqrbwR;{br2OcjG3EQ7&!3`ZVZ&Cd~IX z66Mu8j7LGg7W<68{nubU`lc`Iy&3KM(8C z_xxO}=>vTM#^|0o17lSGrs1*Z-ai#%l&h1m9aQIXu^!dA?=1Oju00blM)e?@`1E~4 zjK>(|XNE;%en-X`A3?H z*f1&9WstCVS^3jTvt1Rgrj!(YMA|5Sbe5R)6w=#fCuL+Nk4+mx*mmpKwPRpMotYh* zo&{tklF1T@R%v5nGSlNGq{SqR71EPalQLq`GsY$)Wo9BVnV3j0qkv6Kj*l6YoR%1q zId*(TLX!De@)*L^FC!^FE<@)R6F)vVHPH`-Ci;QaLi=TABv_Sh|67@ymXJDsKJk-X zm;b3wK{6YQCu4}tR&88*dgkYe{j!tN631pFCHiIPVvI>SF-aMzF3_m7eyVtvy{l+j^=!-8?&Z{s#a6|NkY%dsGev6aes#TEke@FsH+rcU`8J zw45EIaaQ*T8(|FT@zI@s$MTirJsRuIyc;t_6z)u{yh^D^x;f2So~ez^*0~-#Uh^K? zIs5DP^8sG~=mkBYH~2vS42B^P2_s=LBtR65fr&63l3_Na1H%ef4y#}ttcMMd0h=Kc zw!&`M2m2up3ZMu|p&Tlq2ChIO+=Sck0Nv4n9@rF~!E<;5kFX_ww!+u2J+?t_Y=>Uh z3A6nN!(S@lv2j}8ET!0I45iZ6hn1-L@XPAP^@C!`G)wl-N;kUR6w_qk_;dac% zotT5Wu>cG4C>CLVF&@JbEXCtkjulvmRd^b!@eKZiwRjFM;bm;VU-1uY#G7~vZ{uCO zhxhSse27o+8UBOM$$>AZ3As@VYDJ#pMXysEYD?|t4f3Ye)QLJ%SL#ka1k{uKD1d^f z4+T>w^`rh2M&UGqqG%M2rm-}hCQvlRQXDyH3MCNH44O)RGbx#7Q3}nc1+;{g(+XNk zi|A`wPnoohvS>T)plr&aowSQ~(_YG@A1IIV=>Q$1Lv)yqPyrQEF&(2aI!-63oK8{& zouW!os-kN8k!t8?s-?4Zj?U8ss-w$PPYra9jBe2lxeOVV6kk=EjWC2ho8+DS+0EItw-ff6hsGDzN$ zff6ne5-B4kN=C~^GFB$YB#9T2sWMAaWwxZr=fbi|R?8aMAm7L)*&>@|n{1aX$&sD1 zPWDQ!?3V&LDn(KyrzKy?<)j>x!*WbYTTCWX%dPT2kqh8m$dS73do4K2ZHMbV#X|1ij zd0PkTXkDzE`50I)^D}=7vc48#q1MmBY>|QVhJ|GW?Hh% zw*|J;mf3P!VXJJ7t+fo>XqlF6Ikv~Xw|$mp`Bq?sR%FFiVx@M%Dow4zYV5R?+Zn55 zwpzPj^;TyW`66H9IHqsDx#Ij&oHOD-bvfhx zU45bxoT<@H*R<&57?Z=1QY-O00;oAp(0qRT;~aW0{{R31pojamyvY? z6_>5m0}p?1XdFcpf3tIcy|h_(t58tf=2}rBcN;dE^o#C!rXf<3Yfbdy-rnt8vX{Nx zb$2gKM4_~?6cyCcvn&P-Ezw9tqfkNo+ae_ekt!AxQHoelq*z2+!7t9-?b%YH9~^kI zzxlo2dow%p-j0dg0|2qV9vUe;S_r0w0brS0Z?Au+XftG9tC<$D^#&1qy+$;TSbAX2 z5zW!`fTgqN|Ln*3@gn|X4 zKtei)%BxCop~dhNa)(>lLRKmEx0H0Q|JmYbTd{YrRb<|mcu5|~iXy|ygCltGgs5^@ zL~A`DsUi+bNnQ#|DP9KjSy<+TQxZBmd_jLa)`>Vzk0zo^A#lRifKt3F;1I@n6{T)& zb3953Di%AmRKoCB;7!v1M>#r;HC%>wQcF<7)MCsR;Y5Vf)NZqpj?KPL*kHi{g5sFUxl9@#Y`eYI=i=kI>$yEjv>7su=x_1)RlW2R5C z3Nsyh7Lr4``*YiKN}F}ufRa9w#SPeOJDTt6du*-hG+bKOp5}T^mH0jrBf|iZRl8+Y zXh+zF<~vQVO4>ei7PjO)QqesnuT_7Vre)-5sFA1I4xRVCYFD}K#!Az!T215Mytm&_ zU1zdH=OnYBbhWzc`d2s3&k@^jJYwXpYv>&9tvbNNm1&b+!7KYqbpCCA>6tG+-grvx ziItDM_A(k6kY#!FVDh~~->w`uKRJ%ynShh)PhI(8gTL>VCs(I8T~vO2F*bku{^A3N z&pf>(*M8D>tet&p3Cd$@=UyOx_BJjy4u5{&*oF0#kBrgStDmflahH!SZhPYS-;a#H zy|(A{$?wnTtB2N4-2FlA!FT&_{q&DZ+H1pojzmr&UQB7c=t ze{2&~9Dmn$>~>q-Zf*C=DAaY6mce>kH`p}OH^%5Db1Q6s|CsBwckOO#ugBeW8wQgh z1Q#VT2tqC{anVju6f1E?qR1eIY~j~p#F!Y&G+;suaUlvZ{^Pr@u>D2iC7<`c@ALh9 z-}}DrUBBJjy{#4iirv`O@?wj=y?+q^!U_N=&sTk1pE%6*>AFIx;;y8q=~i>W@dB^t zqDsbjMT1dMw=ZY8a6*nqQHzwwi6qoGujhzrjF+PnR&!WfmJug4tmLDjtaA~SkH)c) zx5VWqutXJOBCm=0+HOr3RRjoUL$PbT*k@G&MP!i!wN@fuJ#uYA_Fe=91ntNVT&NBj zPKpHxI+_~bGY!zW(E;E_2Y+Knb`q1#Z3N41cOU_)85ascgUzT(a9}Y*Og{^!N>N_Pt-R9f#2m6fJ28*^6L?z4NXh__%OD#x zKo>JYgTz>Y0Wm0yTnqw(&iXA`yI>j5 zEaQu^JemDAELq!;-P6>RFN-y8OWSZJhx)9xEbF&rZIC^qILlkJ`w&hYMwPkh54{L;y>Qt)4 zG5PfBW2q0^!tb6`)v;U0OFTC}rlv0-cGldFLmxb+9d~tuM-GJ1b#>L0Y30YGi2Szpss zHu(3UOvf;xycd!eYiJ z4S=V-suIziNNyn|~hfI+Y%}c>d>E{=|`) zDbJb6^Pg9joV&Zgy>YSk_WiH_J?ZVfm+U%;zYj^qy&4E$=`nw2h33n*(PeZwy^>x< zyXZ=~ie62-X%D@IuBL10I=cRUm*YQBO9KQH000080IZ=RSiUa*iSq;i089q}03iSX z0CHtwVRD!I{sRq{zw-+Zf1OopXdG1(zB6~WyVGvcyEEIw;DgcGU`t7M(%q_wptqYQ zAtk1Z?IQk~eavh!+1;7x>};BfLg@>u#n#YT1|yc%Kue28!9uZWD_T+%se&N*pcW&D zls=G_qEPSMOxhKpKO8u7?>XoD&N*}D&YAx8&vpS2T$}1!^UNAIfAa(Y%n|@VTf|AI zqxyt8>biFQRtP%!rkY-LgPLB<)yi(rcC4aN*11Z~uEG)B4b)))-El03wMtF-no-tN zpS4JL$~kkSId7ObXN&4uYT2l|&Huuz;%g=eZkW0j)@l`!SI=p>Q{{?QSqqn~oVH*I zP;xau?d2sEORJT2f7LLH)>2Z2+Mu^|G_vAd9mSg2p>#A;I;~;88fe$tmX5}ZcaWgh z(sTgWCejlG=C5N!T8Z!s@(?RLl4hhZPU8fJ%~bJiA9ng4yI9e2KHp2f0Tkid{WG`;%$ACs~IH) zk=UIPdorSu5mZZ$ zvaE1eVeGJ^V41^W)U$&~Iu?bV&7m)7F`SH}FrXZen62JcR`^z7_H|FoJv(IyrInNn z5-AzUBsjq)e`Jw4v~s6>2`Lgy2St&Y@%FL8b;O(rBZMJ00%$;1^bUm-1u+-A8CF;v zLCi(pji`G7iQbuzf)R7o1H>N%ObB5CdSDo(nNVcjh?tN-3#{9vEE6ImVsDX{P_(;A zggr#0uq2VN=cK@x@lge5u!5Yh!#?|e-iF8?+}U7rZAxv+7yw#NADtS z3c=77r4fBN4)(z)6eJOnm%qVvHqyf|Sh4A9)AK+iKx7dS3JDTneH;Cz!bBKfM?OX% zBKmuZS>|IF_N>2eZQ$NV@qH8Zyvu!V)b|Vi9{4#H^0WOllj4)Ut>_Z7oFmv1jorRFf6kKf{%AT*4f>es9P=DCB`a+1vJoRT58qKZ8p_{Rk6uLbW}1L z3wpIm!HlK>kQWSC;N{>4liyG8y0~!|zPBEnoqgfvPfgWr%QhVy ziC#|pyq7DT-0{@@^Dj;*#Y?&VnF}YTe?WF{=DR)mpGzy3EBim+b!2XK`m{E{z4^)X zAbaERj`rtXy>f8)-I>kj&i-&dcXV?0!xg8BPrtvi<Z=7z7jm z000O8tf3-UKA>Dy=NbS2bT9w_AD5AJ0~MDrfx?J3E>x;2FG-O~RZ&YwR3r%Q3y8Q}8e>F3)b1YIZo7MWrFHDt zXPoZo>18%&-)Ee~w*Q=S?psoY%=Zo7N9x{le)pW;Ip?1H-hG?4-`3^x8QBeey<2$eVl**oO(qJdL@qgQl>$~SogcCi-dGN)aepg?uSf&KIYkU}H)xRUl`bpPaU$XgFhK zhwP;Z7vEqNv&q~@Q);SgB}$q5EJ$U_YQU3!+3W%Ct2{nJ!z!Qlgwsq%yfA60=)n!wZLo3yGqY9CG47k*#lk z%;lB!dX@6VqBViEt=6HDi=rEfR;e;>A@wZlSTJa3%N;gu>R|>X3C_!{(^=x&wJ*!TnQ~Ap-61}5Z^w~%q zO~{owbG2vnYTl0GiiPsf(*89hkg_3{FnkqF8 z=h0(&v@SnZ*V#F5JX0#EO-b1)iHU0aIIHT2mK5@Iy224PpeR`!5#wVvQ0-%Xe<4{O ztyir+kGA-nMih!xxlD`+(p1h`An`MStwY!p=R2?)NRRfe!Q6u4kCrs zED3j_j+{$rW*0-v7r5Gdv06Nj#8$2{ZcoLqh3)8?yaZdORI%stY>k4Vs@YYMpm{ix z8*HF0qvH7HGur}EiEYRW!;C}d3pu7%;d(cTp5s>f+%HStVY>dUBN23 z?pCr&7V@@bm2inNsu-lEywoLAf-mVWIR8ARLNS2Ol~-@c5v0awX>$>S9Em_S8|<3XESv+m6Qy$cGjFa zE20i)o;6u6CUaK4QcB>-HNUg!Jaa%@or^AzX47rE4fJDlUW$NpnTV#dN!%Iu(h|d? z%ja9X3x}G|{`Z#8$8Nyi-At2OUCXjC3mfxwvo6wL^f5z*QDiWGv)M4rh~LyhCU295 z-w%*TIY|tG)Z?J-lZwaw!Iat^7P?#Mx z^@!Q;(;JL;*{m#}%wXqEp5`;Bd29GswAnDtsNal)OucR90~DR&*L}e2f^BjdUC&?t z6Luc$vJd6aN9qrM`m|~8)9=xIW+-C%dA%VP8tfxETi5bVn0@Ry5<;VW8xJ-hEs<21`E1Hh@)RL*eW4}26Z&JuvrTuPYLrIHABLJi;#o` zHp^C*W$Q&*E|RbijV?e}mt7+kHyuDC3yEWxIPR)HsQDcCizO`DUmrGk(}9Q@8u3We zGz|%EcyN?|T!y$ozLz&30|u8%Sd1<%Ll;-95|?we;lz~;mPojq6d2HztHcuC99GHa zA>>cn4X(ibhMUji5i>*yS&AmY5|-ei#kgo`bKD5}gS2%_-aWceEPInA{NVU$$S z5phH7EE8*kV$D*qx=XCuA-WlKd+-4Ut0Y{1gYZGUOFzx|AgWNlgTZPKbSr~3-sBDj zYwgiy2J32Rw~HRV3HR~mko>H^H*JsXFmP1BFd}AJx3hA+%484sb>J$7etX82mn@Qu|O?sYS4Qigk7s=buq4v$)gli_{hCO)W7`}Agi zbCBA1AY{hc%xE|5t^vedhkd+D3u}G6J4*Rz!;OV!!~mTUxUozIHz6}kLg!}Az^rxH zE_(H*brP0)xmoTJSYDGGEMcz~8xkaM3o5OhQ+qcFt^55%JoHuu2<$?TA)VM18eh+QlXnQPhhl>UoXUM1(tZlNZqj z2K^GcF{0%d(dO!iwzv`9Tw;?rLsy6JXWDMtn=cUI{@dbN?PqX{gjHyD1zNqe+Uj=K z>Nd*Lb~l3Ul&94mk%2nGjkUnn98S4d!ZpN z?q+b8gd0)ajVJQmcwojkKP>Kl;nNyw#~9o*CGO=uYFuv8qaB;{UeqKQ?3Qp7u3Lrc z5=bCU6&Z+E#0Ve6iljTla2hKz$_Fu@=|d>SikO(SqiMeNFy9F)+D zI_H4P&;e%!#n4ib?h>gTBFP}>!S82~lCS~c&H)#v11>J67^FQ=nZb~MH+hhOWseFB zhHL4yi&1sJ#Z+nc4%j1`Zp_|55j-mF=ts`-fhk;$2izzfaHA1clcZ796QT8DH-iku zL?_uE2^%qGt1)F`)hWxlDI2Ghhw+2>D1;? z)#fp3b1X1cJ!^0Ae%j)H zy0Pp{W1AQ4o-SfftIf9e0uffB=vmD(C`s6YR@b7{a<$ccuGI<3&^|YUeUzbFJjS;; z8JZN8sy0`SD|QZUr5sGuT4)#hR1UU!IoRe|+*XqV+)DdAwj~CClM=Qg+jYoxii#2Q zn^AlpjnDPC-Qd)0PA%?}umhJ|k4x^i1Dq5O*bT0DK42TakHLNkw;{3zkq^4a19pQe z~(qpI@)@kiKm0&$))0nF7bG;cyxz&n8Cx7;t`b~33ri! zD49|ty;r4|#q7iUFoQ?zD&I|2ezZe878}5e21SI67(6C_;U3$Qi(@y8InYYTdRRzezUc_Y5h9K&5@QY~Zn zHglZEv6cgW9pbqGY}?~@j~=H=4(+cuXCpz~*-+5|Q;&LqJy(rw7spj2S+oLc>Wm`6 zh#lhbvpj~(%=O|~2G2_vMj|)i_I{zd)1}+#FHxsUw@)Qe8S(mZ#OZViqcn;JaTmWx zJue|cf{3n45F4nj!i#nl_D~gGsPFT^H?lX-;49UFQz)2~kl%vs`KkoG?+1EObn2vdmE(VX2LIE&pPoq(lSVV*HM;iG zFKSMI#hSG^v54vFReWz__Lc8s0SaHA5vSG116N2l*3)?D!=Ez} zibx}dG~PHV&LE95NaKu755$^B<5>wM1a8FR>PLFfDYM(4Wq;vEKGp)10vwg`Pd=r@&7tm!*i*w=Tg z&H1MASQu~5lXyM;7Tu~te8i_p7kW(7)RTIuQTveABa%*!`x?U^nSm|5#fQoHYI8V$ zYzHpkeh1ed=QIg_teNU&9k`XJ=p#f(cVhY)ZAbzR(wCO^8N4gu0St002Kn`q;+q)c zH`Iq2fyQm?eedaZ<@ZqBs|op8JV_K;=5dtFp$J|UlBjxMqr8U z#0+d#^+03a+tbz|zRv?o@UI?-e@6fPEIkj-J9JQP)A<2<8AI3aNjQYGb|CWos*Q-z z9rkPD0}V7yeayIx_whPTZ4wyp_vu`H;t1IvI&Mew!=M!~?B9^^2%6ieG5DZ=Tl^6D z-$4!Y!)DAg_m70%^dt6BybM<*`?1C_Xew7nQTaH|g7l+73;uuxx{twcN_Yb8-i^cG zGW%!A=5I@Q5=Hl*=y%Nib0qp*2~VNuUKIVFSx1uJm+&-7cBAAE9MB(1cm_oY6n$ue z{z$?Rlnf&2AK64D9L4B=x<>qe*gheCtQkHPY7$k?VyFoHV+qG_RSJhc(F{9FX$;`G zZ5qDn{fUI*s2xJ>PkXrd1@-dJi{fVterEQcC$B#@`#+@LUzmX<^!uk0p2rmyuJ|)Y z^yd;@K+!NJ_AfLutTx&ReUN%l`S?o-FQIxA)gRTUrBeTugcGRFV4#10Z3p@{625?v zJ?P?Z9T$Hm;UtR2Q1thX=pQ7!jG`=>{YOXiPZDNOG>)Qwc0J|D(<{o;zet!xbsmlU zt2St-qA>Rn>{SK&HwmW@v=>4Dei6`X3iKZmUPn+79eu1FB}ZYdD@@uyD>U42BD#v5Bb)EkvKSQR&^qdaoeD8kw?)Tk$-`yU%S-L+- z(Q#qrlCOx7RC#N*&Lzq}tMZ>Ukzo}&QNEQSY41jxbG3Ke+S{Uh+nm`V=|%aDIkT0Q zcXj{WM!m0%d#e0@2DDKLZQO4&RkfSmllSY*r!1S~r8YFYr zZ0Obyq{=%$ngdd>IczSMLR5JtNb^7nHD}5?uF0zW7trQ|rhpc~wJ=rQ1=<48I)N6+ zwa%)%8?=Qm3~iFJij6TU2<;!SzB&vhRTfRtFRel>Hiy_i2$r#-piS8hBz!&MR$_F9xEJS)F z8GE)zdUgL@d$0<6U&_AQUu}`$QN4C`%+at$n zj1dhnHBc#k8{(><5MUnlyn|Su*xmIXVxKK~X(P1O!A?2s>->M$^i|~}Fs!98tbT1K zSG(z5pQ*npH-f1aOz|D;p`$)if+`;a({o@N&}LfJZaVHW{Z5rnfa!VI8U`4B6dyfN z*GgBQqb59dc7()2br{c%u)T# z@p^NAkQ8h(rh|LL5Mu^!F+&%p8!KsGL$8I&sesprmIOhVB5R0OlW?Z~;(&{uY1(`Bwlt`o9F&kberu zMSd1A3;Blt3$}R%FcbOvfEmb70;VH>5AY=NCcqc~pQn1{d<Uy4J2v42g7=FDm+SD`)_->FSs;r}g~uswn66PTBu z@yQVRxU%l-q~`2Kldf#lkXIX`lpQnoxklI4wqzv6KM)tTb=^iaGu) znEl=jzVA$vvxd|zaOGFd{JP}BhT4#ik1ZNJb=QnlBkcPpz8SJMqIu`?L0>xN zKeSfnnN$75G& zwnyGtSuiDb(zu`njlI5TdZk}tV@2OpqaL=;Je}Od73BJ<`}v6%x4y4`EuX7j6>c47>#^0 zU=;EVfCmNtJ{?}`lc&jpM?&hLbDYb2t zT^Z%|!ks8aKErRi-~x?Jkjgzy{zG)?1kK&$^6Cknw)S=m{CIVLovUTj4Wqm3~7|>ksBweg6df)~L72Ww&^| zZsyGYZBduo?)5T9mBnTC7FfkXxZy`rr-OeFeRCsmS7^E2?SVv*5IQV5Ejc|o)u)!a zcrzZ~6xxIy&Qgni$7QW>TJlOuT&&pcwz%A-d3KM7n?;*V&^tgaX1NvzbJ{GP(lU3R z-B-toguW?md#=@;nqtWz7kJ?z}clCX*?1N$Ad%pKV;+@>sUCeWGxrsru$Qt!H7^r?%urT~7V# zg`lE6H4m(Ao?T~hT(FL7IP*rGFpD;vU1q-?UUIo)_0h$zf77(_fGsm<-8&np=EjDa zgelKmU7Njst)b%hkuRF9TUIvhCi@)^?nvr(=vu3#_V{C$ZoYlHK4s#KvTXPoPGC;> z4vYL7jDNoWEt)xrR7NYA%EJm(#w(91la=X8u3}f1Qmm9JF6Al3t$3Akr9xS!)G6zg zmz9r{v&tvR=gODLIpu5Rf^t#0qWq}*OxKXkL{EBulXwzNOk_A2LDESo8AT|`BIC&z zl0&8v3zWX1$#>)?xk0XzYowK2A(zPy*y9*Pq)z> zbQj%C_tFFOAU#Y^(lhh}dX|1d|3=Tz7J89>PcPA{w3XhVH|afkpM|hc7REZWa2CP3 zvTm#=>&0SOe>Q+6vow~@MzROlC^m+TW0Ykxv22#ZCb6k(I-9|)ESEW0J}Y8Qn(=>a z!+!x#O9KQH000080IZ=RSWtx>NdYqe0B~TJ@csiTe{d65me%!^>?@%QEwzk{&5u9= zlNf=FOdv6aHnPJ{0vH>TKuF}a)V76Xb-P<~a0r7FGnvfKW<6w*nVVreCL}YN?A8{Q zt*zRZtx8SpJZ@ELYWHQIsI8j!sj1rcJ-7eVZHua<3U{BMd(Qd3d+t50moJa^2Lh9` zXGVuFe-78Lod^WB34uW1X-v--XO-JUZK|lrr8#*7tdxKdSQLlMT6dPP^}m0EqGF4r4Iycx7; z`$lv{Q;kYp)t7=bd0rv9wbtir$Lm;Bbfcoyf3{X?HK?dGtCZ%9#(cEwR8Ucj4&BUI z-l{h09-kgxG=Ej?Is{j19rKD|Kt-J%*Cw_ty0Xx)mvz`25ml>0IdT=tnzjR0Ojnd@ z*+EuxkhCj$Rj$p1riyTi&W&d8iP#j=J`kH?`mYTiZ??-bc(biu-fWf2n@f0%wl(YpIm&$Qc@;!Zyiey0P^V#M#(uPe; z%3zY^H*|VfYhKg4TJY7h?%d2&)94GB*W#3;EG|~uf7fa`GOy?} z=B^kq_tm)}+WPuDO6QL7YW#bgXquWuM<AOfPY*SX1joxj0u-->EsBN*BEugY&Xx z1m~0`qhnfyDoe9%>RekkyLG-|80IA4J7Cz0?25K8wyZ{2+)zn!u{B)~%^GsBb@`)P z7MqhWHU&KK7e%gFpHHr@e>o82+-?s=Th$IKS)Yk6dZ(co#=QCMDD!ic*&Jg*O|H*| zOv6?_;hGkz!M#`K>~WWHUFEH`s8lh~6uhNYYVFXMfG+9`Te+^zNBOrNM-J!3ppL+& zkFkwQRb_;ED6Y%c3s z$+B9AxM(6Wq1%#63do()3$WLGH@vYU&;z#I>D6;TbHQ%&ui;%M+v&Jptht`=7N~c( za&yBOF}Dn7`*~}^e|yZ4UEjFt>TY@m_;MlcYFuxDlcIS(YQp9RR;8wtDg5*>9GkJt zP#ZebHw9;u`*rb!@CjYqpf^Ov0KZotOH;=yEvdD-T&eMGBT$s-Vj8tMpE2p(JC9y; z_RynCxDD=6?lyy(6-+I;q!neug1ygY_HCi#vd8DrWNx$Ff8Y)rfX~i12$Y@t60kp_ zxXrp)#0^@0Ca9#YS~;i%I^rinLI#ucH<>^Byam| zRc_aHxu&S_RRI@rD%<*&b^ViB-=%Ep`?Ecp^wm=4?HtsT%Weag5+A(9QdNfDabnoh zU{j2yz8Rwce|K*nF!UMxF6O_D`M+-hfwp__e1HTdw8i(OP%76cN^R-(+$hSi6sU4Y ziso`sIw*+=31?U?n9Dwrf?_Nv9mP!QNCgHHpC*^5eyBG-P##=EV-gmn3=XB}jvShF zg;)-0j_@xzwCoyo=g=w}yO2QZY_cu>MG8HU#I$rae;~H!{tJD_G|=VH_YePs*Zzc) z(zk^_=A+V>ln+XUgp|%m;=tNpa5D90eCRK@EjGZGgE@L|=zq~uX#k_Yvegem?+1is z91w2`0V$J~f>=rne3VMDRd5_;>mIux1=CUjhd`e~L|{+a;{O9LF}EHnA`)lcfjWnX z_>ji&e;WKxN`EUvggpD6hZ&hyTA3exXlWIJR@ zf?{_wJxF^PNpb_EPd%i2ApIU-JknK&?0^coHgaBYIOj!lnWU2)Emf+`QfE<86t zdod)2fX_3Mfq-x?kUnpz`(a&*kprztJz$mE>y+BtveZM29E8Yeh@3q^58I_a=P6b2 zm-;-U?DLkIg;Ea>&_i~qM;PgYfEfsw^OkxD*7Y!wZB=U4D)q2a>fx59_A_z>eQSwN z>IFvf9@RRijx#dgQ9S|GiFPO!krx>`2G#UIH817p5Qd6ggfnu8o{VJaAR>c|ys#nI zNk)!uNI7IJL-cfvo%;`iyIb6jJyP?4@2tVsE`7R85!&la*Uj8#qLRq z-64nFp_c5PVdNA9?T4U+V{`=bz!(CbelL^~Fl_v3D~#lXky?fsIZn@Dr>Nl$0ct(d zs;Dzow1N|@&@$R6BO_2$9$M_>gY<0p9@H`7upwR#y)OjpwmZwn=-p6ie+-B3rOnse zWIDXeU%7kEbqCGM;HTjQZ0+MYM$STtDGu760ZwSg)eb3f0qbi{us#cM=J87^>_*Ly-I}TF+?siauHITgcM`YJ(uA# zfL_(<#0PZz0lk9A6^mj(_uq(&yTZqaOt`{ph+K7qEJfrRBbOlN5FmI>ikvD)ZFzW1 zNGL6dB?*b`8hrzi*BQACUZ*ug-uNaWX&C<=8(M?j#LK|!%HRsUf0>+oMBfa@>5XtV zy^hFrSa=(e8}Kj?c@rKrL~eeKjV1aP4r~=BKM$Q1f|E(UZ{0d_YOrZY|k!jcIeMDwl;irhqT5op|srYg9(m64_ zgOO{1=83pqSZ@|Ccv}WsX~_^Rnr@lo&f!2GtU~Z7jsAZ$f6u`}Ug;2h8<8r2y#OIpD0v{}2LK-< zZ*>UYNdtKax56bMQnzru&2csSxq9iMiL2=1D!RFgK3veRi!Lr5k#`t@Py5I#fa|sw z*BuAfyByaYC)XX0OLk++F0Q4GxC$<=DUK^s;LrdUf8K;^$--6QxZd^W>ZNx~TxAzm z+09+{;etbX$HnyyBJVMx0IpX7*Ih5Jdk(H2tf1Q<@l;%CU0pxepn_9&&yD&XA^?6G zqF#ik?|Y-(ccQLvw(dI!?sK+g+=^#xwr>|%ei@d3WP!a+e~iN^Az;<~V=K{*5cvrsHSin*&!0M;Kf__k@%)+P`4dF`ju90+ zufPQPSZbRvhe+g93I6j*VwjU;g|o6sR}uMpe@5UpW@H>*S7DN@a=JfRZP3r~I!uzs zgOBNF$@NF{Q@Evn0k`xg;eGT$_<8zsM1Bqd{vDAA@c1`GK7q%-BJvA({4*k-!sDM1 z`Rr@_$r5Ea5X~*a4`w4Fl`_r+7!eA8%S#jf8_@jq_sdM=)aM~=%JB>$zwD#GN<>bX zf4>xdF^3#`HRq4ur#FaAuv-P`d+@UDnNpEu65h?8+N<(DmNv zpm<_nK!Sn}L<9vjf}#a+Hi~Efg%ED>z$oI0EUf3Ec;AX5Ua0s!PxW*$9r42-e@RzY zS3mW9e^1p@RnspKc8?@>*Cy*J!tR}DEu$^tg1$wKRx8cMtYvm_FU2NKEoy7C$IWv} zEsJbhms;BFNJP%VcNY@Y+Dhk5hSz-+@D_o85mFM*hWs*9CezUcMMH!Bx^fu^Cif&{ ziYivyE!ec6$WMdi;XsJ?vz9~ye}fhjQGH(;C2UEBwX`fSs5EX9w$y!Km+AXu(AQ#0 zo2sn^gx#lDW_X0K`xU#qOKD(K6Jg7j25Oopy_e2wnrN-0Eg>MPu-X<8edqA4q*=u# z86GG#0gpY&T0xuatj{i~Ym(G28_Q%SD$|uRRU?z5qz#@obPH~hH7PkNe<^7~R;DG1 z$Y=&clx2<5Pm7wYraHn_fP+EBCJ&>8wR!uESu5@06BN6`=z4|m;|hm6=MJ zu92xxGC5i%Cb+<}{9)@=kfg$TxF#@2`qS0cBZNIHsV$=sSwlnqP>o{O`Qu`qq-nw) zk)u*_bW%>xs#I!KqK&dltCA&blcUP@sLG>WPh(bxR%NzPWw!BrwnvqR2F4P6bsSFH~iVf2+z9sLB?%uPvy`4aW8jMwQK-2YNaxQk}@x@v?=7ED0Xr#n?a+J44ZeUh+e6`KcEPlu+@6642|55CA@b(O_ zza4|rm|rhUkQ7reLF-PWK`P5Kog?LM1o!eW+M(D2Fm)!F`mY1tV7)+#!$F_p{|ip1 zI|zGGv4zm`e=Mkc$!+;EEtYP}mz|a`684H>&Cv4fFk!FCz@P;DC9z*Sg|gIHI|=)r zVs}83e}M-(mj+}V>Yc8eXzil)(}KQCRmR#?E~iRtqyv78(cTNeM;*g zx|s^=lMF<6H?4=*WE!CwqWkZuMcI}N#sW%p7~Lw{dJmm?7o2*Buy^ud)~>y4Zot9! z6l;NZYVC26s)+S@c?+>V4p|@dwLa``eK68`pRo7c=As*6A1HPwG_SY&+aO^bJc9Zn z!ag*Oe|&^}WNre4ee87g17V+d-Huti_1@zya2|$KTCqh214{LJNTqE@l~1*fyK(Qa z8}}Z&BgCWVXO5zuc@*6ZFQJ#!)?0*q4xqS+_(HL}LEm#g-#vx;?sfJ367}8dI=UD2 zU2Gg%oYxmhmljE1oiB1)5ez^Uv~gra?1|P_e|8;KlqDuZ(N_|$!?cttlgX$|KsLI^ z9~7tJt^(Jeq|QPq%tA2I;)H$a?Kx)c)wAwiBT=jIq}3x4L~^g8{erNs6k9S3Xuno$ zDbSt^wBHoc?sI9sL)v|ApZkz@ndxI$o_3P;Ev?g8*5D+fI=SAbldF?ce@64IlTYr) ze|++tx0jf;Pm?V-WGhUsD?DT{VD}lauL=8Ju{Iz|1JMtKMEhN$A2X0s_d5x6KO|5+ zhkxu;1;cT_vHCq>KPk2ntd6cD+*e>#(7aXA1$4qYO#lVZsoi+op4X{Ckb<0w9dqJn zunIKbUAz`zf@%#`8x7VNi)#usfH}pffARJw!u^V^1#iy-Z;LP_DZfl&NEBOlAeAQb zaq(j&b0WgKDz+Y8JRe@{=3q7m=44RqEz@0yu7npWwgFm>ftCTc3lZL=xo}k7a4Il;lEOBBQ(FTiSWZ=&bX_W=uTT8f156< zS|qxcx7;bpic5uE+*6bmA0tA=$BQ80K`?M4;UTzG6J83J69`AEm4uhU<+p@)$0Lg9 zl~1=>IF0m51bQa>i5@BE?2(1s&^twVkA9*&eW0pOn(*=p(KCZtg!jx>w`bs{7g+8? zcyGlX1h+2&xBC<O&FPTsc1LPsIL_U;XbgOL97wN)4cQnu)1$0LO z-4TQz;jYBUc%-=j(xc1`@E&b$fI4h$fW5C`4?{OjCKm!Sxi~f?e%)6@`-{j(aSY+d znCAZ=Jfhem&^({Xn=q3%iCV&=rqSOB|FyX}nebzs(on)_WQ4~tBlP$7 z8xsR`M%ZkG+-&^V>=6>;e?7pk{)X^`Vow0$CBRr&$T-kt{5>)bbo&~Jj9ZNDEr#)T zhdMSsd8lKfX8fJQ_!Kh!-rH|X4AhKI8^&jhAJ2FgVSo=bj0wX3pxCp(cquTRP{>&2 zGX4=6tK7b-knuTV`?);hVMW_)k}~5OGSVNtFU3Tac5th4aH}!0f7Rn4OvftY;2#M8 zlVZ<$REZ;Dt~N4|5HwfX*r7U ze=7D4w7eX)1*cX~aW<}YXNAO>eZ?95#p#2^X@s991IJNV6Hb?bWAS>13>=TwGi4x# z*Ry1R)Ixf1OCfh9&5?Po%yGa!(TH*zri# z7ioY3&H5JGG#-q{@22J92rUhQ-E$Osf0&=}a~1ml>^FcK>4GDq80&5?FU9TUSa)+c z7GwXR*$jSY4jmt1DH(#b`(x)6mUK$6=;Pkvvhqzte_Rq07xfhv_7@in7Uz!?qX{3~ zAkNczEA|N*NT4ZQvIpyEAt>%MVPQSb8BCwzU^>4-j7g?~afw9m65(SM`^>S>`MnDi z+kNmS7b^C7!IPAJQY$7Beo=$CSVvc}FN|NhWKZjCku zFZC)Ie-mT%u4J#-Uhg%E?sc};ZYdc`N;|*ThQ7wSdu?$p;g<#DwaA}@#vc!d-s=_n z3Sv1~5K+4LQF~% zeFxUljZgqrz60ErG)e%aT1SF?SW1Lo6km?1MX~R(B4XUfVIz zpk4n_OFJ&*l$^ePGy_X%z}!m3o9C-B3oE-8&phFoD7F)JYDfF;KLNCYHSwMDg4igyLst_IoWHH*0*+gy-suC0GY(gBpZm=m{P zT@N=NP=dE_$%$K0yB^e3V=QiUe?l@}@or!{3$|}-7W2XOe6T$qLIS56INP}6!#U>U z#O>HaZ>WywPJ1V+g(Cd+oKUpW1Hx5KEWqY{Ol==%zL4+*IkAv-dO-NXoM?vT?XLUn z=zVie+yPD7UFX};={p?XbBZ(YeHtu)|Bj2CExSaz#>Jh4wp>^6aq|l|ouy065rT#?Xr?Y}m zaSI0Q^H|*;#W9T4;jrSr!fNSl|o%cib*tSeU54KAZ2)ZaCzv5+3{c=vUITLxHSV8ew6cR#r zP-{Z9Xd`?Dsuc)D9VeQ!T#$p?a$==3KlSrcZC3FfU~w(sD|4b<&qC*^9d&a+Ird0+ zJ9Hu|>6oAd!D;2JxFcUruYY{KJo2q7kgvBZUkk+>k9^ZT@~w8|Ys<^GM)5u%-)xX? zZL`<_@~x+O%3Ja9fmLNlpu6ql>LbA8j+|H*)w5mW^njTABPigJs4L;?a1=!8m@s0| z=&Z1VbCeh7Xb;Z%0-Ugmvx(x32WPGaXM>BgEsyhn;(Y;U4&ZET7Jr)n=Mm~A96cP8 z{|^8F|Nop<30MY!@ z^;k^+ONPg&d^H+2jDM8qrp|iZDYaG+qw)#Ru=V^|XVSx2qWuxQNVhB8TdQh7sJ8`@hN;?`zJNYJ|%>GnWtncP?=w|Uf)=sANjWbI^7zR7W-XzgK zTBV1YRWB#K^(v7}W}W@ndKwqK?y8oc_pus(+4Bim3mAglFVR6xo~mhjf<6#M>dL); zPzCA^s+vQgp)gQ_bO#CgAnF*pE!XJ|A?Xoa7%!@4O~6M-I3_Fz%2#tSJnwJze#i?Fw20sTjTY+L`MO)9JngrybyO!qT2zt z0#d(kPgkPb!yG(~&^;hM)!^|!tyjncUI|SdV1K~i`cR@ff@&4OM>Q%IRDVS19@|xq zYgDVDsgqsxM4~%`$_nr)thra-?@Ab4 zSwG~iWIG{4u>>+I)&rT^Y)fR^B-#L>Hh)3y?(+9F)RAZon9UHpu6@7;wM2IVsthO( zOnFLlcc8Xt34;bxu)2BxwG}8YHLI0RupdcSdVHLE9AT(0(Q(kc4T5;96Yaa;f9(rC z5|(LC*~v$udx6t-aPs|xQ*YU+fkelH(+=pUp?W6jNK0yDpPxRs_K{s1NpxRu-G2$L zU;LwMKiRdhME3{RUC?!tm3$zn)~wQy+I+s5MoX#H3jZj7zL|HTZxl6s+pPHAI3019)CmNKejZN=z%c0Juo^NmW&Q|2;aiY2bb_cvw4Ov zFtErQqCSEEG(sR4z!wO#0MHl#-6x_ClB0)6^k9g-7ovv-^LjE?*@RWCMH+pLX5RfbCfIAFa6massXeZIhz#RduJ#bAC*FmCFfUAJ4ItKGBjKm+B z0#FlMla}p6TB&kcog~@_qN5<{9Lz_{B71hxpU|Yqnl2KZ4w_@2i3#Sr${Kn9b^U~7 zxGag4XaOY0L1GBzxepTV6MvEnS<+3SGeL3!B;AAg9(XwDd^Ys>geXfE@e-X4qLUzs zTgmtPdnfe$u+ID1cS0Yu6XMZM=t&rQe&iwVgx+W;^dbzsCP1US6Z*nxhBQzLy=79~ z3l40&%*wmLf$bx+@_uk&`~LHeFv(--_m3_8B{~O2c?w23;1fqpu7B(_P@+eGQzbYJ z3g!pP3mbPm_!FztEQ^LnbRLLKgJ@`t)0q!W7TM`5iOvV7v#|JvLC9t(WP(H&0Cx_! zMBwB*AW2dHbsnf>7OrzeoLX6*Fr?U7=phx|^m2n%=}j1naQ{QgMQBL_2;M$~AswEd zKwi=lhT*XJGvK~x$bSo}22gx?p$v=nXC`Y1wK?LtCLoSHS(5VMMLZybHf#S)Jc*c()3?s1K{INby zqP;+W4fK{ad_L)}hv$_1Aq&uR zopgfx={iii7fg^ZVHhdVY6yD+{6;}qMc#=A_`)-MVHsaY7z%MRMIR^gwM4st>L#d0 zx8cWpoXi=N%P7cYgJUkE!FxK(p=wmVWMUQOH2CAk(N*^a@1wopP z*+!G4k0r^HpMRH>XH3aVN;l>jO{qpxN+Fk>3;skq&@_PJ48;YC6O=EYG=@T8+yvuL zD0&C}O9ve8pl#)VTRY$=?B5AWX9sPJgHKnCIVjy6d`u2LV=$fogcZs&g18LUk77VNjjM z{zJi5Vmt`!DU1hzJ&AD~*b^9c2YVc21K492qj@}vF`C~Bj5~rog8OX`)nSa$7!G0F z7OHZLBcM8fG5VJKaUAr$_hO9J!XAuiu)8ouYh|Z>&YaLZ?ZCJp*zMTI7wk5Sy}@q9 zK3-tAV1KLuTZVl+z;4F4F4#>tH+QHuV!jsG4R%bt2fN-LhxiNZI*i|dU4wo80Bgng zDcDsQKLWb~KiDqGl?Y_qa4sW`&uFI zH)1r#$fpQwgdHooV_dTjoG|ZO!ns}JMg!$pP&l>(Bp64{2hXV0E1PkOqdMXrknNbTL4mfQ(tFOl|2#+?V7?o=6 za=)2@!`AGdIcm`bL*J5?Mi+5aMfcsUvu`^e8MN*0Tc;Zbg5MwPs2%YzQaT&3?7P6q z@_*NrTQ6R`J8bdSqrb?UziovuUwVD{ZOY0X?~3M}yt#9hCF^**$zyu_>=)f)dSK$C ziv3;>Xqzyby3>pgN47@vM297d?vGLqi$^u zJ-GW#*|*7_Z!)w)0-Q~uXE)Ev8rNs)_uAi5+tl+=4)5AHDQe@Nd0n2UvrF=Vzkkn| z7Se9)fE@qf>_O`Qws{}SW7X1nCMqVbf{rCGI0UpLZJ7|;3p zdvy<8Tjbki*`(EFewz9&)@1*S)t*W7UyA7gEgsB1R(U!yGNsL$F=>)>(~9>_CHKztsfk>=3uEy+?=AhPJff*diNW6 zsUW2H#n%sq_N(%066=u@*S$sT#`CM<=NE}-*-tV8*0eqwe0js&W5Y_OW^HK@+eyFn z%(CeRo_G#k*KOU)RUOQmACyHt-SXS|-(PjTKH&M>_zS=MA`W}HE%9#pt?CzpyLn%D z?!UWg<;5KfoPOW=I_a}G^42W>6&KuveNH~btvj~7Hzv?2I`W#X?bBOt zg!7ZkvzZ+sd22=-Z=EzW;n1(Sr z^QU5r&hRN1qqBN4#^{V4kMU}-<1n5Fb}Yu(5dUcWg^%DJb%W4V3CFb+z+e;<9M)n8218e#u%NUBQTByn~O2J7jrO1cVRZh z=>E&X7~SQW7^8bG17mcD2^gb$XgJOj-9za(e-+p?j9tMRF-B)}D#qyeOtE9dTPVpG zS3^m{_!*Q$jM2T4fbmtlp3s?n8Om4oeB^a86l3(>7=rN`D1U=7M(>9~7~7x>#2DTC z12EnVr9Z~#zV3%Hx|jQ6jMhLOj2A$O$9N``-Z(e3zR>q@#p|di#%MjnVT|5uJjUpL z?13?Q-*v|rtqTLj=sU+^jNXIM7^AuAh%p*ddz=Fr2YTO(R)aRd3THvU%QjV0_)#N9FJCV?cwkcB@6OQ=pVwQR%BR@rEYhNc6-@9 zdCc90&$Ba-gECb}$|@`}nq@D<)HUUdOfs7i((+9y7JnhfggEztgzUWZgkDMVPlzP) z^QG~|EF=5``7CMu8mUG2k6$vM6GLr}(dF;oJ)foaE=Vw%^0OUMh=)<+m_By~EQPto zggn7y3H$Q1q_wd$IVUIU|JSF*ESStwY*|gvo49E{czNVanyxgRbri7H7 z>|7zsXimsA=cE|(@{lu;nyS#G0h=WxC!`6c)P%g8d~=FXZWA&UexYV#a*{bLG$A=( z$Vv?bX=*6=+Ud|db4rb8v;XcXm{PLxQ~!?p#eaYGmZz$gJ(EmDs~X>=+}ykm9fuac zLd-E6Q$x*R3B!$}5^|Gay5xNo0`r^EB{?14F>}YG6YKt{g=3EIu5&=|=A1Zh@hqZc zOTAtnJIS?d_RlLO-s~GkZtSVpUr|(j%{{NgYsBjGdJn^{&v4G%Hlf2D+vtV*v|p0C zZhxv=w@{JDZ8|^I_@Zw1!|XZb6PNv3v0_(htn-3BD>$cDOD8lNFy-;QxDA^|9^QY& zmb7|y#gEL6w2qsD>+gN`Bw_O5zE#yb->ePo`6@qdEgaH<3GOr!ovZkdfP0HT=QQC- z^`{0?L#UzDS5yXNqAXMqRZM+DeM?QDW`9s~sYTQ(Y8|zS+Dh%D_EF{3QR*ahj=Dk9f&ZwFCOgPhuiD23= zZJBmVd!_@^k?G8IVPY5q!!g|$3sb<1WQv$EOfgf!jAte=Gnj?U5?gm>JF}NL#(!L3 zb~1OE2h1bpA#;~`$^6BJ#ol5sv76XmOc0aAL~)QfSWFXh#IMECqD9OX$A5_>;zV(Z zI8&S_mWq~+#;?K4~U1wa0KKzO=Qr zwX%iVdfSR^<82ddQ*6_1Gi|DsAU%H*F7Wk8RIvFKw@FZ>oqY zrz&L?Ri&=-sPd`ut!h-It@5vGUKLc;qUzrO00960CB{JtfnAeWJK0~MDYF$4~ObXf^>Q`ec+^ONms$Co_$2^+#Dkii7S;Tf?7 z19>1Dvtw`|KpbQ(&$h5E2|d}^gvEwH(yWB6ml78;Bq2)^vNp7xj3-NnOw)v9nsqW6 znl|m3Idi7W^t7F{ojLQ}_pW4_xH;#y{`>#mzu$XxMOJLsRO@g|cP?*Owr*K}X7fUa zqj;{v;TTZT{$PjE8%%|RW~@D7L`#iCBpEe=U1obpw`rt<(NHE-l1e92@tB!0OWKW0 zuqPDnHj0`pylWUAv?axJ|mq@rVFjVt~{GL!Dup;$(Pt=6jgO6qDI^JXj>|nHbT*0 zTg-?@gAirwOe8J;Nqa-t=Q8;SNz2SUbUU9pUjwRBm^_H?p4B_v}kiGR+B<@~u~S!$(k;Y$P?3}=PG zo>-2JM@zembi36iQ^~PeH$6r=*4D=*Y9g5lhJu}mn81dpX{-#YsMoP||$ylN&lu8+iXjyh6hBC>nGQRJ5 z`WUZCvx`IswOz?qcC)Q@MOK?k=ZnX1v61NRvX`{Yz)sD&yaIcF$?Ue*V{DBDg{my8 z;)1faSR$G=qShjdb6bqr*A-62(J8w(+&Bv`ZG7vNplbq-segXV@*3@l%i=Oj6_!kdx`MLMyuElr7aTg0%nDXvX3 zt1}DRLUGf$(oA-zk!3h!8XP^2o8!6*UM4rytO7SKVhzi9zL8`i6N)9ej6{YYH49=X zQ=KD}mH89QldIWD=E>Z-ZJ8&WY&o?u2>0b$7Yvzp@zX|ss7DMk3z;$kGTgs!>rD>e z1&q-1F+n(%A7OqvW61>9nHk)H?doMxeo(LIN=CcmMlhCWOIkZPZ(Uh4;^@z=t!e8{ z)Mzzj{ABZyat9SP`S;Uozrc(!dNb)z!bod`UPQ>J9krR2=qyPlJ8~S|=$? zDJY-jkv!UeSXD3Z7HJKV#yk*dl3u21y4R_zZe6Zonp2~u?o?e)eU8lZVvobO*&Wtp zRj-l@J>8nr#F|#SrB>EjP@eWkDP8sIjSjU)dr<0UDabVG=!wJf;9pOV!=?QM^AvfQIe zXW7Yu^2ao3Xx>M1nc~r9Cp){h(4)vQJL~lPo(=6SR6I_V4IL>|Z716czv_25%N13+ zu;3NTMs>0a#fru*0;`}Frx$K?Ro1;KyU3kf5>76S;^ZPDXkMw}kuJ@oO348kYdc|- zwXJY}Kyk=$rAkC$#I>&w8I~OITCCROE$Qbhhif@FB;~-h%Ddzu&+jGTVnjs%w?`u7 z^QcR<21MNwQ5kV_g(eaAJgUlN9u1}N26|OK9x@j5!LW4MtGnewjaI2m98l`hZisHc_aEF+be@%UJMc@uJfuH0Flbt#`zE-I(xO3m$*`N!NU{^2RD zusaRc#cb`S0c42b2UEaFftY0ordoE^Se7g+ueD8yu0(W zY0l@}ot8^$n&|FsI#+I0`yFbT*2)iNvs({TVNWsQEmltIvY$7g3cE#T}qEy=Iw`fNo@O{o_vggS(ee*26ry-b7+cYV(5Vu|RC zTm!Gw@VYGDYkk&hfcL3BTR}bVQ%x@88qud-x?Dz^avL0hm)kwKmiHikFw#Rct+#qG zJJ*BRIghhP^Z=)(K9{X7kp@OIWIGetuHYkScIp8>5=LsB9=d+HtjM}MAm-xaH z^e(KLQHdu9GT zMK&-pf2C6*O^hsn|7(!R#{BEGB|Dm%_-I&GukMT zW_#9)`K)j7(anKoMbmjjFp|h-MwZwfF1Opl$kNd@H!^a4o~GH-G|`Aew)WC();Ji^ zb9q^uI&is{ZFXMEcwXBXS#IOu&d(t_4NByuksYk2E!OQD$ZgF)uA>2aYi7BCE5Kh( z5fQ5;w@>lSdb6T`HSzib054jN=sOsx$1H2GhlAKvI-g}g@1`NS3A1eW(bi@h-jID# zLwu4A`wI1=UY9C1l)qV5{kg(g^Rd-5WF4m!+yXNdoY&>GOAMWn1ISEYLU%|c%t#{= zxfV%AkVq3xq`9e^w#ltXB;un+M6y=!9ytUCsjNh~{Sn@O0&q=s3s!N?@4_yQ@{`ZV zYA*1q)(8U4`BRA6Q&_{NV2t!#P1~&0*IKRJ)ogQithKofY;k^jaI_((Cb30`Y)bL`S+%6`gIQzB_bnz;hu%x}FCZ?KZhAlXLG@LRD5 zPBJ|G0v|N!~I@cUSQ= z6_3(z%*`LyE+->5a+O4O^9WUPyF;%+=&IX2iuEnHwOBdJe^fZ-+xdaqR_qzlOBc%J z4iwp2=JD7CFtT0X8a0_OJQoz*dZLyt!lM(fc(*F@?&CA&KwU;h4oGAVBR8RtIuvrt zJi1qZ=I-Hq?*h6n%kL95rHh5lenwhgvjjE=^16u7H#-$Npb$l|p2ADz2Dv~!jnS1} z?`%+I>w)aHpUFDVJMc_KPB;NUxLc-3WMD2mh_K6f*n?&Gks~|Xm8YD*wiYWF^EweK z_FS^+Xo4>zjSwaZDR)TZ5F-(PxLpbMR=u&6yFAQD6rxoS9l?;f=r%?S zh*m>%yI#O0-(jQ;k~NUrAwYLB(hkvDi0-mMcQeuf$vWhGkDVtYF%*CQ0(!6gEpxA; zIjl<8L$wo?0(z8@PFQUKd!M4&P2vT_*DEQB9Al&l+KtfO@1qZ3a*Zh->jAxV9RGZO zkdXxRo1lM4h#qDn2~jh;_=uu=tdZZqf4ihCkBpIBP;Z9%_y~2{Qh%2b6Y4D}>x5m_ zql{!Axe*}-MTqY)(hbp8h)xR8DMorA+6K3e3DMs$vKyl95S`9Ox`{{XwIV&vNFUTK zaPoxGY8QU<@b9HP7U)SvZUJZqKxamO0qwOwPcgC&pdbQ0tsLcnFd+-mIt<-!L7!pd z0H7g2pZ%-Qehd12Mg{;4BlL4V`n=rOuZMYA&u8<93|kzuEY31=2o_OTyx^lR@?rIJ zix)?+xYe?FiIKywFkta=7SuKjI%0udVdOS|+5viX6wvJ!=m(5^2cQnj?KQoBG{(c8 zW8@Bqc0%++y|j~ye#FR~5XB+-vEF!;i~g38yCCX<=ykpEBp3Z1BX>iTfM`f>Jj+FY z&&WLxC6U7$KKiEII3$k2o7s#~!x`OcW%L#!M`5xHCU5)bPq>Ml*H1<k%wTBfyH}1`hL#h z{ZT9)wk$3%@(3)tVex^Fe#kdM;}AFbaFn?+%jBOJIS!K^nEZT{ZTnpd^bsQ`0NM@E z$D_>kQ4915BZC0-0`zGX)He+Jo(1}hk&^)J!N5M(8!zzO{+W?e5ZwZQ(HDB-MK1am z=73}`Bwy;~GIOj~2&DgNOX1(TOD%pTwM4!a-weMN59`ta>zhGt&ZPOjs}+=%I~=RJQsAXBtSdlHP$AcWt^jel zBzT&~Uzx|d^6=$(yqd>*?Eo#44VkcX+ylQ-n? zEArwt=JES3q2m4(b*7PeHo{|1qdX z@IME23;qL8M)2=}Ou_#YlotGX&@REh4N40B4NyYF9RhU;{&i4)T<|{z?G*eEK^=mB z4b(3Dyb3Y||1u~l_!mH7!9NEIi9S6G3JRY8o9qsOIRk1D{1c$<0&^O)P4JI_whGKi z(2as01Z@$yod9hX_~W2v!9N1pB=8S`HVXcJ&<4TZ2U;iidq8W2&$~ct1b+u;wcu|D ztrGkZ&`QDI3aSNv$qsuawIT;ThuIGMoa_|5Ec)seyju{*XU~HyYznfpqC3IQ;9Rlp z7nom(_yt8v|HRM3eo@O;;?u$9YhxLHW(i=)-V+~fylMTLnv$9L$>B4b{ycE>;+<-J zLrNW^oO|cPs;XCCm~`%&KWuxg@V3bB99KDuho*e|pYQ&E$Ftv`dTYF7>f)E9ix$6j z?(-u>zdirEC;xrssWUHp_OlDy?|gk@_^rh9RsUPG{;G6w!^fZh@;A<7&sptx$#Zj>dY^dtrB~Y9 z9yv95ZpWd2;G{jpfB53=_Yx0R{GjBM@mv3NYR%)PJ09BqUvE4=dH**R|M%va50d^F z-#j{0Gb7bC`Ni_}Y-zOTt>&%=n$Lf^?uEZx{Z!*^+g|$fQ{z8<=F$}dy$2T_So_;Q z{@dgq&FiUMbH2FZ7w7I-n~d;&@Yzkcte5%yTPFB_0nieG;pe?h@ccac1<%i_CU}0X zu65Y=VgytpX35XMHGCvuFZ%f0{vQAU|NotmYitx%7=}-~Ez8kD8w+SrBp|hYHSR91 z0k1UJQv8rCC^beB8D@88x3jyQ8D?g;7x5CP1=}LDX=n@$UYa7YMiv8TV-+x1O4J05 z7h=7C#l#=RG&Y9NAH?XHWsrX)ILUL)JMX;TnVIh###n?gc9&p=;Bf&w{g|NI)AfQX zPp=f*>FGxVm7cy|u-MaK!R?-2BDl@d3k358g@RiJ6#}0(&-L^?L7A5+72M$YB?7;v zxu@NXf2HGCV*YM>T8u@Fg$na!ZQZ>o(976=y?dr25$okkUw^)7>#KD?pH8}gnTjT- zTiaACpK_g2-Ds9AU5aP)n4=lG>y>1sCOaW*&=R_;$iC~VbXrYSincnceO;BaOiOhf zE#4}bvePIRB$KR`^th;~%Kx9w4f(UGWs8Co^Dhr93p@}A=Uu~eSFzn#vBD~JL$XbO zxmlNDMw6+fRZB80Bc|H68!k{3Rvs5_T8m0?O;;q_$XGEo|4d7iW>sA=EL90w zVX0AVk<6%M=l3Wenww;G(S#;iuy^}^dNKadGVuHtAJ6_YR8q9L|Mi}NrAtGh(CVH3 z_j}I|b^Ov0@f}*nPL8)t|2WIuHh0Ux#GJ|SPrHj!$J$r)o!L4NivKFt=gz)6z&2Cv zn{M?&Y13p=->Htl-^PbVmDNT2J{h9Ii*L18KHc?af8@Pf^VrFWGxEXS@uL`jiPs#y zd*1QCrliiXhTo?@x-=YIcQF$gW^Apdt71QQv8&gYf;Zh`aTBlMwY-k6;`MwjU(Xx( zM&8al_zvF5yLdN0#y{jEe3T#OC-^D;86V^4__ut5Px1@=Zxr(>l;IZ4M+M5U5LE!G zu^0g?#|qToLDXUu2oY>R1D=9^0u4!|@EqFEf-D?Z*a8D_yntiZRBtmM=**HZ~~`s7U%F4&f^D6;CoEtA1b6`nn^cO3C*T*nnO3!Lb`*N zP&I+>p%8^>89hV~)1y>NL~G~?dXk=@jr1&SBAKETrv#-aO$IsCOfS+u4(gI5Py_d`*Ty*wbu5L>}}VZ9O($mD?}g%lNbjX8A4)Aj*)o;k|0ok zkPu{9I<^(d(m_Wub^<}*ggj^qO-tKNQ$kW`2<>f2+Vn|VNSplJ>CFALlj&d3>CFB8 ze)~MOC1Ymp%}lJl*Is+A^?mE1Eq3lZ(ChQ{zp;JC;Io63gMV9mzBa$l=ergv$J67+ zblMzAPZ?z^Un*K{MloB;8RWNl zjA^9v#Y%i*ORYzBtxWE#?K%aCSUX&SWcam9CW1Bl%(mbij88S{46D zrkGPkN@ZolD!1hfkDJikGTocbW-8fn)fU0B?W|fhT7SooIip-Il>_$QVBMXK>0Bva z(dr}~8_k{7V$K*{ksCFECX;iKLnKuKcCwKO!H=;#H+Db8=CWZ`Ck@12Al=Jy_v3xV zJtuR8(d-c7&z8)Yur-l4U1Fr}DqC1Eier^=I4|TU^A%&xlx81~l}lAq%2Znx-#AaO zxlan}>3?-oyL54kW+DH7fm&o@PAKcOv^uE%vdd>CHT#bHBi?7_E4G~L_@ zGm(pmKp<3FD<#4Glu=L+ssEKyzSx{GO{17=7cC|FKT|19wu><;B19!w%-e|4u^tdR zCx1Woeu;NlD%Xn_39(VEPC8pQYG6QkCclAsWL2I0BIm?b#ZkL=Bn0iF`C`ty#O#fy z7q%DH%;ZR^kRF+FX1=)WDOiq%Ef)%oYEXoQuZ^jE)~LhdNrW4*FPmBC)QLcB?Fj5p z-JJx1{o>K%m=c1$dfRQtl)GPCFBf|Dg@0A{U#tFJ#&sw3f^+u;yAZEhNKKlvf-(#* zU7}d94tjg38wB)=iLBdqn4pMvmcIcy5peMl%Hq9a^fE5#>n6 zGVFOHuH%a@Jx^|F-T}VA;cHGN;?0(dl}x@kX%s6Iq1h_-p*hiT*ioOsI=SX$vVTq% zmStHdTxA=zBN&;H)TJ|)1Aa}zaYG_Km4_Ns=J>n!+!6``K*PC66NHP$=3YAD%xQRS zo>VmIHzrHDYC(*Q(UM)kb^DH#w(G<-G@U zw-T6oK40_--h!&n`R}IBM{4-Y6P2q?p*0Z_i7nRjrf73)2Z`|rL@}bb$6|WauWMml z>EtngjQvyhYeB!hQQ>+^#Md9LcIYEU`5;N^il%oefykv88TQuy6C);1?tclB8Xs;7 zosE!-x)#-U`LyQP=j19kA&Ze)?_XE0Tvs|G@5S3=x*qrI(Xg&{UilKDkCgRaDow#o zUW4nU1OH9#z%Kbazj!Wi#;4X4pLSIB>EWpER{}A%E=IoQSDQkgMaaLUBNil7<+mV> z5gL4-smc}n-x2vD&hH2-dVft5R#f;7+OCyOJ+AOOaJWua{8w10vm^2kWjzZiO~4Lk zK|lXHPFD;tjnEKW6RKTdVW|B}ThGE;6LdaNGz7E?F2kW1)E4uvn?nD=c1gP$(>1?{ zjsF`~f}E-+1F8zf{89MRHARnV5c(tRQBH$6i}0%gM41+7S zu$8XCC3OaUjRrd$p5_d$fWei0Y`JUjK~9&#;1U>I)yE!kr4QAyK$pS9YEG9sIrR%C z5l&Z}3`A7LrX&hKD}Nk5R(gD_T#ygkdl23_;BEC5wnk}>pnSuFisI*V72I^zN4gr3 zuIXbDH>N1358>`o-0fV)9##ncw4R3RhzKkq&>sn$QGKrZ!UC+c0$+U#_lMgU7W6gqvde4u76#PfCo;aEozL@&V?$W z(?R~Qhy39Mkw3!eS{Qv0m>ylny6wQ%E*N+>r(LH45nX9L9krLSWVE#=mO?!y)gf62 zy2$>4$>JG z%=f|s;I79d41XEg+n$UC{Xx<7y3#j3#5SURIPF2g`tn1eB!b@f%8+MLxvGAxUQ;?^ zVZS0i`Z~o&q%G=vbi&_bR5g=?#yQ;p7i;07uU}ztj4HOd%r+6a$srfGwh+2mZt{da z&S?yHx)9+Oy(zA@txM=6s%t5oXwAdyNkX6Cv=Q7?qCG1Ap*rL`h2mFdE zrk2Hr>2H~%pJ#CRH+pCzzv73ds#%bTFK<%QXY4&RP zsutDyHGh^6VQoiPgMc3xq!17#n+8n1b?FB|9`^iUR)D#4zQ;PeFjkXpdp{-bPE`c zf#K)s8Q$+PoD>Z2_d?w-7=A*A_Jqsu3ARreMn7up`mku7YJxu6JoF-s(6RaMsw||=UtR53Qr9A{`!PAh8c*x~xn!T)mCRy)r z{IbKr4#B~(MhjgmZF8_wa}Jn~@Pt_u%5=xR`ZdPP3fS8c}^tv^mGc2%!e2d!e}nnxmfPn16yV zHdk}Z(KHAh=kyt9K7pB;*P9ZUnQ^@-Wsl9QpuuDlgucS*vru}{By<8}(-iz#W|f&r z4P$e*f0mgYwGUYd^QqXnlufoSX9YqFxcnZWllb@+p+$VWO=t-pvxJ&A6l;bZSMVaS z35cWN+@}A;FS~!2iuND=BK$AqAAkN(1S8-$Zd-(wIeiZC^dlY%@MJuvl^XIr+s$5| zMZRlFP1MY+5OcckRN&&pS&^N6PIkmtofcuAK-g0VyNa+YgjU=Fu!FD44IoU(4S<}G z8^D?7GzqKj;5{P-@2Ma=xthJ!%U<2jW(b{;>Nf~|mDBxDcL%R22CvC}N`L5UQt3lN zPs+{D2tDPjeoW}=jljBC%^ti501stoIV%U~0lPF&hL+~=cG_mWCI)X!4BlFl{Qvl` z!GMT(fRzb71JGgwpXD@#Y-~j~X6v(Y&dbJ|A{*ztpwEeHJTHTM-p$4d_J(3QBiIyE zDjU{QoqD3r9#jX#pi<&`pMU*@-kGvzRaVR@)F-Cz8&01d5`Fq+qpvP@&hFF0lAy!V z_u&SDkjHZp-&sQ6;`9aJ8UU`h>v5g;aJ?&Vo%ej57r2f{+eaj>cU)Y_MqKeB8`_J2 z=6tgx^r83y@bDOV$AR~ffcI{r%Pw}_hIdrLJ1Rps+5itVdtSnOi+|8>a{4mhZ3Db- z)x*2s!TYv=cfs>@LBKmEZ69;tVKo`heWv}Aj^N>}i2)JSs_dd>3PBhZcTw}Hk=hO` zm6A1$sq*bc>s{=EjU+9Rq@}lX0}{021&QRFgkD4t;&J{RPVrN<8j0;JgWxd#dugPTH~}ZQpBj z+{G^0U~>{|P6m=|0E;1XNrL?jq2K4!07xBW@dHqu6d+T0ad2fAAhX@OGj^oNbsyVzwL z$(T*w@f4@y(%ZQ6%^JlU%DKyLD6o5344}(m09}p}JoYeHF5>_YlLPGgg#HN8iG04# zX&z7qk3XZ0td zO0Idyx+VymkblXUaI0jReN^{YupNKoq`M%}{gXxuUF@1o(xgmxQCciEq#J|(nvC`; zq1QPrA=(6@{i!I(?A+s&Q*-XUkeVw*zxd*IOYmb(kK_6fuHSILPP0#(CxadHCyt|! z3BAc_8Jatw`7=-R=gyPC*8I7nd6Urp;?#oXPCOZYA%A`YqeloF&;8cowBjFTpQ55v zzqVcRMMa1w*e|tMwC0OWW1-Z1*EJ=k#$w`kU(|Wu|B}<{Za<;7IGuw1U3k2FTK^;M zw)dL*qj=5T_TFN*MS@Pqx7P{xHOJ|+SS5n^Ej;7=%1JmYd`pY({y%lxA?y!9_Pf>W zx4rB)gMaMT``KrNem2d1WfR2dtHMBBn6g*epwkBl=Xv-kp}%r6{+h`6ue;gjiIf`C z1tMG`^m9&6Iu_>N`wge37C!kcr?1zYq!Lcs{gu$)O|#$I>EQIVjLTkWXXab^y%SeW z#PtVG&p7%~dxB}rI{OQue{5v1i`}-L5ohIV_J6Eo^sF59_9_ufh<~8Vy1s79r|m65 z|D?u}0)GO^Gkf6u&z#O8m-yXC|AMO0MJ^M1mEBd6$YrXV-ASQ^?>hZ@S7h>yQvv;I zG^n`+6;J6}TmrjOuiM4$+D-MQxPhFyt7tIlK)icHNgy(Pfc=@!FE~AiK%U0f{3`;H zV}J82Z)|=k#^zVvVEamh@|GNAZ^^Owwphi3sN%ng{^#@^A&6>r3KFUMEd0&M!n-01 zUpCtAVqe*zpO-`P{Hgy100960omUA|6vq~wrU$7;S{nw$C7Mw{rzInUsHljx3t}rG zQCy>r>=w&_Gk~HJjT*zaL?8xiL_}O-zJK{jjGFHfwbi65zE`hazp5@=vkDV*It9(ocb!nF^Q%K4o*|OXPa39c5W|1YpnYrE zVXIj-CY;~Pp#2J3;Ns;z(8QnvCVs!4`bqw*tBF4f4i7sMr-1lcL5l`z8FWNJ zh2VP-P)E!8BP=vs1vFoMaY&hKq~IqJRKXu(ef3qOhmoqj9zqO>qOVR0f%%Kv^t;Pk zwX6U`te2o;(6I{sI1%c~T>V%R0e?Uqui#G*FKivj0(nC6Bi%^_6+;+6XGu;-&F8t z{9hbl(3uMUEYLOJ_$(OK=sdkW{hxDQ!+Mz@;j38z z^d2lUTF;>KCjOjGa`9_m;x97jf`S&40m#5b54}m!X(iTM@^if5_Z@HeGI_(7to-*3 z`d*6Et8dhsywNM*?vfN~s%LHhbD6w!7eC2;2w|QAQ=^~~m?Q=!xl+zwhecjv$@}$? zBq>BI`FTq^e~Il>!CzHhRDUS{15943H5v26`Kt{2LC^~kRsMbwtCJ!n)hX2ERY9+b zDE8M$-hQyk0=-^AOSC5bzN7_;P0^^O=UpHOk`BBnZo)BQ4@yC!pfWf8CCNRO_0m9c zu6gM7j)f~|Db+>Bu;hpSjvxuESYMO-2-YfTVU;?%rt4%w5#aY>FU3##sLXy(2A^ z&!=UDb6RdYrKPGaEq{e9`Qev z*|TRXnGB}-)yUsv>ny9O<=r}BHzn>VXcf2`4X*Bo@({x=3a=*&N18T6Zni3U5- zpMRvF)oy|b`*-Fas#BQ~#l$H4xTVP(F3qLXfBrw1QD1Ac3Sx?>>%=U|rZ%aBjGd*GA}A zmayLH)7=c}wMfW<3_erPhv064z!)^0j10sd2PrZz9#F}wHbwuT7PS9z6J|QSw{>oM zB&fMM27@2LFMm(c3CIgNBp{uJF}Nt`-{3bHAS*>)B0iyR)&rFSl&chZhfoa^v;nAz zK)FdSgh2|b1_lGeQIWfXJ_afksD=)ahk`Z&lLkTajzAUk39Q~L)YwQpn+=TwlSW-? zIt*=srGm7vf;NLy20%eDIkIGd1#F2HvKRv_;ZqpRgnv=5FxKb;r;yRxY6M@fP9%_N zy#aU<;3K&QkiLn6{sT6|xi6)fDrhTElk|+C0C_-$znLKE)vC3!u+z4wvou%Gb{Mw7 zaEs@Lcc{a!DCjd7&W5G7bS$-%f_4Iv15><8r+}kp7f`uCwWd@X1?>iEGPw1l6rAUK zfSLkSK;2AvWTt<;>P&4F^f?Sq1t;x<365k;b5w^vXk87ibJsgO{su?u790)^p4yoUnN5zzG|UXG|< zx+&-=c$p1ey4Q*3)QgU(qA&#=2hrOg>hTiM2~`xXppzh)3&qk?a({aBmE(?HDwst-ixO}M@a`Ubf9!1V*phj0-JIs@DS;Q9mCgm94xIt$!F2xCB? zQD!5NFAOy$LvmeM@79O)ts2%q1$_rbi@<15pfO4{azq#P5}R|X&0qzc2b)5$i4HUl zQEk-nAMz5D3#v(sf-Ztd5tzIlXdGH^GV~=T->ZKn!xVH0Op3uIHqbb{-emYoOfIV? zBNS8vChvjC$Th|{{w%{W^<_B5QHG;Q8IEEMqvpV%T83kw=3ojk!Dy8vh3G_%`TJ75 zqE7zC-;Rt`&<_ySVhC&8OE%_JRWx2f*Ff|>h|Di3qU)+?f`WboQ3;6R>O`gWq8q9x zUO|69foKWTMgm0Nj6|QPpqs!cz$F3KoN&pC2B(>antL~vf6)$8uDMtEw}{zLhf(=aW0xi`1kaHhqUkQSGcq|wM3S->;?FxC{B z7Md$G7ia=BPiPFqUKF>0_KFkV%1QsK6V^N70IF{fEzrrfgOetR;*QWloHS9Ce;wLT zCqKiSY==`!_;@Fr;Di&Md?q>Jr7wTrI-kp&coM%$#1xV%m$5X)GRHWBDJDNt;Gs}V z=2${8iKB$-P4}yLn#RH&UKV$3%+V;E_Tx*$>GS!%otWyp#SS#iU-&LmQ}Z?4Lsv6U_l= zttk$onCOkPXQ_=#UF)A?A@6@XijJ*x^h};(evM0mQv=9;tDrnx-=BOrU1J*cry+b4 z&L(^OOW4|Xr<7lf-81x@Nf@4fC9%(2#YeKc?_QBT^+%EAU)}rR$c5Fuk(F6&^VehzPfpX<^n7&o<5_)5#tyoF zY44D658eI~q(5Fb)OS;~{q`q4b9JR-CKYYI9QHK4!@XPWwl$u9VnFrqe?LeJ9T$0} zaoD26+O6#?H(Fz3gNuLOYWKyC9=gJf8w$cFpZ%jn$!HMHopY-QpMq?xAMOPYgGJOXGlQX^x#pI0dOfku2CyGh_ zf+;57FB8S&9C?jm61zw-nZF&Km(1Cg=9BnqLoxB!Qd9q(YfgEC#_>%jn0()TDJEx7 z6N<^1;X{8hIa9qUCU%V}CfbG+ds6H|#}VZlQcQTRcXqZF>S)O9bh3(xE{Pg8*JdF< z7>l4eEXDg=+S%&r(pS2z+|Am*OCCml1h{|SW5id3(b4}DV zvt*iWS#gss=7h|QES#QXHD_5f6OwE;B4!d3HM(SA({a2x8Cw#~w#*!BLXtX$(=@Gv ztVw_Ian_I^b9@d?PYeRn#30Z*=pdUlp-vg_zm?dMke-wHr{9+UQ>`Ydfg_SMO_w@t zTvnE?e&V3qBuiqZH7PO38e*Q9G{u}1Z>9;cb%6Aek9@!Q6tZ7SW|I@@=9KdM!=E+U zYx2-eC|zE}1P7Z;rpN-fjm2kI&%G2I&2E1fqSTef#+bo|+Yg4>yLoP1Trfr$i zbMe9H<)-ACxc=40J}TEtkgHEGNV?ZJqc&sl7jsu$J+OL5Vx)HI&egKZgQ__JBj^8I z8oj=H^1eOi4#ur5KCoHbn%wK-z$UwXym^in&rw z;g)jc+%m3`tK!yk)!asIAGe=7z#ZhCiW-p-wW6DdL=TY{8;Q+Cy%>Nq#I9m5aez2f z3=_>_iipLDV!W6oP7$Yyd7@36Ar^m#MPjkIKwK!6h)cwBafP@>Tqk}h9v07u*Te(j zUGa(dhxohrKz5PcWe>Tb>?(W7&1E0CjV#K6a(g*IHp$)PPI70tpFCKOm50kQ^6T1DC(2k(mDA*@@(g*V{FYoK7s~I-3*>onf&7mAwme;4EH9Buez`O8XydNLHNAOX68h?W? z;EVVwzJ_n$oA@@qgYV({_z_rsjQ_B^*j??s-N)Y4-ooC>uDAQy1MCKSd%MvdWbbGX zwTIbz*d=>!dxX8eeV~0X_Qeh5TmNrSLt*?EP)h>@6aWAK2mq|1B3KkI(xhny000CE z001ACk#z$Vmw#pi4}X7X9LE)&y;>(Gd(ug=WUY0UZ0T&>%BtI#rp-!jB0G`K zBDRx1Qn=f@ojb2}w|CvWv#rvAiPI3$LS12MwiU8*;*yfks)iOyf7u}oj?)IW)KKUT zOu>ZEhMLeih4zoW*&A6`7xG6J-p-rveeZp5X7)6C?9h`_2!D}(&ptYHaHf9rA%ukY z5kjdhZ#sL7p2~W(&y} zpzxy2Ss$NSuFcoZ18vmvs$N^sD*IHddkrcwyxl)$?U~vw*Bbe9Qdz)^>gq}Z zuQqT(U)EGZe+8UNS`2BzLWioGn#foSj79M(#R>BYUe;0+iyK^36LRJp!px}!=6)=k z4swy$8-J>B*8GhY)H1p*TntV}Vjt?lTIv@~Z3vu=>c+|)yn5$ zy7sLHv~R9!-ww`#_T6AD5|dR`kW^Fd)`F`U-4vwm;3lA&AxN>5qG=wyl3^c=ayzOo ztSCKr6)d64@&E2c(mHg*Op2x!57~D^b{yDooPWq&!E!t)_h>04u8Hu%an&%wqc0x2fxIao{vXIdPP;~Ur}`EPlqj&`!pzM#*^J~Q^bLk z5PuD2bgwdqFDS!UmxsdE20x9JVc>32w#dWEkRT1I1_~D;hVd=;7cmSK9*0%vAjDR) zHIVwC12|!6q`4YX1C@SgV#fs~iS{WK+hq1CV zVH(QBnldg(?$5^rDWw`}xK0dpcJ5KOWB4WlX*jB0d`Q`aFJaKq zqw3myTKGZiGUxQBHyI#M9!G+dHe)zI8TG0l8BGw{nKH4~#PypPp~H3W2>X#9sDISh z_&WQYf{9>lU=i+1-0y6`J6rHr3m$912U_rf7JLLu2gBS?avkPx2NSrzgZmTg4tc*TjwV#CsFJN-W2RpB*m$PQkYf=W;S;_aPFxk$ zzdXBX=Hz>Sxc1^d-hIY?Cw2Vzz5{L%Vq+m6Gk%occZ(?csmV`H?wy5+_`?uI_YK$)k@7NQ;%I~1?u~ZEZ8>Eih!1#e75Mo9pzUWUV(-&XAx~pdo*u( z)9Gx!;gsz(q}plVHsQ4I71o)fAJ25$Lb+kzuRHXy?Bk5q-t1%%O|NsUTFu`OPM@Hz zUG=D)e&8RJNx0&UBlNj^$$x<@_{Nz9c>m?p%Co=xY3Cb8hnPEm;p@WGlwlZiFGjB{ z|L*b&w~u7-_YR?J*T4AD9~J+(o-e#wihnTur>}{NKYaeP=idI(ic$Qt_1N0Y*H=*P zz}l^^(!Y0AKB$~~^M#l1T)+Gidrth;&n_Q`+`IJr=+j^M+xg6Q*MCmFb?x_WTdyu( z|H1f=ijRCh)%)`g?`B_m>&W{b{rF$6r4QX}WL`sP-f`i+MK7U^XM%s{LW~TOA+nW> zkTH@b2KfZpL-vwcvX9J>{bZgTB~Ov3$us2h#3Ff8AZ1b^F7Zj7G|02$IdbOz+BAgz z15ir?1QY-O00;oAp$sBe3lk|!wFm$JkPVkn*#j7Va%FIDE@EY2ZIxGjY*W`2zt7K( z?{)mhi(fv0BrkbRNC=K!;)D=F%OwmlC=G#EN#{oP?0RxnVPYFXuCF761}d#c^q6@nYMHq#Nt%Ud#+Whl?t)b` z1I!jZVOpugY&2`7lBtY)#>nMGfcY5|mh!1=CSS=Oa>mrOku)nzD{W+wBsNOlr<3K;@OZrfAVhlUOm8jx!BAIHtsZ zQU#|66)__Ywa6>*L@I4EM*oa4Z9;NJ96ISvWYck99_lX{2tD~}t@pFu0MY};o$n#T}F&UMAUbjGYf+&J)%dSA1ET{{$qF%zQW*Qw^F3a|$43vO8oQg?*bXe>@pq?$q_+BsRZue)_=gOg~9tP6MTrAm+P3y2Xvr8MHES`Af#RP47OIQ>l9Bk+}VlsE&w88CD+EQ(xV(Px(Z+FXv>S6kiwXb#gDr zJ|T!0po*}>-X4Pceo?@(jTNY*o(j0yuhZ35(TKALz6J9=m zHb}S~5{5#x(;hXaXjzX&I%P;!JL}PqJ})v}+*l$$O7VIiht(iBc0kd=(kV{r>@PD2O@?mbg4tYx{W)*5{8lO z9gax-&R9nJq&@-n`Bi7L*&&3eU2p^&7+Ved&VgsF--otDsIM1ByBEsZJf`0lFr20C z{Sg5V*mw&t_d%?!!l;%a3ixpwNA_zXda7U@ zf$!;N{_Btr^WUv}ILv;D^5IYqSPNXO2Y3xz zI7f)R*; z+j}@Z%s&ycA4nBgtZ|TUoLZQgoSZZ#Czmq6^IW=rGyCm#Zhc5AT3-3hq03JnS+K8p z1IBcZF^`PJjoDag-`?jAB_?8I+%vK*^5kH6XLw?8@U7sGh@Q}DLkF@`jQ2W-^j|fU z=Utv_d48Q-@8H7f3xc%sn%Y2Gx!we#ay*-bW{z(FsptG`c4Ut6qqTYS?J5*37Vm{L z^E&2#iX-tvKI1%OWOVGW*Uy-dxx*dj|M<$g->aB9eth@7Oaj*09K6WzUVgcmM&7}m ztvy3M5yzU%v726=!8~!)2sfpC&N!KgPGx6uaE-IrZFS1b=NVXt$C1J=cW^m{OCDS> z^Vy;`WjbrAY19hmCuUfYaMZahhQT!+25iZH9L`%)E6DaQMy4`T>0*49x9JPf#~G}m zl*u#-uV9T_F25Eye8SAcvz8eTm;E8=fy?}HguWC@rl9=GFRBOzfBWvb_jSK} z^2)V~LT|6G>!atqZ(sWTjk8Nf#>6)dqWA92ef}pY|9tH;HJ^k}DkH7qX z@#zIU@fTz7;{7`dXma1;C*LvuRW_Pkt1tcI z%Geu=CqH`c4<8#hFWviD*E@-wZ*|xG`kxP@FMf37Z=e6--?zgDKP!&ihC>VgCLpsr zXzjI!pK&2Tnn*JlB-_X#a+rLLm?S}Jj*$W>l9S{VnI+#O&ywfJi{vG8o?IlC$rbWl z@+$cOxk|2)AClL|>*UAeCb>m!lRM-M@+NtTOpvdTBV>pSlO1F`X(6qoji~>3TmJ)4 zO9KQH000080IZ=RSd~#WOY$240FW&J03Mf-bpsWb-t`L%e|%VZcoau=@1c4$rA8&U z)B^&WqoD@`7J?QM2yxhrK#cJL7~~6Mk4Bo7G%%Ov0EEF7_`>J1tgpscV2rWB$6~zR zWH;G_*qcqV8}E9(SMCtLFaJCK;=hyMtL_mJIpFK*s`p;K`@MRtOTVee??I~tS`1FhTZ*%a_lZYVLB&bXzpn=O<_^mJ}-GLueS^FvFEsKVf%Y)Rzu z<-{I$Bmr2()nRBUIq3S90m}ir^|A|NRKMp8(hRIgm&hC5e z6@xZSlR@M0x*_FGazP{PyAD)SDi;x;uylM4LYFKK-7tt$FGFBrAd{?=+{S_7B;^*g ze@FyQj8t-I;!~4JmeaYm#vFwtgAj}}o_-BfbRVgti*AE-9Qb>`q zLy2rTopt#@62XGGIF}{|LluJ8lH=jyf5H3>*J2{~(}IfhgI$%Q<_m7F7MPTztVy{U zx9swAwvZ?!%fk)uZyqv>$y`ciY^mPQwW=p;lPUjXiC0$)i+ag1lg{mtvTKn&?m!uG zBc+infw@dwfH@RN0~ZZLcnvWQm8dNK*+GPKAfaqe|6|U zx)2^H4zw??rzJvyo{~(tMfCtOmm~8LUv#^mHBAiwn{(-Mjb8zg0Hg))o?MDc1Ji>e zYNb55syA^uN?vhGBQ(9Z=sy%}C4cX*Kzw3v}O;E&99=TK<#V?%$7nH3C z3cEj9a@$r$NGwW~16e5)++zLYf0`CS!u!-LmEIe!;{UsV=PI6CL%OF= zX`F9|%=$jtu{JtJuV<>de^`0Gmw7f8`yAHcIanNbSj_1)9A-Ijg?ZalTP$~2)awK8 zeXlSztZd(?dZT#vZ``Q!2K}tjWXAPak1@qKYhh{j_E?5-mSJhL9%itfxWiaOtfALC z<_qfePGiyGT|5}p^$x?kd@yY4z1GyQX{lE8ij9VTOri6~H`4DJe@4HTSYw|NvRitr z&jRdM>h)OXL^!^U=}moCqu;PIy}GRvPSYiFoA)IgV21kYOw%z`GU-Z0u^m;K8f9U7 zff*`zUr%Y&3^O!(vSN%?j3%eQeY;g>I}NI1MV^{E!y&qfAv}Dm=o_~jkXawJwYdIFM2#m^7PQa^4*tc--cI)7`w$9p{zqQ(luMzrM*tQ7`jVNL#*-vLqykK~|DPlTZ z!eFcMnB7bc4AeJ`0-iA#)1-#wn!|}7K`Q(Z+7Bele-r=ajw&1aUDEs z;XrdlAEoq+^9I^+^BCmZjPtEH-y)W1OGHbEw*cQr=ayh$(4w`UVLJ>HUJ%}QUKPjA+-al_3@v)-&E!g{BP#-}TN%?e3u z^Z1e}jaQLW%RSzDFl=A6+CAl9*tuwI$cNiE=uRwR}`pJl-BLQO+G) zzXGE=J-z~mI-%bxSdY%do0#2nT3e-cX{(vGK$wY1a? ze^%Gm2Ysn?STU!)!`ZOW+0na1tlgolLl9;f&JGZ-i2ug)Q2EnN;OYe28MMMX7s ziCfWL95ylS9cnMPpuMaM@pZB(+sN@%*43D{pyQj3lN&R?MenoaK4x9(<^0wbFx3#^ zTS-+jYB;^jiS-_}ZGa%STk=)V?7>w1e+yc>smV0O=#*huVG8^i@Jn_{8*!0RUP+JH;) zbeGt~^kX{p3VNh6&1}*grGxh-I8QF(9^VM&o51|GF0q-yws}m%eIXw234&#`e~8yzgRh(GahE-?UeyfzVb{kUY`ySuy z%TYmhyY#e*oV!D&huSW4gE^Mrv;%r4%>oC-7F}13C1Q(FYr_6ntkHzLEsMogl~MPN zTFzClzl+3RRHji(C;(+UPnG{2Yup{c!(ia z{KmcmnQe55UC85I%$|_PyC{zzL>}K8;`a`#;y&rPUXbIqxd+!_T9q>Hf7`9Lh@I>b zj_gDq+#(*(wu<{h{C@anwjb>Wu$<#BcV;a7J_v!h3U*mGT>~v*m!Y+y5V|eXPn`u5 zlV%r`q-=f&>QiE(4c-f*w!x@Gb#k~5YEA1DeQZJ{(AmCIqMzB99Bq5GZtq9BPwy1T z>nW4+w;!xX)_}(!fQ%iGe~}8>n(n)q#sxkXp4f?mc$*OB1cV91gV!NYWx&h>W*1@5 zxUy(muE!sOgkDG(>=Z+637#oun1QtsNVKaZ`mk)C4|9whAwCpf9bm(zcn=JQJ)Qus zd$3G*iD86tn3*6$G=uA#2H10f#X$+~_IMJ9?!}=!GB^4WQ;!eGf0CGwRFhW`#XJR< zHBCO8@wkh__v3JOOyp?Zv zm00-P^LPp#ABKdTsG>(&MA6XJRRc!z9dAa-rni_*h!@9)AipQg1ZKBv+PkS~>!XT) zDoV4u_MqBh#Q7qle*sCn2Lob@D53FD>BjkDsFi`OGs*8J{GNf1UpSN@)PhQ~YRewa zLa&Eld8L|txroAV)W4>$fh$@c?lAeERTtI&5OK3ry%6&eo8L6fd#}d};Mxza`|2HV z#jyQ|s)$2mR-CuMrXn<(bxJ%Ml&(75A@=+8DqXiFsQP%ie?xM|B~ga6e=ZRfIAJ#U zoe~G=BoUYh_FjPJw1^QH->n8Euzz<44f+OgbfO(cO*3GWiK-z!fs6L3)JXEJBPA%2Iez2d5m;7#$J5wLkdukJm_0$&VCrdwN=9~YR$49^;jbK0C zDIPQY%W7zwe?y)505`vcUq(ItC}?(r=5UudVywnvb{mS-DUMj(hTgtH9BIeb3Px6^ zJt{qT5FR|{PwftCcVg_Q4bLBpIDB4rPnSV;WRBf!M#8HM^f50$ppfNs!#+VgPX*uy^h(9SE z`hki4;kw%ZE=L_GawM{iFK7`jR24aj6kGxU zPjHi4x)k>A%calh;Fqu{?6S#rGI z)aCckvq-&{i93zj&w=-1k3Ub}sZpfV3)%2!f1jyX4}@z?-DLjD=&|G%4ZhjA&2Z{P zOsmMY?|J+saNiH^FIQ{xJE-`ZAqPeHiZk=S{-fPFyur~9;1Ygx2Ofwx7U{#M0D_|( zvKnGii|-=7OX!=DHpih-D^jun+`bpluK9369Bwni<8XJ4nxCNx0rls zh@b2fZ!&xxejeg)c8M`u9%I$ZLw+&K=KUIky(Z^+xte?#jGYv(GI@o(E{A?~s81HI z=NIlNkDq{gPhi?VT~ECl(Rl;bFO$(Rf6gzo$>e>L_GshZ`awj^cl4R3~?Sm1x9UM;;fudsU@gU`DuUp zj^JvSaXB4~ZfD8&G)%wa@wdS9Sy*x|xOxlMuHJV&eul6(@!t4F)G5vzL!;6Me{VzR z@)mKPeJ004ztGRh`~0kbpPwf|r2GYszXMj!f$RI{#D`2b-Zhw65g!=lVntW15v&xU zm^Sj1uB$d^doE%Y=vSxjG%C9$;QZFn)AFd&Oj|B7V%w!*CugY%HjMe>VgdgkyBb znf3xw`eVFE@@lqTl|>en_vl1aCt8E9Aj>a26F#iR6ypKB6h?n;fs2rGKX&Bj{+}S~ z(Bd`d_orRrCrk_THH=LS^R;Xq;eX<2QF{NG$G;D{6QKKZY%il=uzdsDzvvQ|QHPhA ziMo6(#6R=+MW9Xs_4z9Ce^Z7uT+e9mxHw*xwECR>5CSD-QU0_;d`43K5}g8P-^AIU zdHiE|5Z|SLgYdjnLg1;;4rq z@H_*$U#}9sVRbfm#$aW1LLWjqrHRqx7~8%=Ma2vms+gQ;r&~(-{1{ya~^Y0DdE$n}YQ$JhOwd^YK^#f4u3x0RRC1|D9HSI91sm z-p5B;7^KvwJIWK~M;x3mNkYE%T#BNmhBuSwtOP@TjU&*@*m`G(0Lq+Vr=Ja+?~~2Zb2xfF z=eyZ*cmeg3|FD_Ub_<7Z<;1y-!%H}x5{_QR;X62lyQ#mG#y#raMfDa8EskDJ^=3Nf54xW;0nbrg z8$T^bQRM`6%Wk+CB=(ozw+Q}9J9uSLQIu)!f1!YYH5EZ#GlGh0-5&A0V%8QP?hDOp z46RAeFWQjuQ2y4?b>rPcm7~9Q;iS%vor3v-`3<=u7YE$~_PuZSb+@)uWk$~F%FK83 ztS}Cgp8UyPkeBZ~cmJ$M*0;T1&Dm9_CD+rx|Ji!y>#{w~kwLE0*SV~F|L`}9s5J)0 ze@{+j>anO&*G$`Jvpn#DkLmbbWqM7v14h=M?9FsZ3|zTyu_f=hcTN8KALg9b{qACa zMqiHIY*miRJE?|0{HXqp=6tohd#9qtcc1;lAgMQ_p~+`hB>c5I&@bJiy6ueLo|4F4 zwtjsu+vH)U{+XLYQPai`#xk?bR71X-e_ifxH^@y{-u`38V98uhb=ZBwV#S2AZT0yM z*?uS3f_DaC_MsXw)B33ew*5)!2cw;N#>d@sA0}qYZR0)C9~b@I-#u-@i+Mdwx}8Uy zl()C6^S%0Swa%YTd|?*gBPrfq)Y)b-!QFGm3=@^(z|~KEt%|x*D&;fJy;wMGe-T_1 zaw~lbJMH&b6?dklS_@3wiZ7pdmUZX-2UJ=1s)yO_h^%v{w0LWDRum zro{$YSM2Kc5EXMdt!8YnCpvs+Ncvu1~gHv~pU~ z(i4^2k@9tZ>bH8=jN^lHTkX&8w6$vsIk(iZByH%i+3QzT39pkXms~eje;P;5Y^kdL zCE{tu78~!)SyQd6PZkefk+1XRn-_ZxH#;QGb0}~&{MF5w^_S$7_h=i0xYsYMuQzXz z7Cv^?jIMp~>C1l_9xt1m5vXIQtgXmCX&92e@KD!qpD;Lluv66E_@DlX%C&dWVkHyh zg&qf29U3;esOh&U{$^@_f8m1ch@Rhco@_ghS7ASSIIambvWEyhs(Ws&bL*R1yWIM& zr2H1fep<)ZFe)hT#Q2tau6$JMd#HX1Lq+-bAJsj#*17dvO8aTTD5Cl?j6%wfVr*i@ z*5!@VUrRZU&LfYmQDi;OqMXE=K|N&56w1k%NtBbdJB}H-$oSE;fB#GjDdl7zjG&%$ zj4;}k?5ClWCt|FkezO05N%hqj%PA-Obui_m?;y%a-^(Z`XNJ*p2{~i@m2%SV3uf%R z;=_#Xsos>6GmjVL}(xn9jW>hZCliIhnbohVEX}cOg1@5i7%U1D z%4IQ(mXnR6%{&{y2UQ-IfJG&-X^I(RnOvxh6D7-p;qujS(pZT?7^jejOO#4t=8473 z#0ad$O2dQ^Qkht&lqV{}B_G~NV;EDuLJ}rY2>8OVL}{#;e~+7r`Pe%m^OcJ5QK$Kz zJEgMl*hKLk{eJeR?r35gHxfwLr`KkytMN_wzHK}^J6l^@pM0(3#a&goy+MoBk1kL?6sQmtqjIzZ)u38*8eKrG=o0Ecz33kL5nV;s(G7G9-9@+2BlI(Rf}Wyh zs1Nm{0rVQZKtt#)@IVdJK?Afv8+1S)3}F(Oz;rN$nP3Gr-~`Sv9~OWsECe_30e=X9 zKnRBAfAA%I1tB1XXplhyBtjA-Ln@?07Gy&%&QB>^H^urg>_|T zv0CsRjzI=`iC&?>|0lulPx>!VO9KQH000080IZ=RSTQi}Q{fc=00=6V_>lxDe|Qte z+17exdsg^MR$74*j%3Y>AW#4iDTy2)9ZNFgBnFEn0fHqY*%*u^BpCt;i3vHJ+dvK` z2jD;s=Y%G`FX)|~&@{c9hO}vV7uu#xpZ@><{NIdrg$#=mKacnL=ACb5-g)2owDYf7 zJlo-z?ufRwT-{Py5^*@BqYj57f1(yb9mP~c4iA5 zrChn8r@L4x))%ttvxQ<-;rM(CpIqW9AT_x)^?kkT`f{ag9k!=>yQCTQ#lDriCzNt) zvc27#vK^^XZtYke;+eBI;zM_Dmxwy~rB(oIvxP#gP`5IbNfo+k0)e`0e=(iPXO#?g z5l_-|Dd1e0>dlNPzjvfpr3ww5xx(61sY7(G@9yczcBOipeZ_2{t~=M!)4j4`L!rBr z?I@NCIA)!BD`Zz^)1@)xw~lnKx8y3W>CShEi8$Bh3RyXu>vU#vy;+xt2~3@MERHz= zEsiPQM6)8|{Hb>1MkEcdfB8Zduf@L3`rgv2QmVUWOe(Xw4|(Y6?##l8qZv z=~8#DSDMvek;G{^(|YOdEoHm1xVrLv%-US0uP2M)&fJ+@*-}T@QtsLf*Wsj;!NmXwfrto)$MXTyHEK~3Uj&~}WSJh5>BKHLgM)XlZD-%!p) zFy-KojIoo^jea;wlklSG8m`zak+b-U5sv#z+z20!J6(Pn(eZ3XJh<@jxbX3FWbic- zkFpXBB?FU{e=Ia9ouDhdaIR8-bCe=XQc5sUSq~E=GJzi8yiKIr_-ztZbbddF9^toh z=@7r^^bo%_QDp+ZPo~PP{BF=K{N}^|DU4`1`~)2Syk?jpUxNR7RCZ`6En|)bQyQsj zBKTdCV5&r>QiJ#NH#ma$ggxukL6r(^w=Pg+p$0OIe-STc)G_09>P7{_`4R~*GL{E* ziJVVsLR2>DD9n_|3`WNBIF-oEFwBzi-{3TBWttn2txY%r*XMTGDF~a@kf&~^+w1nx zP?#EVswJqLVdN~Q18%2Mh$al`YKqZ}Mx}Ol1{vUONw^bHb7crOCMks4;DRIsB@)D? z`l7P#e~rr28GzY7R}&*=Z?1LZy>5-hF_;->(=p`K;bwJzS@sjTPl8U5xSaei{zZc#O|gPEfB*4UL8j8VgZp zoch88!5||Oopgo+nc``DXhFG_!bLde3vkYIe>9`wNCr)WXgDFJIEllfUR{fNgS=09 zMBWEU583xQXS62Nz1@5=QVKb-8ieIn-^`1!0~<<}%X6gShB< zf6Z{Iyc_1q{VsQdZk}OLIfz_O#uF*(RdEfv5%tno)Z5k`^(NC%Z@x(75E_s=wc385 znETQ&rf9$u*g#`?88OPDiBTsmznywRD4jIlG^uFz_!uKTK8JpZ&@=k`X?uo-O0?L{ z_bMG@WJ+0D36$35Tw0g4!Tcn&NTdZRe~+Uk!YD74SWrkT3^>z+j%cEd(}HV}oh4r; z9u|!GWKTbm6b(RY+|?9-xYsphD#Roo-~JNzE)67c)6NrGJa!r_iu-x8Yl;wMOwBt4 zZi@3n10N*L$W$(KpPQ$4``lr7+@0VV%Z>B(cFRvOmOj0w!be~90b zeOTz)rXl z1`X2{*a6S9%W=!OE3RN<2I^uee`@o}X1JQWB2;$8RUvdme{i3MXO^Lr$n=d}x|Am}W^B*HPu=P=zN!Qcao@=4vVmst^xua8U(2<1eByye5N?7$p!&orFK`k2Qj=o+~53A8| z8{fr2mtS*cyotE%v&6_va(w_+$lTI5Xx!3EC9fz9GOuF7d04+LI<>@{@SJlVESJdA zFf5C!LCeu1whZe{B~5}Sx_D&Kl*n@PrR;t_pBos7m_@E){R|Qxe-U%YSLToqM++m( zRz=KIDq^#+I4c4C=MRq#UWnLo^)ph9T)w59`RhQKsAQW;CQ*tEM|yo(A=1s4pS%&$DW& zZ)9AZQ*;Z&q_J(=wv*HIpR4oV?6qbt_SLM}>)S7$ z&xFLSbEL^>w^o2z$D?gFrKhk;DDhg5aAJ5!PI|7E*(5EuzKq$VmYG#6`Yu7nB0_Zd-9( zbZk=C{oMMmO_x(@iU;-Cp+=WP+0X_d6$a!1bJ;#US3zK!IYtcitwzYQX39cjw}Z>W zY{Hy--lJDg_%c@jXFqeNf|F^g-Q4n{`7(wNB1*#WD^g`mZ4@u~9$RHsb++*zCAy+>fAj{9e?`Gd#7$^B}u zB<*gLT_=!)*$VCY>(A?{We}WJb;-g5goSfJR@EmZqZ(h`RB!aGso*yoZsE=)uY@eN zqy^2mVG)sQ|7B-h^gZe47CC3JeR2_WeJhBn2NN%?lB?K%v-~e8b+r|d6uVy4VNs!k%Iu2k!>Z|MBcAhj*9K#jBr(S!yJ~)hA zUy8dtIy}U%WtA~-aWGZMh1r@U8i(A9!gumn9s5l7tjqQ;i}LQu`d$qF*#-3(b!x{Ekv$>lkOkaFPkg{=um7l=wlREe-X)rNSJ1So7M!tb-sd$*9 z)mm@yFyEQ0sNVAGdNIxuY!C1Jlg|2jaRQE>m^#SuF;7HAl}2@5>WH22zq0asR1zN6 zrsc6=4IIsL!-D3#P#sTA?u%f3nL&UlBO+rWX3FlH!gWQB@Fv6!ji{X_HwAO;B*bmT zA18W+8gtyL&GkL8^*bcD31S;Qic2)7fibalNwM{Cv2_?^ci&h87JGcAmGFoJE2k%9 z5^q)$kJQ+Qe`{bY9NI4Ha=LjpfzDn(-gf_n8Ph%p{NFK(fK=91Lf#&vbwH{bdY5(O zw+j3f!qC)ke|;>(p+TNTRFy>d|wmvpEJMi2ho!%zlPNC8L(i$8F2d2^s`9V zn8BL@)HdWh#=!k|owBZI>{tA>+jiuaUnB@hnUOYURt;8?M6IM4;v7EPgKgHqemHhP@(J>N zp+0R>Aiw+|iC#_!%4WPZQ&wZ_<1;ie75>Y0A=x+eag9##ijiSvvgfC3_wDvgQXU(> zp8q|L#LfIUG3UV^XSf;+Y{jd$=%&&_-i9B-2haD32JJJ53Al!5v_r*m(0uBE*3a9m^f$iaf^<>)0COSZ*M@qZN?3y>7-V}#)fHtoTI)Iz-F=5jgi^%KVTWGs!2h zA02UC|1%XZ0QOJHYK(XIm~N$n<))UC$Ge|**pJSuGwMyP;diriXyqJut~fxo-*pT6v)x>#^;zO8S_znZ`? z-oE}rs?3asvr<$>9ojncT=N*^++2l&fH)7*L#VJ^>izTQ87gO!9TLhXCZ<7qjbHsq zWAjXJmk`PkG_C|};3N+s@DCy`f#888nHVN9GD*<0FN{1al>lO}U*1zwvw|3@FZoy3 z7ta&mI@x)({`QG$+-XH%n=H^*kxYiUWOAvN5-;`)?QJn!Ws~UeeNkn`OI;jnGwv0F zT zNtvt)q`38=bzR@;y2)l%u~!M$zwH<)tf7q^>z47%o9@1r*J)f#)4bVaIf_Uz&uW!M z6j(Epuf%2^^OSl$Y4~aD${l7Hh0U3|>9{yox(#T@QSG#J|9W}*YO|p(XHwOqqsalr zo>6-(#GFKy!JZ0UCWCnzRuycWt6-~dv3|$ns@4kWa`{f-GM1cTwV=v%#d}eAafDEF zaj#CVh+=|%JP2|>l{l1Im57z`6I0)GlwOp`C)G+x@vkxmQ0;?Wy41T!$Da0l2p2NB z*%X-+w=xG1rpow;$D-SM(#cs0Hw%H(Q2`c49y)Zpsi1Sb46iE$$En3s0p;PnC%dU# z!xG%}sq2?h#CC<&XpNd7>h6UrJrMz=G&gw1l|%)bb*c5f9TmpS6$VQ9uvV_u>jE%|6cbS4 zrBu&4=<=;Cwuhdk!sv2vdtM}H3LwfnPv;-`?;n^bhdc)3hDxrEhy0r)E__O5jFYN2 z_#<9)A1`g^E5UCDzhajtx8E9W9mg`-$FZ2A8AkTv#WRES%_BAvR*Gr}CzzO75m|(n z-7cf~ZQJcm_Kvc+UuE96-vJI-`r!PYWfT}m)wZr8xcG)=elV4ktUKF&EARW9Bgg+YNZ!06xC|AMouDb!k zR;Xw_Dt%R;gPEo89~xk9t^d>!f*y4iTci2(@~>B{UzY1sR^&bi+Tsuj#nQBv>vLA& zap7xyzUz8;)9)k83*e|Ze8A`*m53g4{Hp7)E|;mW{73U3>10xWKA!fI+|r078*pSB zr{X;0ddbl0t~XvktvYm(-6-Am#v5X7h5?zi_cGo$xC*L=D@SPPdg;q`_UwHctG?6} zmEy6~gy4HTZ)IW8j}L31kfOhvs)Y9M#-P{vj)?o z{p9c#i0|8E5vx9A6fg9PE&YAvppES?*gaLMMRb8oHrxtOk8E;9~kmyB^zl zp!YS1mEVIMCcx{vca7)NXa0=jzH`2gjjAuNkj zPLy~?u=H|7xE;f<9(1b#;}+!XA>|fq-9gnFJl#GvJKC*4 zt^HCo1%D{}MZ&}g6a-#TAb*m$#uD|52gNz;66r8zD8D|TsWTm{13%1$YWtp+{sA!*=-l%n>#FX4HLUF>KwMnOPl2soR`@Q`!3&$hpIZMMfML7$k(2{i zWJ%(&X2T&eUAVnRUSmCS6G}%n54uOUch}K}LJ7gTaC7}dUB{VtOyC8q@$$1rXe#{{RLljf3Z3l;>C(L0}|etPedEf{X`BH5F+x(_9SX zW0Zf%yQhu@}|KA<{>Y zX+oSM8}xz3pGkM(=vv@3ZP_d+&Db%>l{7L?*Z-SP&t^?7nJ5f_e^FXpxy}Ga&A~;Zo-*Ecg_g&6>qlt2F<34vdjS_ z@pjk$M8-zKA@BULuH~L1cGLqR^oU^0y(TEW3oDTrdy3>5$%IRD{?@ zQ2-NTt-s)Fh3Ps75%yQq5o1cxvZpXCmVo8Y8ejo4UCM;3m(tIL`*?DaKQ%Q~15RG+ zGbX?d3o}L%yKm2Sk8daGH_5U|v0W6|@(UH!cDnWGi+&NU$^`Bt8QFSw#LfvwWbb86 zU&1kR3h1W6@?ac7u@zdHVB%oRLlt$;3k;thnD#$P2UY2X6)_dAf9VbgV)Jqk|4N{S zCQxYJZ)J(|Z-9RHAQ{#`Y=sSXlY{CR=aT54rCKj|>PMjXm`OMHTqXYT^j-J|N*BLI z2^xh9w^gKxrdFrdS@31sbLV_f=OaiY=t;*dr`&9bE?o{dtD2!>)KeW%5XIhy@Po2? zYD#$x*aFh%5yinYPp2#}+E>NOind_%3>;)a`D}`{4TWfsEkj=ui(gxKs#2kRackzLJt2J(?$XVV+uv=^KU*M1-xcEUi{Vf8ef4ET8>j&!t+M2YqDz=``_I@Hu|y%d-$kSarh z@t)h;)R2%o-FB4+k>9ZzYmHVaIg?OP9%{d1{FKE9As9};Uw=Fra|cZ}2y)cFwBsA1`|rdWE2sHN z-amzD{~sI&{Xc(>WV#A42*67n#tC&{BYVWE&XqNle&iS8res~f&|&T0TPVa-(tt$O zKZw77uS>a5q?5fFm?o3{@e2tF@#}yr2`NboLw`eSR950f+e(sMvrwjL(5Uny&VD3X zeRMS?H#%{Zahx>%yzBmYI@>fZnzF{7pr1srJhMEUFF=W?LEkMT zWPWCp6*z!8kmixPf5!_Az6HH8Q;Gurh_#cdtx|}yV8I4s_(R*QSJS($V>Y^ zmbhGYuELQ==XgakQ+4lPp3GG=GJ(jPQZ5u=zEnkGWKx#@*5|{oDu`V zQbO?0awtg&dBqn8da-=HHW>{gTqz zb}HNeEN>|^F=c>#yE_bBYL4VYj=ks++$RuO%(!9F&vg=#F@a1>avClx@ik(P z9c97yNsHk|DaNt<5%m2he&IgCa0!{FfDCc+{F#U1z>8Os}Zlad4`FRf7pTmaC>EO>{9>v>6C7-b31nyOvm`r7>%3T}}neWe9YmqA_L%5PCHb7g4 zjbklYpgFBL?;@j2vI3xyy~GPj!rEBdus{C(jy7o6~4s^K!$h^3uuXnTasP|v`+Au$q3vW;V0p_oUAKHZs6 z_XLn4Gwx1Eoa(<9MP+{)W!SK;mP=Y7jbLTXJ=VdiBFVmeivR33gIUZp- zEa$!VmL?{;OX;VMyMgCKHwT)4bBrszY$utHv@={tP<2@(nW=4MkeFy&R1k|dq!BR?Zn73 zjGEW!>|I8Bv6cfCGn89;MyjS9Icq8G;msk7$!PLw)I7c!k;VB_+nJqJ;ukmM3wf+n zSr-!?OI=QL3NkOu6{%8Z}!ERUa+CHDVCz7)AGqP`xo0X9Br( zjgH$c;>pgT$xfl@BjgU)NR@f}KB0FEak34P>SS@1h|8emruc7u`;H1@Y~gt#(6lnS zWl4tY#{mMdJo!kTD!CIoMpFlp*8^nsIwOlX@iiHGwX#hx1KwnsGtEst ze#1I(UifkkO<_NhLgHk5B?-z~HUSb;JDZ-#6$n+BK$@S|peEwE`_4)*@JY)ovrAMq z7?}n<$;QbYxZbYBEz6E#O}Q>3DpL8yvBc#^><`hs9~HYWHo0u3)Pqf(8pnNco7g9| zXj6m+1K%~Yfwhn!$F9y^&SZsqL;PH|s+Cs~9Exy(U>Vz2>1N^S}8Btm`u5!S)^KNf^! zgbj0EuZO;W1NHcoQl}AWZ=pYs3Vq@xER($^$U-E23;&kK>U$t|EcAX$x=*IjNrIi8 zpluB6*XQ<-)Zrqd4Ls#-`va6i2cOBflj(xekmOWLVL}_*_VQ|(F=q(CBr@vv73hIFmLnt)A_Yz1b${RiilV+ zfW7O}fvLifs42AddM2LWmMy2?IqSvaLJZh_qrn3CY0R85r{@S@c~9@=vo543DIF(8 z_>qyQr$`DSBx0w+^Np5+v%!pML&c?2iOMI}7g&Lxf(@ufnOXKjj-bU|kn_Z%+r5Si z{j4hhD=3plG0P4(7|(Y;DQjek(r7o!WwDJXvPSQf`yux0Pb0{|d9G;q=GrSnFAK){QW_f|PI9LwLp^HC7{wEE^8NC~ z<4ktuND8i(75usDFWc#}1s)xUE}R$AHH_1{Gp7&L^^$o(D7_JLfy~0Nz#qeJ64>fs zb~JynBWWOx!PUxRarAtlYJP2vh&8!57SZ@V?aBQL4lr-Hu#(?S+Ffq*2%5pDRj+Uk zamj$w>I~ciPA_obg!#^Yk;pLO%V$A|Ge10Fw<3A$;wMS5F`2;l+5Cec{-&Lrx4iL~ z&xzBMMI;Wc{g}Hu?Ao@gPguIjuNrkT#J+2 zk|U$phW{a*F{cvXtD-m}O`%t{UWNri`s97%(YbF?u`L zVG1JhAd=G7G(lez?y=|gAmrQ#g+7vy3CqUym`OOrZBFp7Ch;=%=jeh*fJ!yeN0T`| z2%J&QOqGE`(pE19m}N95!P_OTD?7i33N{-7{uY>UuHO}b>(i`z*Y7M5&Qz5P~XM}0$@TiL}o&7W6?B?zu6 z=-<3BfqiDw&m}uMH6s4y%{(*Fa{0H{vM=VC)vD0PpCp_)PH3`CZ)TTUe*VlqDL1S@ zDzARJ3BvYgL~r5innp)qb=O8jv+`@Y4}^0LK(yWq$L?1kvWMBiPtgwUnMpopf;7+t zAM(pT8umK~kj0|kkaBy$}E`p&9Yl7YZqoOVHiOH2LP1gag8`2~miDw^N`WAx&S zCXMirwRy&Iu;Yi{SBz;!hQE#tUrqWOu)X~WSI+QUuuBx)Oeo;9H0c?u;;k|XHAICN z0W|dW;~+l~?Ma7{!ULapk;T3eY5LLZ>S$W;>ka7m=`Eab!=V=b^`hv&!w8%J`-x?< zb!+um*o(dkkBcq%c8kxuqK2P)MNW{NKTp80mMSNfbeb>EyKH7YxEP3^cMig@@JOD{NU9q(){sNIf?;I+e{Hpx3z2!!uxak)fe{gr z$Gr!S=A7v@WpsgsQDku5D4|}>0ry@1$1a&QR*Y2?4fwxg1SY@5NFaA+Wm@JTZAZMK<_Ihi4XGjZtFP&& z?U%G_wg?4F91kWYAj+ergE7H^3HRj3+CHrNKSP z@ttSOFhZB)M3Ox}ACgf`tx3C!y`|EJ^TDW`0Wfm;mgI0HRnm*9W-j)w0TL&Esudw1 zVKRjLdfx>kBT~4T=J09(m*$<-Sgk^aA%gZE{8Jrw$U~Qt4_ujQC^eUR=ZvdnL z9Hagy7pY*^T!Q!xvLbw-Hu(g7g#rqG6iaVG@iiriuCH=xk7g2)qZ0%-tG`Lc2p7Jk z00j3;!Bx`A1lLMHv7T}|aF5#ct1r0}tHx+{NpB&IKC-VN0&_8)J~DkOg72V{w{H@Q z{2$#^om|S1m4XD`QF_x|?T%?%WDXyiZuq)zq@S)|!>BjB)`otkP;HFyyme+@+vaw* z8@=LBXmL&N{k-^aL9U%91n%S?)fD8o|T{-shuJ}iCLC^TdO6Asw`qErMOy9i<3RpPQ>CpMz{`-tuxueUq9T9U3(aR&;90QK<)xxu5fk z)a~D3Z@MC@X8*1G-Wxn-VN*Lm;b#Vb_ z+KFG7PahRWU0SVMnr_Ay$rDeLgH#mFaFd8> z7rb{)j<@@=R3=MkUCuV!AWPk@XJyIV8n6A*pK>>oRj~-gx?2ir6`yyhQ@Hp#jA(#Tkn6K*DY`iVc`#md}Bv0OG z=XYwaH^SKnSnStpo=JR8Lyi>P+S4BetQMChHclBzPfc?Zzf=*Y?r7uaC!p@i_tnq76bncCKFZr<@HK`YbG$Kp z`FK@=c{6`LLXx#wm8E5)zuDcgW4ZQr(gbp;yWYA#h_IBos(ds3dWRC0^!U-S^98}@ zu|EdMOL$eO{q$KOeFV5ZBIsu6woW?k} zLy%REQBafW%x-4`=6t2t%^&Bw0d2N)%%9ro9F~9awh}+;ELI=C(@K1-@}}=f$|QAl z8SFn@Wp#cGTz$@^+WyUoZq#z#ZE0= zHP6}l$4ShUj_2z{8e4rG0oSQ|Vukl)dgt@}5DA6Oa`ngSF?F!b4g&7trx98=K*jSu zr7tY}z58V;oUWt!GKvqrF|(7ghv-O++9MDqILnRvU1oDG8@R7Vxwu+-_&Qq`#o5XN zb`iF`Zg>2SEPd*pk7K3_qx1HzI@PraJl<{-B-?G;wOBh!)|>Y)saWhUuP`LJWIkOs z+ehoqx}2gdU8Su)k4LX$k91dn+XCBChy9hJ1&PV&4p#}) zo)e3bX-xjRYqc(E^qJk7((ECPBj=`^2&^gB3W_RE95Ku{PBQ%y?yPvgnEvjarNU1M zrY}}+hh}4Ntk)0!t{R5e6}=a?4_Tjeq{uAxo5MMTNKZ>}vSGMjgPVfgD=Dfekf06s(&6bUF*9lrD=Eg(0v?#CpL9VGk@0jlqep>9q5+p0U zrq`0HCk{T}rv2+YWc6tDx!Xbiux^8lckjm0%UJTerVQK1f$fx*dxOKJ?i^h7EQ{Pcp;9h2R|d2W5@_TlaRW4mkI6^L*PO|aYNm2tJLdT06a z@J{|I;{yY*bw_SZUhO~KzP)^Z|9IE`-epj$SaH zb#LhIs&A|BY;U^{zmmT~_=dhhzf!)KzJ7jTeSrhMUqIX!+kbR*+F>qg;eXk|ZA0z@ z1Wc@B>W%Uk(n1=1nD_yQ6OlIijCf4?kVtL_sS2at5aA)P5Y=(2xv&yg{k?YKz87bn z2z-5B&-uS|iOD1qbG0M_sTM5e%M`2m^T{VP8qMaa1dH)GJ|Ki3$b#;%So4-j81~4e z(ip9c2P-)4&}lU4tOm+h&RDHh>#T>Wc;4V~IUUaXOPMexwc8vn1}nLq@VPx6_Xo<^ z2)ew+AO}v^*Ahdi=Ta#p{=uwvFTbRX_p}h-LH~cKApfEdfcz&E(GVpgf}oK z0|Dwk>`<3dFSGC&Y(*^0N+J7+^!j0gc_QWwz~Wlg+YpOM{r^I;<}XS~Vl!hnia|vn zvGol8S+tsMlo@{w5nMS#Tr!cZ_SbG&`bD9sRCeCqE}pbXWxLq=?|gkUQM|c)YTF7y z?`2hv{(SrR`s%#;w|p+qnvOoEBtwbsaeFO$h3cKMVH zw;HSj;{A$(_#L7S9xiHAoCfAZX%Vq*Wqes-N1~bXw6S&r_db;|!zi?DX<;G;=HrLA zj^F(?sFJ2h0#Wa867a+CF%)LJxHBCTbekL1XtJE)j$g!_NLBpE?%J5LX^*3Td%2&E z#nkLS>3e!(-_1m_i%d~8EPfB8-d_H&la-k|`@!?$luVr`9@80N@a^6TI;2WzHKR2& zWh^;GE1YRrrj0o*ia$6NWFvE_euVu2tuAn$`C8KSmNXlGR`KS$M{1HR1<4DuU;G%? zorX;a_~bs_pO75m-8o8o91co;>9=a5i5WM ziwG-VT`d;<*Ps?V9FinGp!i+pmF8p#nL~U0^h2G84RgforAI6DM7_+IG1N^de}<*6 zv^Yr8!QX?JXnO+2nzWo^R)c0Xc{)!pA&4tp+Ej|y=9IpI4BF#>^f8cs@lWNMty52?-xtxfwk?6+bQB!H#UMRzP4XRVes=#$22y zRB58dFh6Y6Y__4qrfVvzAcL@Ih`a_N0faSJDb04W^Tcgb_y_r}w-ISph;XCTINp>n z<#!(PigIlV(*`SlfhV3?(CzVb#tW==%zb>79r!Cs91rTCp~H=UF*GOtnCo49n0-a(<)WnWcdvad^_)%#tlBPHZ2vqrG zz|eA<8LTFiLratEwi73WKs-`5Iuns8#r-sbykAk2u1S^C78|sY5~hlrCPO|!;&}ug zq!ySnV6S!Gu%LQ&FVLp>;VsY4#00}Xuh+?w zD1-jkF>NH_g0~cwx6^W~a_<>h|EV?jj1s}+J`#hA7EZcZ4DHh>pn|90=#K)cTReIb zmdj41L8_zd0v$)pL!$cH1Y<#P4kmb^MB3G^Ur6Yz7})j!m`TbnNGo@W(0O@#iOhf> z1kjZ7s{LF8PsK7WnNjoqCAq;nr)&1JY_R0C!yf+=8W3-#q{KJ~3im11tc5k^W9krs ztHxBtMcYFLl>Y@Wkypzzc?p2Zows;QgHPojkDk-Ot|jx8)& zbffyE?Q2fAFqKxC{gO|@CK?(rwX9B=FzKB zZEjvQ{<-$1k*`N+K?Za$X%R3BMptNm6;EbKnDAw2SgyikLri7Hk=yrSuz? zHHcx!E6bY2M0lVV;(mH1$sE+5!@SVMpbm^*a`SDXWADT7H$fj9St5UDZU#xy>?Z!D zFkq3T*UPbZqy6ev66h>`gtlr1VTg&)rQG z4Ry?EJEl&Vm(kDjQr9>uLR4o+6nl0NdQS*RoY5R6%&!wJ@_|lG)XkVtRxDwbDx7uW zq5=`WQ6EXTX<8$8hGpB&#Xu{Wss`FjBZgciKWOh_Fi@b&pd+u zNptDX` zZhBQ^1b^go-6B{(Qv9Lf+os7jQEwT=xoY}NS745jTOG6-9K;HAghvM}qpWOag-eN@ z_K!L1Uk6kJ_QbQ9GQ#U(_Id$7)#Z31Q!X8TE-CeBShHxgX^%QD)K5yKgxa$W93Kar zxFlbZ_@CQ}3+3U<>@@Ua6+r&z27Z^1(`=CYpM#O06*jN>Byf3v)QlbEHy`u-F8LOj z(;{-+K*Yfg3(<;Ee+xGGPpJz2QGL21rph?{ahu+~PH!JU{wKl~l+OwWdgo(+b(b&L?f?*^S-1+b2QtAkva z7Afc6j#)dokhzP~7mu1Q$Psn}U%>k9$tp%?rsRhRwI1%pj3e~@oHc}YZl(*on*WtD z?}1&D1_zpR@OM6H0O^9eJeuQgMd-AmR z(`I@0Ph_#$qsX%E_~mcMoE=|3 zs&tLrrgcF6;amW#5a|SboZ1nmx)N@8V~0^$xo=<%Gngep`AL5v@E9QDIVRJuT_Xuy zbE})6$)i!~%31#Dq}BVzSsPA~8TW5Ea?$c-72O++ugYl=v`d=<>DG{~mrYi3pRL?v zw0~mecCYKyIY%6i_VuEOf|GT@ET}ysBFOb>Jq5$0ceA?7?ShCdOpdYa{T+j7!oeup|QS%D|6(};>+hoAEQ z75H2kAMS`0xc33!4uy%`zhN9^wCU^*asy(q-!oK50o^Q!^$j0%8z1@@&1@Ngh!BKT z(sJY{)XncezXyWla2SL@;R8sxTl#J1zgOnSB*!O6{QTLy(q}*cTO~jIchj4A13I3W zl-Trs9e0+XYT=~!Ep*S1uLEb5 zf---ZBePVL?4b83nB+bg@MC+yq2I5V#*i=vgqS!*(mbXbGyJIwxgk~IZV=_SH;-qB zs^AYZ_Ic#c=QRU1#XeeMowAOb96e#>#Zy~3qjc(F+ph?&OJ%xGW28TR)U@|4^2_FB z*~>~YPC!SU|5+G?M6I{YCgw#38zd~54hzM>Wq<0iq5F6~l2aXXXEd7V{gtcnm9fL= z4W??0?3E)f7;k|(2U?R3uAw(nQ;l7&TbLt7!i#0+f58j%8|ucV^h7ZoMy;62t6Uoi zXUoceG>A66;gz(~V`jO`TI3b?QY$_G`A*$vM#diC!go7spUBc$J##i=lF^4=YX*;d zI{-9ifHZ3``9;$wLAayF&5}m_Sr`_=e;@7(Ne$MYs!`h(C!&nf+u7xF2qII?6@FHQuPIL%j=ulW`T8nx3Sau(Kv@QByyx^Tc5qn%aUevNi&W{vzYiAAIK^S_hWN;f% z-ZjCm@5YLZ{>6r{yArSTcG*Yueo44EHaV^R_7R#~I>_MV138-h6}K;!C~PO;Bp>*DUL?>h#I2JY`pRlj<(WjKc*7gHoGEz}N6at#3hq zLrI?7F z>b78A;6hsF(|w7$`m4Z6b7Hn|sSKDeAXIiVaaobPLqjZf3(9VhD)qxfr>?XGh> z(QT~reSo^U@^u~K`T2&-)u+Y#pb?|J4`}`jWb<)+p|Jg8S>f8c(EeJ?1>S*)&?i3k z(yAApx@zZ@t0|fczOF0N^{S+!Y@Wvnj5bxkRkO}ha_PBoE0}nJ?t%89Tk+*{1i|5m z?Vg+tsAlGEzkG;Id-&j)qDfm)Zna0N*7o6e3d8(b2?R1d7vBsYHV~S-ZZ7^Pc03PK z2Lmll!2~5O05Hh+Wqh5D4(Gl8(r#vJ*l-i6%=YNLH>gOL9fe@3llHJq zaY0e_b!GhJIS1ICYQl(G+U)C20H5VwK6hJel^GvVH}`bFYd8l67w^dvIW9u54R;gy z1JM1My-o1-+}`b8R%Y`=kYPJO0Hm&^Ma`;J89z8=419qOGN8W!^_Ze5UJ!;6BgJYy z?uSo^@zLvXfaj!W%EU2kYvU2$#NkP+XUv2sgA;2vq@dxgjftS~s^+EXV>`V|UE0d< z#<_F@qzgkUoFPuU`PWLXmojTf2X=ne3bVmHL+U0@Lv*qpjqT)7z1ybu_&K?M6s9OJ znM>y-vCF&RVR!>1H6!r3=AWLpHhsLjOZ%q~N(`AVN$&A^Kkh$d$?UXiub126J)a*L zTpo{mZvj}>pRDiUj+43FoL!$c%>*6-CtqyVeTkNI+&vFAZMT>CuaQ_ITjT`5$Nn5a z$HVzpE&Ne-=j}Q9P_ZnYuGbXF*UQ5sMs-BT%V~E{qaGNRekZ=F#W>)Yt{|Aam;dkf=`uU5HR1QUe|e&dcu9ua!qVs?HtClqjRQn zgxnb0V7cUcf_Cj}@A>$@s0rC17iB-lx1k%p(5~_UOC&Nr|1UeT%%Q{`(&%{4eYw}I z7f+p|cUdf0lMyCi5fs*amY)g4>O3OV-~$2Ea8Iec3oWI&@CZ8C=0)-J+D>zVkUjt? z*pC?jYU^jSA>v8OAA@$7Mj`tD2pL2Y1gsxY^dqQqvm&!Pu$nTC{rERqc{X!w}r6Aw?lke@05}p0(Mtd5Vs+pc7a5Au3>j;L3Nd;! zoe0gHHA0*}yA@va!}f`R{QvtjVu?-v<$vbu63B1gu>RLVi4K_mhj0|BY;A@sjG494 zzHbuOu(38~Iu|XSmZvbHUHYs%EorFSha`NqRvwh8# zuMH`8emuz<_}&hY*ZD*eko_C#VfHT-wy({zi@*H6Ur8%#wMY|6kwg}bN7RBDc+`$c z0UQqLnk9{APYM!Cib|~p(oB@9__w$w&Br(tk!@Br{%vq9%UL!p#pZQ;?{z4v{A0BO z1K~ELgvZz)bo7Fr-NJ%$UL>m+VV+BF3)-{Beay~tz^x$9twA7W)W4txaYV$h1(kb| zGK`qRYJdK2X)JsN{Vk}ZZe4*?xs_5TrM3f2U*&7F3Zu_fF#?+?}8r@ z0lE2}KB%nG?j{ShPFc6mS&AE8)aT$2-Y}=s`Ktpj9)J9d$t5M|da2@B=nqn$7154T z%SsvX0Rs;4mAqYn@QOpGk$3n6{nmuC!+3gR77D}c12SWY0m$1_=NBPzzC=BD+T`j9hsK zLGcB@{iXLG}QWH48cMD|&ezakkgNw4Us|-XbZ<~eVSqt&PMJ+`9 z^Y6m3FVfHEjyR@N7#E;0QxeEyoy_jLOL<^|mO>JH+)4NmN$dPI(v}9(C6MJfl3E2BTE-IF&u4>;Le5CtSs5;D^dk*k z4s9w_XmY*#bcdab@E`MT;9q{zGS$p9yVJi>!RgOAj<1is<3f@5a>po^6K4*qT|b%K z*vqxN?y`BVKHr}Tk!;MGsDIg##``KqMQT}_XJ*B{r5|L`YUsXotX5kQV;7#fa(&%-w2`zJ5Q$9dsYa`r?cDa=q*w;!9$ z_<;jgmaI4X!$xS|U7yD8$Hwva%Lf~vhR(b6ct0X>)+o)_m4W;5JS`|n$IX;Ut;^Ch zHQC7&3GH9M)n)Y2YH+@~JW~zdeRKALlAX#GSM6t$qce$8lSClGw<3BDySVccU(!hN~i&2`HdyKa?O6mRp?Y9H^I!EUr&B7O;1Oxu*r z@_JBorXL>j-1E1k9-e&L5NV#%R?6>lqVta96?WFhL8qOQKYj16`+NBHU%G<*t0bEh z*0*nzw8^$cP#DQRt6(U=nJbQPl4|>^im*HvZ8I_oVFAhyC`__UH6j=Mf?d%JusC+L z9jq9m(hJI1GNQ)YKOo${G5fBeY{OI;5%D1)xa+yvncj?^GCn4gKeFnN&Rr)z!Qa{! zXocao9rvrcl%20Cx4c!Z_&mNAMbZbEgde-Hpu2rTP~QT1^@j z=mjR5&T=*F6Ga@IO-J=wrKNI~>e^n4-9)xia1U0sF0Clk4Gj{_k*QX=s-P5lmndZas71JcYbWR)l#^Tn>}>E3JSD^+Hy zI_cT%*6Y=}PJ5y=)%slC%PNX7oiAoRw?3C8olSoy4Sn(PLcUUgn7WH)#E_4QM=dp$F0@R04Nx~T=tRfSVI7Jm#c--jaAoBRnSCyezaljT>kttEaO6E z_GGnsx{q?D#4B6Ndnd!KS(r56EPE@PwIaMu0kyJ&3vU4u@nzFI zo6Fatq^a+|b>G22D9!*iu`&<~MStM#_^%zEc*Zcn2*GgCUJd-bLprp<`L zH9@q5Bif6vOj2f-h zOK=cS!Fi?9)b;Xky0r@YK(V%wNx|63d?Ty;qVAPu@&&KaJ(Djt+_hf)d^QN6<$(1x z3iVRWV?aL&}Z{qw+7MHfGp8}-AYQ4G=I0ES@DxqaYfslR_Dg7FS-Z* zH;v$M&s{^N(KF}HO}ll5v=jEK*}PYs>zxJ`5am@>l8Ms4Y%sS<`IYZ$#JZJ@2$NN0 ziH`R~>KA=v%eGXxkbKig3H!l zpl<{Hmw(p#Qmr<kxOAS zcYoJ**7X@zrI6P879nT zP`W7MOz4>fJ12h(oAab4OF%+wOkNE;7fHb+79RQyeyKVBAyWX;G)t=3?{_r z;H)XqmKaM&ZgS`1q$VVfb@fHo*xF>&w0}uui%H%WHkW2?p<6Lsaw}~AFzm8RO=d@K z9APHrKWRUOmQr>HNIN%&A{pI zNsX_GPELwZT@1!WA}Opi3BajFv3d)b+|*(+JQj#13>;<2WkL}RTe1G;;1HNYu*9a6 zcWmQ1z796&mwXUncSD04dVdA-N)k>bc^c=x#m8Rq?c{9=CCTqbEUEu|{eZESWW^Vd zL&%qQP}b4?!|s4P-sGEN!GvE7DLu$lbZCpH6cafBJ`AO8B1HDK-U9Doy7Hd29sGAf z=!TYnoh<>BMdC`vF38xpeEW@(@5bpyaJrkKNQIV;GtCMyQG>Cp7=L_bkIFT1rNbz6 zxCc5U7LlSRl_ap;3${s`Y*|deVz-(azghBqV7?#B*R`|FY!3S*AK^B?4QpZjC3Aox7|BCnWj3zybe} zNACz}e52&|L7yWy8jJ;Kj#@pAal6KmCJtj_5)-$oiDSXUZIU0M37p{esEu?*NC#)V zh$w?x<&yyPC``Am6{eFw;7%a0N%CW~A1--E)DdE^1;HR8Fn@a)i60KUm<)2iN;dZc zf%Pp$QvstpCC`A-6cia)mN%&$^5c*_jT8wlhw&c>hxh<04~=Yo7toXZLC{ho@lYc0 zUR%(t!#7i^h>uddr>*WuV-T9&CHX@n3&T`YD5VqeRtP<;QiziJ2$f;-9t7banJ~=) z4x1%^lsF*jw|_))lQ8_~vh1jW{)CFT6MoD&2zfI9V-#&vG&5Ax`Qtdi7QDwY{@B@L zY}L3!C7Wllb1S(9<*%Gli_Vt7JF;)9tu7L1qYRa%d@xg zP`IbkbDBIF5IJaWA}5Eq{W;KLKrAsJ|~7LY&oMmF@oHZvU|Z z4{X~6-A5#EV08)SAC1-=a*)_H|2SDRCRE4bVb@TpIArW6McE}|zvVodjhm`iU#k=_!a%UifgjolNjv^ zI)9y~Fy1YSR;(!bZ!q2i7BP6p(-_|q6ozLo-W&9c&tfcs;F`yHAKF*Re~TlH3>ahN z4`{&ob9f&O-XmFSk1?+CF%dZfNXM~2j1?DhK=XMp7*nQu9x3ZCpuFF_7*gign5IYH zHQT&Ewy9}+;#6_KIKU#sOU3~UQ@{-n9)Ca!yMY{mhEBu|jN7sQLa_fpIRPyWO8z2D zbr!}xG-Mo2>`RPJ4I2kXcj9M{jD`v$qvq(Yf^o2aK61i163H2dBaa&QM$Q?BHGXf@ z%n{gAC`G@+72?paai8De-6taF{X!4lUcl+bgCZQsvc3=&92TRA9$`G9@gq@@3x7Hr zl_kZLMKL4!OUmZD>bEEf1u60vR6Pm^DXNeHUWWEhArl=#Cc3UcI+-yZko@<^5u>Gy zGBb5-lD`7R`va4{iZL4O%r4z{s6Y3daXfO=n2H=T?$`MJWaig=gJxjBJ;rg3PZ9BT zpLiTZjb})gz8thUZaTirvKjUMz<;UCfbpQR(1R@t4TG=~KRd`IOhncv$p0?ZOiF+Z zUOVxFMHeG}QQeFNgC=c^($*tLzYi+IJtX-XhjfiUEcu&&;2EIu$bj+aVB}bU&7(od zMahgj3(tB>omy`-Id2mzeq$*4J2=pR0pkRl_>meiYTRMBcd_H(b#^<^F@OIaVRzm9 zW0JoQb)G}odc5PhC2{e$@edmkR~RF&R2y@_!>5pYf^CW0shx37a3)_(|kk%)9~(kathF3r1X5b-79h z8Zb^#7D&=DQ5+~ap7P&Roic;8O-`UQUyIXeqfDkO`<{Wgc$K(8CLDfF^2?C<8e~>b zw)SYeih>Br>)>1)GM-@V0?}6F4=})Xs-@{faEVgoRPs;Z4W8tmQGeqYlzT*?-YU1G zpxr9Bld9b6aVoJ2(I?VIoz$!^Q**{wvjOH%>LQf#hKwdNoO#FZh)_@U@fCL_T3-d& zc#<(+hfV%Nm3?(PYpbGwQrdO`)x_@%S_}w!1J*e^WIV}wO_Qq1uyGFM@|m^h>kGU z&7ciLdfI{=PcH-FiVwsyl79h?Z(;t~VPhT~=db7Z<*#y_U&iqZpW|=QoP*=rn17Cf z-6BSke+A@@`HyXWymvds3viR?C11q)cd`D3A>#rg;cZJu{(m*F*w;clehqQDS}g+c z7k+N}3u)s;EF-{PT&Cfoui@`-C4t2EAaP;Hc&Srj!uQB;!1$G4F|)eObQj`k~}m0F#mUrigYk?7Z!iT=hH z{W@-K5G^74B7d$v8#Mj~u6v+-0My^4OD3g3nC4rEynh!n-swk@Hr|e8jkh%Z7F`Hb z8vbK&6Y=vCgV{EK!P}zCNBbQxIK(vmE-s*8{t@Qh8#W|7Ns=c)N)lBiuKH8L_B%iA zyze)stt|6B%>3c}XzQ+;Xzecj*F=dyIWar_I+^Z0=AB^^wCVg#zxQK+lPKfq&5 zK%XW$+)3SS)rrwN|05vD-~wqaGM|Q9$zMS`PPgD>`*YCWKzd;L2aJCL>A}{0_Mg!l zJ_Iv-Y1*Oj40Q@up+;t5(iD*|F#j+9r&M*kuYcrKobF4w8?%xa<-?Av>W^h~rz+@fu1sSdCxQP2$sO`>Hs#eb^kMzrTn$g%!SG@H z7=Ai_S}^Vo#y$9j@#_lcD>~?rj`b^P{Qm#|0RR7$R)0{G)f)bGmj&JgGB8w-kWryG z6;|*UlDep9pArOe6`iZgvb*2{`@>yOhJUaOiOTQ?geZcFA__{V#MK;&u4E2&)k|60 zm~>4l=5~hx@0iY{={*PDjdS^b|6N?nnA4hG$84XBbU<48vFkB|{*?IMPEHf{9OJ2&EYAh4jtiUl7_(7Wfou8-FOo z^Bgp!b3RS*jHK@(mNCQ&^Z&9i-eh51DbnEM%9HDV8K%by^Q;!yYKgC4SV{h? z7*-4YuB8}0Z$=@v41s3~xveA4J%6)^TNte5xH$; z71kn8;7LM!fxrs|zKJ*=*Jh#Z8^RhD3v=?OG*nk_3H)t=zdMY(+6EEl^R`Qv|7s!r zppZ|zkY}Bc&tXB2kcR5Hp7XeTdkT&5DTb}&ok^TOCWhtGVEs%b&fD{S_YDETx}{|dKtC;6T{Bn|GlBjn*mdM)|5#_5Vb zK)D?vKPBn=DXx~@OQhG3zFVU6fb_kjms9^Py^4HyGI$976{Ht2cnb0Fk^Z`C{G^wY z?pgzvzMcBz`(cDD&vNrn2{|eJwwH7JapT%Kfv2J@c;y7k7GvI=Ga6Qe_yR(U>2x&CNH`s#qt$Z^ZcTBfw`Dr(H!Q}F1^)qhz{C>MURQ@(^h zT~4jZS|z1|wr6u&)6I+p%R|={RrOD{e_p#n)BQ``{@10)kKgkQvD9svxZ}d%?|e2s z?maW|L+#y2L;Hu)SMsH))BUfk+}>4HezEyX@=q6E-gRV6etqk$4~l?H9fGqhFi*Zruy@P{$PC>+ER0Im`0f zW{)nWynG<$s&YW?=KK98Y_ok=CY} zp@Hi0_^{d)>s72#u`b10+($mu!)j5iKe6Wa5HF=T&iT1gNxXPiEsFIg)?69&yODgk z&dW}M7$&2^3P#QdMxKAwk zavy%T>PhE!?|&L;=#E}PI^Q#?E><;3}!_Y(Q@{~L>m2NUN${H$Fd4V{xO66gP0 zW|EJ;G`RLWXMQG!k4TTdh+XCDjI}mH$v!M@qHJe&|VUKh}5m zg|hNicmHi!JYa-5(`s{=bomZb#y}t!y)tYLi#}Ipa(_5%4!t6aUzQ7HE4x<};|IJ| zJj!E($>B8Htl5&stniuP(cw`8s?DCuL?;hZ8>O*Uo6c$1Z?x(RHjCYyV{+*14x7Q` zbn;+@(J1+4Fg3@VuFEi6jXI|-&tWhPyfbG@ArTH!y512Lp-ay*=NKbcQ)2{+9h4)S z4udOl(tjUEnyrSMJmc@aL4O!MFfs0fnPf{-U9oz*-8t0pAk#5XXEZwvHZ~dICPz-z zqV!B&PnBEvE%cxKnoXBK_G#C6xuxwsP^OqRO{38)dE29<{G0lc+bIdE6N%FKj)DhW z-p-W%tthg8(#m)Hb|*BS+<5W)x0mz{`;S9D0fKMEiA(awo4jD|5V76Kp;#z7D~2f;7_Cc#9Q2~%JW#6Tjv z0)I)c0@i>L)$p&c&6RrmtFgm2+{xC8xg7aqbRcmltn5>=>1Z}h_e48}kV#mN|k zkr;*3aR$ysEym(9jK>5_#3W3{Rk#}0Vt*Ry(S(_pjT^84i?IZ^VkvIJGOWbiScCiU z0M_C`JcLKE9uH$3HsDcg#AA3Io3I62@eH=(=Xe=E#aq~g|H9kYjX&cr_`NL2YS~@( zkU<_HKO_6fe)4E}j2t9CCr`k)FddC(z~>Qh90s8~dZGtBhHm%~uEE!E9sUW)urwTA zgt;&uv=9pmU=b{Y#V{4ZAp)X612g_)!Tk?VO9KQH000080IZ=RSj35W0!$VF0Em|! z^aLe;gjorD6ji$JN_FUyPEM$#I)p``q=OYQf7mcbRF(CG(y`+7F@RJM1ALiHPeL!r?AX4e@>yQy@kl*$!Gi+aJ?7tZD~ zm1s?qsx(v!S!Xm?aI#^ySgh2Krt@W|(Z^LQxqP{yQcR8H3TbzLU2!~umwc{VVWYXc zQ%FxZ;oZetLDHdTB3+v0WKx;JUb0J9iW5!f$>%cZO0HNa>)D*k9B`!|wB-t!d^PKT zWK%(ta6VTU4-;`)u|%45{-pj$v05RtzPDUxtd_Frij#t2d6whm(}l5!lXoVZLPal) zkA<^NsoYRz^F5fD3Kxor>IIve^^4LT;iu zQG0Kb9#hcz8pkaI85_x`GvhP_nEJ}HywErOUtK<=+_Ec;nDbZ)ZY4LG&S3OTNNgls zb|__4I!KMGL?o+hD$YbHj~v`tC}`GkqbP#wj8*e#cQ&PkB;d@=Wu0<=xLO(QZuVDv zN-g8&bT&(}B5w8}wUL-LmM@N^^Yx>}d^X>RC0+IFfraoom< zVz!DXsg$#6bq3z&cAe~E`8i#q)k0UI%c$i>i-1Zsrz<#pm#>g^E4p;_&;M>$KGU%? zfoGy?HmovoAT#-Nwd^c^?E3Bkg~u85b;Ya`3bp+cd#Vxg{~ba5JKCUT2&1lbaY(b8 zjgYV+p^&YIMFLNQonpMGx3dMkgYw@<9sTPMg>_B7yDnO*VQL9sgTc`3X3G!*5yPQ{+0pTRlnGORD~SW3OlSw1O9?wZ zWSdzrDOuMm)}&;$^$vST7l$H-1&1U~Nij^~G{hr#9wk-3VM4GHGjXhk*KD>HN3uhU zg@^Q$;wYKgq&S8+Z0%6<0oz_VWXH3%SrNzoMJQQaFhj*ca*GOvR4 zsmOSSR6N;}<2hh|YEy#65YT`&-8?y9uN$&gX6<;z*6*-QP`7LFm9oAdIWMT37u+Ug*&FC~3ErO9TK*g~76MCN>llHNLMnW(vVwGry zv(qBRfleNO)1yn!dC;%~TO2Va?XXG2@rZQ@RvtHFC=GGz@s^m~K*i~ZB&ODd^<%V- zm}&Rh&@Excw5_oiXfM_^)+X5Ll)0s`IUaBG%WKK<9*6ZA#2z*&H9hW=F0GNGZmeCf zGXd`ee6Gja3C^uUSg5d%$*cy+WhEAfVkYgw4&m#Jm zehew=53;9`EW1?rgHUjp`QOeFei97S|VzCbqyLcYfsd6d`%b{MrZ1Y3>) z`kQgcFZ6&u6H=H$lBt{`SOR{|hM!ZLLhyqjS`I%eB8g+G`9j^}i*zfx#7G!4^VIAN z0l2V#6FsgSJ;#4t1jwbREQaYE@XawacACd6Dh&Z)u~@K`)B{g(k1w97xm;&ST4z-9 zxJ@%8$r4kb=n(8&U{*q0;_(ij?KrWWF4%e01GY0fzLa_>cFa6k&sCcfQyN?95B{pn z#MQ0D6t2>hh`9NUF01J0E~^0a)(w zgiIx5QDjGS)b4_ab=>1Cbn7ssF3Xef;TK5w3J5d{&k<}b(O~_p^!T}cMX)!APcU5w zrqw)&Q8=&0MuV2@#CvoZ)`fgM*C)SFlAi~G24a<97lC{g$h$qhsz&Zl@gs5bh#nVz z%#I{jt?aUHwqN(vg7pBr8sZv{ul8$YnVtLsbFj7#9oOyt1DGvq5N9uVT6)6mn9w?dIp}ht< zFqhDl9ae(fKWUO=m5gD77ENFOAzkka>vB(n1vBLibt$CS`8w}ww_Uz2x`g*?thY8O z>uTw(x8vZwOs3Zd5Dm@-!8THCQ1yF!gP-QbC(`TzV3SPq;y{|4XVN5t&3>AHmk726 zR5rv*J$}hdnl{qh6iAaO25M;z$TX)+k^*TOGieT-NOP-7b2Fv65oungvCC>{Zmgxb z32EZ78TORn7QqH7EMz#~@hyQhwH5BS$>g>QHUyg|{4IbZNTG3^_rlGHfVhtJ=e8{9V zX-E69maZ%rhDCLZ+x^p~9laQNUWIkhuA<)IN4Wx;k+56~5lmjmc`fF76Vs$hB8v;)1@<5eF$ zMd*EkO;8We*Li$j0R08f1&Q7-SP?dxfWF@2`+f9LLf;@*iF$y4zR}}11km3Cy+@*N z63m6oLZEN<_)R`~n$WihR;C`H4|x2R0QwuCD-wOHU{%;`2KqLS-|C~8{1D$R*k0-a z{0@)b9{hB+0KZS-?-cAh7;ORmE|1@-@GXSDTd@7q1^hve-yOu$R(id}-y_%!Flq(< zUXS0S@U4WuPp}(*sSEh~J$|3+GO=AffbGgs$0)hw-BjDJZl?X}#3?T(HS$2lTID)< zAh1u~GP6%M$ajCEd{8i4fTD9DKIHKSYi=o`Nx^QVE=2UO$0y~t@}xL!tHp8qi4*

zwWFncaNvcm4d;ef{h=Z>`f+7fL)f^%kT6*eSGXxI&2N z!33VTnI&ymaiYl;zoC61d6Wc=>XTlcWXy$Qq*#Vqu%dRT{iPzG zubt!#xzcc+4Kx#-EDZr46Ru7ubXgS%A&9V0i)XN*O;8;Cd;2!V92pMTM&ve=ENA$}vSz%K z;pd+X{b9{Coh0bcRBtyCOeVznvsNd|ZZC}iZpw)_E^(I-5phQiB#subd9~sExJ$Nj zR(7K9PqU(}@-9~GvF6gBO`5gg&8ROYHZ?Oahf;GYWQteaTdJs&7%dw+l>NEAS{pgi zvL#NNzKJ)w>$vy>Je!6F6V2YKo##LL>b5asy!s{24*OtMF3)T;hY>s3#Xm!sTfM(v z6d4MZ@Xq-4rzG$GK*A-LbH$t!MgGxz$vt-sy)s!#dlfrYaHTLB+@9>UF<`=7Q`eYZ z&s@{dS;(PYGE^5XUqxMxP|!?`Y8n#h?>PLEBiRsBr2ho(2aP#3*z6E`sb6C*Q&toe z&xk5N*u%k+G&`ZzorH3&Cjp>ZB9D{H9pqRm!vkus& zRQZF5A)!UA05FA`QLq>T>9_<+orq(nf(v499`T!WTTB?!ai78q>U`P$0cZs2v%ucO+9vPU;MC zzcs*00NSVBq{KPp_@r|k;aYUrUi2l;gb;-<)(o{G&mCHnm2j|dS-4%9f3U<5d3mac zm_b5`CYF2Ljv>xt{L}}%n6UEmD08b69@Z(6|>MP{kS}BY(4H55)Umr2erY zCi!dgEcZjwz+)EB(BkY-ixok?P)5bmaF|hA16HJ|IY?HX1bmR#iGjMUxwa^eW+`@| zVrUUoY3^g{A-21R%22`M!m}(t+;LYjDAgMDK_g{qQl02ylU&I3Yrxu>MRB{hX*!Fn z?iOjE{iQt(&0n>X5}C>~KRRiPLF5-_F#cN>^5n}+A%8@a2V{-GSCC5^Piue%sI(A~ z0DQ)e#8z2wnmhsUya6C--SXq>Ic}?hLq$%O3hz|%?|Jp1=)E%%^XU3Fr+8>RiEuY& zk_6}f9*DO=DdyKh5W@9)RP*w1d%!AVEO@zL-E-k_^o`9quehn(J@IHo#_16?K%VJ^ zEo1jvc-@ffRUDmB=@P7BVzb~mt$~nT0P-SN8P?*FX!8Ds>gDh5} zXK&bIKe!|9kRaz`c)ehk-8~NSGmPpV&_DIk zQz_t4IdVHkV&7Hg(dt@s#_3WOV9n9Z2sVX6=kkR2z&h#vmoUjE>b9YC-@~yZ-H;G4 zjT7;y=%MQ6%oq|p{W*l2Q%r)q0#qi;98{X(69b?lM!vbA!gtUu%=CR^T|ZooKE`{0=qD>yh-S6xQ z)BGi=0T(g_<-pnT6b@PGGVVE?t94}FdBhT}A#Xe4(+FDQH85dtcbs+yPT0)OTV}7< zCNdmb5t_UdPg+P9c1({|kp8Q?a>GrOuM_n&&lCxFp7W~1vMPZ}&;Xavg}JQA^Ypk8 zUAG#6*OunYaETA~>4D}#x5=DlEZXs!oAIG@N4+YN3>1%a3c20s8XD`|w=kz=FMBp& zpw>a;8eDsaQK@K7t_1zdWOJ;H8m{YW3r~?Vz~EOIiYKxOh}N_$3^VQrG}vB< z7e=AhzyxFOqvqyFB96xWy{wyuw5ueVq=YGf2iq8g#k&*xkH_zo5rzWod$sV+9~|)@ zxm>axFuK*x3V}@B={;)QcAi@8@5Zg2#8; zMI$v7wlGNju2i70m#DZ!KHKP@RWXhKIC|K=wQ)ocy8L};Ee9~Eox>Pe>leNW;kRco zKcjn60?S&tttX*8>Z%Ewq*;eggHPXf0crIUQKEGf0+2|s%J{gx$sqT3UL5rPX#BT+vl`tw?a6-3H!kfU_5Ro9PPQZ`>G{mJiAW~t1#u26X z_e$U>@;*rZR(Hp@9h$$fT@b}R zz=sj)dfU=NF@fLY!uc}8UG(g(^pGWNrzHM(=)>=c3fbWx=B6Kw>HILcGXwCGpFWHXMFw)?#%1#h zAve> z&x*T07yoHp&iZ!0`no^1Hjl0iEj{_xjv@oP&ZbroZSHq3-t&D8-`e*lO)vOHz(P`& zs>^r4dv{r_!`+|S|MI&#NV*%(1ua1H%Y^9w+4K_w1JLF>Cx-3tsj|G0+j;mRm+l(RnunM?Z?eS*puBa-`VfGob`4*MP9{C z{+8ppsp0zYZ~AxYPk&R-v&H^uV40!g<~ptUs{QLlPv^Y2slHfCs_WzD)2#KVR`*CY zjpF3@PW=!Y(Q391i_@ni*}kOUC*|$&{I_$?4?b!3q7r6m9tnh?Ca5y ze^8$v4}Y#$bI;#LlqmG;=f?4RGo}y5>!ZT3V)t`K6_x!r<)`>vE7$ksIj;MGq4$U$ z^Z935x3ACJW^*>Lk+a`5@Ve~4m?rr9=m}Ad&?CiloApzQ$&GP!*6I7UNAbI0)oB#OpkWWPp9U(dCwIv|Im8;%?~s>jv4V`=&WwgD}!nhj+=k0Z9dH#_nnMsocvt zONIzc+jLIDKgl9v*K@&Q&Ko2-&7;{9ioU2TuGEr2cxK>sHBo^OsBp-Z89pvuyKv{1 zqE+C{W^$Rl_qVQ>f3x*Zqa+ZblYc`1!FDpQlIOa0zUSZHZYk1dgrCObb0klWPUaS| zQ&IB}DdPUwKi!63sHW6d>G|=ooEUk*WY>rABo5GKX^{5o6e?K`3^@3jMI~f#_Dbe* zEJCj*{@%nms@p>b3d4Wfu$fd=b``aCt(B|I<9-Y(4BA3E{i9EcDM>p)`BE2%lOa#C zB~Mfs#?Tm?FymLeIZ8SF^JS-~fNWtJ(Ur0yFaLZ(gw&ZWeQivN`|NME0p0<%nr~t1 zIhD3OohCh)5Oh24RqVsmoSiD#pEx;i!SBGHs8Wsd6!m!p7*(1j#uXfGeL)2KZX@xx z3K9A_z6*Wzx7;TLU9AE;_H{*)2?aBZ=cAq9K4!;!S(gj3Dd@+BYfp#k6<#i*k5cbh zIUe7Sj9&Ni&FowEcZkTczSoj^iuyi#BL9jX2hQE8b7F~J%yzPm-38yi+7qu~i)exiN?_vm=SWrA*kDHZtXH~JCb z8RVJhnc*4xN^s9}59t#A8u6O`DoHM(OOI0dPk|nd>d)an;wcQv@Op`6Z3fMV$Dcth z!hZ*`Y87m;wIa#~YFZR8(8%`uxyg$LGX}Euvi7#ay$9b1!Un|l@_!zGx66P3Uugsd z1O}uck5v2j_hAv|e`fVa@(3ge5Uqu5i@uE8eebg8Jp^sJ9Um9m6XF8aOCpHo@L~7< z^9MqOu(XKj?;Y+Z9`2WITdI)SDis=b25Cfuq%8m5F;di8kuh;-z2!d5GW9a`qNb?m z=sY(oJ5+`<&bG4O>(5gu#>Qn94~JXtUE9x{*PmPer3|pDZF44>KXia!!1!JAM|vxQ zDJo8=b&X}?lp`mGj3tK@Kjw5lbs9qPzhuWLG==qly7U`~Be8V3&aaM!{iTkogf0=(_ek+=()_u+fT@r}BbYHo_U>?P#Y{j-(J}9=w=i<_&-1BM0}LQqMz-OZ90g zpi&d$xijVmR3cgEK!%3ZI(NnSZ6uvH1qxBLShL3m)eI!c(1>Lsfh5<^d{e$wF%=ok zAJf(`VE80Kh?eyD%c`?|h6pb$X1r^OGorWZ^a25_s_fO-uE;K1MLOYsyo1iN1mv+We0EAkj4<(6((kKY2ulIckouM7Y|stVC+wRc?oGaH`5~u9txOJ7jsS%f8rVvOX1@& zyE~H|1bW5tDTO-N{vJ#MW=k4rQAJZqqg)KIJpB*_pv7xg)g;kjYM*Y_2W2%Dr-V*K zhxjh(aNz1-{DRSsbX=hT0UE^#`VH2$WRB7W4AF!`66U$No;g3ybH^#RKZ>W+3^#q@!A1D!=LA zf+g0xI(3n!Pa`{#IA?@nXxpP))Lsfi{1!tM6zv_TAKbVG{*$$&!FyD#DEvAVT0xKQ zZ&WU09s4m!A|tVlfAf_tjXm{jR~f_0y|)XPKn%1;C0mSSXnxw5v)XBU4?j$wb6sRl zmra{5;a2p8BC?PI0ul_Z+DfCDc5#40!!jBqLH0P^2_*T{KBki(Oh*{RU_V{NHq#g! zP7+zfUcO5_stA*;Dfy33usab??2;02+TegbT35|C$2^sPyD=_swaasrqI>??Q>B^k zK!Ph3M$O{e3oUs`U4VOdZfQ`ZL!fC|l?&U>CNnsk?GT9{Z{SLe9(!yxwBQ-4`X6IW zdi5~KGVx$KUIh4s>bTvmg;<02`rHib6N|G1mQK(9z2U{3TnEv_dxHjL{l> zGAEKO9TLafN34dOyt#BP1Xilr4O@?Kz`qcbUcu$n;s7NWE4U)WJ=StW_XDf$9~kPS zpdTk1st*R?sHN6??7*BER5m?aFU@g`m&!&C~5F=1@ zeX=swmUh=u!Ev8$y{6$J4@48Q$$v)uTVWTP{h@}$?PAN76UzT%S}mKz@+ol=IsqKn zcb|hab305kny2z5bAEHT zyP*1FyUx~XV~5XZYt8F+NBC+FNRPE=NWWi}T85t$a&x8M@V?bOy=qK(qJoZfknZSo zx`kf1Womqgp76Mq{wAs_7D(bqaK7w6t;)*V1-*Uva~$*^EFsoMe_Ifv^vis(ZMqqb zZ@St`)OEwp^&9P5Z{28>_gHo@)UI=x30CPkTK~$*2y&3KnW z(iLpn;%;hp{_1w?)B(zhxmX={l;(Jk!<)4}FDLePp0eejQ=aX7%1<9I1y;?w2@;mS z1-m}e;zPogg;rFZEJm+mK2IXIV*i~Ega?g@3?H8LelyRd2tiR2DN1Q(WU6lA$#-Q4 zpH0>lQ{+tdxh(z%qPA-Q#Bmog=koz05(pJJ@IAfqDP3v(5a z?51p3z@?MiIZREO+VK%0lZ$>DXPXU3Vwj(Ygktb^C`8#Q?l0pXb8zCXiLR&Q*CbG@ z#mkB{0w}YriK; z!RLd4>mQd1_UuN0=TzHiVKL#x`_MB|N$qoAWEj)O1IT>s@0U%3H6coJ2lD5xru~t| z_}-L!k@GJts1#lM&KvbhE_c5=hr!$3VM8JJ*XWyB0kYz==>bBoS+%FLVlJ`iuCBNK zX}`xomg_M+0lFBG&jGa=Xg2RuzHVsA>vs76zVgF+-v$}AK)yTCA?=xa@O)wUVF38m z!}yNSNG6C50|wk-_bt%Rdy_A)AKY6JIlb*S$Tx%?$loYJvCsP=J{ZXR*tgTSOkYZ8 zI7i+8>pw{!$5eIwcOhAm0?a}(0y-|}GU>FIxP@>O2F{qN8GRIrCMbVFR*$65dp2Oq z?5$*y$LfTWgORETEjl)9_E%d^OuQ^?ypWXG@L4AMp=z)3d&j%((Rq3Qkv-J{eST>= zb@c76ZtJLr6CYP}-4hG7F4#|A{O#YUaxvMsc7{=2{GH$Xw;p(O=TG8QKsL=^=R;`Z zOD~H$-JB^7AvCcg!zx1>jfm(i=h9i*2Sv^Ckh*63?Z$INZ&ZaLKAkC!47b8&W2J@& zL6T;D2GW-svvU2a!*sQlN-GJj=4RDN`Q)*tYCFZwZ3-)Oy~mEp_%vDjwISOfD)d&U z9FKr>@UummLMia;8ZE zR(?HNvdYRbeNjGN2O4H-(I(BOO8+WE<~4vdU_}=yW8~1m()sac(QB>+n;SEOP@^*;piw68CU>ZeJwsBi zo`L)BSE=i;7NT%JBRM(6gxOVvza65-5ql1Xe$%2eeV7rKIXa4YU+z_<+OYU)p3l*{ z@`eRsjC6sRFx{trX1)r*8JjV?*+ zcM)z0LnpgIxt53y5QOendw{{JtwB;ddy#<&_26RUK2gGgUgm@M_5`b zpk5855>SPSvS|P+&Y92nSNkx&EvtG7zN+5OeXvwWx5YRK=x5t>!zIPF(y-Smd!|i; z^`>=ihFF)k69XBCU*Cpe&IcVgUxnmimMx#N>47Ub45z)YgGr-KvMK!VU;Nqnmx&|g zUL)kP<&O2psc;1y?(u$8xzh3_`RszDcO41(s9dsig)fbc%NExybCOT0NYm~HhJERt zg1Y`*`6coRNI`yZ+Q%QyNK@?(Vf>{*m~4Y}x(5;HN%p{q$v6W)ORwQKQgOFD|xRjTS#hG3)y7awXczL)>2}9y~+gxl!D54=++2iH-1BY8b1E%#=iN4 z_9>@WRPla;LKwX%hgv-N7dA#oaBo##Hq6ptGCkwR-a&-RQb0--rLjmBLa@)BP$K62 zM%kxoLtTnU)#xa%R+(48k=YQHYB6A%Ohr(yPB$kTrkl+Empx3cXjiEaj5Qj%6e~Qf zOfabiI87~^WdY}*S$#<+O@mS)J~1;-s!*@DL3_+C2v$~=#AA*LiB|*XEk4pfy@5vMPMRypPDR9jP&=6L=r?)G!AqL0DC1S? zggU%CXzCEuNVJ(aE3h7rS0bF?5HmWM|58r|(peSG7k(PbQ)JI@J0GQXvX}hFLkWDq zKj`?|FCI-2JGA!%W<5n`@l@WlDjI#IE8s$xr%G1(XeCn+kfjr?^Rm{+90-Pbd~Of! zI`ESHmSX|#EDW7w5nP16fLDAAwwSIK`<+D^T@-(xwJa^d{iDKbkzS$=h6%}I5ic?U zi21Q`tYN9W2+~yN3|q1YK?TgQ+7F5~12!ioWktF?Vjit2mwA z{hVcSh=!Ja;-34UmH6%l4l;&CzYdlp(6JP43I>UWWY|m|r=#F?y7MUDJBFv>CE+dn z99lm_ciI@l#hOdvd%2^4aF-Q?=wE>>r;Qj7@@Dbp!MdM37xzr91V9cH$fcMhWGhXh zPx_Z^NTSdODzXM$z=T`6C}CqwaJL-Q!6eT95UmRj4l8RTo!ezWjrf~k_LiU@5Psqe z^2Aq%t^PsEix)cWXlfHxGFkDt2c^k zj?YtROI7@r;Gm!`F;iLeprtM{iYDkFwJw7^Nq{^rSVn(U?&p{wYngU1EvmEnsuB_n z^vECQI0QNsG>y;^5%bg^%97IJ0FSsEUD#-@bMS>~OhOhM#rj{BI*ssn2H8xABnXxy za$Mz@_{lLGmcRRE`Obm1%8Ch(V>ES&JPJ#+mLqtF^g5N9WS_DT6$v+G)U%VvMNmOc z`tzjB%IG#oxTdIn9l zKE7P^`%uVtUTYG{U&146r(<=-4U(1IQ6xAD?patY@b~D{RcZ_)y8K9@2X|-k$2Ie> z0mBFB^bs>vqERX>I2dJaow1R65SKgaf$ij>dXV$>LlCb79Of7lfcK0GL_rGVsd=>X zeN(`r-hTvgO5=S(e=-nz=p9X+PY2jADKqC%K$1s^{t#@nxM0-KJX0HOiw|hqA+XQ7 zIQMsJBQ6JrYt0?`sr2N&-X*uv)eVk|5eH6$5x5z6y0bTDOp&JwN9D_R>kVQ4+j>u; zZ}nv((8NlF;6NfBII{WpJtEdLe=~?$PEE;9AB^Oh(4{3Dn)1-^3&wI+glGc$kw?ul zclfnyU-yTYZzJFWJC1C^T$|7sdSTZ6;xH&MtZOv2q$k!-nK2YX?Sh~!3{vMK$SfA7 zXyCaK#9gUYkvQ3UN3PtZw7@)~lIVE7JF7uVRP!z@&Q*H^;C%{R`Wwpf$=_+ULt0O> z@@D5Gzko_&a}maTUJ~c}p+KM(W$iu@c?qUb#ZcyKf{Qt)_+eDoXeZ}loiDmJ`kX{L z1eJ28G&vJ0f6wc^s*PGk;bF7|<@+jiM#qunAK8hZ#ZS;h?1NhAYx1YI>uUo8tnGlF<ap{=5@eL#ZWJhN^Q_hz|4v_QHC=BQc_gapT(Trcf}@um zw~Drx6#1sI(WYX)zu}k(3T|NtnDZv9zBP zgs=d;^qLJ)!{Ug*C4t|TMxqJS=stTwYN3h89p4KvSy9_7>4+Nfd41XL4mEbXL5pi@ zD!hCY$!*uFfeqkS1W)aW)%oys>TD=Ek;dClhUK+qR7_ z+Ss;jJ2|QU)VcW2#dOtNPt|ny+iyRQAyA!5O+az@^LM`*7F0NTy%H3xMs2bG2S+3t z^iXdN&W|cFNfgGd{6bSjyuViYnpViOEQ42m@3P6)erV?|#L(9vK*O-{`(5NJ*-B!cAp|tLv>lNh%p{(;Ly6A~0+rS|| zuJ>$3AM68Y349!MI-)K;qIyZ}%V;jwE-M61$#O4oz5~iYW;!C%_@HYy1|g!Xy&V>k z)H7e@^2R-=xq*Y{@%G&KxIB(G)Hn@Df6RY3X`j}9@5an8H@deHeK7*BLj)k36#T&! zVln{OTxIGcT)ezO$*?0)=hyCb13)kbm6jtmf^s<-Dwch(q!-2WxIvmD`OGYEjfY7j z&!SgtrE;a2v?;_n(?sP~bzYq`^HOCfr2g_u>zHAuv0_)(bW4`yk#{Fat_lhIq6VE4 zs4m;r{YAMTQ;ynXVsPsOGsVRL8A>Ve9!V@kx4VVojfd2-ZTrLhX_JJHJZLGdTh$24|2Su#kdE5; zv95zEMU}0-|D`kv23i&K0lY^7A98tYKf1>M>gv)ok_3H^_ZC6k(c7UxCr3iQ>yFn+ z;$4(D(_ra(vNIekv(ynUzQSIW{rf4ud=IxPkOD1!4A~Xzr*%-~Sae@hCOrv=*HHvz z#cxSI!$RvBH6qn)iH=-(bms5F+9(^z`J}~E{F7m_O>gYAB?>qKo&pH6@xl`54Xn-y zfcpy~AodzRe?T&(&xKb6XP@~U%EaeqwI5B+Ql8#5Q2==j&A31p0pjSu4YtNp{V;`H zXWDJdMk4zW<aAcY?ik; zto?eSR%>gNi3fK(B0uTB`9fb`Nhuy2%0AdbQ7?@{VVcj7;=}+QRH(*KMCZ6Sc|2f1Tk&pScaLk2@Y5L*bwJ7dbH~t^fpiTkTKoAQF6Er2r2MrX(g|Y7&FG zNKHVT*ezJpo9x(ou?ce5Z!v@wt?e;lpKAsyqzeFUFW?xQVSj6K0(%B*3u7jL2YjT# z_l)uy@iQ6OoN%l^;0>gd@MBq`3w0(yjj{fdd_bX?pa*@#Alfhr|)c z2nO;uxxdZj@=eKv#$=KFp9pUIAb&)Qixw^~hA?Urv_c}>pkjsDZ>lHrTP}`tum3F^M&CG6dHvSG20NQ%A^{?u5M!0(i=PYLyKhhm`3^ zugW%YVBQ}q(iYs!d)gG0-(D_T@#|OSE?HD?2^#Aau1+YM9}YD3A|yeXMFTZz^;evf znbmgxW)zjDyVO=31gy6SN3*;w>y`ns6IH@jU$1UZ_gf{bofu^p2$mhu>!79+>qdw3)C|aF@DcTzRnURDbG8ice($g8ci~xC<_l_5 z#9r34`r&_#S+xZ9%n9>&f5I^~g82uk1STp|XJQb_3dpXg>v>}tThSCRI+X$ES#OQD zdSQ>1kNz_+M0h@3w3SLpCFMk_M$agKh>R!JMt4w9=jj!_dN1!v#9X4Xg9Y6%y+eMP zUr<#0mkLsR$BiwK%KE_gB1S8;&64{A`>EE1qE*afy@*4>u1y%&LS6dl=Bz4wWcl=J zU`%EH?mMc;xRcUpR`)0m+75tildZx}=QXQv>3`DtQh3`>-Y=l*@#Re5{^pqg|`YDehCS{v0JicGO zMX`DG1a>W~#MyNOfp59U41Il@$LP;?8&XbSTaZqwUM2bY-UZ?$&nF<^Zaknb)$wy- zz!SF@A2;VsorJ7KP%UYHn)1S#Wb7bq-RTXDk_}Vr={lH|q*+WFe@|8#^Fzq~BI1=i z|6<{m3PJ$VJZbFHu-->?Qh#^uKeLTs$Xt`kGeP&c;0Dw?Ub60wxQa=7T5)l-rv$v2 zELbR9+`T2L`Y}vV)?`4ge`EIN;_03RZtClQR%zt3xZ#=*8uc){eQLhZgr z)&!=!|CW0|x!JiBI<|jHht9}GHC*)H#IPNT+RLO5Pr#aax_S+ibSbKBHM-F47_T4U zo=|n3y{1)J@C5@&yebVZ3c0Hp2^^MEIER=>P=!}-@Fe%p0lxvCLpbp|t}RR45B=^B8*PbyG00bkM%%mnjGsjzn6@<0 zvAZ6{B7}}HMh>xN{~}7p@pX%fKJIsUo${|<^KCQK&P3sP$g&s9P^K`;o(tBY*7~%y z>kbCX;OPfVT@iqXMbksCeDO!}q>dU%%@kLonpp39tPt7T!22aJ)DheXqLd!L;9nlW#~ zo8#u;-nY&rI?}F%Hjrzv-yJJ_ntVii1|^l*z|sg~&;yXi^GZ)&!ylAq4n0!>3a{As zR!WqoEW9Ln6{d^n^$69o@$FCF>lQ8%>I#RZ^rUSVvYdt6tzvHb(j0^j*~+Jvrr(X6 z!coSISlRPC(Zlu%RsWjpOtsf&Yb#c}d|AD}1oonG_7&G{m$&$fzuXzVwKIX=JiFbp zs(D}KCIi~iuhepG*t42Hm7+(nlhtIuK!@)YZ>3%r9G+K8+k9ssWeX%4cERLvcoH=WRxYKX#EAU%KL$F(6agi_e<3mWl6j1sWrysV{i7*HUXn*U<~xGVl00fE@jEiV}GIJaCMD(0r|p~ zp|ow1^t|WiD+X`NMKM21VV|J%$U+~VBW2s?EThA_X)PYhy;%kI%dxabe-(Sc)9Dpu zubM$==EO2~d(xUmZI%RQ9zPwQt_ixN3l{U)@gqADr}re{{V}%~qJ2E;>_Mu#UCFUx zWx7B6E*X+0C85<336sDOH@RIP0ytA!k(NBtb`Rt!ZqhPW%Dh(~6Gg+bQ7+FZ_2%IZNv69{og3qYhGLuY7CZ_?Qs zEU{2sn~$kgsRV-ry#e7Tix4KpKb$`14+K*RnB0YU3yQVCL5SE3{E50AsU$UvX0?*2=;%5P@smd>EAFe!2I;xS>qgOdH<&&k+OjQ2$9nYUr4I)PwG_M|8gCM ztq~%G_8cJ%P?LUcAJ>lkxF($gPgQ10K^x7R_FV)E04cJ;Nv}Z^H^W|GXYmu;vH4gl<*MZ@zT`2TWt zu2nwTP10Eqh>>cqA7GJqwCWJ9-`4uu#M^D9Hx}ZN9w*yVgJ%~PXJ?5>!xxI5Fi5Yn z&Zku#1*FBF`(Pz)BhPyk`O}`b=#~BkRu5sBIg1@y&gi`%Rp6`bgKaJ@Ov%si)xi1U zXFAaD+^UPu&{9c0djm>wP1D|j>taiVh#Ia*3n8nT!CXF4bKIx7p6!wo$qR0#X2axb znPeEhQ$|}>rb1Se`g>A9$lllU2mAoNo3D5ASf~R5NF+q60IFQ7R%B)H1wmQ`Y!bgU zL7(X#EKsrnO4D#G&~Es;I|8;HiO~VxWEdjln*C_6IJkXTFx7%L! zKllB#V-aQr_!$t#{o!(Pk&x^CaR|(YA?t%(g)U>E8}V=dQpEBP_yrK65BRqtVRL~E z3OFdxpd#Y_{gsDGfimh(Cy>yOoF zR{jWejP+~mx?8n>EHg+YQ;3Fzf1jQV(SRA`;N8V2z-%oripZ$4*5rDH)Jb9JF z&86l;GU<@L<%E-xNMpolbFw0|DWxVSfz_Z)i3-2bSpUs_r=Y?v(F%v`u!QCOzJi#B z@@wq4$uiFzR;9r(L$!%Cg>E&tNB`_(d_|@%_C~cT9l5q5S!41nag_%9p!}jpAOA0= zYlMMDF{%1LqhJ$kyt_ntD*!dB+<$hZ646j)FA`t7>r$x66-{b-V?iq~U9r8q12to` z*fv9yVwmRbXR$5~&D*Jm^-}WLM$#N<|6)aPcD1r~JauBj{9MY^DNA5f_fwcQQwXq1>SoZMxBPJ zn4Da(B8u@=y{+Q*YT%Ob*A@Fbu8tc*8puO{w`f6TP4*97kTMiLV^jEOs})GI4Eu+k z3jN>evE&FhjZY}L7D}PP0ntPPP#$-g=!v*0ez_Q&=>mzv#O9<(g$1Pkr46O%v#O7J z8M`VM1Q;^EJH?bJ{mtb@r3*Dmh+;|OiE46VZ}IYbo{+A=?STtYq=zvMi8X^~@ z1<@C9)c;ESj(7+sA^nw!^hB5Rm*tGRcWH3N-Z_u@2nY1+>AY4y>ihI;RnXw!$H%no zb;)!!Z_9Htr91siZ3#{A9Bz4tc1E;z_UT_1hG`pjtIOdDj@omzr(?(ULIm+V6SFeZ z38{cNjlU+g+(3(gzg*vzY-L^=YhhzK$#xMn;VsN7;F_)*xdzEIK6{LY`+ zjovt=R^nDe4A#(nNIMP$M9BMF`5wj>1cC_37)fJ1wr=)FAR9kpOrYIps4936xLQ#R zd6CY{5A?&3zGE<TTD$ticVi zHkh7T?vKlDHXFkeOQiB+>MpHUgk&$ivq151F2O2aO(PbIZ`bYv(A%`MM()MJW4Q6(A838!sn(1S&E@3!T}^wcEN|0wp?ry-02?|lx0_=#kFaP!OsrbwP@Z`=3YI=VNas`57w z7s2vYKqwuoyzKRnqZLv4JQyWD(e=)KwD-e$d~d^^;Fvq8|6V7d*g!S^#3tdFfh+=f z5eE=cLL93B%26pu4%cd@lRD(!j=V8U5X_es3sKt}5n`5!n#czGYwBN7A@vQ)EFR?3 z@D8DcJb_Ho-#x;PoODu8w)Gm>hJRlA+@>CxnTe*yFe_)J|D{6IJ+{4Fx@`T$y^m}a z2Ywv7&B({%C?4O4(~hD+@@h9l&mkTNm4N~Be2}*Em|%DHcIL3Dh~wZ;dJqVKH{(z2 zFID3u?qwt7<0Y9Fvc9nniamiZI{I1}i>IcaY_%8TSKw!MyNJv=|De-%1L5R9aL2b` z*v1*)dyr$;ZidnsEKyAJ%MK|Y3vVW{J@cj@mSs?;@J_Gzg%;FCo8TmHv4%ZR4vCLd#*$(TTS^8sGIW)ZEqr_J_ z#k>;-%epsU28VgayFn*28wFuAFzlA9>(bPJn~dA&Jguq=(%N9_rj~eJU5qT|TAifVikM4ovpU*FqH~RlwX-fTC znU(y$Qcv++Yr;y2`w58(7+W~f8JZZ_M5)8tqaOKeb@85bUpZ5&x_SD!<8s}o2TjAk zkl+QN5g-^K?Gk1O&z;zkW;xSe5~AWY!==V^*z`&gVlxMx1|iHh_EzU5utob9M`v-c zxtoMxFLl;t zx{G+%=BR#%g7LwmFTM$#=#!+A`Q$}ThW?638VRm%=S>?mO|+-WT0~jVr^-5y`FAe^ zLyp<*PLO;KJBWJr?ZD{^B-^sEFu0xd%O-`(`7!A7rofc0@DmjRtK~c*GMD;Flq(&R z0GT(5F-wL#K|_oK5c0taPB5V3$s9MCuo?Wt#ClLaqh8E{BSnHT!@wi`w?5D$!Q4Bo zx7e`F-@Gr8E?ks(MaDrp=IO-3i!EzTl4ft#}7BT_ckT}Ny?vV zBt?ToH{KDb0f!&Gtnn+c5>I|mVJMe0!9kV*#Dp>EK?Vv2;V?q4vcxEFz;C`ee*zZ~ z{)_?E-C2bFj-514W=Od17@5~_6T?KY&b*l@9?lm_E7B9u7<$l?67P3W98RnOX7j>A zoD7#WS*0>QFi|Yr`)>1xEZ2Hl_A6!Fh7T)r7!vo-{b!k!Qt{F}Wyu4LWv~`{NJZ{_ znqtMzO4xh_>fX={|A3DZV;)Q?f~*zeb_w~C1Pf?8fN>Qgyin*IMCGC3-Xt*1m=1Tb zij8$jgyi?66wC+1$hXM)Y5`&~k>vQ*m?mW*;?;3Rz3|kYBFh~KS*8#ADdk19c#qV6 zoko}?jR6}{hpdUUk&_AC#w@^_WFQ{=m#U~{h~zvAx-4A5d9vyX4NpY$%N5l?;y^+i z4w%skkk)67x~{f_p;~Mp=|iLKHN7>hepQn*nA^km`kLdQ=wi`_gi#O!TR#I=zTk?Xu|oo z$?1pM#jlRt&4QI8BD)ZxSNNWZjU%+C#mtg<;B0~CK;DbTg*Uq~j}w(1V$Hcu3-$+J z)juO=?swnX0o^L?+4BQ4)WPE>YIYhIv2b@*Q5h_w44&Oohs8r*CFH4gh(XMd5u$@& zf@tg|o69HE8_zM5Fq`#y>f16^jks0P7(_Msj*^jvdP&uCPm3*RmRmO5XYm_VTsE+9 zz%*e!K)f>}#D7iX0s8V4zZI6(KE-RB?(sjJW-PY|-=hW#&>HbO$$vV#7w7|C#RmM{ zKA^IlHIVJHh(F#pNx}-pTg{xf(L_mXvv=QIO{z_w{N};B_$G)gk9Rm|xJXqtDF$dsvv6`vlfZw#Sw-%F31iVN??0*ykB+B6w5fY(G%hoB% z1b$K9nGVr3;QdgQS>`SHalTlwlR;rL%{)PnB!kFP>Y6u>cU%^bT{aCq>gd!iSPZ5N z<0!Vd+!%l~rP)kH$Y{eP#xxP1Z4qlp29MHkOIF-J05><_49l`czzk9jc0&8m2SD61 zCa{}p8a6&zXYq(Tsut|QF-2&ESjdgRHaWH`Rzv=yRV&Z+K-d1`nS?Mx=8`C_7@}jS zpC8#D$635@U3@SW(BJ__25||hXM{w*o`LY>t+~xb#RUEzr_QHt-2CrCC@wSsItLL8e`9HX(W~-Yv4HZOrcsY9z#OcY(i7=rg0Q@+Mmz$q3JE%d!p6Q zqmis)3|+vGcvXgVnCJMmcT&jJbl4lBTjGYO??0W=te9#2l1TzskP1laI29UNd&ISJ z;a<@4l*~K$`f0l1J63XQ3BCqpSQtmrc+VEFzzBFnXm~12_~JZK(_ru?WMGkKA6)Dm zs>}>2HY#+CARKss!q<$=HQxjt>g0 zeUX-&5#ijspuTN?^(t_3!u#C~{;s2|e99j%s*1Tm_G4<*C zM^J(Flee&KoUxoy%L+&8Cv$3)Vm@c*lT`R+0q?|l3w9yyFKL20Hb8rk3pKwsO?FXM z;Y=_|RuM&YssEHK&{<$e@mPI}%1yugHQin`qiXFBxm)F(1-H5v8aO~3j}U0hp}H>` zzB3-Ym&=pQG!@j4e(c~5Bln|B|1Fo=-hM*z*MG94bgDvZn8w7B)j=PArgs7!B@%4E z3lkW3Bufr&80oX9iwt`@RY<=72QXe01lO^owW1^$@V7`tLi*CW`e+!(Ov9$dKotW9i{Ulo*f2J93iejmkBp(gX)hQy+BaT7ZG8q$9( zRf|JxHw1ScEEzR$31-}QcBX??MfIAl6|twL9^7~?@oO=^ZPr2*Q-(|S}4*4pe;cgiQ9ix?)yOR#6eD#;O9V(IgYOr(#WEj10diE&3Xhq z^PEu<`FM~+1%&{6ee|IeX;I%70uX4p*`eCu&0bZ`eJ7W@YS(yhe<|S$3iqFFa&a2E5%us@trS$T;Fx(;1=Vd$# zweU`^C4m2kH|{CmVqQgN%{-O8{=vq)-e`ONm*+c{XP;G>fuNy-DcE@a6oP<#zG@c> zYHZ6L3;q(ua*PyeXUHZ}$7&u+AxyaN$9Fi3SM`ruUa2s}sgxvM`Kilg^4=t`D z&}^S$#WO=39}h8N&8){0I#U|QNg!xxZ;NRZPr%ccVT`ER@76N^5RYyg9a_drP978Y zp|0(;@)4TT%-K~sD3DEUwwAdDA0?Cjw6I52x(SttW*r+MUB_cDiHH9qwsWMLszDPk z&H!ovnEpWES*6N}jU958q|&2{A#7%3X7dwAQU-s+qUR@zXAHzSd?NC&z7_9em4e0P zn4#hCqsd{h?G=M#yXO9Ifcb#LcHKj7eAd{Q(7XN{5W_x3KGWG3ZS6_^COnHJzhil3 z`XyOQLc^Urd_pAGAo`qS$ob+6gO2wU0BF_&POYJhGi60D6{98TSWeTIYiY6+fOaa0*J zVdyWSUvvL7T>pP&wobn$1+!D*4o zWXDKd9?{4#f5Ooq9;rETZ3UWSnyMG&+WtIhxTAzk;ME;xboDjyUOO~TUT~%#E}n^a zZPRnzm`R+&STDkzSb(qAkndJN{`~ZrVZC%+nhx%$%h~LFyp$N5sY?o*5e6p((Cm2b zR!eGn+KW&AE4!q0+I+?M7;S_<`trJ5p|snmC`)6cbH(Rn{I){ncpt@Bt>?h|JRia@ zMcqv}P4X|;pV@8ll3(JFbv@q8AI5AnWF4o!cJ)R$w|ex3?B#SlKWE^clw^0@RqTz` zBrmotY(;&&nm(^zeT^*M-fVRNcLl8Y9eh`>23>pyt6{tc_jzlp*k;wrI_^9BjmhME zKEx`E3twyOib=rjPSFbg)w}fy=!Mf$#ueYwi_Q}F4mQ8;9io+O=i}}r?a1!Gq0K8U zpWZ*-jnB(Td3$F}HQo%Molp=wdM2GQs>s) z+-&m|t@kXS^C^12;(>97-JhP-JU0Ecq0j~>dHGmaE_R7P? z%ubA*ud4Uci&JGUJ9&R)$kdO;_wbwboW9??_XX;Kopj>0eVIAEt79jL`dNgod72|Z$=v{(dBl%d2{MEBB!!?Q;6O&t>!s$= zr{iR;Paf~3&@B{HDjz(|SQ9b2_`w%tIF;`6b=unr>sN?6!x%I-l#hKw!KpzgW}W+erYj5JALn)0!|;qHQEY!6^=s?Ycfnde*-*Q;q0 zUv__sBHe4oz(6kWrMS9`(7b!s+RwZYR6SxNRx;#nxDCL>k_|Fy8xGn`1CVUUikEMV z)k`OgR;afm`{7Y)wlU65mSbzX?ffPN@5on_w)w^B@qmZFv)sNZT~B>QPKHJWTUUsg z4m?l${Ca#oc7R#ReOFcAMM{kKYjq~)ABW>nffsSvuh){tos1Gy;4aAsFJ(+SXx5taehmw z399CKxrKjt`>@Kcs_?#He+ty|vN-AiUY~k^VuLespsm_$x?%<*8^zGKyKd@EJw#(- z!dFQ1c3Nc;qu zOANoDb>e%Cz)-x=6?rL`Ip8i;G*6f(oGmB5!Znme0Key!s_ZwQr z=#%sbM1E#=i*6NP%{-m-%pe{E(=o@u%$JI2le$ZJQMq_lzG|7$f`w(1W&xdoO4A>m ze73TQQ$?4g)#BAH)mI@atZ}YWWrDXGkT!n}8$PcxiGQ4@& z%JO9<R5WPiK`FlCE08ei{Y3vYBl=%3RXx~k{DcCG!!d&jUo*MK&Xa~ zc6B*9=Y~!EjN%xFTbbiVgcV}LFMqZ`Yp!||Z{iTtR7nen1=L@!bP)WP5$i1r-LlI*Y%Zg<7SQSn(|l?=A5wi4@gji@95_Rq2WCi z?InJWV$LFL%cTsC#;li-_flDwaMF=NMIegvwfn=F@4Nx_NRpAi+weO~VU%L}Mm(mN zOY7rTLuv~UhKZQZ;p%(AnG%$WYCYAdRH~W0fGh(xcWqO$vJ>1T=i!mM-mOp>LV|Fr z!_2Nsig|gb&O0*CRBvo%K&s9T4gS zdrBG9zD}T+hIc@V0HxPb-6U z2nVX<`ITBvaEc^8QVir|M@E;$EaDy(qWK*SwaNmXWt8sll&8K^69heYe@%f(`V@mL zA%vb~a=B{c-?=4FEoyQHBnw+VBP_jd3Cs269$Gv+MPl3JTDRpE@Ey+Jc0QOKfoD>_ zA`l~ZVc7c8Q0E9voW#6Ps9^{C$hMgsE5etb5_p%tlK1dvdKt6|ZT)Z(6gbGjW>;&c z!8+EcSl-rx$b#*Dp{4yhO(alkD3Qp@ZA=tm3)b^3nGRj(7Ua6*#_^>zKK6sF+B6zj zYP%54?UJ~8aR_N1w<)@C#lYF52ZS&lrUwI7MmysJgICuL#0W4i3N{2|Nq#<}St4

ty%-uxXhfe4TwXbGrZJ^05OY^;5j^c?5L_fh`4r;Poncnq>CV z{7*4P30nPd#Y6xcgei|9cO=2$-87~L$3A2z=V*Lw4dWUuhMB&ht79Xb9q?)r;|1oy zKdL}}WOt2&N6GTk>H?K1-`bpJi}5Feuv#z6F=o5XmTvOv=lt-7Bf6*a8t!_-sXZMX zoj38t)g@Eauh(v+nsMCcgRlhds+h%-gNsKp&QDiw*VBRi9$_E#$9kJ}A-}%*css`N@E+{e@znV|MZVe5d~+Z3 z$m?@HhKS3l)%~HKcOBqjF|^f#K7{yr;OO(#O#byS(v}m<|3>o!Z1$wNtl>SA;U+Um zy-9m|t?o<|FJvsd<2AZF^5#19;w^bORe^q^AT1FSQEszgxW;br*4TEsFJ|Df&N(tf z+)Ij-{?3T9)IJ`CoXI=4c;EKs6koMDUtHoQDkS60!7D{vu{9-17yc8td5`P}u@6|O z1IIagdq8bXX{%!dOkP|qiy=!EAZ|A{9Mp!=qgARGxbZ2gNWv_giER&lbvSeE)VDc{ zG4I*WMq4I}j>s^D6{4y$pTxC#A!v$i;w)(Ed4jQuY$&;^u<*spo5lZ8Pmcs^F^|?f zJmzV5xl6O~VM&nxWVhWos^92MM5j&@i1V-|N@=hqkN&&@*p;EZNqTeHA|s3P9OP;YoAkbl9rV{k z|K-ztcr$AaYr%W#YryE?D=IWa!G>k7k_(7$Wj{}(lrw;{uV_uYDUhW&qM1}RDi zP{@E=YsU~M;v+7S$H#9i;*)yNPDcfHzbX7!w6Z^@g}<{RD~rns4JB{qN3x+6d_7 zl0iLsY~rfOhY2ov+ef&3lc#cxb(PvjxJD%EeZ!f`M8zBXsC5;NqjY)+ooG!0p>9?= z+i@Yv4>%am9=zEQ$==*bcV_60LP!E@iAC@Dp<|8NFNVmpY?rP=cw0;bE94*Ij-kbT z%cc`G^Q$Z?Hl}tn#)W^Ag2X8I%Zvcj1xyrd6*~?B?54t4=e8T)e3N~c458U8;r|azXNIb_=1$C z;CCAF{sUio{riJ^U1}shFyyDGbn`Ir3|n|iEmmZNZ2v;4vg)Bg>3Dc9fR;vM>fv7< zSxZb~(OieBlLdDbCkrws0o8zDE@z9G)X=FuDI_8N$YzL#c=!j2UZ*jaCA=gMnQbgw zemmLxZTUX$Q;>~hgk^kA7TJ}>nn^G><08@ihoGWSrO+>!EI|h9+J4G*(!OZOny^&c z79~vUw3S>jzd#Uf({a8SVDmyWz(xwD@4ZsDJ9X+X!qbCsO`?#x1!Ki=qO#p!L`dXG zlp>TpDgSMd1vQU~Aw#l(L&`8+mb50>0BISSB?BtVk5n&L!&fyPdGT$WjY3z4Zq6;c%5i#amRLW#u1RJ>q0H+lxG#?(TD#~{V z86}8m5zruj6p9uEB?*Fs9)g9^K%k9d7ttT)u#6Xyuxsy#BNx`sfl%^qhXsXOU{;oA zeLaQgZiL}QCKj1Y{#pX1p!HQOFfuHHd2(QdfU|gt*;EF0m?Ul0+&)^fM21&=w-`-jSr9>PW0UiPD&K6+nqb{TxFUM4R9x0aoJ+w8|(HK`7HeKF1F zkFG?9zmF{k%{F#xh0&IuYj-JNdOSEUW)q4y1o;tM8%l-I%T#eBz`RJUl{rRDe$IeWZ?#GxVy!;~h zL(;EWCxX52{XA^Xr|X;ASLLchiQ~|FJoDFeP8^+@toOtH9{SPYR2l2SUiy}E6FObx z*t6zwK}sZGrvF%VrJCtQEgf->sy?-MTe3sG0#E=_bctQfT?tgtHmCOVlUaAplY2G| z(nAMbx(yu<7l|X-DK0(_oB40NIc%N$P77rr@(3qmx@m8HPW;mq?ev*`ff|*byHhM% zJj{5;`5vzO*s1!r``lPo*$=h53KD@1D~BL;iyED%nkCf6Le!~To^o*j<;CdA9{21< znh(%XmCZr&YK4aO`H0Q3V#e`n_EL%Ud8#9dzfojqDbQdOY!Lboj+l)|H}EL~Dm*QX zpBQlcWC{FB{X3S+LHb&e7EI8JLaYyFORr4rX9Sb9s#a!1kM& zCS4LdiTN1Tjn2p1$n3m29WkZKv%Il*5s~z2MH?pF`;Qt|!qf2I8@i6dwqx69 z3*(Th4sUByO*z>$4db5 zQE35o)yr)doHFX^w_DDd=l0@YU`f}(qK_E@U9I=m8ZC;dO;1nDuKC+5tXWShNz+BU zyWwp}HwKqu2=K1c^ELsi^5uCy(}T#J;)`F+0e6+8=kJC7@RRj^90GYbt;Auk&DxfT zJuIW|z)`7-0oCp3t@4SB>3+5Xg2uP+JzpE*D>9O4FVHaE|nph#)@9|frT}R<_^IH$Id$Q4r~rueyqj{ z+9AY5SMqCd-j2-&@1tg3UA?BWmU7xT#BwVeE5i*d73}Dc7ATh2G6}GoVOehd#*=ib z=3A9D2O}-3V!J1sq^@f2x;m}lfpjHoZva2)S)bspi;MTefN3ISX?(J^3(wuO$t~8p zk$0`KhV0`%xN}RS4wB7(hD9RZf|xfr|6go~TCP=Vkq=F)vRFMNE7Ojalxq(i+4`84 zTTDen9Q9`5dwz8c>&A2@mkbvY= zw~bdfFAFQHw%tl`h9yTe8fTTL*fiHJUPG*StH>t2mz7FQ+%KNKKxLFwo0vX^b2m*n zhS{6*;0C^cYxcl}LhMSY;<@x%H*wEaX;Yd1bPFL2oPC1qY#R5PxQYJT8#Y+5ZYpn? zP+bzU*E01>MReP!$(sBj0@2J)c@L<3xGq(!)m?Nos{Ii$oz^uPhlbis_pRR-+Qc zQoA&lIA!UB2CuKBvDURg&pFECxM%iTr$w%!ylYbs)7q+3Ay(FH&u_zg*#+1Yv#pR_ zYvN5f-hailf~X4cqc&cXm~fD_Ga6_J(}}_}7p=K?LAhDOopZu%Ir&z+dR|VJxu0c` z88}s4tR-|sm7fe1eh3wbnGlgs{EK_FTDd+vQ(sTvKzDir+Vy_nd?i=kbR_Auw zT%(|a8)_90joY$$s`;lw)|nff)$DuDOk`|=rjqNgqg~V@Xu(q%=kQOo;+|OeJ@8sq zy;gu(mX>_N`oL>gF?`kQZa1NQC0(uQ8j@?oD{6y%l8qFC?W3EUeF5Z?$0hukrS8No zI{ih>Qi2a+{(A2R$6UU3Q%X_VID=zVDbCoPVW2*YMgEH@Dpk>qhdk7iA~2oBh^93D z&3Dm3@PddR+(TB$>9JPc5uxp(e2ej_mD@v{o*19cYO5S-Lv9QYg%qW#VgABYyWRo% z%?Q*mOfx_Ef|v}ctp|YGZ+O6D7$%IDKc3*^P$q`S_>^x1k)$VhwsqM!9)@b zlKe`ZJ6TSa;@Ez&PNAa)%e>;Ct+4e}rGgT$CZj}QsU_NH3jFUbB=gbzo=N~AyU2XU zuGPP-8Uh>lKbGT6P4-%ojtz;SIOO%c4`SqV)I7y`S@SlPyJleSQEX3i3EN?@wB@^G z6Z2q+?NfF&EQs2{;`-4TL)(HChP_o6c8r)Var|%T*p{$<)og2v8wqlH%k-gH3))>D zL5cN&P)qc~RSV26kAQYO2Ku?KRAbNccdd#{B?&E(q(9F>>F&CDDr3Yy5Kl*%^p_dG zEe*sB*3`-e5<}oS)EXGSEB|=wGhb?A2-VhScz3HjorDOWj*`r6PldP;WBr+ciAQli z?7+o5jE$eH_9Rn-=PmbRZDwIQ+_Yt1jvX@sT&Rmp@POyqx3yMn@LmQ~|4%9<39g<@ z;ZZy)YzV9uZ#Ew;J}woU$WOQ`&reh2DMJImpmw!*j1drSu2q`zp-g}zbdL2IKu(WF zdt{lsPXo=@#f^6rgDFfm%Z^Hm5|Ozt(djaA_3_d($ZdI>9C04WKTBzaf4S5w;rl&7 zM!qbeh{Fi3B;t3f`JgFm)k03pX(*(M-D{ZpJp+El$&%80JS0N9&eIOCNuGVKKq*+DiYiZ>p{h?Cf-jaBFaC zy1ybUw>TS5=vJ(3=JjP?MrGt`yKhRu5W2qx0gc(gi*r-FS-mIYPV&SN4S%yboVFBs zS0lOU6`A%|T8qG;&K@uKB*regbZZcOwFW53%^s~Y;8DFkO+DIw2!=dmfAZhTVPJw1 zk-n!LF+;x0-jj;q6Tvs&8GEugSj%HPUV^-xdpbWXJ1OGiMhUrGmSr`_Uu06AV=oih z0i#5@?WKeMT41Lx^d^+i7X$~d%#>y#LBGNVaKgFbDBcmIX^}r{_TJo4rdTCmoAO3b z(v2f8q>YVzEOUIb5KoBz1t`P8XJY=s=MGF*pHKTg0DnM$zufil`v%toF5e4_N<#e~ zeoqd0Qq$<4!DWly7n53%%ga2-@k&AuSCpdqpIS$M+ma6$|0XZUL-NAE%k#XV=|r(C zhn;5;l=*+_d`O(%O%^$~^zg1?6uuAdE1LS4%Xx0eLj1#|vB>4@?pLLm;L04Y7$sgQ zF7a%R%XOaD`MjS|GTBU?#6C*iUE!&0Ps`(2%VTKyHvDmqmM6QlJc+evLJNOdOFlEd zX>@phSq^^^YW4{=cx4*tnb)y^qK%McdYa~Cib=>OAzL%Fh55>&4jo*VX;15HTUh9z z{*GHvx&o!0C9VjiSs!{c)+R(NpvscYtlUIK%GltQyO6stY24+q4kI$XuSR(?*f!;Al2vN0ZuP@~Lv4)DQC? z45>peBm4*HZpe;-mrM7@ehc2Yso&0l7cQ3+p38DQAF|_uUD%cGAUFYG-2@wlg<@&MM z?gwIJ8&T*GM>fA{APV_`DC9*H9);|sp~=+oqV#y|_aY3x-yMdNd&2MsL;5ia!ylw4 zA$ysax%AVRs)<0T+EbBu1rn%=vO_z6E)2u$qQo?G>OyX4Zo4$MohyxCQ(~Ery$Yc( zEJ|#0Ow}QEX-d_XAeArjxt;ih?36Gql8A~HLb=5B@kb!Ecq!oeJuW}sxvh{*CAA;P z2pL8I5GoIKo_Y~ccr96c!mSd&r}M2`#>#B*2iPqrTs8XSTfFipCV!pCE02MH7kHUQ zebRsJrz!P-oH{8si%E@X(IYdZ?w3=iNH)5f%_xTYJ03t+j&JGo&ef}YIY(HvMJWr} zX^@vR63fy#=_v_+ z&Op^GLMHmelACvD%ZV~tYdh9|dlmrjB^Ab&oI$Y8B@H$SGjy=zh2-C0D)h%@&FVFG$5?@{-NQLJQ@=#(Lui~;K9Gi8{(oCz}bG7m!b zW|Ff5&KC8!B@Y{g8;>Nm@2f4IyT|nh(JsHYwRztN*+tlSqm@-8wjUvXep{B<0VIH) z2SauVRWqYW?9jNO2~#g)8i*uTe(b~7&j4S%@c~_#D8{pNS;$t{ zq`Z)qA?jUW=DU%Z$H`2dK*zI-?DvMVC=zL4}jtod~W z#KH@M4va^X$t;qj9)&%}%IXpH#YtJ0*l{Q)b6*JA2Qc>>`q0>afdSw#iN-i)M+gyz zN&&s}7E4h4D0Nr&V1nFGXw`DlA z1c#1Jsd;o-$d~vaEhdeMw@ukbOkakfYZ;z(`^*(U_q+ z|ClZSAjXgc-~vd02{_T@pCpUy1+qCWXY%w)1+PS-X^zIsyl@28pDe2{=6H!>@^Z*N zg-;g|i&xTo7xn{IQWs(T4I05REd31e{x2%_3fm7mJ_k_Ke~lS2ydi{2jO&|JOJv5e!8=Lxnq4}7GH5{tqJTDxg$q8v< zT&$3rT2WkwWMxu=Pp^i|girHG|EVDCcUC0!dRklIOG@*Jkkf#iJea3L)&#*-lu0pNj3*OWnuFq1 z2z%vCSuGuZ;Hhis>C`E8Mq)Eq%acXedpfGv4B)^)w|$HF9)M^^c*Y6f^#w&2QLM0o zG0J~MD8Ezqx1i$HNY18#PHlnt6i=O!Clk<6f(=(u`4~uRu<}Dh=1kha)l?y&sp~qI za{L6Ox~QQkSb9NNdJdMJmDt&kwUY_Lb?yK+)Y+nXMPgT|OsCX2J!PX@I3(V!J}IJymKJk@}gT_jE`Ca7Lhc2uyleK{vd4ixEc;AhP08eN2uP@bxR z^)|9oq-arVu43a3ZN$Y7j1#$@gSiwl+}zfG;f#aoT}USqu;U0gHjsdsDfKNmbxlye zHJJUcDf@V&hS_O3l2!@XA3@3*Lh`ogk8dCVcWHJMVfls`ixB@#$bJEuHZbIBdIcDg zSsuJWg7_GO{}PO48o&@W>H zuAf2id>{({eH8wC5_>PQlRi&GfqoA`uEgHQ%j61zgGv1TI}!u-{t5i|(EA?__I}9z z6h@xyUw4~KBkYs&nP`e_;`CgEjnqPa@oTTgHhZlR!TFyN`jyq|avwIXi#DOvQpo-s z26WNZ%Q4_Z5d9bZyR;bJe@ShN2SCk0-aZQ1UxDp5?EW~tSfp>goupBe{u=b>Vc-Sz zOSIfg*$@f%8`yaPI;PZ5WJ!F$$(xIiem{boji_)wnayh1m(@>Wh~naF>DOR?eWN#% z=yM5IGt49lmGdbQ2@A?1nSU$NlNX;%^HIkB4$|L*?_%ghA7u|HXQ0{^Bow3{z*l>7 zUj0I3@dmQ^nZ$0qtbU$FLwqJ|TZC<&^VB!d>Wf|{{sQgt_lV0yjK?ph;A`qyrf>E? z#2)uwp5wz!gzPFnu^O^}L|}h^iF~XySq6H{kcHU!7CW_c(a6LV(qxljJSx$LVaWc8 zyc8cIan=CszY%lxH&AMdAShRa zh^V016^I;4kRTws#&y-G%cqOGYOT8hifEKe1@RWoRlIP$U0qQZUBv?rypQkys(V5w zOMbsj*Svo9-v1r->Qx=;Dt<$e$49QTXEDzjP{|xq7PvB}zBaY^zMq zULYBfTb(+^R~I>4mf16ZnKc0HGV4k+-Rel0#jG2PGJJ$gyZXmXkUvVYeLpH?G8KGm zer9$A;qZDp|}O=Gya0q&3r?V-2up8+BJ zl6hu@J=YwHCsS#9nwpepAWT-8F`!JgI-cUU63Qf~2UVI5oYKa(eo2Hj_d0!@Lx#!Ac zwl(7@z6ntn9Vk@%4({<<$KdlJfS@-iekUMb3A+}=9gmQ>K+*$|Qv5D3_XG3Uif;zf zRSgDT7;k8RU6Yft3K7Xlw_37#nQ~=0gl|FCTmaoAn7ajkTFGX`@76$ znz9vnGe%Z}cdP077K{pAat|CZl#o=DA$`GcZ;@B$ta8@4C7!jSy2h>L{ao0JAFPJC zt`?Z6R{Xv|DN>px(&101$*HlfW=v0>YSy@{8cGvEFzyG(u@&|bb_}zZlt|BBoUxk? z-b^WlG)WpW|TIc9=d;kWYpLgwp@YZqg);W}DvbtQ>S3yT% zoxKcxTTBwm>?JO|K_R7aII`ia!R* zwV+(k9&TBif#}EKig9^c9_Mhl!CMr60_@jecwr;&3CzD&{7Fz0q+f)^DO35?Y5moO zOAuYf|522YPF)bK>yvPJc~}Fizg0mxJVI;x4~jnpNq@)i;*OGTXqWVKMAFI*l2%0| zU84AZc1T(czg^nFZ*Epc=^4tW@I?j&>Gv%BwwH<-RA$MWia!U^m1XuHnN^C!O9jq+ zUgwmVqM6qC@-T6pZ}KYa<&-wdDQ&LE*sTU{jik+Toi>*vZ7wtTWwb(Q+6$B;(%`xn z$#l6U?x1jictwyS%kw!R;qO06>&9TT97-vFTmDb+7h&y92;r3!LKiFMpD?YWjRL1f zT?_emld)n4)2>qdC9vO&5dO)Ic}@+hvMZNaDW$>UhwYwIK|*$z(F!>Y*mTg)QoOouyPp3b9`S z?sX8t^n{HFnf8`y-*$)x&i$+6Zv);Yz`H)0Zi@dK>LvxR$$Amd#6oFPX8uj_cW4#_ z{Cya+Z!6hp-DS5~o9$cs**96^?HjFsE%qwwZhNJ*)xN>tH^eL zrtHGUDHZl=Y>lh+)(F}M*fj2lPFSt@hn;CRMuhwqLQbr(*F(s9=7Edkv~%8n)XmG} zP5dZ8xFJNiAwc*ss8dl~D(tlou@*SY7^6)TK3t21@sNG9HNswF@HKFPp0T?#oSXAs zKI!~r?a}rvW+U&dQvi6w?-`aEWo!h#J4fP_c_HHug;>|q_vrK zLFf&B8%hT#wI#rGgl9!3D~h{q7vFQDS2pt62J zk;MYLQ}Hjs^9Zos)lGWA;!yl6aQ3I&k7~i!L9L=!@0@)%U^8=&z1b{Kd@pDN^&;P^ z*RKA(uaVe`1$J_oy#?#Lzri<$$?w_sAa}RGQM&Q^hBi_%xFG1g?uIIVDk@tQ{}#|5 z1GIbM886>+wK2f|4s-NI{yh{5MDQU*aGZUgRchaB@OvFER$~?f4bgp6TTQC2Kac^z z>lD<|0WOW<22E-rYttb82+@lF6v};2%Y6WH?>G4UWI&R&W`O$VPN*M{p}q$(nczPJ zaP)py@n2xZ6NtjLE{?r_%ixbF{wpX?g7Rn_83j8;EY$ae*7rE{J!bI7WS9ew2L`!7 zJ&-A3Ab8U-`w7e=6;CQr0D)6M`UeGCup(y%MWL1_Qqr1=DJBv!gLprsL?Kw8f>WRF zMkO?<_mCA}=>lK$1(-&Da99aM5&Aqb(j;#M8?zD|&Wa}Jb|nsffXJs&nw~k@-kyqM zCF;sd8_b=UU_tQoGW%I(ZHHhuP>HUv;8b9ERxgg{l!!yZGr<3R-XS*D!AnYXgI`XA zUtZLH*#W=2VDJ~T-W~g#A|Ud>&?zrs5;^6cN;nYu97rz(LK}G>s%jzuJn5|cGRwR7 zWhIKC?$t(qh-PqqAu5e|uPBj(i03hUHHdWz$_|6SW743!1JLJy)f@}gz~=? z_HGo`-MYZ1=%_O+W$)I7^hu0_A+@a?U;QKVp z5dIo?{}j}J8kCf)@#!-(58!$e!_RZ}mn`nVilj*yr@M-oNTjcHB7K2G+GFrN`Yegc z_%dXD9yRU@k_s9O`!Z{PMi{;V8zt7)=+mI(EqvOWv%f<*OtF>rw<*thn>KM>q$*QE z-yr%x?W{8U8)m(u7nP^GiD#*Np<_-49kUa5e)+b4@@+a11USA4`ezup7whZWE_fUK zyR7{^A^JW@O622C5{i6iH24on90GXnqQw3fZ;({_sGX#~vs#wd>m__>(01zbEOgICYMYM&OgcU{6ib5gltSU$GCxL!>YCQ- z#gr%m@OkYE`d59r_YK$jNQpr_#?Ndp1h`7EIST`3BAY}3d`7>?t@Oqs@DaX`Jwp`c z{34xQ6@Hi0P&^*x7o!fgGKJm+Ii|$nPr#35(nV;3k;tCgO&5UGTmz@(XHsEB#xl$MMtU+$N2=-=jD6?uV_zT z(KYMGNm0B*u5Jw!*-B)f=u;@_MoMUhNh)zGt9lgI-UM31$l z+%}d{;^+w(-eibA9eh6|aLFM`8ACwwa7_IMy3_HPM|O$f$gf<5 z-ycBw6A(w|w&;&9l&%Am7=bC@LfgQ&BWdF=4=|1-C1t)~)_LJRD+KnwV04(`mtop> zP+F>!Is~bNM2r@c`DLs&(~b~-$E6H7%Mdl)V~IhEFTnI497Fi%sfHQiaK&eV@*`H* z5%HD=s(J~1&a2WzFEvBD&9qlchPk8kl@Qj8z7-Oc`Y>JZ(xp*>_EBvM$W{5PAW?Mw3=eHzUA4o?{+Axbt`7VZw>;-(J1^_w_Oq-5+xN4qvsr&IE$hJ@Qm`sW!6AyD z4n@B}(b4&xeJpm^LAtwt8jJP15RuC%ep*3sX$)KyEB&F&vqm84^c|@f2lE#OeT+`G zp=2%I!;~10IR>)f80s^b8FsKKOqu|!l|j-JX8aM5J6ws0U@rjsh@3B3TxNnhn0#9x zCX%8ue9*XxgSXvWRJ34A(4Q#B<`eP^nV>*=aX+gY?Rh*13L{N{* zcV`nicV}X(;x*uZ>Vo08oIgI_W8yqYUG)*=_=l6JIKD{oW_mfEK3LY6jz2DnZG6@r zOHd{#J__;Th(y?L_?rPR~h0&iUnQWVwF#(^K*l}5WB}z#gBpDILtq({n}KCdO%uK z=AS}hsJTVIaY8^knMh%8B2EUG>h~#_LGNjbW8W&ZA#HljKP{TS)IARgz@4g>Nf1+i zI`Z+<$W0i3oHztdoF4VUX<5I4ywK1gZNjbYH%d%_&Ti28+xF``ebw*%&%!tn0L}?- zg2fb6ki!v^)0LPC0S*Mr$nRGtfpLC?KNI`aOxk%ftZN}(%u0&`yJv!f?46}}8dF_N zZPfdBdV*JHh%=Np8RR4~ezq;8-aYg8wmOYcNFZ^4if(oBpD#f-F;90BbJ0!AF~ppp zn+R^ctHKM9I=dJG=jJa^#XL-==Hg7n`$AiHfH+Ho=&M1@R~(0`(h`uHG`W{1FHnNY zKo5vITVo3@^;k!2(>1o~A+|*tTQjgNG{i!}mM#c3)pD5)ud4}Lb33+0m`vEtQ5@&m z(w+c+u~>uXu0bqO9H;8iULY^c`R8`pLwI6;t)YXgSQ^cWbF=<3%8F$jvcd_n;yfiz zg@)e9uJgNO?28TYJGBjjgQ4<*oZr$(<&OPm+y;dgL>0DV{R>Iqg;9mu0)4+%g0`VP z(038^U1W$qs1^_ofxe4#{w1CCU9}&5Ezoy=aa7+WS^rYfcWG2#3+ZFASZs3h+4XqE zjyKjq24r}{vn-o4GhNu&tVanWueSLc>Kut@CeU; z5#gBRW;)qohV~p4(#!E=Lw*_$j$p_|WLAXyBy(tlJ}ji?WwMYyJmeXPM@D=#Dl)E& z$f*kHq<3@#Lv6&CG2xheH8vcN$1@=uPsCFf!8tj6)9*>K;E#USM=+caj!(xkBODXv z#*lXop1BbW^J4pQ;H-$=rid*ILSCYOFO1NeBm9d)U1ZCWh`r}Vc$P(MnHiDO65+Wp z{67Bw0RRC1|CLvJTuoUU-lzMLXecr%Bf@K@q@vLz2|tYd2t&Sv7f3vXk~tnj z-iP5h<~3kAi8|+hqCSKnNU{!ph7y07q|f#eeo->_OA`H(M8ASMUy~ci`MTYb_yf?d z$54yn9aT>h~&Lc+CC+cRc zd1NZguZ?J*c>u;GCuCE^I zx~=n(jb?2d$-Ho%$y@|~+}EfJ*#970U+iD_2X!ab8cFoGt>c#UV=W(>F6wVEuMOTa z%quiw-oCisFt6|m*P%CSzmVunn71eH15MtS=(l2R$B*kKyg)zSJ0=o;1L}Mqm`e2L zsDCN6K2sX~DeC+_cb4dnQRj0tljwip{eFvkSEER~mh+7|EZJ2`su-YFi7V$#_M{;=+8TMf{$Oag&D?zBoDmeVNnD zynGkiaz)_LxH)%!JhnAvU#`zdw}?3(w{G}cV(2v$d;^{ zbVo1VG3xkj&+}{M&)%aK=|4x?;iU)_(0hVL*v}}tn zj{9fw>XcQxHYM!x-ahGs;gnp1I`^6%*H;Krg41R%-gLiPSiP!D=0EXQqnuTPdu65$ z-5@g`G|2mZz>5^)lwtN)gZkRVZFVsl*j#U)e>FKUd%JzY7#kP2#OlG*f_*J@?)B_! zkg-h-nDrn^(GUWql>bSq@}Xr0_D+AU|P%|t_g62E=+`HbJ%dAWPsZ}zg8xOUV_ z<7snpCrvY`&8jg}IIn2_`pn%UCLM~*jUp>ce{gQ>eQ1LB(&DxDiD_OjF;=-X`+8>V z{bTC6L++y2MY&jAHRA5<&WoQGEZ>(EW_l*xvE1Kf@A6Iu+HD*Dtwn*|`seFItZOSA zD^4AM@ECO?r`9rLzrVGe#p=U`WlfbjF4N6It0tdcSFE_P~G3xZ^Vu=gUx+^@0~p56K8c|rGD_5y5UN_4(~5r2-frR zbM;fmF4$~nyV^2;QRg71t(k`T2hZ8n*;L)I?mjdnU4Mgj*D<#u?eExH&)Ry<=G*3f zj=RlyGBX#NOsr-s&YgU@!+_E8xpVDiIJ=0)YF6DFWL%ka%4Aa40k5|$x%A4%+s|r$ zbcmVZ@~~(9pUe+(pP#67N_i(Q+&e1Pr|3$W%@@~dwaMf1zpK}})p?iQx>;%d%6xG0 zMSjQ`8}84ZEuDRgvS&_aiO*m1r?sf%y~OpopVp$5_Y&9RKCMO4`XjA5(mErpEz)`- zeFsSEiOdN3j|^B_Wm7(_EwVQFUZ2Q+K+wZ$zaHviKdm`39h}QZ20TN`hB0W)39g^k zqEG6NwC21+?(kX7k=7Y$ZIRX!X$_Iq4QZ{A)(2@#kk$d|J5^c-{)?Qi%|qn6pVfq$ z_`c`)tMHxu^t1XPtqIpquluYfNb7*~-G4!7{qR1{qt4^bA?Mz+$ocis*0pMX(Mm8c z*L={!&yA;%^K2~*?wQH3 z6#Gvi@tdMj4D~5sS$M;Z+ zPKHKC^6^ht5X#jVS_(lsTCLKZmUGCn}w>Ed_M+d0XVz@Fy(08&OXgkQ( zp+yakh-9Lg+jJE|7iG9y9pM|Jl>3K=MTnsRDtUw|+&@6A=4P2fA#@C6YN+TZ4-}OO zxjH;b)7OLpSysdISwaQ=PwElOesPqqwQheyw z;$Ph@3!~RMk`TdG_+{ppSSvY*1{T35A9v!h2oM~ud0pIiKa7uvx7_?JKS2K6LKKQ zbxrB)bi2S>U$@;CbJB(BbobSifEVq<9*3Y@ z{@5LJy7)$^Z}#ev{ct#N$eyn|6*WAQFFEa5|0@6O&VJ*YqCD8Y_pzdq{a@AA7CwBm z*uIm=`Tg%erjVb1h@4C(zC=L+NiY#f2$?~`iIPN+FcL}BB#J~66^SFUWEP1h2_%Uu zBg@HJl1kQ-jbsB!BN-%+3wQ}{;4Qp|*HoVxP(s^KL)w-aQ8U_=+EWMWNC#3UI*5Kt zovAAwLPyY%)Qyg!l#ZhwbRvzWF?2RfpouhzCeuZ9F;y0p=Xe-)@j^Y5(S^Q3P z6-S6}A{EDqrt}8ANpI1GbQ1NXUeufV(5ZA9l~Z4;pg}a4hSD$^LC4V1bO5!Wmeh*& zpgpN$H5I4~{)Tt(1fIe(_zSK>CFFoRP#6PaVH|kCM3@AgFdBxyKc_iCiYXl1*d|nM-Dp(Es;w75)cMO9KQH000080IZ=RSepXehEE6p06+|v z5CI1lm!3ri3V)4Oe{dA_72mz@hTXm7!rtvAA>;>}T*427$%Pz{K-4dRkVYVc6sX!6 zH=EnNWRtty>+W5ksBI=7Ms0`4NbOD|P;6QmsGK--TCEkF!a!v#{R3&GYR2kJZKa~M zVy*4;eeX69oQ~6*dAD!h`+Ps&_x-%xySH{@Pa{PYG=I0XuiCIGwWXP&JgpQ(wd7k` zpJK@|EfpQekDJjU**4QwRP*QJmX^DfhnFxUJ6kok8F5olU07ibrX-WHZp2Ka>Q@( zm43stQ+kxo?GQKn5!fs@pOrw40k80dL&~ffi^X+AbH%b#P%rA&QpH~cng)TfT2zkd zaZS(-TZ^VGO->A{DJ_w7*QZl@+-6gz9M|m>H-F8+0!X*yBuAE#^Du*^Zm>$9oxO>g zs>Z2WTuv&f0X|nO;03pTuqMZwXC-7S=1B|}7@DTqa!%bmRqIRli!(!Ru>~>HN+>Bg z*EO9K)i)~_%TJhU8hA-(XTx#j8TYH99ExJL!ZRdLxzAy+g1L#N^+?MWbl^0SMulFX4GHK+s6+~6 zzekbN%Lx*uyX#J)6&VW)*jtL63v75CWEc}DXNnaLPJdTh zar0JF*=jS+_??J8>2wbv`hs)S6Yy3_^i+W)xo7Yeg|G->%o80>V8fY}$mcJCs88^U z|Ddnp4gm!uk>&(aVi4s?K2f4MKY)Rj3pkt~D52e)SP+){jN4)^kn6F-i7a*UFtm&d zvcd(@mYNXRmO(fq;--+p6|v!|I)7gf8XE*uF)P5H64?qf!7)K{xDr<41ac9`w&W6k zEVPuH13u^mUTH2#F2OoqkxMq0LJ-65&wY zVvf5i%PpcTsM6u8fn_NiWL}G}615^wJ}>w*vHm*8yG$31xK> zL57ZApbK?LTnWup5hx%+D}P18;4SfmycND`nyVJ1aC^uD=p>+Pa_G?Y;0Oe3bHN&i zf>N3(nybYDYTVzy zTCn*phU6B^z$|p*i@x??cuN2K7V`pL*;)O=k~4Wxpe#UYgQb1 z@BYuun-6cDy6@wm{dZn0GS1xhfZTf5^YvFsdv=YKE|UIvtAEfpNBPaE;_@p0`}a)k zTm92}20s7WxgXQ3U%9mZrQnmV4cmYJ?%HF+oBqAw^6PtdG@D28@0Pszmo1<6D4(*= z^c?%+M<4#Abx;kw-}_EyYnys_D|htURj*Atk6e6lF}LI1O`l~9eD>vrKd(EIi9A(! zYJvTu5$xc>W9Oz^iW@qMHT@AYX_11%`?3mKMtbN${q>Z-pKlgv9DjBL?ZxEH@%3 z7KSKWH;o~RU0%PezNNk?8-sO*V5!;w*j%{5wxZL>+Al}yMl_yQuk%)X zDLN~RJHZW6^QW;e1==?@ZkRFS*27#%G+NWwufyegJGv%gcU*hJXO9+roP_^)?A_3qipU#cC<4}LY->G|Z)uG+6X`oR-j&t$fp zId%D*^7P)bCl>u8cE_`I#V>tyRet2m=J&3>@PDr(4I4j6cO8M}2}U!h>>=vLXPNt+ z(gD7RFXrd+Wqdimke|m(d=($!Yx#OU%s288ektF|-^Sn0-@#+PjbF{L;oJGOd_8UO&KDgXc=0001T zWtWx;1rL|({RB(n6{P` zGhb64oof@zZMSJ{_kx{6xv^xz4HBNo4;SjCBk7HGZtCUt?T{&JR?IkQQfI%{CX$6xWywT;DFe7|%3%sXxS>KG9)=36l$dajRfeO! z$AOWj z>tIDQKMqP|Q#u$n$Q8!T+Dc(8HQG4CKqB*00*MD#d9ww)#w(ee>BT;%pMzX;tEB~h zdNYNI9WV*Fu3RaUv=c$`##AC-K$d_Yt$Gs4vh-5P9EWvGfq5Z2RAxpGgiOBR%d!me)jS zLCjz}uPO*+a>(eIvqMIUe6O>^+e?9eZ$r4O6+osuESV!?*+j`r8|LH+=`qK0b4m89 zxvy=OKzq1tcr4!*YHOB2OtY!Lm7(@^05wj72xDEF)+Un@OS33T+x}T?Y+vsXtt*cW z4`+6`TsB{wJ`!_S&kO)-Mo{3WACzW1wpLr<}m(2Z_C2ECcl?^b4oxT>nkkfoo^ zl%i$SG{2}TyBc)e(A);|S^XYWH{4p28*h3@)zv;$!?THqi_u~>zGI1+aXSWVpC!<)3<^d z3ko0eo@{t^)~sWX&*G}2G~c1r1W1!dJp>-M4S2Jhxrs8@JnG@iOe}0Myeq^4Z>yN6 z@Od`dT3sxKi1V?_;yxR1(F|UT!dr}_&2xdOyZU@J4EwNY12&x$gnUW4xDZTWQN9R7 zXk2U^qhX1~=fMaofxNVTA*46C4b5k;fT4lee2;oW{vIVU1Aw$h2!4-NnG}0sFf?D_ z(Tnt}9y4Mg&Eh9{bgmjcye)L=Vp&j}%)G56YY5_;49zBW)#8iXs#tBnF^Cm6s||_w z;2GWGiy>7Tq*~S?0&Ei=3SR$azX%|5g+D2lD}1@(ZDkEzwxeC7WSuHn6~4maOTca|IJY*qAkymBui{rLc-vJP18D!+{&%*}9N_*r@PRExv|;A%$;@h$sUQ zkcc)IA%u|MXJ`R~VPl)rw+H(6%v3BlbncNlZ-UMp3hzK*AtXmsBce?j5PvO%TR1J& zCL_X{K&l-I|K5o0f$$-$FCy=`y{AW{k=8-s#eghf_3em{%@J=aekn%1U9iAjzcee6 zS?>w)rI4+EQ=F#o&4xRo@Y7^Igpae?AwVM1W|0}2MQodOlg%KpcZI*nXLx&nxdB8^ zMlN^BTs|GS+^O(RBX-!=Db7&%=|=2`PvK{PkKqeU>PU%GDes2tyz?=cdy~4$h#dvB zjgs$1$@iEh=fu~j@|6K7`KSxL$w%Fgj^;fuIn8^2Egpq?&Vd@eb*Z z0k~seCj17ZE6#^2&Q$oB#vlaPWLKsOr8eh4o0!65a%D4$28?I);{7xmt6yTB1*|O! z-vX>AFzSRcb*cottO>H5jzffgX|vWT&Q|zYa)nUOu%QY+8;(Y;iw`OM9LdZ8k!5m_ zJPFi)Wz!8v0lMw=Zn^H0pdNeOEhoJYFeuKYDClKIV9<#4P8zU%pUu(mIaTU%SPA9i zynx$q`^mEr356Wz8SVkY*JA|2M#OLQYLKCJNNmH(`HrGzQq;h&L_Hle zVjQOw-y(gwWhR!kkWZ(?R;MaO#08W>%|oz%!v)jFZHzY|%Fe>>v*@&Smiu9Oa`A=m z6OCIzjK%@DmqrM6He7!;gxuCp96~w{_>7)_5sn!CUPBu+SlZ&}cwDHs%snM8!kMQU zuD(GCoJJ595rim<7C%=?d>$lLL#o|1$k4lpOh?v`3ZL(Wr8>mLthtD_i;=-i5qY|Q zT>zJpg;_7Ah94?ZCovlm7s3Gk4si(z@@4-_1Uj^~N)ZN55aE&__9TgVKx^_f1)2t% z(vs;QNdpC^#HFAqMY@z~;t5FM|MNtZ!KSA{gs@l(EYj`cb=((S95z zs49^0VtHKp5Dyl=r0VFrv?_Vsj1pLXlRIOa&i~7#)R)bio!=l=Tl{kHPX@&m&cWFa zDXxJ0(j1eN9#@ck5P*noA6ZNusaI0_HG4jzx`WF+aEVJULnqkgN{c6;(FKUdyyzA zyhyDte4sJIdlBqggne5nC)`x|%24F$84QbG4INemp*Y7$`fT zd{E)n%W_BAdkr;CAoNX(UkjQ;c>R_`Gsc&|T-Ra88rvl=U~!kluLoWduQxOzODM{} z3BGMG%9OYnrPbo!LZMrKD_ewJ^w#ZDHMx4Dh9(f%HVJbl7~zF@dk-p=JQ&c3x5X#9 zdIWyH%i_1w7D#n>MC<|2J=9GB@AmVtSK)i2;vUH84+q7){upioVGSbQt?+vy;$B;% z-5`&aa<|hJ-y0Qw_pv9LKPv9R#&Azi+#inN%QCfV$@`&C`X zlGf*E4&(ug-vJqOQ0u`)U6l#(ZSV}EH+j5e$XgJWw4^qzV=9Cac@- z#Dwd71B7|l;(HKsr%s8-PTXnnd&nNJs|AT|wvEhx+`}$WKSa zUe@5!WfgnS_Wy&9|G#VThale=)cBrM*H8|X4;*P0A*u`IEeao@VINl zL8@`jCB?G}f0hnS#~+VUV`%$BUWXnlcI(qci6#g1rqQJHl2>Zci6AEXicthc@I}0nZ@U*j_ z@Hb>H{aw4+e^(NE3xwWO_?vR&d(uMAu^M3xWd3wVen2XW-=ha8^`4Q_XPgEqU}uAD zpwP#G_bT8#D{b*CY|$y+R`^?z&U1FN|D4=^|6EmLc2GhNI^7MSexD?g!%=nDER(8E zO|m)#sLxAQ&x4i1-?lA($nJSwuzQ|&s@k=8sOO15%U<+6{nYcU28kCXiI-03dESN5 zD83HE4K)7H;xEHnRGm{`Si!o58{4+i*tTsnMq}IBvF)U>(b%?aHMXrrx98sbavt~W zmwB9-f35FZ8PK`@r|CqVFc{TDBun+?2phthZ_Mp$yrzBkHMHoReXv8o&g21@f^Jcm z1e9K&5b{Zkdu<;^LtVcGc7ir>)}RpWjUqq}&6ESL@rV!K2#!zY=sv+3M{549Wr1B5 zqDghaqL%he^(1ErfRx5zd@`3`rt&8(oK=CGS*u*GC@Q_7X@PJ1KkA<#=N5eYRdIWE zd;E1}M3KU=(?wn0=?a>n7zC`Q@#+6NGNRr93_L7JWf&;HuK}ScU$T3W~P!Ujyrdg{m7{elBQVr@9}_E8>xiESJX+rsH`0yY>pS7{&5#b zoWmC0?c@-k8dM6S*w8Q&W}X99`sxGK12F>%)r~wudi+Hw<}8izsdDU#lXEz&FPI~Q zv8_J+M(y!~_j-_16T(1MyrD}b%@r|g%evA+qT26y z<_-Bhx}r-0L*e-g^nWvSKP=VMBr*LqtAwaSsem$&?C3m0yJ-If$O0}JSR?9RAR4nJ zoJE&QS7sPWAi~H-N~u3X4WkWp!~O9@{|F1mdc~|Opy=Crmfs8l`fOr2v2BLc7{6}7lhSN}AoJFx7A&_ryzS2GYiGw}y zVSpbjNTixWe2YE9(P4GGkH3xE!2-gE<~xlVbXATLP)4;FaPh{5sh3#=s|?28-N0!Y zTrDT9W?2=EnbQyFe^qfV)BPCZOM=)xA1;SmXjFMT_uH|MkvW|%qq zs)>F<5+&N?PvmBKSP#`d0L_g#H_Pg(=nuxYjulbkmb%+3PDeRKQ!lJ2yu}X;2^Qdc z?!{bfb?aWytbOBldk>n!nV9B%%yJ7oV|xhKrv|@sW3?imfRWtBdzp=Bi|hmvgL=R0 zi`&Nh3ZAS0t?%rtPASC6Xr zl_Xnn<0q`Vt-R43SG1`^(V!udb+!8)?J3Y$jCExWwrZK+;kzW4d#uO6&J`6adQZ6NWO$e#o9m)U=NxavUGpe*9c-4KeOenxHRGuF(UN<8{+L za(m&+o&q62-`@V7jC>waWeH56jeL>1e%4prd>Y7C&gi#y^S16OE1B)KC zy!KZ|tj9z@^simvYufPLNjj$7+>gHJnnM1b`1>5L^K2FNd>2;0qIW|;Co~UQf zx;B#iOY1`*s%PAC{A_rf8TqVu>csxR$jI7M!(}p8*IHKF%`LXJ?nbdr`D~-5@c6J& z7vY<&-#o%k^jC0MZ^}I-Co1lp*l#3uF;A&7C~QDC@oW#xMAGgE6C%so^C^vanC>TZ z&&1r9wso8cHVa8fI{>^@?%rcn|2nEknP+`ZeuTVd&6=LJz9yP>z+I3BxCW%7SL&Zr z@2mpBj|7MRf@K4Y=P=3FMuzDpc>YR}6fN55j1TD+SU>$CPTIDjenp2ll>59xDBpCa>3ClD` zVi`?{7<&2aC?E!D+o(@Ngs)1)Ey>xXOS%lVDVAsvB6B1mVrn(c<}aS9;YKQ-@vg@3GQ*ALHC5bQ=wc8HHXS-Bzs(zS^lP(k401gx~iGqWnSn zIU&~la8jq~YI%SZ_W9t>Y|_Eq{dvRA(`6;5{$V3O00@jEz7jF+kJ2fGr#9v!)US z$DkG-IZNn*ol;0vhU*)nP^hgI#!zVH0--1VSO(%7#iNY%7h_YH@&W8jJZ_Hc0sXNs zjoAMu6Ru4k;zb__Wk0zC`1f>p)g9m3Z?d(4%D@iHy&;H45ToJ02oDJ|s3A^puN8EV zgC?A{AwjqhEK9sUHhLa7hRrv2i@tAO;UQwH@EgPg^@iUs0u@P!V0Utr;Jx$Vp9V;% z!uB0drw1U)LU=5KDO=$+@*t}FTwf5^cPZ0%7~Y`;`@l{D^j~oP&udZe z9QYK5r9IFvf5N01amV({WJNAG;%MM>2mM7< z^P_Uks(9(DUXU0MZ(oxQ3<>R#0a{Wowq$<}#RU9HH5)_Hg_r0+pH+>^Yl(pS(Xdx}(O7t57UbCuWfN zFg&k1I7rHYPa4ljkqSR#%&+=4@igC|nTl}2gtfZcgX?d&U!8&Yc>_lnvu}pUNl({R42B1M{UMKs34Z@*HgN3F(xj$n`6R*CqpS>#*;0_ z%lhG@$g*KTT4Yv#s!2+-e~m2t%U9gTNxGlSn>Amzf-{Rfr9W;qImd+y<@EcS!y?Wp z{T^;)NSOzVjHdKIp9ROH6u^aGbDG6XVMkf8=BEKbw*JJo)L-`zCFf3tEuB(gAoxBd zKLsnmLTQf0Kiw!j-zJ7~68^BzjlCQeCYPx|+3tQ3?k@3u|}$e8HYpQdvK)6tpnXI>h2#-dRsGRCPpGER)O2Ip2!$_At@`wxv;RZ0ap(5L@&b=65NLpWttIC zgchBo#^61mRL>cP(9eOtqIGlW&`9AeA6EEI`qX%a{57nUBUYZQChM9ywLsA|n2#$Q zwnDW)w~66U2J$GbQth6|5=IH}+rQzrWLh+;I(xVcPQ56%>FD5@L-|>HnQM^63C+B` z`dMvzw&~2dio0=Iv&1>aeSOE7PtpTBy3H;{GH%sW2|2 z04Y@CIOBu7)uN_qZ+c=!Ra4)byK=UYQ2M?cE{n7iXBGV#?z&#=#`Mov8hgd6h`f2p z90(FR$a6if3a>VsZZT1$*357O#RF|$NuBc3G3{-gy>yxIV!2U;2oJ~7Pnrj?1y_A) zmwk053vQ|DK_nxaUt|3edYJxEfJ)}EZrw`Dc3w~GkXiMu@`@&q#-UGi95mBq%g`;G zVi}_%7eNY(zQT@to+2uhXX4LNyUUtccIu&JY33BKbu?N{S%u3`0pah=LRmH2cl-kY z@h(LRq*$$>IO}Vs3#*rHb-4+E94c(GaJBU-E~xe>6E3%~wruF>f2~<^l@V`K##e3J zi&mkWV}Wmhn5mTbRszM~5rkDLR42y11y~k*)t@}G(XcjA7K;Q;5V+3T$hw@5!$0eq zwu_afVG}rb{2ICW3$HRW)(W~%!pgW`V^u~Fc2cE#&o!DcQJKHs866*=Q<4&SJ&K-1?C{IQ?_!d#NbWNkeh^A7hmiV!+6F=>Gs$p0xMyno_ zA+>JWKbiIO&73g0Ge5k7FeCYt$!OUlQs^6PKhF%QdMWDd}zA30VR61vJuzL z_z}M~Yl#Qj_))ja_>nKqsA;8ueKm*E$op^goe+iEiWAbJ{t8$VzS|T#p(fWF7+eUH zO)Az{MSVQf0w^88#uqGo*D%dG?lbJ)hke5&iSc5XIyD&64^`|*G&!PeHUtW7=8B+H z*S=g3^>Ybza7iSglDH^RIuk)mRu@Ejxr~m~99?k!o`p*6=$7n$)a`QVOxf70$&9H> zR*z?C(JqI^)QCuw<{RCeXmH0?Jct9tghy2VJQBvQfISHy3KAREp`%sI^pCbn#)L4x zRQshcPnd)Bl{%>!xZp&-rB`7+_-bmRe+NpbKJDDlphUj&7%R3HA_QKAv~x`=LX1Nh zX4M-@mMlqza3JeBh-D1PoGC?s|L%~HU-cXYUkV)Xd-B+Uaj!~k7)3;59Hh8TxtvUX z9JZhZHLMxXR{il!xKL%goG$IesZdqSFq?>#heCU%=f~;@2iQ!vhPDd^7;^kc1vN7D z9Ap$nUMUX0(yTw4h^3)qFp5$p`TAgDqk(0avAGxlFBxu1lhpRP$KR*BhfDS46n8RT z&W_4tcB$P;iuQJJyn1@TVnHHJ#{4A(v!8UjKt3F3LBQ0TnsyohBRg+xvJLfSpgvQw zgG^>mg7}RBs%;B0CDOZY4WV`j!KR2=s{n1$0D&zcG|>e5(COdw$fSaR%_qGKQ@y-P zW3;#?5H#aDB~;?D&m*i;$G$@!)eIwlo*$7tavoCRRxLekeJ7+UtDlr02+cjiVOLXS zbQKIlA}9s?8I4ormO6IaDS{p)E`p$Is9J%o*W85srPW0kuDNM9NBrkcM+2E#^X;O#Y8r59PiozyciCw$f z0VQ+&TJ7_YJZ2Y88HbD1gBkQz09SDA7a+Ba9p73WFo8>cd>XMW<|D4RZWP($W zi$ICZafTZ&OO;e=VTIM4!f3`Ai~Xk*%sd1xk;@GRS!gTbf^Cxwl*6(vg8c|rrw%Nz z;|k-U=jp-QL%79&Xuo3O(V~wq!;NUC0n!>2K%_c`7n_wkzbtg-o=)n{b|YgChDA zQ^Auf7-$)Ku^aCV$KWX39$rgGS=$2K?4%6Y{!Tk1gWjF~=M>(*d4t`Ngm^#E7`Q%O z{|mP>(v9l(W%2^H(o3`h3RMjT2=#6&7ycpT_<^;p*`jMZz0=Bl3+fyi%q{wgE$!ob zK*^naaDXlCyEH6=DJHE^Re$e=XZ^+1=>pOj?VmO_FyY7Fi655s3;wz{EzAL^{`P0O zQkei>l3myT3^Cv=mal@h$IqBxuMAOM!gVFBkVnnmDoy-aLSgeF{-jD(J#2R?`oJ44 zm~i;As6e?5f!nXe*ZM&Eg+s7=Q#fi1?8O_$su>(hu-gi}rAg*wSJhn|DK6@ruHQ5lG|E2kGED{i+$^n+X}yhsI-Ze z)@SDjgWfb!ZK?~~G30yW$HW}v?3vuMD*X1%nq+H@mj@8++C<8~@`?zBcUN+86{Tn( zhX4Khs_ukYvz{PrQ0h|`e@d_%Ui^m(i+sdy;FX*z+SIu&W?83fS~2v6ed3Z)9VC}BrdGD3}EZq6H;xcX(RZyUX1 z>wvn_&3muWn%&<6W>a8*v-pSi^_h5(S#}T87h1-xFOkG;lj3Q}g<8{(>q`^@0r|E# zhhY-#@=AyO)*FH-1Wpxj_-XYgg!-!Aq%<+!qTr^jukae(TSE~tBa7=TtxkIHMR1Bi zTDV>N zC`?b6+YU@JJBq7_Ze=};KUx=0E0-221mHESG2Ss2z2GXV7wUmWo9TZMgq?%(UWxPS z@phQXj9s>W&e;b7SNyGPTM?X6mY>yQt{lq|d6pu?eETdGO)p)+WPaumH?s*;%s#Ho8l$7RFpFYwjCqIOl|cA($Vj;UV^V9WR6@ zrux_*lt(Gv^gNI%{m`Fek>h~|pY=QDGTl=_U*LgXgD*#XePCajrB1nG*A4g9aH7kw zWM3)=D{l#aL7d{j%Knq_TkFTF!1O^&#KB*UJX69-wX>$WjkPa7L6g?=rG=+BsymbL z_tM{w@izpBPUysvBf_yq!h|U&%8>QKp8jap8iccvNH-s6hn7PHfL^>}pvWT_T919F z)8z>>l^xcUa+&!%Vc6+!0g!3Uk38|2pkw_k&42cca_k~J|A!8ydGp#{zwp*K_|o{> zS+P6V^&THx!|5hVkJW-25jFGzg|?`Zcj8T{_i@X;>LHZq+XV{5;NI<8tXu<6@b8se zgq*^Y)@p)Lrg7L;+RIHSdfQ-|aChdMW;A1dSp|{K3+r*-xZ&wdfj6W7 z%i3(E8??iYF@0OFS6vb0H~}R*mhBjnmWcD*aovf$gts`@Dy$fP}wT#|%$PcCR6o2WbOP zf}hX3ocg}YUe9yW$DDCpUwy^KfWY&yJyGnZ(~a?C%Q0q(-(R_XEY~kjjfax7t+&16 z=~c-z!mo#%w9?HMpJ{vj&qDi$lV1_QD$(y5KjhDrn-KS3(;+~sNB7^o%uP-{o-S2Jho&c?c=l9ARDNED-n!mtRUi5Mz2=h7O)!1(dRnXJ$roIXz4!U zejHxo-+r<9Y9PAsv|3x#=u)U^``h)U@AD~n-O1c~d9v36_#E!$=HU7@ymIFHekG9! zzLpMGh$FavILzg@U zaafm?pd)ni2{&aj<5BAQQ(YQV33HNVwdM;;XX#T`4prFN1t%^)-3@AvJN2*c|9%P_ zfAAi^Phaz`71Au%#YHn^ta)UAW)sSLt4QeksC*uz+LGCRm*QSZ8y=Nov~40AI!s(} z7MRw|#T8eY>MHGX ze~AC82K{ap-=(}f&&;?J1IA&-UbHL4^0DhCQ+3!8II%D#k293%dX=V_yCO&`pGq>p zm2SECJ6&oxIKMu^8sRstKn*8k61YmRq*(NQAlNw5U&Y1esbJ8!zUAj$6CAUV#1b!;^V2xwNw8^(mtn2BEUHUR zYUycrvU_X0z2B$%%)0!$u(tpbdRB$|^AnA-BH&fVdfC0oX5AExb(5Y(G2Yvie`-^6 zpyV*rpb&_NeY+>3wPk6kLgL#Y0L0JMx7buRrxn`YXlkO@W&pW&}mW$WzyPn z_po_5Y7J=m$8J{Es6rl>?JGteoES77!|&8HkVawKC^0An%6shNP`TgK#A6_{up=&| zWvFi2&6msm{A%~KbDv9tN)^16IsD*omiw@cngje+jvVUpXqbo-bKF}xRq%ze;FG>< zm*gn?-XKU>V&UTI`sG!us+^eS_K<(P7Mn9b;{`eO=!)vhap?&%A7ic9$)GDdM)%z( zeQ@1wSui3sUS4`yO1VwCNbW}&yQX?)5w9Rn&fR5YP%3UXWmT`m8>ViiEcS0iVjW-U zvyhO2ZPRu!3f?Po4wydw zEua69Ud1e4s?$Sr^j+&zrF@_;;cmCmD>QT4+s?C5xxvt4#8r}v!qFymDIV-mW%9eq z=cb~0BpbEg*|_f-Vq78X!Zg+Mp-rp?3Jox=yTny9y*lnsXp5}Q1 z6n*PDKR-hk@nnP}+d?i8p&#xGGi6@?1;_-`wai!{nzp8`RVZq z&|*E3>{H14xX3Iz02w^vEqta-83m54>?NT!xjn_Vh0Cw#_pOat$_7y95h*gOJ~;9B zx)Tc!Y@+bnT(Cd`9Ka+IM}F0=syJZFK6>KQAfkMSgc?ZbjTpS`O&H{k5Y=~j;j)vB z6^vb&=gZ-0=9=e<<@zD}>0?%VV6Rtk>>S)+O1;ty51*CGc>(Y3Cr$(dDGab^_GSRY zP6sd?gCZBHA+K%IaE)!5l*fqMo*))-L{>~uc!LJB*um^RlrjIh6>=asa~Ya9%@Fnw z6Tb!yS?iPp7OeZSkTkp;Uq2UxqQ{P%U)2g~7zGS3dvE*0nm*Tq+=2WRQr@c?D>hwf z@v%`E3S1l#qVSR16( zI9?xdRyudBH@E?NO7qS++->V$U0*{-FBiLEZGx;9Mb*nkJW_I-(Myi9f72aL6r`oB z2ZZp{lb>oqHy_t^Du5cb=MApZ)w8?vhpsiy#09n4e9-s1Ixe|)!BpgK_C2f4$}C27 zAp*Al)S9n_pg8)>X2Yrsp~;ybSyy3n43RD%;A%mwYe9Hx!@zc+75){Szd=~+KnV1P z7z;rn-GB=~6GMv-qxTboDhQ(wir^25f|3u!#e-_>@`Mu;0d}G!=zRpE0lKXa6iTqB zN{CuYAWIHNS0V_D1K;?ehA|Igg6UBW12T4W@p)!e%x`hpIYR<{_{UbBU%IK z12Zf#<;EgKa+c#VxTm(O(uk=WZAGTmGG4z@2|4Z&b2-)M3-L0w3#`iDi4EkKtr3m# zR5sP>y(-v@waKJQ$tyQvFK0}WVheG)8?}Fc9~1Cfzg^hsO3YZXdAf}*A_#5WSSkff^q1+&(<+-KzY^tGtgU|=A2tcWt}9`r>j^fuZ5Q%HE92YJPgV#oZN6vuLwo%fHtGg+q=e|Rmo$KaDXuIh!~U- zCXZ)8r}#OS^3H;4vWufj|G3M9inEsDyPG^14^c1VFiGJDvOF3bIu_tY*FX-0!rAipBUi9NDXtU|u^IslWjgqVxmHWe z0^@E-W(KJXI`a>C8zM+NnPVtYZAeQ>j!R~gweu)8ctzMF6gyPcLBx{bfiRyZ%nUxH z5^Sahk55u(As+pE{E0dHtFX+9tW96gBn0ex)17D zs!4)Qa!b``8+=KOf^tSH9c%^7ilY!dxfto(I>Y~=Rk@IB?YeGlMjT!KLE$c#%z3GQ z7XpS7k)cIAXV$(DQc{mb0y3WAO54i%)lhf^|NRg=6zz}Y&5(L4el# zVR-2DQ(-j-Tfv1JrIFn~?l+?PuD5?DM{q?26lhcacxC>$kX~8hF3qWwSZF0^galtf|zI!oGUh!I|1pH|k$!XjBz={%4Q-C~0+#Z{_^ z2y(=x!4ZhH!wOAqHANqQ^fVDf_DXsG7NXO zb1YvxU|9)lfPK~hHCTsSj+6_>X=fT*R#6Ju`*`pW(-9Oa)RQAHPM*n#ssY7l=x2D; zcM(*`Uu~>$`D%Ztji@-GnpNW1#vEjI!69jPzxDRuc9co(5S#p_g5Z<~zkvuD6lW57 z=KUe)%k2_(U}#DO=)i#E4tiA*g^P+)9D)%Nfy08l^0hH3>hG?p=s#=;3eU96>{s&h z`AV{TdB9lIg=vAJKQhU0bwrJ)f%uN?+ke-kUU{615wwYg79aQ;A5Y}>~ zvk|2ebo`VEv^9YWDCzM$87`~_#?;fL2tqz1z4x5e9bo?RS`wmel5o^ ze%U>qc!#aLzg$$VsJdu__;NIj?alad=+&rN;|H$ z9c|3qYp$n&^Xu%FgFCP$#tNXv^Wm;?sxZQ!2= zJ(Yl``u*8abKv46H*q8NZ!z(v(DL3x<3*+UBPqo0&(y?*$wspc^vNj= zuuat({%T)MfxD~GB=eCRzw`ctv?Ye8@+8T2U!sqL-I5vsw|&EznNo?F+%_j>&j+A4 z1g)hjUF$6hL%mcWU73)78@ODSB@$eje^c&0<__iBWs zmw?=OkF|}qfp31bm%-I)9u^^%9hcgxIj!FKHet6A?3Oeu?d#u85-gwOVQJa$s|3!b zUwUCGwdmJoqor!I{JIS%|Dj7mQK`K5Q+LL9I;Zx_^656O=gsHMT=#2Vdx>?X)~1ks zz)WyqX`!u+wWTn7KUmQTpObH5RCi!Dc9vs=fcNy4X7}9vR7Br4rnX*NyTHt+)(UM3p{yu^x013(h z8L}i>t_jWro^%AYwzJi&rRNR$zD!KKdgfeXei{bm#Yd`+mmwds8+6w9y$H22DK^Mh zf>RrqWC_Jf_gL{Ht?WN{J3lk(&PXv*SXfg3`Tyhrx}P5VV-HenTAJW6CEsX+ycZ&p zh0GZ=r$qQDylARQopLM?alf1M0!Fq;GZcwh<}T#62Q=`$_$nxM>y@L<5%i4aV^rF= zgiNR{vbu}(YExA~#-gQ;+$z%M!%{^~Dx~rR3*RJA%gY=`Z7Wq%=5Jl-au>-W7B$zc z)(bpE_j9RK@9T%_d@7V}8yBRUmDGFD6%d9t>;2&p&xQpTai{1f^`$AT0HG~ir8;Qv zL!)xze`xcFc?-%yQ8KfFHvWUQ-NEcNIhKT^f{a1i>WQ*m71@LN zVUijQb7mnsI&Aq^$SakfYIu5HDnwyS=XhT|tn>IZfir;)66IB+7A%=FXDKm6?c{dF zXjNKmkPLZOE^^0Qf&M96z?(DS37OEn=U6iSVH0ePhuRRWMZKaZ$kgtNNfCXL{7|t| z35`_2ZxZ2q5>;pEL`?q-1GM-fID8FqXDKD|8p_cTifMdIbjNO0R9bF*cGg%Y(cLBv zy(g+jN;urRA?|Np5YGL@R0O=Ur+g|U;}{u4ut#$cr{K?26-w1Fz==C|!8TUBC#Y;i zKaw|j$S^V4eYEU68@aU|OvVC(sl^=?k1KkuomOaGmQ38B`D8>)crH_^Hww_-c|hg< zM*az}4(mMH$&m+tzi{M=tReMY%px8*N*Cq)k=cQo!KrH{F<&Hk2#Y}s{obC9#-bha z*&^aRIcBOvQWW*cgo!XZbH$1YWn2XROjnnG3f-aVy0M5r`zW;?tUwf3_Dn z@CSmKz>b>4Q@I3tjYq>929VvtWEa^xwy#?G|PX^OW24}t; z+pyqqii{CwIX)sj2Jj}>4P)JT@SY=oJ%-*hiuf6JM+_m*-XARCpcw3q>_4gl9C9M8 z4ZHMrZ{Kw=qAD@P>PX<^I+2N^zNxs%{4NQpd}xVccPhm{%R#RcC5BhtZO+rY+&)SO zZL|}H3kc8#(wwrdz+l`um(<`oUB(wXiL`6R*D34MthI+A>OJmto_$AjpLyXa_31%WaE(s8p4>aHphG@hBnxbTtz9)WEn$~rTs18D^>rU9Jr1j=Mq){f$?b$KhJj8(`UiB9gVBwk(~;)C^3qWLhyi*Rq(sm!jdfLMNb;fUdvT$K*F^p-g+RiIJ7MFAPo(|TJfvERoaoi2O)jYXzA+b>mjGfN6_=x zSz@KV@qX0zU`SYC&>l~y)nV{(AGp^n_M8gR z5*-m6!Wv+ku^XgIUdntJ)7*1MKs&tYDmLc6kM4vhZ{dsv!jTkKwXI_5yO?2tMj z?8u&3zwX2?wG%sN-TR$jB@-Wf8O~L&G=(e)FW!MYj$H>f!Cl39IWW-L;}1kB?hlTB ziJgofuOEN&z0N;=x*kmr-|X~8`gWr~n{||&D+WZmz6Tl2T;5Cze!B))|Hg^H zh5s#VSGo=h|A~~V_Jia(Z@bXy1{_WcP-N2nVh}zJhz)XaP2;&0c{cNcZ|`%s^z=yF zeN1kscizkt=G_?La72=OFC7x_;KGPRY8io4f}R`%UxL8-Q7uZ>g{++$F~Amrw_6{K z?C#$RsQiH@v1{Z!RnBL;M1--}#jD3KVQj6i#^RiV16eC1YU+OpUkF=_l)Z`rUN+rd zHd6w zbffVOMmIw&`;Nq`va9K04dSZ9JLWI8T68`s>7QwwsUlRG#oK=_I{p~Tv-0;Bu z+r~HYZ`=VVyr@WO{SxqE{`f+qN;pZX?{vf70pmDk9J5)Z-G-_rd^0bv0DQx@vWn5} z{ugax!G&TUy@aWuXai7mlWS+SmgF^iP%ASZ+F7UJnr~Ld?Kd0A=TwpA82cJvRn+}- z#!hGM1WE358Zwlnw>9R>O5LTSN07s+yt@T_4;VlHrpsr}$cx{R{QGP3^R7y0Bl2J) z!{_^KbpigC)?gau?LhK1z%6w?y$N;zd1m^rfI3s>Yb6(VWI?}gc?Ij6k9lZDtpu^Z zYwCZ`oU`tZaXF)$hwzFXLo$4TLb*Nmejn?d+YYmjNO^7k#6?MDc*leX_Yz~XHRWVG z#!2DSIFrxt9*3xK)M9fbNk4xSh2;)a>i4JN@BK%b^_TDR-x$kh0PVW#ugYcV^itQS zoz*o~s^9g5jv4>6-PD_FNA8!)NJ8`1>*>VyRxp;|`)GtH7NdICr{-_Lrn7`x`Zn&5 z`dxlcu1E#*=l4;-P3Hs7Hov-?^U6W+b@qc-B8UI6ep#08r)pNIDj+MLqkd$=R>AKz8ez;PV%l$lF zbFJ_DJTw%+`%2{CULB)Y{paca`g)7oWvju{Thzz+xxVH*?%DS_qFvD!cv7nuc)B;ICG;utdtRw|A@n{5I<~%J@jE|M0X{E5+wY&c z51I=JEl=6}AMeiBQUi)#lFvzN5H+2+FIvw!NItWj?}1!`&Li7?G1%99?;nZ+pHJoj zt2YC`?z`1k-}`(S=P2dl=GXQ1FPsf@JL2j(7X&t^tT=A+c`#S1TlKS}OOKfbr?U1j zhZ&80B?Li$>F$OTa}u4x_6Ndp^LL-n!uAySkp_Vj9K{hU%!U@$G*-huPWFk~tNY=O z766C%cORL(GpOl59=pSfOSdnIn4Pa?ein=n$&`ELjro#@xPQ|jiuyM`Kf~|5pXU*t ziiZu`#_MCK#vyw48*bp-h>oqSwdWS+(Cdp%qaW{q?EWm~YL=8(6V^Gf8(vQS9BFb_ zvP9N3KgF9)YHcl7iX6`^q*3_e$n4I|a|L_&&4PH6^w@1ICR7o}#~A0B_6 zqh^00LBDmonOp{Kx0|aFyg9lIJTx&XIt}Tb!YQLus5&uiv}LR(@jBl60OOA4mEo24ZsmaKmFpGm4u(beio}sc4Du@r zw3qS|>A&xGUbIuL1&H6i#bc#ibNsv7F?_)x1KFBzF6gGI)n^q&=Zful?UkY;Qc@HI z@kOHZ(olD#MTxVP1GFTRG@^|Q(i(}R*;ge*$Pzfp`dU7{|L6rwf#U=CDW!P@h2bZp zBdjJudVIVrS`u#VYSTMl*zRJ_pG!;gIQY|7Puwl%TW{O#U(cIgpMa}|sH!Zpa1r7% zz-@KB6uL1EBhnI291dx$bmju3*Tr9*hkhp+sE11bc2Ij1`>HD|OO@LwcYIyg^;zA` zJg4kVyp!m=={HUWg;p2<dsEc>$sm6fNys|@^%$GWN~pa+z$sEn<0Wou_uA}34l?$=@{2fi2~(p` ze(Kmc8ngpyw+WWry*GDj4;DmG`6|xuPDHvMSlnkv9y;FgSV)K>sTmql*Y}}-@I-6r zL84RS^(X1an z?FB=Q;wRn|1m!==fT{GL7ht^srbK8DZv*M2<&_$NDe#O?9Qjhj-+HM>v+`8({*fY` zdbZI(Vp~Cm_qg|MEPi|SNRmPioaZ@-J+Ok$!}lj;^`FXdgHmHdE4ZLy&JEHAZb>+B z*nLl-X;h+wuBYb6E>|TM2OEh%uL@`DNKi-gh$Bz80b8s_s^`Dx<` zFl^u9yXn=@msd$m@?yW!Epto=Pg5@5;`byeVj$i{1d3G;lI_&*D}#NHbkl~J#kW2N zrHR}}_t`+4Pmb6hiy4>6K8+T-n(eh&WU`4KxM7gI(r`PG!veoseH!SzBqk4o0pI$I za(w{L@hbt7-Yj8U{b}D4AdX3wjZlcyh@At~u{4cgqsa7hKwO75#r7GJhT)K*A9jGS z-lG-v2mcv1_^9r-oJ>QBqs1`hl7Nr1Fh{qX7MZF=x>OM@c7GgM^^QD5n?|zaVn?AN ziE_jfSxC?r>y%DCo&cvJqa{|_i|0H1wuKkkD`gJMab1JglFxBz-yTcSZd&prqMeEvqnMGw}fwnln)BQ z7np=OLZ*i-WXgq6fZ1rG7)o(*vOo@npYyIIMJ@>@DnCKQ;pp#_3(ywr*i``XF1%AAbu&R2SO`(bVr&m{OqEv-Pc)VOO!^_DF_~^+&cA$UfrAPJ zV`aSI{-YANe;@ef4M}ppg;)ZcJ)Wpa>G-i~>yyeccZ!)n(YtYRH>7CgnsaoxgR{Bw z#8!U%r1P>X{M(gyzJrhcfG)q=y$1!cx-^BHP>JQXV{a$8MLS8(?;dR(d)FBg$a~Wr z;3$E8Bt_}X2BeSCq;Ie>wG0Jhpn@e~K~Wk&Bz+@%g2ZnaANI=l1ZvenGY3Moz)vDq5|e+vemH-$I@Gz5iY8FsWJ7DNPnvEau{O!zbb z-*WJT2Vl(mtjZkpgEx=Ci>hywx%RB7*5cMIUQwJ@sBxf zP zS%U>hyWhqyv2)y3la(kz5P$XeG(@47pkU3HFaZtuTy9h*bs(`^gsW4^Q3KeXzlYVK z@Xn~R4h2!tgJ6XIhpThyt^``vXl!v2EM7b^DC_bk5uQ z0jtKSwW_{38>JeKjdhHUgdatlG*s!iK?(7a-_p&b(ai;9wV=+8EVw&~8`p&8q4GB6 z6vZEbN7Qhvaf@8&D?M&UPB~W4$YI?s!N-eg4(b)U-YO|rOdxRDpt5pt7ZQuan zWbpITN@Byc+F0C3^z=fEf*!pYC~8$MkMeiOxHy$6Of^tV=Cj*a`s3~A3i|clMafFMugg?=!JgOof_PJABY7t!bh-7Cg?wM z3|>o`xdlx$UQ4*_{xF4od`6U&5bN%c)a+=FlrRfB+&tv7dJs)&1G}}=PJ&Jn&0qay zq>vTu#Ig`jJB5WCYPPX60yDMP`bcpdlRp#kltEbZOkEO;Oh?X3plEx6Qndh3Wka^4 z;r5>B`DXP84RRGu8kHAJbz*ZXa+*~wp})DNXWw~39R)&oY=8KjgnaCwc+LSiEu5Ll zWrr#IcVnE#ZbE}M%A6p?zHTC5zFm4onEiK)BAjfL6ij9E=A>q37AjKvhP%7KRj9y# zO08B5$n;_byE1O{55pgeUy}f+k~GwGwh<<1|8Apg(VIu`&|BjNB1l#Z62l+R!4qh5 z$s!ZS>8^IJl0~_v2;#1se1i6x?%zACvACiBI=hxIy+MKbE=04#z91v`M9%9A( ze5+o!-f=^CvHTvJn9KmvtPSr-W60l4WKkTLg`Ea~SDRk8*Q8&0`2L$;V(+t)RzvV6 zHFh~3Pgo}=Pmht`8`jsan;zY?6^GIqu+!7$Et@SZR{iA$xm+lUOF4qgdv6y}HlblG zn8^p4ctAfY{_of-yzl+Rodh@Oh-Dwo@l}^J;O2MQ6L! z>HM#Tq_VZTA;+ccB$br?_X&x6!S}n|MvEu@#3#QojnSdZOwR6TTdsC_qNmCi`|sZO+`)u7CMm){ZFJ_i1$uhgHww#@&vu=`_AX(pw^#^NMGrYM^tD zuQ}7}LWft(oFd>HAlSt79>&Xv3@unWsoQ+~nHr)4KubQgI?@o_c+K(fyyBLb97p@! zS?8~}zvtxsILeQ#K<<3Lm>mKputMgwz;DxK=(H7;)!R@XL+4p77v+2>J6*hA%;S*> zD*GuM6l6=t+jZ3Yd_I z=ZN3Am4JH#uBDqZx3-fnB7I(8>fR)`cYs#QIF^5>kKtoy)o7jp|AYB?7wTEu@>EMF zw*N&)LE4jCS^PRyxcGY~Zys-pd2c&7z|yw1>+VzKd(FAo{MT=T+!5ckg<4~Svs|}J zfPmL!)gqY&X09IdsRJXSfQ!?oL|jodVA+4C7x0Uy+vzNaD!C9^kRmRgs5RH#?<8?I zmk7;+2pK4j;_|82dJHX`C~Vy%-~ds!^hR`Dfs+~Qr>2d(&#zT|>RFR&#q2KUy0+D9 z{Z%#)!wR3rXwrxs)T~gOtBHpMz1!}af+g{E#s|^FcF+%Xx*vWtM@E1rf7p4qYO*Y_ z3NT7fk+5PF4%ChULb@bHVeNw# z%q}DLYjHB2q)GS2UpzmezSZ}ghG2xaFP$HU~Z1d3{7%0u%o<@ZH?rAZeBG^;*q-DkQ%pan0~lAQ z9saz>R_3;ye!jP?wllWkJt23u#++Dge9w~2wV1p!K05H!J+(zqd35bM&FO4!ES7e> zU7SgI@PDLH9ba#DUv#y6U-~`Y$K~;5KWlqTf}Xdw0D%|U-}>iyHC)Xyl6M%y1O` zDVcSDK=4Mo6!DosdI0lAy%c+&0e$%4jYA+TG3zj!bwck7))loSK0Mp~2YJTo;r)A^ zMTGf(B?{Q26{c=*ARrIO|1VJxDE^N`LBr1x{Sarn%OmNZBcXrm^4|uNq0v8eLfpC| zG`5gu(uUIzGT$VL{AX*&hFv0ATF1US@{AIE3R9y$NbJuB3|cj8vqD@Yx}~)tWV*FI z)56Pza`vVI0wtS9dig`-_f8)?KEbo}jN`3izue_&wJhXS#27E*W6Xs!T0tO?Z`=_o>8fpszE^U@H^Lb`0w?<8LWltDTt$!(V z2!w<%1B-{!`A5*XZh`N~BW%Yd@cS}mqp)ca#xpNsx z_m+kKs)@=dbJDZXmgwpdEpQ>KirK%#t7nWLA!nT)K(je{Cq*5zH&Oo@RR!u!mh5MM z>bNvusFG%B*p1p|WE9c!sum+3HRy2th{b_S8n@;dJ5+Nl)ILQ?7^GAI-2O(eRe`Ml zwKi`cOVG%_3Z`eBr@K}ta$|Q0^Des`_yb;^1?+{Wb*4EfYcyA|LNPb4trW~%QWc-Z zQuP7wRG(WH(`=gtD&8S*BvFOa$tr-qFa%f3b9O+IFr_ioORUO~o zGT2=*nrg4>bTg=EV>N#Pwr4@IT>D(Q_UxcLa13*2R8SO13BsriEjt>dG{=y^XcY&$ z`-Pac(G}oB4PEHNXO+$9LVa*HE{6n=Q$IBk4AFZxN8&HQCf;5W(*7qin_jGek?neixSN11Vshw_NLxfY8Ial@5z>$~w19k5wD$ zn{uET3)TxyF#VMX2=C?Q%4M^ejpC0FMTR!TCvhX;mS<)LB8DYf$*41#T>KMIBr(84 z_q$FR=5Q=mfOTC0!TKCLNQ5s$5;sE77nmc}qe-P!id{*@pmEV3R(*vJehK5`{|Ohx z?*qBiraC;~7m|)4?V$9+zW+MTyk! zyDAz~)gqGw;A%u{ZF9GReQvOFF<7o>ynU?j!~`AwrQm zpCNP^(2Nj8`5DV!mxJ7F49)l#L+A$^%o?SrdSs_&5CO}zv!^E&;tp!U0SXTY3cO^i z`VR&YSpre^m&*X69#e7bI%zrI(H$9V-kLBqXOg>f0CC7&E!cVNES>2;z`SUKeUM#% z2xq93JLO9r<}_ zhpku2#B_D<4?%_92Ou^+LKTZ`7~Kj)exw4q&xN>lMH7&Ix{o_E%AGF>MxX; z06S8~1ap)qth-6J4(y@k)}mGO3r*=I%u-Y>otU3Jy-XH%SU38nK(39}^&mfgI_O&L zQ_&j{%+ZEmKbFpyn4dn;Ro`#xwFvsb0eg@4aJ^~}qqdyxuB0Y#i5XGekG{blL1#Eq zz0wYl?t~~{#sQXpoQXRqOn!3_XEk|f0yyUHtbnTeR=2E`-(d%pFbB;{=3r`RJG^vw zq#S;-bON;Lz3}2&f;H&;VdJOk zsqV}$6%J@+m>3%m<~t|X)+^h8w8=j8Ruz6N6s7v6m>t)7jzy+0j&^{RPSm$(0r*q` z?R0cTP-MIRRa#XP9bfxJki=AP`&zl*zN@XMzm+uAJ;*j)GlkA`+*Q{>A{P4d2zXTL zpT%+fng@dKBBx`tIEQpDj#UcX_x`B48itN%+kLdKnhHL@)#G8nt{U>v;!TObg=a-U z3CHcuWn50jtt#aiO!Ne@@3m44U~ydHG~t%Fx%=blU|^O&130whI_y4d*J9vuvd>$_ z;r)%VKkT6OylMkYSlBskuvz<=l=2-J+gaGM@$q#sNOhcZ!~Xg3*0t^6O0eo>`Z4O! zkhj@gXuLf;?;*(4dbm)`kIbO;H3a0$FhR^}ex~=^e`WX4u&*du$333{WW!eje4q2S ze6Ai{6uzE^Dli3Zr)L#SRF+2&(*!<0BD*hu7TYtk8X^)`Uazgd=f`q6hY>a255aF= zi8oh;`;4uM4~5SQ+0TY+KgTntqk>C|sjsCr^3IcBU`<2P_q$%q_ljTbRqDXY8zj|I zT<2p_ro>Y`r8!#Rm4*sn@EA(x)AC&!S^9j-{?=^?IFb2{dv1#nKfg!II3)jz_jJZ^@w}jib(P6UUvzP?c|5P2V zgwq~E%tN7=XrYOcgH-)#(lI~adYxnp*lDMwBat-68ee=Pw%IlsR$%8!$!DVwi>U9LVIf2s5Jo(eoZUuUL9M|d7i4i_4z96lc8 z^xu0YLSSA%QUA~h#WM&`AuH>cRLJLUVC_{ zkUwc~jIUlF^9k2i-#yrDTQ3Xz&IxhW@AevMy3qkA)k%Qi%-l1Bdrg-|2Z4;pfA?v` zCr`ld%INyg0{)yZ^i{@eg#AGOEUKHqX2^1Rn`_r(YP5|j!Np}!7m+in;_$4MTbG|R zEo+#5$F%yl&5<@&h|TfGhI4h0&Y<3ikv-z_BxGyT2E~S$E;829k_%pIMVn2lO&dgs zbC7Q2xD#ns>p9Sdge3icfk#!82_`9!ARt^YX+nGdj9*{1P(J`EYBrYW8tA|VkEKm1 zj@zrLqr(D%9E`Q%QTg_DiMcs_PV4ZM*222Ck_mfv^Cb493@3Ar;v&e3>J@0V&_4vS z%#DcDn0S!zb7?=TsKXE9h?7%^8xPMXp4!7Yw9u6>W0kM*ZE8=G1X z1O6nhwSKKWPBjClKn&YKK)@WzKDA^BUGZ6|EK({@OVxXnJuuYPZ*iXLK^K)C69N*Z zZ6Ng?D^krgn{xFyT;ZV5+4JbF<$D^VeT7w4cPp)a?P+#lcbuTJ@mj8D+NQ@MM(i<` z+~fRW{~Qb~9?|bKD=#v8{Jr)SnA@bEOztg@}v+Ve-5I<0!b`Xir@sSxdqe-A~QSOn)aS8#vbuxRa527@!C<*$kvJLC7 z2YnIx$*&G>^-<$~VL_7zuR_E|Vv9Q=7$*$FGEcNysb>nH2H8^vKqQN^&APRszKL#7 zpmCm%SCK z>`K0%rWpMp?13C*-8UjyM!qM76cNUpSq|3=bA*!wd}=*1ABk==0ZOWA8W)QM28&_d zD|yfa+(b~jmS{fUh4Pt;;fMI2(*DkHvVh^GVLo23i6pQPyZbknfQU#A7yXeJk48E@ z!JY@es5px!pGl5C&hUEXbE=S-5P=~)mD%IJ6 zlFR}T*BZd24n{e$592C~%#vZ~ib{UOqiv3Z^~BWc?eTl30+MoNMqdS)HynmUnAmA_ zJ3(B8GaSs4Zjclh?PY@-bb%$4CTtbi%GnNZA&)0!?xkEK$h~UBLzsV3#8m6#HytGu zHxk_;0h#7jC*lv-?5F%BBv+OT)&!9W&bO!sgGVj9y*U*175xw;6@7v84;ps3n__?9 zs^FVSyt;pQr}4cm`G_ev^d`LV)$o4eQGnJ57JUHa#B%PE2q{$$415{7#n1IFSMdOy zo@Tx>z4^c{QEzc7mke3TtWn}iX@B4ljduRB!*w&@^R@W0hG+ktbFmYb=lPRncW%AG zPh%^CYiZr}qL$CeRpL6OAMGlEz^@uW%DFHW9oF*%e}W9sjLnVf1D(**eO6vsG+RL> zTL}EyJsSxOr?;xA^7*8EK>ng zb1yU|{b==fW8zh2id4@i4zQ*b4qdSHD!tx-6|!i_r3z&~OpkAe5FY`d#ePG9;oExC zwDGa|8=d$nE0i_&0cSafxXRJc;l^!&kY!q{!;*2sz1aLSLkYcS-l+!T%?!X2hp|eD zaj;Nd#RRc5Fg`Dcnl^sVE3ccvwad`wv_3}9yu6vEHRGbK)Q0Bq$iI3RN~cq43e(8o z)KF}-Er&jIsTeBOb^CaJ^)Hzr5( zkF|&2uRy|=U3Dg(+oJXDg%yC$xjO+-_?D#aJ0|bL>{kgDLl@uA!x{_D$q7FuSe8vz zjcrv21|lFc-|gY=XJ7}{XT;nf;iql;y2ttE=XrDY?R-M63Tp@BgumZUAK#C?Rp{x% zJnl{1?^j0nyK}5J|JKjjYpQ)62Mizi#Cr*Q0p$$1D}jp6h5=FeX=h$UU9#nYsvULb zingZgk*a;dz05XshAF9y{_hqN@{{fVo~X0sdM3O6lQvo+fPmoqe}HPE843|F{7>1q zdOum&RSV=dLExz5#xt2BI(NV`!Op_|CEZIgGs7{v`D#B+CvdIre$6EQhX>+ESTP(q zk}F!vA5m4SA4Nz%lq&oo{!&sXhbR__rj)Czua%Ng2PQ%lKIH-1bb=NI|K>ip`@bJ? zUVm<7<;>e|Hg$yTOB;g+eQsX>rmrrKxV~s(_us;+;s!1)*KjF9GZy8BN8}mgG_A&C zw*b$_+=I8Wly>BMTKz2G|Mob_v!MN!utzhb_|4M2=PbCzcA#4fwToltmV#@yu{Vhv z;Y@p(Vk!m8;ts??i|LF=?X&zho9T6eKW0Vwa+d6r6hzVXrHWxP-XM$tW10bt{)!4{ zL$v>Z|9RT9us#un=o2=h4M7P3to&NHyGnO8*VgoDMeyrM)^9l=R}(!bFQ>>SX8MLwPiHl zqA8WL|FI25?Gmz$XNdee<*uIgJQL=Bu~dH(klAV;X2Kfn|{8ACrR zTWI(MB}|u|czl!ROaksq?ZH`k&L@I{y#9va)7_P?y4{2TKx`2-y??t8CHKkFfMnAM#Ch-l)5#Ng9c`fF7Lh8U(;p$xx6+Wl_|DS|BDMEPIb`XNa`VVwen4hE^)`wVA_R<) z9b9P&MY(uC08CAUfdP%HjE+<--+w4DG>YZUMj3LZyfh||2EDaBSO5jVr{j;;YT(i0 zpEg{!?dhvMXGwnI0O`8Qm{7y}=7Ygh!F(mcnphA&VOqo{1)KV)G}byW$5fL?^b52T zMzgBQP*Gdm{XIBKD@1B|6^e;7TwHOnOx1A{$=1M0fN+ORen-e2b-C8dKkrODZo<;v zd~90jpubBZ3@^FGYLa?5iqpsM5EIgsQ^HR?SQ^3u-Yau%$r81HwgxMD`Z>J#tITu5 zqcno3z;dD1`uJiP)O+Vaaw*rQ`6d~vp%lo17fM$k@Md8ss#nYOj+~;R`k9u&T&gY# zE;PW-0B*%W5}G+-*mexnpd!2|Yci%d-j{o80lppbXQlDn5E!!k5f(FpHjrv(iE3Ub zh-PrxW?>%UP9;(2#IjVb)fK{P!7oT<(4v*Rdm43>!RLkc=>ESh)KhMP8@Vg3-!Y1Z1nMD=P5eD^b(zrY55?( z1Ac7^tf!+iC@x_!rd#d$&`H1^x^2W>{T zE#T}P?7vt>Ub=c+UD-1#E}QL_wfWb40KlR=(_fc-(qkS8&E1yh&g9xOY#>+Q;`+VY zHlm0g9$q`UZoTlgk;uj$EP8YK*s{V0k4y}gnif8O@LHs0XSe!X=gIi(@tzFC0Y99d zlX!U+vCg+EI^TW*>fY~Xwgmt$2a~*YODj0O&XE zYZWW?QGbKPU(f`s-^ODb+XjB`tAKUJr;q)f>+>_6SYPLzp43ZoxTCRq-+>p<)?6;f z61;sdJ-Hk?emOg@niL}}{;=5(fK)Zu6^=ZmfRQ-bk|$L4FLyQ*9oS7L>jm_-themb@31@MhRRo!&$sYRFECSxTp2|<}Vk64Awu8@B zHG^@19!oJu3KQK)Fs$mjsi1+$=j(1cM=8D@el`|sp;3!m+|c96Ukwu9fSW9eYa0rY zi;K2-Uzw<4t6%fKqZmI<`{wU`-2diX?^Wya`(869x>R07EKB?{X#9NiGP>B@&oUR_ zVW}t*h;hj2_*$aFS{Hb}b$RPfjRi0Z+;@~1ykDG!7*K@;Yxw&ZbzEOn4BHTW_Et6U zZs%sb+>GT3Ms|PoEy_H017z*|sp>Dfcv}jn1UFpvw`#ub&F`x_+HxG6H;`Jbg6h*+ zp#|F=V;eM&X*X!|zS@R3imzS_MN$68Bn$pzM)A>^Y^e zwrE>P7#lTpaCZ1WKRd$guzCm(A)6Rb4pCgZC`;tf_LlL{BIoHbEOR?4lOM`3I1P&F zEfzT+wu7#cfAVnD<#Y}sSlB6E z+<}2)^vRZ1RgvV;f_q$8*p}Ezl(@V`UKTbynYvNt(8^+wxj8B6$>udpGv2gSDk98a zSx6Usyt<+Sma=u17&v9I?pHtl%5r2?qt8>6z))m`TV#k(dx0v_k$lQh8Z@0Ys$|Q# zFd-m*6*{Ii-MbIQPOMEuU)B^R*GwJ2nX%iXjUf^e9_cfMiFsGpRf;hq2Ue)l&htmH zV7vspj_Q)^XCqT2xo+L+{$ICb86EW+6+7?kletbQ85ZCjZ#xeqxhOuBc3G2b?d>h zwWyCCYe-!tn`!yAslTs0UWdB6V-_^?VixKkySm&tPQ~)$t4Cln2)1iY2PHB%qV0~& z=~a7xAPv^s_nzaU178-N_Qcn+0Tpuzi#gddqW+SK5K{}5==O=VkjT020>O#1s83nQ+`+Td%_X zDT%4{(Uehxb-|JBnxY4f2SIQDajFyl#La5J!JKIoC&*&d;>de=3zx%VuklhU`4h<$ zqTJp0m7yz;nUifZj<xm`2kZ7f7hHb< zlCC+dGmA>qsqHssHsOutYehWEq41hj^(IatAK5p%LZzNF%j?e5+x|*jUDg`4d-P`> zZmBhSY<7q?kiXhJUni8dW{m5I@tWqa0N!JbF3lT_;ju=ABBHS(MaNM8AQA~HoZt2|dV6o*g!Wr=tz z%Vb8WeGllVmd*TtoK81J6-E0Z&D=A~I?;{tQPut7HzGEHn~Kht)>`;;0X-svMQhE{ znJSgAA$cI$tjvivUjmI`w*LqsZ0Jl;RtU|_G6GqP*{oPqB5i3F-mS4MR{vfJkVZ## z1o79>ebgDE5q*y(iT!+C%cXOcrGU>VHMcg|UVlWAl}-JIw4fH20L6i4a_%j)I79!Z~adcVA+HEpZd=APkYtY73{5klW_!X=#Y`6p)jJaI;B)vx-UJo zxDVbrO3o~|^+u8uv~?iidf;nJ`t^mGPBKBI2D5UcN9u!dOO06vsm%&QhV3oL9J-Ym zzfg+=N1YufakG)FxeoWwxruf%sU?aehHkvBNoJQkiIP7;dPe&KfJ}%_?CzwU-=ZcA zr%yss!=iL4@#sLbme3gJE%lu&R6Ylzb*^x~0@CC4@ZTz_uJ2SRZ<`eB{kXGNH=~3M zud>%LYq6pPb~7tKr6_ec8}yvI?Yjz~FuSUdE^Y!Y;}gO zTd^HG@yxXqLW=subIcG%==042oVj>h(NN!RA3YWjLH-+7z(&6~zhKveVkhGi61xuN zxi84VIvRQtt-$B{Fx#06;@K-#kee9OGrV2ySu`BKE!d; z#;i-VPTVUTz#?4+zW5B^sEK1=o`fz-F!=)stc1@BY;JJ1zB`1@o8*R2dJqitR)6e% zGl>JD@VKqS_| za2RSlCbY%yf^doe$#GG0WdF{cyZJZ4Z@2wGHT9}n0Jdz0sX;tmIkmh(v>w-ig-=4t>NjrguNn@OvigC$i6s6n@4OW2!+XL&!tuUk7=b75@nJFq#`Y zUV|Ez0UQtmroB$suR*v1bQXSgva2vl9bZ%GBie9Pc2g%+0#F}J z@f-*(k%fKQ(^hAigVtDQj%r8-d8I_ySs8E+0ckM)L49o=>3R3~NB*i`M-n@%MC;pI zWk`N$(N1Dc|4^v0p6%r&xclWW2W(pz;Z}?Tx{SOr>GIGfJolyPvEEpCf~PPx_{;NQ z1w8lRrLnM!>3s?x`?~XSW$p>wQ7!z&d+)4qocblQ=1$LG^~jP&u^1SvEP*eMbLbr? zfYN-!_vVqp6C$-%efFSEqJxs-bnrXSyo-sXeHJHQXPjrjc;rCc3=_3@`Grub^WJG-QOv+EN!Hfog3r&zf+RB?GG`G4qdLm zvAL1RYM$z+3!JK_3^1y_F%6!zm|VNUHWhDj07gPe zj`zO!$8XNSkgjXty{+--`?XW&f-F51S4e}4A=)YCFEIzx=HQYIXeUUhJY{AP5z4)v z(6u4hF?+22n&d}q8|Xc0FkdXuha&31L|P-X;O!M)_+&fh%tlG$cwkTKAsfGqpl;-i zT;RnudcK}O0AW3ClX}Rb>;~YV0B(%HG&Uafkwg6(8l1k89ZIMG=Rb zc$+p&+z}Po5&VMRt$ko6zj@eTR`Gl)AoYj&4hB0rMV|lOFpiE=ZTIho_b`TAAPO=F zbkkfc-k5-WSKes#4?|pwjb5kW`fKh?N4!|Sl=kEeYNGTY!*yWPiEHh=10cT|51tJX zx2bMK=mo)uFg^7b{+I)5ZnDt4DtCW@<)wb;SMT~j2vEH8tMt<+gu#wpdmsy@SKNHu z=%4X<*jO7I8#^0|(0+9J?k^$>EP7iU#2|mK^XI*9Q_;Ovd~dz@Or_a>-R!+jNpJ#Q zB(^_?b>!xzm)S_yp`%nf0rtLnzKq!@{?&r2NrnW0O^8dZn*TYpAs_Lk?;Gj|wILia zrZ*Vo2DYuzAE;pa1@=G7#KpH=+KC+bSQp$3KHG|nT#6iJt4FuSzit^Y9$=1cOOP;k zYrdF)r$qMOJ4Z+m@eqtjkFg$teG_A`o#t&d0q=>5rf3)E7}Wve)7^{Cp=g*#=5S)e z@_8;7R&gV};$J>i#feY;G}9v%Q-;Qlg#k_D<9zFI79p?Gdu_(MmFQbrz^bm6uJK<{ z6kq-J8S}XHb9W-ldJJZ{^b7tN?k{mwD(-C)qs|<(ryps{ozJvRBv}kt6;sKVlZR8% z=RBR;VI4ekNs9nofpySDTj*AS4JG$t>xona^CLKwS8MmJt1|^tp9rlTi66DuKU2os zh6x_OO)Oh-J~H@|SUVMft`Q!43R9cjx6pv56JGNIp4+z#x3-3Pw#i-EUBS-VEj-3e zL5}i|UZd0XYrf}kfXQK$Os>BR<o zRq-^9HT~uKbmU?1wu!vuwa?hlf0p+B7Lza|VbHPReCP80_;zx=?f2xf{Wbf{>agW` z9H;Z`>;25Rp6Y*Yfjn2^d%Y6Y{Sjr*wdnLxaCV|&`K8+{ZuR@>vaZzG`24j%7!ilZ9Fp%Nonbk>Ms`b5i9-lJ6C` z;G;S4wNYh;50j3b6z0ngv1WKWlL8X^@7CHamkfVoCt!u5iNm+V{+9rR#JZ} zYOXI}&+vB>JuYEFHbWf3*i*&amozt;as^spjg9yyJz=y}MP*fz&c5ACzy*N7iYh1H z8rTcQOV+u*k~Uf_3yxQCOWvTGedE4;@zO^9ht^oC4%AeI4#$dycdOp91}AL8_Wi}v zlrCigchnYYVt5#_f!HT3V~v(dc{W{dHn%h*!6o{VyiPhCjV&QlNT?PZg(bmk_s4tn ztDYT%4X61O2P^L6N_3i$txC!x=C0Y^WsB z&3hAng#)Cfa{St}f2@5#)6l}?7nvsd+GId)K>dX{D>MX!idHK^yE5}Hlo3=zjNVr- z+!OOZa7uOP8d6P^*7^Xsf^yM>xbl&?jYyOW;UHC!)GPJm9h#wv^X4cXKI%IxnT0Sq zn$tVd$n@-^ap+cE`~U^WH5@toI&oc!V$xzA#70bPLS4^!09`Z$ith(x^%3PVXvZVS)PI+Ld0{xQ~7KI!V6eEcO zbIJ$XpE(qonM1%28hyI_a?cDXloS!xc4IwvTJW*TJ3Uo-gzsHvZzP`;wGia_ruWA6O+l@<7*&%A-Znq%yY_AMfs-tFeadBDm$x1 zOf?#QL}sL^TOYn!GLbU|5Z|9l3$AwyCFw~Y<}m4Wf_U;ShG6C*JIXefQX@KUsuC1a zi+>t+^xy!T*%8{_&fg{|OdxZjufIHO|-J z9+;9tco74^`Xf-P2u2Vc9KqH`Xl~*Do`!{*?}@1NR8m`ftA; zLTwi445i?ub$uT=DoOj+i4{rq=Lk|BINT%6PgN!RHPb|*VHK> z^d~mdM&^|ED&ohF7U#7-nawdT+(6|&A*EcZe0&86nw}Xt-I7Y2rsDjc`Pa>^wJ z0m5u`UG$*{u9i(*^^Ogr?D5;qn$kkLM!VtwA#F&>-jDI*P`4XxSUDOqWXLr7G#lZ5 z`A8`LrZK1Aa%UK$9-kQ&V?6Mjpq8`&OIV=P60+dNFeu}zzj`sIObFL`+(FcP!A|HT zh%!+OSy*u!jo4Q-Fsbk2iT9x3Il(WyNAx}H_2FAU`h1V;cC1>TooLZ!Za`D&aP?0B z=ytmt>35_zwv9KqAg8}wn&v8MO?Y-Id$pM~5uj?D&U=UvY)2wQ`on~_ku0?XZ!)_;iE-GrxsF_^m<*R*sr9ThZ#b;IvxqDx+5OIL=PN z&lwzR>{Hffn7t6`REb;%wt6|5gLbvPtPrtTDw{B`pejLov_1HjIC{>UYS%z*_zmsG zjhfaqOI}MJ4M%EALpVW}hz;Ei%rQezUw4(a4Gqrt9m_l-Ta$7oZ$KbcU}BH}3uAg0 z$`V|!jcPtQxcKrnsvFdhz@GkuK!e&+)&8QtV+sUy zCFb}od{fc%3DxTW^EDa3?)Jup(74(6Yljm|YU2Rop#-(^8->lHs03sgK(b^(j$k%v z^rWy_Jj%wW*VMBFPC^VTdGLWhg+|PI%r6uo#9+_LIjw)_i=)Whk^!wc`UoF@dGxC8 zs)+aa$EdLs?d*c>_V9Unx z;BD!KwTvIv%58D<8QRXo@{rJ<_Hgv79>PDm^m-J(yC{gh&M@aC3x0{V-+%L;YRIeV zYw~k)q08csrEmdW+`~-YQeGYZFt7N6nXYks<%l&JZy9LVp%p;}4ARa^v^eN2fB#Fr z^E`EJ?FIVi^X6>bf>wE26scptP+xOePBQvG=MkpMZsc`7D%4z`_p_$ezjwbJ6baJi zIBZlD`oH!)0Q*k`_?mdqD%4N zsvDWV?YoP!0Q&nuZ5loXAldlX=+Pv|^!dKN4gi$jEiKdCcE_4Ja9eD8+;!#fYVbXD z&+~rd@wu4Kem#b{dn?2VKE15YI$ZJBY0+=I_l|amB((qlC^zjv^WCkZ61f}S$T5O~ zk4KE+>fO&l{FA?G1m8}VuRhc!RS*9OEj!PXBnUG-cZ|lZ+F+E7XGZaM^gI>8AUa7-5Z-vSYES{WF;Z=Wy~C5v_e3kXiOc>igwd2jO+(xht>yBKt=gZ`vVci0Vuqn9HfpZ^BZx^<$u|j1xQw%hCke+yP_1Exn(b z4|I;L>^fANqj1oNs1kp^YzZK4$J_m)?7aF^RRwG`!K3SCCGP*pbub|_Kio1&$9b{u z)^X%quBmpYa;Pu*`3?^V&A=E4_8L_sPl~;g6MxBqWbNC2XEI^NL{t5uB<|BE6V28@ zYGwfHlv>wrL$k4qikIv~Xo9y6;r1+{a+*%mWgjMX+v6z2QWP6&IAMLM*>o^}91h#( zQvxjTyocoFu73~K98UDk@#baT_-#B~wG?iCJ1e}C3BJD_~Xb9kL-FafEA-=h(7)zf*c7Ne8fFZ~HXqKPa1_j!Xa zy0?X07e+dOlkcZXZ+)6z7yj$p>MnBk%WYow_RROI>h?3Fkqn=eka=3LjiATfJ#lKh z8M)+{BpGZ1$DI5Us`OGkBDpL_qJDi0LXyr%l__*vPIMxyGxVyMdyh`J7C1$ z8S9mXTk?v6^#Jcc_zvZjl3NrhNn+IGfcL=3Ihs#cKE7mx&MTHr!C&+%+ZGy67|_P z$bZi83SKnAGOh#nxY(zG_9kN}$R9%#!sdA9^Q%Rvk{KBfr4!#k)7yPCua zD*>Z}^-eIqsYkr1B>{$|yafDlxQdvN3+*Kb(hs#@kDS-7m zqg3LSo_5{Pe2OV|4tMI5mfE*y8B0nvMU`}sYMTX(LDf-I+tAq$tQP%otn$kyWoJGE zSq=H7Lzz(#|IW!xDQESnh96S$+EhuW&cM2+BG*cGo0w&nONJPcpAt;k1Ol#nIfF|R z2XhgwKtr>n4COjYGi!v##8VLxEr6_omfBH*^ih=7IeDG+Vsp6~h_(%hL6w@$`J!?W zCW;Pj_;8gpy8(^*XZit^JcZ5aZQx=`)0moYlPjrKwI)Wqq(;?JglqnqVy(8!GF_2+ znT|kQa<*+y^r>|9WI6iW;6@@P$EDi{YXYP8YIB5dzPZ|3RFSsxUkFEuSpW|+RZM>l z$XqI;dX;olhoZDa(y1}WX~c1ajPT$OTv`bkuV*{nCwF?q?%$n_iZlcjX-PB2V>C-^ z_2~>PS!(+0y3s7)7epYW#USYU3Ev`==r-NmH`gU^{3R zyLyW>>gO0(rbVnc720+UkpPNXTe-OYD;eB5*x)i(ZdmZ2BCrN>q7qF@knmB6G-#j2gF03$@&)f*zeLWgPptm-qSRY^nb)&f++v$OvXjzDq0 z3-P7(auy-cu;V@MCK2Klt6Zus@sdWoEb(Gr%qb+P@Z)jhx4MMg$cg)D9`CA~PVLbhtDaC|QK+r@jm z0*8_Ev0R5Mc>|U_zFl{WKL>DsLci6~>K-9qlUz&k#pCHhJ}c~gk7GwCHyvCGKR8*4 zluTDLllLwPz+x$Boy=I}8YI#dNth%c(^{EIo)EgiH&(0TtM)l=EftGY>P0fxH|Q&t zoB+`lhYQI}l@*Mn_$joCpCrlEtR+*WY`q||W}@V1)bzf-_j|mTK zO{=~}c_`IUkmjO(X&!ZT{?(7fuux5knY>l?RjgXRVr7^2&L<6M&C^72e4!2uQD;cH zT<+>^O3*nQwB%}Qd+(Zm(0w~R#lC+%+}qXLlo@m8Sg{B?FPIc7Wi3hSild)tlKb}& zhUjm7vSw>7ojsat40Ms*q^p}7BjFu#c!)^)5HZY9m%iPvg}JE=5slb}Pm^vb#17-B zaczXOkP(IGd{n|3H8f4;VyD=p8JgGv6f9#aKV)cf+>W<&zGldODZge+D%b6l&oJ`J zwadhq1+qSHS>JhC4-e^*xy!uV&D;O>`sN?iQw#-y}xVpw{OR+AQF7wAzizQEBYo`!B zYVu)0yaN<;CCvW9;d)088h|l)FanfY!-ShUkC-_L*d4)+XveTkyL`r_e%4JO&May8 zFRsfJng`u~*>amMgIMb&nXd3l5+A+e+L?CyjQ2xqB~d&1=vTT;ms8_5kXB;lAz0;J zo36mVO`1$sE!8xeu7sO52tV`DuYrEA&$uIgwXd$RwFoq>Zq<2*>C#^13x6GAt4W%x zs~nHh)lNi>ev5dZniCLoH6mhCVQbtiYkbV`-3px6@ zOxOA2yrW@666N~v0P7(;F~S;`hN6-peC%b9VASCcHQKcbyXj;sjvSO^#7F0T>uyo5(e`#my~! zm(I4prw_1=YCqd3(~Uw6MbrS=1u~m`2JISTn`OEwggvt|-K>Qj0cRD*{98cvVdxmL z=@!Vf(~#*_6br#wh!GOLwdvL+t#p~XThSYT@LovLjfym+Xu1%285z-u6PdjW1;5V` z{5~Ogi1m`$h1`3wBx345oA!bG9vG<4pIV&DlVfa~O~Xo@Me<`I7AES0JP;91iRyfq zGn^A`B)CpJgR)g(=)uQvs8Bq*25MnzZXQu%vc6dbrDJWj= zKMGSkK)%6`uCC?kgD&`if{EXLn?@ncUQiqme$bU%SO~2?2xIP`VY(cVv(u*c11Sci zU9Pii`T#t=0<~?AQ`-hn+jh%z_d;`j4?4`mY!5aFw?AmpeozubxnI+S-_(6x9T)(` zl_9p5sH>bkd!b&d##RA$$fi57?IBEKZ5sPTrpS$5fb0ik%st80_{Ln)ua3O&P>vl}+~`PgbMa?7OLx>!1L>qZkj_^g)n+KNObf z{wwSu{VIlVQbc6_FpntQ3~=2^eiM|rD4#)0_JZn~M(2z=xxd`a?jccRhS?#J#mM0p z0{t+e1T(0;HjfH&5EbMgG2GUynaH$*-TWK&$SiwUrVnG?#JU+CGSPt9LB0KNL#ClS zzUo9_MkI7ZZ91%Q_LNt{M<9%UagD{@?(3DyeMk^d^@(ToQLtY(zz!hCA4QIf93x)s z8g}Ft{YwdZ84knlS0wg?C@Bi68qsGo$=$eL)HU^S z(kWrj!MTP`SL$MC*l^7AFpy9?M*?#-^Im<%@F*N;rf}%!FcM**jaTV^GjYkG^GGx5 zqfk#oT^zO$Y5$l_55lWYAZQN1#}cGlQ=dfRcWm(_HXnj5dJuAl5fF*@*qA`r-9Xq4 zgh!hY#=zilo8rKuK7-dMAjWC}*poJW46tX>C!V@R^ft~umg&tClvJgYwTIq=L%*Dju9xEWB|aO7&!YGM6?qnJClOjo&<;W*H{ueJm-tE z#;oEd?z21?9`z7Af}H0{Y(ecz2?E0o0fgNVE#*`eH4}APoD+dbh%X$Q zf}zASZPTY=X2EgXt7c`COq)JK4Y~nt&)D=?AmotOSw9bVEXw2O7TErI*Y*>z{e)wC z1+}dNdEHBgiIbcmoXT`6dH=W)fvxt{8 zOjz+OCt3pBMOx$R1Y|f`XU7uLfkh#m|wN0A~jsn^Vp{$ypmdIRtf`O*$tk@8-=!jUnJD2QTxELOnv*1$vJ`t_E zY+l#2xpXt3Ur&%!vt(slMw}NNFE7}10{%OS5UPBn9@7Xp$Esx75Z4Bp6V&lQE7^!j zP`yqFpp%Dx@G8@qP4n0}g`IU#N=9jqs8}XK<)|-AH{qnSnW!(g1?#v?j{&)YFgfwl z)=?TXuB$GVe9wXgUR_bihEff7NOBXVC0lk?AXL zhKp-Kz~KU^&o~F3h?~M5Y&yzbGMg>?D#(el{F+V65UCD*&Nk`uf}_v5d40|;*5^F* zdEKTJY(5Ts&WG3=jy^LB^m)St%=4WIvA2NN&t5nE+*7kz$Eu^_yN-?*pyQh|eREzP zN5}JjV0U32=7|H~TMpIlFka0e`~!#ZJ0N^frWf6-mQCv@GFz~EX>RpxnZCW4EzJ1N zBGU@kyAWDf@)GEX=MTV3Jpaj-V4IT&#vgJtc?qEylIf3JC?fH)NC9WTpj&17&nR11 zc@cX2_zJU8?`%=;cE|jNb2iok0^cOi$EL@B5w*P`_7gJeG!dw9;(+G(=_k%zPt0t$ z%fmn~VLb^h_sR69D8t|)g7Ifp*cEWO;&3tjQrM}!CqYMK!IcHOpK*7;XVX*IeHy`X zRWuWv9200aK^_tJFCL5VMC_KS-y#t4MI7BH2iP^@o<<_#;=%(7r2d;*)4HZAXnH;oL6r;=T+V9g5H6_p-SAweiP^D8#28W zWgm*%_{gSbKQrqMt*-V$;BDj1I35@+Jq#7u-Oim~`IqMirvETX7Sd}^ZSupHBFJ0Cu> zfids9M=+|886I}esIU8s+adOAaY%5xtvK;wJuE?L9C$w#is6Fq7SUh0N3LIgM%k}~ zX1`hxO=sL_`Zb7(zI@xJ=b`o+u-_;B>^GkMK85{06(4P8BI;_>ALiV28VU(=^-U0Y z!x3KSb{@ii;tC&Ek0=4PNA1Df2i2J9TYHtE344gmA;e0OB&#~mB-S1E>oXPK- zKMT!JZ5lcE2h2om_&1wgfB+W}M_-7zi04NR8>@f(|EvFD(~DSr z2T^k88vBxnqb1Q|1MJJ#P7E9^!(=P_6N!Y_pCjrTnf^(9cY#2EW>PADJ`^+Di-k$V z^)SLT%F__qop<@I*co)VpZ?723z308&UV(35EcM_5-}LqhIvbki z8@>5vj4e&@khfRNdz)mq&w~s4;U>P@y?F#9>dhN8#e4_G{oY*YKH%{Zbar`j0UvCV zdC22Cj4{$A+h~&=9`^7LU_8ZX@w7J=dOh#q zBr#GRT+kf%<`%|;Hy3giJp3X?smT}RCY%#Zx}R#2`Bal^hZgy4Lrz&$m5Zf0Pb~-N4ccNeF&vG z_)w}L)y&jX(nZ+Pq4RF7&iQXXpR+!n?^@4#-rw_k-*>I|AqYZ&AP6dvs=msF7w(Be z0YS*MM;BIk`U=!{E%UYYU)50Azub$KAh$D%^>!mg@waO}p*j6d8b2u091<^fH(8I? z+_SvZYgBN5#?Ul(w!h?l@_cS_p091Y%J{cyhx11czixPR?|pObpWlzaxZhNN?V7pn z(!bxV-gfF%3iSMJ=dW4*(%jbK*Z8CJI?u;Gop7TxZ_XI&+JDpHl*TPWZm~j_mD!>B zylKrni^rRAfqzxjQ?br977V$EB{zx(PN; zw^r(^@{Z0u+1xHL_Dfye)u5|pFOldg5=rQ_P>UVRzK48PmsPLprtn{KNM zHRmfb2C}!m6r!wp)=v&|k2?cWEB;U~kkd8r+&mC_GNn53ywN#jrmGw`!(6*;_xnbx zW|^3OQ9~22qw+LIrx_~efZ@S1rONixr}lo-|22vsZaF{|Yum-=S@rF&y_xNrSRAI_ zZGW(@F1;)-ICH@Eb|Pfwy1 zyh?b=>r~+p7W*7hpZu^geEId{Lr#w*?Q=_iM}|eOCqJFrMxSQCGG_RMc(ffW;pV?R z$vkytQf+EYOzp)91EF7<%SxuBo?^+kL8p~@_E&vt(qZ1^w!6N3UCU*DYjMK|=8!l` zWIvrN))|r9)0w$IRd~IldbTiq6L05Xnj&i~-a_@@V2g|E$T6>iX!*!0SL+8`sx9V! z8XO+@^3}quBv%vK7?Q=S&4)7wC4KU>bNinSG8>eX9`TE9i4EF0oh9on)1I69%zfBc z@w?6ek#Ty*U}KNfY>%Ew&5~T|+SHieH$2N%Nik5&rDe?Q*3IwG+E(pyXwy$|`zHE$ z5wBvtI_b>wnn%4{$fAD zf8DqIaAU0X((oBT-&}&^85kgYSX~*-Eq~Hp_fmF>w8P11m5p|2K^pP zt&puc;h@1nkBg^AHdZP!DL_-VOlZi zWZaBNCH3`~WDEsUgE>mpUce-Okog}mM=)}jGE6+_a~2av=8G}07#U0fCWiDmjfo`V zBuoUUKaL3}^U^vIng1RmB>kf>q2wB*bwOk-9S4NS~I(%%PjfcSjv zBa!;-#W<6(1ICGrcaTVZY%$x(*aq_rIj<$gl=Rob7?QPGn04e_n&g;&bYB{n6=bdI z_V94&9z2eRNSy;^*aS6F#FxHM_-e$Vz>KM7p^c5DXLrAMVbPWZNd-#00KP!Tp+^fj z+y$XDsmv7!LmA<8j!-BNGAKdP546LX&&9)93*J2FLO4Qtm{7pth(uB`h0P|^xi}lbW74@iKASEQ zL;78FkoA7I&B_ z%le-#$)cE>o0*yIN|CS4zF(g7*wc-AX%EqUBS!L2MRZK#yUKuN?Eb|12c@ovZJrb0$?o$3t^ElS-f7C~s!(k}YU0cb1txm$=S5?agVoi`7xx z?ZZ8c%Iq8UsDb-`bOR*g=g@89LC`-yn)v+2~!|}X_$do z_z3@oIYb~Tl0yne8L6QqNFA*}TF4BUBMW4SwxF%Z9nq0LVj~XXq97E4qS0X#hmN45 z=r~G6X($7UQ4Y#Qc_<&9ML(iyRD*s-t*9H_K%q#07|0WOAxE?e*&;i%9_b)mv?rOIUU)x1dVt=u(zxV21m{QrSg*Up|YV*r?UC+0-Da| zc`cis$QQ~+Hp5k|z#xE}Ng?$#<%xr?o>%t*((S zPk*J-GrUD zKA@DA1-C|KvMygJO{a5pnx4}QeMuE)#mMH$&h(x_$#7a|>PmXKk~5t8bkR5@S`adY zLP;Z@zEqeFuGJkVl&193lIa4;iGP)fr4zc5s`+jXUp_suDV)m$Z&V{)VKv-f z4M#SF$1C~p`mksIlW25V8bUQ+c0@}-FJoj2`BWyCu9Wpf^VP5vwwPOFVCf}URVQB1 zbc$mC1-y$?^>-55DEX#EQ*?M*lC)BvOu8AjjN?HZjbW7FD0DI)okW2N%2aN4ihp84 z#)!dtkg2#t1tST|$Su1RRaC@&b4NNsqN%N5>n)Bu9-*h~|t-9Df6#bkX-C6uJ%Zq8BsBBTDp4V}-uUU!jkgcR4(~ zTcJ-0l&U-=`4rwGna5^l9@HjLRZ@zCjU5RDhKCBJ;FMDT=DdQ4*OW@jj9c z8JXv0`hrjJpdP`Ewj#mp!-^m#Kc(mT5GC~?G0=yUXdhDJA;B&2jqr@}UVnH77aKoCoWP?}xl6IKDtW&*RB!1N~tAIG+O{01N=&e z6tII>Mc#*yPk|)Ffb!U^qJNsK+oc@CjK!h6z#-cxDk=^juT>5MiZq*~Fz-hu>w$tW zS&5}iQ8eqf^MbZW1O=}MEBV#Ja++Q4B}Eeqn5?`L3XJIyR-pR@1u1;hO@JUsAj~9Y zv1qbh2E0P?L0Y7K2~|_GA_Nl_>l1KEnqAoifyacVgctKIppdX4S$`(uV3dR+lf2n+ zG!a!ufVVdxF?N9vuObBDlNU-cW=(+HH#Z8)Xm*vKJQaYy1JUeilWhcQ0!5l_sh=qV z7a19k1aUluqX`@s#gc}RL9+p$umMPk5mI7=l(wW0q}kvCDKU$bHH%1LEK(Q=k4o-Q zll2oqG$2HSdcgt7&3~Bea*)M@R;^2Dl|_|;^-MxRK~l5q3fVIQ>1xHn5gd!-MAVuM zX~|#%Ig3Ke49%{&oX#1W&XpFOO;tME=F1%DRDEMWXBg-V@g89<&8`gz9Woz`3GLb? zV+LAqIvT)yFoPYE?Xun5Eo{_!HAy~6z-Y6|n8`NFI05+|!GCg`kQJ-W8MR=J(QL;e znCb$|RTfO83Nu7htb&00TD?>05!OL*Z6d`*Y6!})9|0_g{}M7yTFAw^5a{w&ermzj zs)S=g#0UwU5bq^KF#cb$&g*wVxf>85l#$7{FrpC0qnSlvCL}w=h^SfydySfUG-k!3 z$-+j-WUxGfgMY9uMj<+=2&`E?pz>6xo&fI)2~j|WCFETvbd5T07S@a6s01%X)S$rD zOM#fM9%`|xW`7HsY_M7+>n{%()f7pzONhxLR)}$60QJiVSilP`v|22zsj?7j6aqB6 z!B3tWO}1^jMzfoM53*poO?E9Tm{wSTJ%UOCW-!?YgMW7A@(L6V7;KkmcC$~o0v62{ zpl~Zt=&OX|f$V6^O%{vbs8@L$;SVY*nhz10J@7U?Sh*wKjbfg526DY^K>O z{N#D1Ndb^GfcGlwK_)dMo~Bt`6^b<5OBRYHSI*OHACLzFYhfVXE%c*aBmuI7v>BRG zPfkjvPk*8i*>pe?fFLJO4cdu1c++fuOz0yU_BL|{%(MgB1NC?ek1#kc<7f~EVjLBi zPzZWBi{LP2L*z7-CASP+I86tM99BIqS_dqcI_4cu7W1p za)Q-u+aobF*zwlcea3DMTkhCa$rJm#Y@ZSPJu;8g_1kkKkL|XN0ozZVEf3rJSI@^l z^@Qb@|c${UvJbmVClaoKuj$qfwpH{th zf-3*!1|KG{My!Fq(60o z`oytUc0YVYeec}R-2SuMpJV-hbTr=R`k%{#mO7xxl{C4X7CiIw!!!ID01DH37*IJx>5DeI1uruXh800#5p zFuc;Tjfj?OKiuu$elbP4Hg#_7?CFeJ(n673I?6=NsZsuXAyvJWWD3*8EL;au_Qi}S z)0##};~>pt_oU#Sq@~J*N-3jTW7#Qcb;P=MMI)&_m26Inz*H>)x>a$cTz|^UYqoy1 zW;UP6RkTZXo4!)pN=#i9$rRN-ubVCw%L|Spi|@gma0A|(*fWuZu=|)QFZ*C^tMyyAAdbcjSZf9{jmPY(&-PU?|$y~2j6|+`0up==BG~|A9Q^7 z^IKc5|L#BU9eV84f#=V>aV~x0$O})jKQ(^U@Qd3uNqe}pT!>r8g}LqAAg6KT+mas-13UxGcAqo8&tF zk0kyFP)h>@6aWAK2mq|1B3RnkF5`3%006`tmkN2N<<2GxMSC!FMghw`qksfU`9VZS zuhoO8Y{^#3#fn{%wzGEGuIW#u5lf9-%%*Zf)poI%FV_2`PUcc0c8QfL{pDh6pj?~D zm47KG17$=ODCgZcC(+cCL&bchfCNCtWBJf%R@AgzC?ZY=ZmnF3abo#vU6Se%bg zY=dH36m`qmk3g&fVpVjqmvl-$mEjTC)xfTf2E^2l1uQdR#q zrfdm!Hz&B$U3a&~+wjfZX9fiZuc}cU(`M*$!C@gN)amm1T3rWZNV3$hRb`N2 zHZ>6IbJ#Sd>ymuMVbg&$O!;QwJ4$VjS*O;8ETgksQQ{qn9t{!h_N2{RinC#{fTshFkZusL`ia@br-D4U0uqoY%cIe%=vYW}ywP#*1w zD*|LeKrawNSkOsgp7&+@Hj-h;A3AIy;+1;QYNQWYYPy@y4Lje3)f;RTVlVARliT(2ReONqxZ6{}ORBAu6F$ba|>$}cr$n3@Jto@s@l z-P)}qv{?>|BeZ1@&#p1GE}6}7*h)y2LoyeVcA3p{*eXaGAeoO0%$wd9Y}6J8quPRC zlLn!QBwN8I*j$Y)N2+yg19H5;G88M=jGz_#CW;vklzJ^eIsxgV zlHvhb;sLS~$k_ymD?>4>6Pr(`<*tL#wb%u~9O^%VI)QeAIMPxVci3iZ1!yy@>^O-H zclDTPjxFT9BczM=pfp9}+ET^Mi-lC7rJEcH(_e?0He2W-(8S{IpPr?tsbW z$!vwgwtr$|1Jc#nrmZyAfXvz$wY(AOFr*NCJ8T;q&u`P>%A49M#rzcguXaQzj0m;~ zH&5H$+4SgLA=RMXHk5i3(Zm~|5Tp&OK8Dwox@C45RYsR$7*?wktAQ zjl?0G4g{J=Xl)ACGd4ZdOY#D@>TaHCM^_caU4O)Facwm=A+`o}fo>;s*Cw=eqqFm)gzh#> zr9Gi_D77*4YouKXd0n)VmYNj_Q=_TgW@viScG?Tj0cxnrP`sRV#I;RibCY*$iA3%6 z5`Wd{uw8Jp9c))Z+dR?HE|{GEkRy_IJ?v;RXbZL}*xP|gZ%t_1yh*F0yBQ+rP<6xA zpy_S{ZO6t7jh%>K2Uvs^k=agc+7Na_xGSOcVAX|{j_3YKWa^+OFF_hWhk3C(u*%Qv-js~J2{Yq~`XV~5_O!Vo%$y20JBjIl0C zV>2C)nT<^d^F1oFLEJN7@gU|flz-5UkIjK1?4@Pwt;k-i$R!hlJ%I?1!)ONMuyGhs zok(aorPgu};fQ6#k-Lx;JOT@QVPR>THjH@l-mauz!(Fe9$ZWU)qTo?nuH2(}ACSxF zp|j<2Evr~n9xO|nA+Bh_BLMA3VjEyUW+SwM;$9!dFU8Opy_o+Ae`Dml>3Tq9fu%@ z%B)1o0=mda87?ch+`&N_4o)Ss(+~P+*4X~!UjF=Ho(7%4^$>bC%$!YV=cvMTsMJ{6 z6EZsoz~>xx7=S@bW=|xv&wt~>iMopuajbGjsB#V3lUPELRx2LH4TpA^Q0FBJ`8lk@ zn&0u0epEFEdt~hxt?oi5AlazcB-c-v4KwW~_7NoFt56%B2&C5ARRM)wHvf;*f0xr2Qet#vQy)*`Il-Wz8@E*YX#^8+teidVQd&GVq_j2cc?s@o(c?I+;(6E(-t!oMGwFixx8avcoYyMom7S~>- zI2`95R7aqCJ)ylmrg{&W&EBSV9b>P@HHXkQoV!5hA$cRAeSdZAFmpfGO+3rK3VPG& z0lonI&4l*W7~cKtIT8Psq3kUe_tA&jZ6gIdt2)8PKq{yjq=0Hb0gx8(%-iKK)ow(&kEz?4*VKgpfZkjeq|AFTaQXJ;MGW@f?-!6!;zD zDGT`qF{f7XbmcoUW%4xwI)CQIKwn=UNYDRdP@mi#U3TZkI}R@0+IVN_fu(m2zSAIW zf3E75dZD-Un;%)lO0}N3sh%!1T}el`@7ms2wL-mKIv*Km``n()v2BC76VtwZsJ%OL zZtz&k)_=>>mw$Bi>XDus)zXq`bBKb>55TYd3(4!nf|$ciP+stFh`5Nq4$2Dt8faMX zFHgwNaWS`xpdlfD3N$Ek`2@%o^0OkxWaktpCHRw|!@>^PJ}Bg5Z@-XJAM^_OQP3X2 zADK{>-NF}Py9K{%LXCC^JoWT;A@2fh6MQ>pi+{jS4Lb!->#9TWi3#;xCHQ6Ho$S!M zY7qQ<&~m}g6*%H&gBA(h8Dfm=8)A$wb)cDop8}dDcunAl4+U1tSkRK^zvUV|9DCP(3Qi;HFIxQLWRd_bjpQN8MlBIm5IAFVDnPF)`v-^8TYjd){ zlF6or=EOgDD+9|&tFuB|MZy+FW-6MMr`n(Q=4z!{l*QcZ`h%74fw zBQO8uvtRq=$G6^3Z&6?W;MTUP-@g6KqJz)>=e6!XzJ2Yb|}e`ncu2RDAJ zVcPdUy`TKjNBe&DyFdS*_nLSAw$l9`7DFayk8WY&?@uJL@7DjC>%5NFbAyNYR6dQ* z;j5-GFFfrE>zT5AQ35vMbjC7Cg~rH zCIadhH#nA2{ORcZ3JSzxONKI;ls}tRRDU3%Br^W68i>SUuqfwsNZAKoQ-8^{C9Edn z{&XggQHllwX~iE*Cc@E(Nnf<)zzgB|N7kW)GRnk~!QqnJ5=7{aCc??W90I{^iW7)Y8-ax@W320~0m%_=5ERgDWnW$ zBU~=jSTce@3c+tYkYb1)Ln4A@h=h#H zkrp(ez_wDNrD9pKGFY@@_wVnmZ+KaNK(B>K$>h81Wn#=5+o!hO(JI%L`{yH zL`tH>J>%3}rc>h-+A}%+q^X-?B!~36i0vX~>=S~78KhyeB!9z_;1nGAI%5$KwgCW5 zNR-xRma!lU&mxDUA{)dcYtmLkV{M?oW)UU2ub2|Flv(JTQP6;;R2UE-Akm8?rx(c{ zFTw~^`z)oR)1#{oXem%nY2%>okm&IeNx;aiO@alCn3-=9BjDO<*N&To0rHjdGLi_G z2|>Uj(r1cH<9`f7HbJB(wMj5{TssNomSOs2v5c{Y!A_kiK%XOa84tEQF%q$5c_(%u zD`T}=UD`RO?^iW%BGo2~^f_aTmI_2J=d?LuuaCYu^Nt7sm5le{&Mxfi$6^Shj92VK z*ePmP&C(=BjWfJJ@(_|-+WdYKb=-xLCd924kT)dQwSW1;CPWG2cHm!Nt$HS0doXL$ z=FP%MjTIni$6b2TWJnq-941)^c2;;tYLIdKMA8?9Z|erw2@A*dAZhW*X_7m_Vi zxEp9^5P!CZCZqw4wS$Rj;T+}dhmG}1qMp{OE(Vd2WJ3$|4T$u-ZCs>TLAwg6VXYdw z7|2{D(X0)3%LrB=pi-Es7HHN!!;1*)2**EHH$JhT)92~*_>c$roCvNVgWVW7aP&%Z zG`m=1mw=Tum}X0-`Q->wZ*^fOX_F^S2w@1P#(y*9;+JV`nOVLXb+z&ps0~E&A__bS z&J7}8;lP#(zFg%UH0uC>697)x>!i>I#4mMVZk8`|%ls0WU4py&^x|O4yiP%!=9h}c zKpax{fGA*4kq8B{ibNf>6gt6&3Xl&9)n`Xe+?^69WC~mAU;|6d@;oMFK|`?R!hq~^ z=6@r{Zz0R`k<0dynieEt7+{mPV9WAh%NL5d0aljx3M}wk0VE7VHdbhC1(3E5vLTg7 z610_GRV&O;Y^{71@Kbe@9|riT()n?!d?n3R=K7MA0Bi$#T(H*6J1xukl{C8&O2sCD zcPF;A|HGg(xufzgqwZ=MOazlz{-NlnB(wjKgh;Q%0 zo&DGu!eR!aLtbZ}3mR8rUEta-V1aa&&aTeaaWTh26|ewp$dKNE;|~7;cNRXE#Q_Ei>2Mp*d~F6 zTHFsUi9l6j*XWG~_Uy#o9PDiq7_0dLV|B1KLTtUpt_2P^!<1Ys$)pF2yw;Mu&hQPn z`-G?)Ale?tK?5w423@DIb)bzw+kaDxWvhOAiY_?VyiU?>^>E@~j+{;PW{F+wwaemy zd6^OT4Y(0tjSv+%dVuW)K$W)gtx!v?f6{BMdM!1pd?U>^PV=qKV<1qbUE;U-R#6tS zp=4-_*zm1Aml{fjqhv z`sgZbxDnf36Hw@SM%Dp|3)ELJcD+iQKBqPh>2AEKHbsZj#!>0;v?uwrgxNfVV<5Z7S|t zxQpp|*wn>u#!iT(1^D*S?0@FXUC;wQSApn5A_U9|Mgz3yMIsty-wKHk$t4tPqn`CX znuQ{612;X8?GET+toLvy^stv^y^vH$VjOF>K^z>Tb60*_}a=Ti?8p}o$R*AnSbURPjC2T-77!XbLq$T z^>lP}biY=wj2v)W?ifE*HFNQmkI#;M5YGF***HBP}A){o0H#D|N4P} z=;4v2z3-&Xc&{|<{`IcTUnCCAzIX52Td!R{bMS%F_NjYL?Y`XBQyp5`zw7REky9^T zs{PHf&weWW<$vFf@AkALzy8wp_wJnC{6zNp`v-cCZ`*nLll1Iq(?j1m{~%RX`XBes zI<{QjSE#LW_&%8Z4s*qI=|pAKsmZ(BAHC!8M}GSKvv*DXME=j(J#Xwj;^X#T9@_P= z#l7#b%6~KsJZ0Kj^p(S=-SN~|{SSZq%!i2`Prv=vzkd#G{`BBeONxHqKGk$&)$cAm z_WlbcrPr=>9635TQg~$QEIQ$Nx#9J4?&Axuj#(d&-u+3{J-xMM2X+fP%04Q7DqKFY z_T=A>-ubZg-Fvn5?3Zin#?DVZxbw5>AO70=z#q1Z9_7NeYlTf;oy*SM{SU#_*PmW7 zzvi+3v45?p4jg`@a;|^dKF4#r?wsx)dgf{Njc-o<=KTZpwttG>^T8`uUp>40gS);} z6S(jGbzdH9+tl{z%SHeE)|D53)bUbX^^qrk{zCEF_VBSA|8jBWxz+!^>vCf6y+sqJ zzc={NTLAbZ@=pf2pc4XBN?$BJeu6^YXD_`rmHO?(UIO3-m7%~$G?`U(Scv-k zAAb%yt|%Hwkaxs=suI?bWb#km-vjjCy`*V-5@A@DZpC08QPgxanHZ+b&2>$6t#uxK zHkl%Ss-y|b6r#!!Nq;&O7=^dbWIP2g+NwXLCWA^kO~7<0M2W_WJG_P`LjGKI`dW0D zs;oCoSpQ%)8Vl8fYN#Htx#{|}8eBkD{eO96G!cwtLpSX%|FdYF7~^l3VXAfk8%U+n zw;b1h{y(NVMUCz48jM6?>yJOw2j3s=n?CTfvn8*~#mvAHGgEX!gDlJ46XsJ#-#u~o zYJV^K-VW-u+52z&f=@rV_`c^N_K!SQ9%Y7KI<)B<7xquf;cI~{C+GfVni}Xi`G3yW zmESFje-!`58;76y)$EBgp>F1>zdO-W@aeY>Ro(FyA3xFi;>pqTuU)_k|90b*KYq7<$EVre??OFC6YwriouO`h?pQdhZCnMnf?LTsITu&Q z)pHxUc5V~*C2l)6$OX9&r*L5|!fg$4F>XJ1kQ?V7<)*kNxF@+|+$8q`_ab+Sdy_lQ zy~DlB{hWJ`yTbjFyUbnV-sgVHeazk9KI4AR{lQ(}Hgo^&wz_TZrS4pvLv$v<)@WmN z(6MdXwr$(&*!g1Hw#|-h+qP}LzIX7}y8p0dRg*e(P88AA^N>0^+govTXPBxw4rv2 z>093bU-q@yNu#{miI+r!2c}J(f02u>mKkK2E*^6$L%Fb)8PfY7At4EJ&?K~VISRv3 z2HXq?iwq!A+0ve$>z4WPK8xcA-;W>PpP!cx{_YVEFSp;#j({xJ=1TL88rx!@D;)II zQHM=o$3C2BbfY&J&g5ydi-+J%MP^0E9b=1PcQWP?!F+wDWRW%nhQ?kLN8__7&BxSV zrj5X5nJtFoaKB919y$DUNi4mOTOot?iiw+%ARsmYbV+*kE^&_o17r*_#|%BXji!gH4r^ylDS6Owe z)6-_*6G+FerJ({ADkU-skCG0fOdTnKr}#1HO^)Uqxe}dulWcQ}btY3HYS5`9q6ZLH zsZgd&X!Imdt<7=Fl6=Op=-p%tFE_a;mSqN+Ei;RlnINGjijeP=xKc$$4kRtHumpY? zo4E>6nFA8ZgiOdzqD~S_2V;?N7)tNkgvvEI?P6jxz)92T$jUHu?vu-9>{xOTf`Emz}z+2m8EJ_$K()Iw{hGkfoSunG=e6Iogok*h`Gg_=7%&q zyqgTnm>lZkCX%7(%;pQ9Nl;Be-iZ(ib9fcA=>chBMc8oyH8z1qB6NU&e;hg7zvt=6 zLWwFyi<4xZN1|rbLc6Hu)w=n4fM5(mt%EeR}V22MHkV6&DZ1I$Z zMixZA%Bss*ERm#In+I=P8}?qBBO0B%k1BA*!--MDVd~|@tO=4VxXX8o#K1}+2aaTV zO#qj9V@u{X0;b4wn&^;G8+Pqw(c$s6(e}~)9ejdnHH&63D?dpE)FGsF_cjYP1N~9d zn!IJ#=#C7uf^I>WA)jbQIqpV0u~5YHDCncCch;mJ+9>OX;rMW*D+uR zJps<6*_!5^rU#Kp`YdIGBECEwDNF)`aJT|j(Y;!Yeecz@+6VP$7~AAwNWdp{%BloX zAfVQ2u&F~?@f-0k9?uLT^L@`wj!?QUCXRF>k7;W6UBFYws$A%LZzUn6z!$ zwQx+X?rPYzlkT_04deS`I|#`NRmMCsY|5yHa%Mb=K}|WEWjHnHFgdQH#Hk1dD$oTB1!;Z_PPIxov|8uG#ZUk|6bxX5DrS2TKCU+z`T^ z|Jj0p11{{OC>KpVT>AqhxlmWggh2Uq(s-ncxO4~>X>i>iNlZmgu-sW>)Sw&^ndh@G z%`ptQ9PGKMU8>cve!wS>=z-^F*%zO%=y;R?GsN-+D3|!K&o>u%N&J9n64$McjhHYl(FiGGXQp;R>4?f z#Nb{O_ugo)pekQy8Bv^@Au^K?%2nYBoO|z3%&+ALmD`l~)J+gEjkG@w*;3qk%z5pA&KL%#P15tEQIB>VCQ){FM;cIfVm^->r8>wIh~sSP9!thkuz3XD zcwCLdBO3AY4+$e#xCRmY_%5IGiW;SJh=vi5z#dj`vK|k^OZ%cn#@5LoqrdJNQX-Tw zcK@nTI2kMsm`7Je0Ut#0>49VAEnL%kyn!2sJ0O$ay`#}xc{PMic?{qGNj4>IxNr(> zwTlyV-?L{|#erI8ZeQCxT%57bP-a$bh;kU=-WS}E2jazk5Y!X5N%{e#%B0r{R6~>& zo-6ybn*AIs5?f)=74MJsFKO(u!AoG-%iST@)xJ>*F|PF^Ip7KBOaW4McylgW--U

K!EvmIVh$y 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%0A}tGLjs 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 z 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 D5ie%7 From 1ddc4a5c5d0ced5486503f63f4efa1606467036c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 19 Feb 2024 16:04:54 +0100 Subject: [PATCH 0432/1302] CI/Runtime: For OTP < 24 use rebar binaries from ejabberd 21.12 --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c86d7d158..1b7e46a48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: otp-version: ${{ matrix.otp }} - name: Get a compatible Rebar3 - if: matrix.otp <= '21.3' + if: matrix.otp < 24 run: | rm rebar3 wget https://github.com/processone/ejabberd/raw/21.12/rebar3 diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index bceab94f7..587adb71f 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - name: Get compatible Rebar binaries - if: matrix.otp < 23 + if: matrix.otp < 24 run: | rm rebar rm rebar3 @@ -170,6 +170,16 @@ jobs: otp-version: ${{matrix.otp}} elixir-version: ${{matrix.elixir}} + - name: Get compatible Rebar binaries + if: matrix.otp < 24 + run: | + rm rebar + rm rebar3 + wget https://github.com/processone/ejabberd/raw/21.12/rebar + wget https://github.com/processone/ejabberd/raw/21.12/rebar3 + chmod +x rebar + chmod +x rebar3 + - name: Prepare libraries run: | sudo apt-get -qq update From 8c64ce67fc27c86c35a0070d39024df7b23c33c3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 14 Feb 2024 21:27:00 +0100 Subject: [PATCH 0433/1302] CI/Runtime: Test also with the new Erlang/OTP 27.0-rc1 --- .github/workflows/ci.yml | 9 ++++----- .github/workflows/runtime.yml | 10 +++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b7e46a48..815780cf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25.3', '26.1'] + otp: ['20.0', '25', '26', '27.0-rc1'] runs-on: ubuntu-20.04 services: redis: @@ -38,7 +38,7 @@ jobs: - uses: actions/checkout@v4 - name: Test shell scripts - if: matrix.otp == '25.3' + if: matrix.otp == '26' run: | shellcheck test/ejabberd_SUITE_data/gencerts.sh shellcheck tools/captcha.sh @@ -46,7 +46,6 @@ jobs: shellcheck -x ejabberdctl.template - name: Get specific Erlang/OTP - if: matrix.otp != '25.3' uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} @@ -111,7 +110,7 @@ jobs: key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - name: Download test logs - if: matrix.otp == '25.3' && github.repository == 'processone/ejabberd' + if: matrix.otp == '26' && github.repository == 'processone/ejabberd' continue-on-error: true run: | mkdir -p _build/test @@ -191,7 +190,7 @@ jobs: find logs/ -name exunit.log -exec cat '{}' ';' - name: Send to coveralls - if: matrix.otp == '25.3' + if: matrix.otp == '26' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 587adb71f..5835fc3a7 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.3', '25.3', '26'] + otp: ['20', '25', '26', '27'] rebar: ['rebar', 'rebar3'] runs-on: ubuntu-22.04 container: @@ -149,7 +149,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25.3', '26'] + otp: ['23.0', '25', '26', '27.0-rc1'] elixir: ['1.13.4', '1.15.7', '1.16'] exclude: - otp: '23.0' @@ -158,6 +158,8 @@ jobs: elixir: '1.16' - otp: '26' elixir: '1.13.4' + - otp: '27.0-rc1' + elixir: '1.13.4' runs-on: ubuntu-20.04 steps: @@ -282,7 +284,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25.3', '26'] + otp: ['23.0', '25', '26', '27.0-rc1'] elixir: ['1.13.4', '1.15.7', '1.16'] exclude: - otp: '23.0' @@ -291,6 +293,8 @@ jobs: elixir: '1.16' - otp: '26' elixir: '1.13.4' + - otp: '27.0-rc1' + elixir: '1.13.4' runs-on: ubuntu-20.04 steps: From 4431fbbe5a427a1b24506a7387595e9ad216b43a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Feb 2024 17:28:49 +0100 Subject: [PATCH 0434/1302] Include only observer; runtime_tools is automatically added --- mix.exs | 3 +-- src/ejabberd.app.src.script | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9bcb3edff..5006552ee 100644 --- a/mix.exs +++ b/mix.exs @@ -150,8 +150,7 @@ defmodule Ejabberd.MixProject do defp cond_apps do for {:true, app} <- [{config(:stun), :stun}, - {config(:tools), :observer}, - {config(:tools), :runtime_tools}], do: + {config(:tools), :observer}], do: app end diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index 83a639095..bf0c92512 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -7,7 +7,7 @@ _ -> [] end, Tools = case lists:keyfind(tools, 1, Terms) of - {tools, true} -> [observer, runtime_tools]; % for `ejabberdctl etop` + {tools, true} -> [observer]; % for `ejabberdctl etop` _ -> [] end, {[lists:keyfind(description, 1, Terms), From 9275bf40b2ff60a2a90b5dc401a2a4c222e8eb38 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Feb 2024 17:30:37 +0100 Subject: [PATCH 0435/1302] Rebar3/Mix: If dev profile/environment, enable tools automatically --- mix.exs | 6 +++++- src/ejabberd.app.src.script | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 5006552ee..38fc3eb73 100644 --- a/mix.exs +++ b/mix.exs @@ -187,10 +187,14 @@ defmodule Ejabberd.MixProject do {:ok, path} -> path end - case :file.consult(filepath) do + config2 = case :file.consult(filepath) do {:ok,config} -> config _ -> [stun: true, zlib: true] end + case Mix.env() do + :dev -> List.keystore(config2, :tools, 0, {:tools, true}) + _ -> config2 + end end defp config(key) do diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index bf0c92512..089295804 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -6,10 +6,22 @@ {elixir, true} -> [elixir, iex, logger, mix]; _ -> [] end, - Tools = case lists:keyfind(tools, 1, Terms) of - {tools, true} -> [observer]; % for `ejabberdctl etop` + + ProfileEnvironmentVariable = os:getenv("REBAR_PROFILE"), + AsProfiles = case lists:dropwhile(fun("as") -> false; (_) -> true end, + init:get_plain_arguments()) of + ["as", Profiles | _] -> string:split(Profiles, ","); + _ -> [] + end, + Terms2 = case lists:member("dev", [ProfileEnvironmentVariable | AsProfiles]) of + true -> lists:keystore(tools, 1, Terms, {tools, true}); + false -> Terms + end, + Tools = case lists:keyfind(tools, 1, Terms2) of + {tools, true} -> [observer]; _ -> [] end, + {[lists:keyfind(description, 1, Terms), lists:keyfind(vsn, 1, Terms), {env, [{enabled_backends, EBs}]} From 0b37d50dac4f00f5952101b2e363385b75067e0b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Feb 2024 11:28:38 +0100 Subject: [PATCH 0436/1302] rebar.config.script: Don't check REBAR_PROFILE here To get release with development tools, don't check rebar profile here. Instead, use "./configure --enable-tools" and it will be checked in ejabberd.app.src.script --- rebar.config.script | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index 0304bf37c..bc1e060db 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -353,15 +353,11 @@ VarsApps = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.confi ProcessRelx = fun(Relx, Deps) -> {value, {release, NameVersion, DefaultApps}, RelxTail} = lists:keytake(release, 1, Relx), - ProfileApps = case os:getenv("REBAR_PROFILE") of - "dev" -> [observer, runtime_tools, wx, debugger]; - _ -> [] - end, DepApps = lists:map(fun({DepName, _, _}) -> DepName; ({DepName, _}) -> DepName; (DepName) -> DepName end, Deps), - [{release, NameVersion, DefaultApps ++ VarsApps ++ ProfileApps ++ DepApps} | RelxTail] + [{release, NameVersion, DefaultApps ++ VarsApps ++ DepApps} | RelxTail] end, GithubConfig = case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of From ba1917d23cf398a1fe70ed20e461c3d18fd2cd99 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Feb 2024 12:06:06 +0100 Subject: [PATCH 0437/1302] vars.config.in: Remove comment that nowadays is obsolete --- vars.config.in | 1 - 1 file changed, 1 deletion(-) diff --git a/vars.config.in b/vars.config.in index 8bd4c9361..69e51f07a 100644 --- a/vars.config.in +++ b/vars.config.in @@ -24,7 +24,6 @@ {debug, @debug@}. {new_sql_schema, @new_sql_schema@}. -%% Ad-hoc directories with source files {tools, @tools@}. %% Dependencies From 27d5a1d5bb71c96bf64bcf718d3e444965e9b8cc Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Feb 2024 17:25:22 +0100 Subject: [PATCH 0438/1302] configure.ac: Update enable-tools description to its current usage --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 35ee5fce8..f4c1a48ca 100644 --- a/configure.ac +++ b/configure.ac @@ -263,7 +263,7 @@ AC_ARG_ENABLE(system_deps, esac],[if test "x$system_deps" = "x"; then system_deps=false; fi]) AC_ARG_ENABLE(tools, -[AS_HELP_STRING([--enable-tools],[build development tools: etop (default: no)])], +[AS_HELP_STRING([--enable-tools],[include debugging/development tools (default: no)])], [case "${enableval}" in yes) tools=true ;; no) tools=false ;; From 5fb76b49181471897bf727121523736b38c539c7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Feb 2024 11:08:44 +0100 Subject: [PATCH 0439/1302] Use http_uri only when matrix requires it, ammend commit 59ff77e --- src/misc.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/misc.erl b/src/misc.erl index 1800ca253..52a2fb15f 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -98,8 +98,13 @@ uri_parse(URL, Protocols) -> -endif. -ifdef(OTP_BELOW_25). +-ifdef(OTP_BELOW_24). +uri_quote(Data) -> + Data. +-else. uri_quote(Data) -> http_uri:encode(Data). +-endif. -else. uri_quote(Data) -> uri_string:quote(Data). From a09f222b4cc1dff6435983c413e3b7fafeda2cc4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Feb 2024 19:38:50 +0100 Subject: [PATCH 0440/1302] Use Luerl 1.0 for Erlang 20, 1.1.1 for 21-26, and temporary fork for 27 The Luerl 1.1.0 and 1.1.1 hex packages contain garbage files that break compilation with Erlang 20. --- mix.exs | 5 ++++- rebar.config | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mix.exs b/mix.exs index 38fc3eb73..28fa171e8 100644 --- a/mix.exs +++ b/mix.exs @@ -140,7 +140,10 @@ defmodule Ejabberd.MixProject do {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, - {config(:lua), {:luerl, "~> 1.0"}}, + {config(:lua) and if_version_below(~c"27", true), + {:luerl, "~> 1.1.1"}}, + {config(:lua) and if_version_above(~c"26", true), + {:luerl, git: "https://github.com/processone/luerl", branch: "otp27"}}, {config(:mysql), {:p1_mysql, ">= 1.0.23" }}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, diff --git a/rebar.config b/rebar.config index 8a520a048..de4ebf951 100644 --- a/rebar.config +++ b/rebar.config @@ -48,14 +48,28 @@ {if_version_below, "22", {lager, ".*", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}} }, + %% Lua, rebar, OTP 20: 1.0 git tag {if_var_true, lua, {if_not_rebar3, - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} - }}, + {if_version_below, "21", + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} + }}}, + %% Lua, rebar3, OTP 20: 1.0.0 hex package {if_var_true, lua, {if_rebar3, - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} - }}, + {if_version_below, "21", + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} + }}}, + %% Lua, any rebar, OTP 21-26: 1.1.1 git tag / 1.1.1 hex package + {if_var_true, lua, + {if_version_above, "20", {if_version_below, "27", + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.1.1"}}} + }}}, + %% Lua, any rebar, OTP 27: temporary otp27 branch from fork + {if_var_true, lua, + {if_version_above, "26", + {luerl, ".*", {git, "https://github.com/processone/luerl", {branch, "otp27"}}} + }}, {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, From 1b5a9c1a1fa44da533cf0f0ac8c7756e7a903095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 22 Feb 2024 16:12:04 +0100 Subject: [PATCH 0441/1302] Fix dialyzer warnings in ejabberd_sql in R27 --- src/ejabberd_sql.erl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 8c192a784..7d2766459 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -72,7 +72,7 @@ -include("ejabberd_stacktrace.hrl"). -record(state, - {db_ref :: undefined | pid(), + {db_ref :: undefined | pid() | odbc:connection_reference(), db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql, db_version :: undefined | non_neg_integer() | {non_neg_integer(), atom(), non_neg_integer()}, reconnect_count = 0 :: non_neg_integer(), @@ -1122,14 +1122,18 @@ mysql_to_odbc(ok) -> mysql_item_to_odbc(Columns, Recs) -> {selected, [element(2, Column) || Column <- Columns], Recs}. -to_odbc({selected, Columns, Recs}) -> - Rows = [lists:map( - fun(I) when is_integer(I) -> - integer_to_binary(I); - (B) -> - B - end, Row) || Row <- Recs], - {selected, [list_to_binary(C) || C <- Columns], Rows}; +to_odbc({selected, Columns, Rows}) -> + Rows2 = lists:map( + fun(Row) -> + Row2 = if is_tuple(Row) -> tuple_to_list(Row); + is_list(Row) -> Row + end, + lists:map( + fun(I) when is_integer(I) -> integer_to_binary(I); + (B) -> B + end, Row2) + end, Rows), + {selected, [list_to_binary(C) || C <- Columns], Rows2}; to_odbc({error, Reason}) when is_list(Reason) -> {error, list_to_binary(Reason)}; to_odbc(Res) -> From f4de0cef26353552608042e98a54d74a0d1ecb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 22 Feb 2024 17:25:56 +0100 Subject: [PATCH 0442/1302] Add alternative types that should fix dialyzer on r26 --- src/ejabberd_sql.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 7d2766459..58580c182 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -67,12 +67,22 @@ -export([connecting/2, connecting/3, session_established/2, session_established/3]). +-ifdef(OTP_RELEASE). + -if(?OTP_RELEASE >= 27). + -type(odbc_connection_reference() :: odbc:connection_reference()). + -else. + -type(odbc_connection_reference() :: pid()). + -endif. +-else. + -type(odbc_connection_reference() :: pid()). +-endif. + -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("ejabberd_stacktrace.hrl"). -record(state, - {db_ref :: undefined | pid() | odbc:connection_reference(), + {db_ref :: undefined | pid() | odbc_connection_reference(), db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql, db_version :: undefined | non_neg_integer() | {non_neg_integer(), atom(), non_neg_integer()}, reconnect_count = 0 :: non_neg_integer(), From 2b7b92edeef6e413570cafacc392720a2baab23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Fri, 23 Feb 2024 12:09:10 +0100 Subject: [PATCH 0443/1302] Add mod_matrix_gw doc --- src/mod_matrix_gw.erl | 62 ++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 929cdc477..6e5a3a94a 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -884,30 +884,56 @@ mod_options(Host) -> mod_doc() -> #{desc => - [?T("TODO")], + [?T("Matrix gateway")], example => - [{?T("TODO"), - ["listen:", - " -", - " port: 5280", - " module: ejabberd_http", - " request_handlers:", - " /bosh: mod_bosh", - " /websocket: ejabberd_http_ws", - " /conversejs: mod_conversejs", - "", - "modules:", - " mod_bosh: {}", - " mod_conversejs:", - " websocket_url: \"ws://@HOST@:5280/websocket\""]} - ], + ["listen:", + " -", + " port: 8448", + " module: ejabberd_http", + " tls: true", + " request_handlers:", + " \"/_matrix\": mod_matrix_gw", + "", + "modules:", + " mod_matrix_gw:", + " key_name: \"key1\"", + " key: \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"", + " matrix_id_as_jid: true"], opts => [{matrix_domain, #{value => ?T("Domain"), desc => - ?T("TODO Specify a domain to act as the default for user JIDs. " + ?T("Specify a domain in the Matrix federation. " "The keyword '@HOST@' is replaced with the hostname. " - "The default value is '@HOST@'.")}} + "The default value is '@HOST@'.")}}, + {host, + #{value => ?T("Host"), + desc => + ?T("This option defines the Jabber IDs of the service. " + "If the 'host' option is not specified, the Jabber ID will be " + "the hostname of the virtual host with the prefix \"matrix.\". " + "The keyword '@HOST@' is replaced with the real virtual host name.")}}, + {key_name, + #{value => "string()", + desc => + ?T("Name of the matrix signing key.")}}, + {key, + #{value => "string()", + desc => + ?T("Value of the matrix signing key, in base64.")}}, + {matrix_id_as_jid, + #{value => "true | false", + desc => + ?T("If set to 'false', all packets failing to be delivered via an XMPP " + "server-to-server connection will then be routed to the Matrix gateway " + "by translating a Jabber ID 'user@matrixdomain.tld' to a Matrix user " + "identifier '@user:matrixdomain.tld'. When set to true, messages " + "must be explicitly sent to the matrix gateway service Jabber ID to be " + "routed to a remote Matrix server. In this case, to send a message to " + "Matrix user '@user:matrixdomain.tld', the client must send a message " + "to the JID 'user%matrixdomain.tld@matrix.myxmppdomain.tld', where " + "'matrix.myxmppdomain.tld' is the JID of the gateway service as set by the " + "'host' option. The default is 'false'.")}} ] }. -endif. From 6aaefc663b3fef5a176100a872d5bb67a8b5156c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 24 Feb 2024 22:06:36 +0100 Subject: [PATCH 0444/1302] Fix XEP-0425: Message Moderation with SQL storage Use the correct server name and room JID when removing moderated messages from SQL. --- src/mod_mam.erl | 4 ++-- src/mod_mam_mnesia.erl | 11 +++++------ src/mod_mam_sql.erl | 2 ++ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 7363eb9f1..cde711f01 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -361,9 +361,9 @@ remove_mam_for_user_with_peer(User, Server, Peer) -> ok | {error, binary()}. remove_message_from_archive(User, Server, StanzaId) when is_binary(User) -> remove_message_from_archive({User, Server}, Server, StanzaId); -remove_message_from_archive({User, Host}, Server, StanzaId) -> +remove_message_from_archive({_User, _Host} = UserHost, Server, StanzaId) -> Mod = gen_mod:db_mod(Server, ?MODULE), - case Mod:remove_from_archive(User, Host, StanzaId) of + case Mod:remove_from_archive(UserHost, Server, StanzaId) of ok -> ok; {error, Bin} when is_binary(Bin) -> diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 2371ee1a6..f643da1fc 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -77,14 +77,14 @@ remove_user(LUser, LServer) -> remove_room(_LServer, LName, LHost) -> remove_user(LName, LHost). -remove_from_archive(LUser, LServer, none) -> - US = {LUser, LServer}, +remove_from_archive(LUser, LHost, Key) when is_binary(LUser) -> + remove_from_archive({LUser, LHost}, LHost, Key); +remove_from_archive(US, _LServer, none) -> case mnesia:transaction(fun () -> mnesia:delete({archive_msg, US}) end) of {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} end; -remove_from_archive(LUser, LServer, #jid{} = WithJid) -> - US = {LUser, LServer}, +remove_from_archive(US, _LServer, #jid{} = WithJid) -> Peer = jid:remove_resource(jid:split(WithJid)), F = fun () -> Msgs = mnesia:select( @@ -99,9 +99,8 @@ remove_from_archive(LUser, LServer, #jid{} = WithJid) -> {atomic, _} -> ok; {aborted, Reason} -> {error, Reason} end; -remove_from_archive(LUser, LServer, StanzaId) -> +remove_from_archive(US, _LServer, StanzaId) -> Timestamp = misc:usec_to_now(StanzaId), - US = {LUser, LServer}, F = fun () -> Msgs = mnesia:select( archive_msg, diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 8bc27b65d..2646b0397 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -163,6 +163,8 @@ remove_room(LServer, LName, LHost) -> LUser = jid:encode({LName, LHost, <<>>}), remove_user(LUser, LServer). +remove_from_archive({LUser, LHost}, LServer, Key) -> + remove_from_archive(jid:encode({LUser, LHost, <<>>}), LServer, Key); remove_from_archive(LUser, LServer, none) -> case ejabberd_sql:sql_query(LServer, ?SQL("delete from archive where username=%(LUser)s and %(LServer)H")) of From 417294339dc36ddfcdba609b8fdb7a4fc607e3ed Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 12:58:53 +0100 Subject: [PATCH 0445/1302] Matrix: Minor documentation improvements --- src/mod_matrix_gw.erl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index f44ed0317..17426a289 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -884,7 +884,8 @@ mod_options(Host) -> mod_doc() -> #{desc => - [?T("Matrix gateway")], + [?T("https://matrix.org/[Matrix] gateway."), "", + ?T("This module is available since ejabberd 24.02.")], example => ["listen:", " -", @@ -911,7 +912,7 @@ mod_doc() -> desc => ?T("This option defines the Jabber IDs of the service. " "If the 'host' option is not specified, the Jabber ID will be " - "the hostname of the virtual host with the prefix \"matrix.\". " + "the hostname of the virtual host with the prefix '\"matrix.\"'. " "The keyword '@HOST@' is replaced with the real virtual host name.")}}, {key_name, #{value => "string()", @@ -927,7 +928,7 @@ mod_doc() -> ?T("If set to 'false', all packets failing to be delivered via an XMPP " "server-to-server connection will then be routed to the Matrix gateway " "by translating a Jabber ID 'user@matrixdomain.tld' to a Matrix user " - "identifier '@user:matrixdomain.tld'. When set to true, messages " + "identifier '@user:matrixdomain.tld'. When set to 'true', messages " "must be explicitly sent to the matrix gateway service Jabber ID to be " "routed to a remote Matrix server. In this case, to send a message to " "Matrix user '@user:matrixdomain.tld', the client must send a message " From 863b7eee8b50be9320be5c965c728e0678e75e21 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 15:25:39 +0100 Subject: [PATCH 0446/1302] CONTAINER.md: Update documentation about sofware version numbers --- CONTAINER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTAINER.md b/CONTAINER.md index 06d01dbcc..8c0294778 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -19,7 +19,7 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. This document explains how to use the `ejabberd` container image available in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), built using the files in `.github/container/`. -This image is based in Alpine 3.17, includes Erlang/OTP 25.3 and Elixir 1.14.4. +This image is based in Alpine 3.19, includes Erlang/OTP 26.2 and Elixir 1.16.1. Alternatively, there is also the `ecs` container image available in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), From 92c533f42aa9938527d2d82cd7581bac2e671d1f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 16:36:46 +0100 Subject: [PATCH 0447/1302] CONTRIBUTING.md: Remove anchor with name attribute --- CONTRIBUTING.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0048c4b6..4b1fdffc3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,21 +3,21 @@ We'd love for you to contribute to our source code and to make ejabberd even better than it is today! Here are the guidelines we'd like you to follow: -* [Code of Conduct](#coc) -* [Questions and Problems](#question) -* [Issues and Bugs](#issue) -* [Feature Requests](#feature) -* [Issue Submission Guidelines](#submit) -* [Pull Request Submission Guidelines](#submit-pr) -* [Signing the CLA](#cla) +* [Code of Conduct](#code-of-conduct) +* [Questions and Problems](#questions-bugs-features) +* [Issues and Bugs](#found-an-issue-or-bug) +* [Feature Requests](#missing-a-feature) +* [Issue Submission Guidelines](#issue-submission-guidelines) +* [Pull Request Submission Guidelines](#pull-request-submission-guidelines) +* [Signing the CLA](#signing-the-contributor-license-agreement-cla) -## Code of Conduct +## Code of Conduct Help us keep ejabberd community open-minded and inclusive. Please read and follow our [Code of Conduct][coc]. -## Questions, Bugs, Features +## Questions, Bugs, Features -### Got a Question or Problem? +### Got a Question or Problem? Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on dedicated @@ -38,12 +38,12 @@ Other channels for support are: - [ejabberd XMPP room logs][logs] - [ejabberd Mailing List][list] -### Found an Issue or Bug? +### Found an Issue or Bug? If you find a bug in the source code, you can help us by submitting an issue to our [GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. -### Missing a Feature? +### Missing a Feature? You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. @@ -54,7 +54,7 @@ If you would like to implement a new feature then consider what kind of change i * **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr). -## Issue Submission Guidelines +## Issue Submission Guidelines Before you submit your issue search the archive, maybe your question was already answered. @@ -64,7 +64,7 @@ the effort we can spend fixing issues and adding new features, by not reporting The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to make it easier to understand and categorize the issue. -## Pull Request Submission Guidelines +## Pull Request Submission Guidelines By submitting a pull request for a code or doc contribution, you need to have the right to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla] @@ -123,7 +123,7 @@ restarted. That's it! Thank you for your contribution! -## Signing the Contributor License Agreement (CLA) +## Signing the Contributor License Agreement (CLA) Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done so before. It's a quick process, we promise, and you will be able to do it all online From 9a9a466bd4368658b30b36a2ab0db40c1ead289f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 17:01:26 +0100 Subject: [PATCH 0448/1302] CHANGELOG.md: Lower markdown headers level so they get better rendered --- CHANGELOG.md | 40 ++++++++++++++++++++-------------------- CONTAINER.md | 46 +++++++++++++++++++++++----------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a1f840be..b51b1de2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Version 24.01 +## Version 24.01 Core: - Support SASL2 and Bind2 @@ -59,7 +59,7 @@ Rebar3/Elixir/Mix: - Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128)) - Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release -# Version 23.10 +## Version 23.10 Compilation: - Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum @@ -167,7 +167,7 @@ SQL: - `mod_privacy_sql`: Optimize `set_list` - `mod_privacy_sql`: Use more efficient way to calculate changes in `set_privacy_list` -# Version 23.04 +## Version 23.04 General: - New `s2s_out_bounce_packet` hook @@ -251,7 +251,7 @@ Installers: - make-binaries: Fix building with erlang/otp v25.x - make-packages: Fix for installers workflow, which didn't find lynx -# Version 23.01 +## Version 23.01 General: - Add `misc:uri_parse/2` to allow declaring default ports for protocols @@ -305,7 +305,7 @@ VSCode: - Add Erlang LS default configuration - Add Elvis default configuration -# Version 22.10 +## Version 22.10 Core: - Add `log_burst_limit_*` options ([#3865](https://github.com/processone/ejabberd/issues/3865)) @@ -415,7 +415,7 @@ Workflows Actions: - CI: Remove syntax_tools from applications, as fast_xml fails Dialyzer - Runtime: Add Xref options to be as strict as CI -# Version 22.05 +## Version 22.05 Core - C2S: Don't expect that socket will be available in `c2s_terminated` hook @@ -520,7 +520,7 @@ Workflows - Installers: New workflow to build binary packages - Runtime: New workflow to test compilation, rel, starting and ejabberdctl -# Version 21.12 +## Version 21.12 Commands - `create_room_with_opts`: Fixed when using SQL storage @@ -567,7 +567,7 @@ Other - Update export/import of scram password to XEP-0227 1.1 - Update Jose to 1.11.1 (the last in hex.pm correctly versioned) -# Version 21.07 +## Version 21.07 Compilation - Add rebar3 3.15.2 binary @@ -613,7 +613,7 @@ SQL: - mod_mqtt: Add mqtt_pub table definition for MSSQL - mod_shared_roster: Add missing indexes to `sr_group` tables in all SQL databases -# Version 21.04 +## Version 21.04 API Commands: - `add_rosteritem/...`: Add argument guards to roster commands @@ -651,7 +651,7 @@ Modules: - `mod_pubsub`: Fix `gen_pubsub_node:get_state` return value - `mod_vcard`: Obtain and provide photo type in vCard LDAP -# Version 21.01 +## Version 21.01 Miscellaneous changes: - `log_rotate_size` option: Fix handling of ‘infinity’ value @@ -686,7 +686,7 @@ Translations: - Moved gettext PO files to a new `ejabberd-po` repository - Improved several translations: Catalan, Chinese, German, Greek, Indonesian, Norwegian, Portuguese (Brazil), Spanish. -# Version 20.12 +## Version 20.12 - Add support for `SCRAM-SHA-{256,512}-{PLUS}` authentication - Don't use same value in cache for user don't exist and wrong password @@ -721,7 +721,7 @@ Translations: - WebAdmin: Mark dangerous buttons with CSS - WebSocket: Make websocket send put back pressure on c2s process -# Version 20.07 +## Version 20.07 * Changes in this version - Add support for using unix sockets in listeners. @@ -748,7 +748,7 @@ Translations: they were passed to handler modules - Make stun module work better with ipv6 addresses -# Version 20.03 +## Version 20.03 * Changes in this version - Add support of ssl connection when connection to mysql @@ -768,7 +768,7 @@ Translations: - Fix reporting errors in `send_stanza` command when xml passed to it couldn't be passed correctly -# Version 20.02 +## Version 20.02 * Changes in this version - Fix problems when trying to use string format with unicode @@ -786,7 +786,7 @@ Translations: override built-in values - Fix return value of reload_config and dump_config commands -# Version 20.01 +## Version 20.01 * New features - Implement OAUTH authentication in mqtt @@ -819,7 +819,7 @@ Translations: failed - Fix crash in stream management when timeout was not set -# Version 19.09 +## Version 19.09 * Admin - The minimum required Erlang/OTP version is now 19.3 @@ -872,7 +872,7 @@ Translations: - Correctly handle unicode in log messages - Fix unicode processing in ejabberd.yml -# Version 19.08 +## Version 19.08 * Administration - Improve ejabberd halting procedure @@ -917,7 +917,7 @@ Translations: - Remove deprecated pubsub plugins - Expose access_model and publish_model in pubsub#metadata -# Version 19.05 +## Version 19.05 * Admin - The minimum required Erlang/OTP version is now 19.1 @@ -994,7 +994,7 @@ Translations: - mod_mqtt: Support other socket modules - mod_push: Check for payload in encrypted messages -# Version 19.02 +## Version 19.02 * Admin - Fix in configure.ac the Erlang/OTP version: from 17.5 to 19.0 @@ -1048,7 +1048,7 @@ Translations: - Translations: fixed "make translations" - WebAdmin: Fix support to restart module with new options -# Version 18.12 +## Version 18.12 * MAM data store compression * Proxy protocol support diff --git a/CONTAINER.md b/CONTAINER.md index 8c0294778..1ea18519c 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -33,9 +33,9 @@ If you are using a Windows operating system, check the tutorials mentioned in Start ejabberd -============== +-------------- -## With default configuration +### With default configuration Start ejabberd in a new container: @@ -59,7 +59,7 @@ docker restart ejabberd ``` -## Start with Erlang console attached +### Start with Erlang console attached Start ejabberd with an Erlang console attached using the `live` command: @@ -70,7 +70,7 @@ docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live That uses the default configuration file and XMPP domain "localhost". -## Start with your configuration and database +### Start with your configuration and database Pass a configuration file as a volume and share the local directory to store database: @@ -92,9 +92,9 @@ and the volumes you mount must grant proper rights to that account. Next steps -========== +---------- -## Register the administrator account +### Register the administrator account The default ejabberd configuration does not grant admin privileges to any account, @@ -111,7 +111,7 @@ Then edit conf/ejabberd.yml and add the ACL as explained in [ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/installation/#administration-account) -## Check ejabberd log files +### Check ejabberd log files Check the content of the log files inside the container, even if you do not put it on a shared persistent drive: @@ -121,7 +121,7 @@ docker exec -it ejabberd tail -f logs/ejabberd.log ``` -## Inspect the container files +### Inspect the container files The container uses Alpine Linux. Start a shell inside the container: @@ -130,7 +130,7 @@ docker exec -it ejabberd sh ``` -## Open ejabberd debug console +### Open ejabberd debug console Open an interactive debug Erlang console attached to a running ejabberd in a running container: @@ -139,7 +139,7 @@ docker exec -it ejabberd ejabberdctl debug ``` -## CAPTCHA +### CAPTCHA ejabberd includes two example CAPTCHA scripts. If you want to use any of them, first install some additional required libraries: @@ -177,9 +177,9 @@ documentation section. Advanced Container Configuration -================================ +-------------------------------- -## Ports +### Ports This container image exposes the ports: @@ -192,7 +192,7 @@ This container image exposes the ports: - `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD -## Volumes +### Volumes ejabberd produces two types of data: log files and database spool files (Mnesia). This is the kind of data you probably want to store on a persistent or local drive (at least the database). @@ -213,7 +213,7 @@ It's possible to install additional ejabberd modules using volumes, explains how to install an additional module using docker-compose. -## Commands on start +### Commands on start The ejabberdctl script reads the `CTL_ON_CREATE` environment variable the first time the container is started, @@ -234,7 +234,7 @@ Example usage (or check the [full example](#customized-example)): ``` -## Clustering +### Clustering When setting several containers to form a [cluster of ejabberd nodes](https://docs.ejabberd.im/admin/guide/clustering/), @@ -273,7 +273,7 @@ Example using environment variables (see full example [docker-compose.yml](https Build a Container Image -======================= +----------------------- This container image includes ejabberd as a standalone OTP release built using Elixir. That OTP release is configured with: @@ -283,7 +283,7 @@ That OTP release is configured with: - `config/runtime.exs`: Customize ejabberd paths - `ejabberd.yml.template`: ejabberd default config file -## Direct build +### Direct build Build ejabberd Community Server container image from ejabberd master git repository: @@ -294,7 +294,7 @@ docker buildx build \ . ``` -## Podman build +### Podman build It's also possible to use podman instead of docker, just notice: - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile @@ -315,7 +315,7 @@ podman exec -it eja1 sh podman stop eja1 ``` -## Package build for `arm64` +### Package build for `arm64` By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, it is a fast and direct method. @@ -341,9 +341,9 @@ docker buildx build \ Composer Examples -================= +----------------- -## Minimal Example +### Minimal Example This is the barely minimal file to get a usable ejabberd. Store it as `docker-compose.yml`: @@ -365,7 +365,7 @@ Create and start the container with the command: docker-compose up ``` -## Customized Example +### Customized Example This example shows the usage of several customizations: it uses a local configuration file, @@ -408,7 +408,7 @@ services: - ./database:/opt/ejabberd/database ``` -## Clustering Example +### Clustering Example In this example, the main container is created first. Once it is fully started and healthy, a second container is created, From 8bbc27fd399ea284b64351c5c612be7938675579 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 19:02:06 +0100 Subject: [PATCH 0449/1302] CHANGELOG.md: Fix warnings reported by "make edoc" --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b51b1de2b..aaca3d2dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ Rebar3/Elixir/Mix: - Elixir: Fix compiling ejabberd as a dependency ([#4128](https://github.com/processone/ejabberd/issues/4128)) - Elixir: Fix ejabberdctl start/live when installed - Elixir: Fix: FORMATTER ERROR: bad return value ([#4087](https://github.com/processone/ejabberd/issues/4087)) -- Elixir: Fix: Couldn't find file `Elixir.Hex.API` +- Elixir: Fix: Couldn't find file `Elixir Hex API` - Mix: Enable stun by default when `vars.config` not found - Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128)) - Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release @@ -345,7 +345,7 @@ MUC: - Don't persist `none` role ([#3330](https://github.com/processone/ejabberd/issues/3330)) - Allow MUC service admins to bypass max_user_conferences limitation - Show allow_query_users room option in disco info ([#3830](https://github.com/processone/ejabberd/issues/3830)) -- Don't set affiliation to `none` if it's already `none` in `mod_muc_room:process_item_change/3` +- mod_muc_room: Don't set affiliation to `none` if it's already `none` in `process_item_change/3` - Fix mucsub unsubscribe notification payload to have muc_unsubcribe in it - Allow muc_{un}subscribe hooks to modify sent packets - Pass room state to muc_{un}subscribed hook From e43194d59b5263f2deaaccaa4180a9ca0b6fbfaf Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 17:16:34 +0100 Subject: [PATCH 0450/1302] README.md: Fix typo in link to COPYING file: its name is plain-text, not markdown --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a46c42ced..aed86a486 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ There are several places to get in touch with other ejabberd developers and admi License ------- -ejabberd is released under the GNU General Public License v2 (see [COPYING](COPYING.md)), +ejabberd is released under the GNU General Public License v2 (see [COPYING](COPYING)), and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MIT License. From 2075480b14305f33960ce13e2e89554279cb2c40 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 22:36:31 +0100 Subject: [PATCH 0451/1302] Improve some documentation: URLs are parsed later when preparing Docs --- src/mod_block_strangers.erl | 2 +- src/mod_muc.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index 72c210f1d..c8ea332f0 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -301,6 +301,6 @@ mod_doc() -> desc => ?T("Whether to generate CAPTCHA or not in response to " "messages from strangers. See also section " - "https://docs.ejabberd.im/admin/configuration/#captcha" + "http://../#captcha" "[CAPTCHA] of the Configuration Guide. " "The default value is 'false'.")}}]}. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 88e48868d..d3f37b3d4 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1732,7 +1732,7 @@ mod_doc() -> ?T("When a user tries to join a room where they have no " "affiliation (not owner, admin or member), the room " "requires them to fill a CAPTCHA challenge (see section " - "https://docs.ejabberd.im/admin/configuration/#captcha[CAPTCHA] " + "http://../#captcha[CAPTCHA] " "in order to accept their join in the room. " "The default value is 'false'.")}}, {description, From daca3b558b46701fb69011db1d0e180946b14195 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 22:56:49 +0100 Subject: [PATCH 0452/1302] Use another example, because "archive" is replaced when generating EDoc --- src/mod_http_fileserver.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index f04a9d464..f48bed8fe 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -557,7 +557,7 @@ mod_doc() -> example => [{?T("This example configuration will serve the files from the " "local directory '/var/www' in the address " - "'http://example.org:5280/pub/archive/'. In this example a new " + "'http://example.org:5280/pub/content/'. In this example a new " "content type 'ogg' is defined, 'png' is redefined, and 'jpg' " "definition is deleted:"), ["listen:", @@ -567,7 +567,7 @@ mod_doc() -> " module: ejabberd_http", " request_handlers:", " ...", - " /pub/archive: mod_http_fileserver", + " /pub/content: mod_http_fileserver", " ...", " ...", "", From 723c53e1dff473aecf479892a54e2015a7e659c1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 24 Feb 2024 17:14:36 +0100 Subject: [PATCH 0453/1302] Revert "Container: Update make-binaries to support setup-beam v1.17.2" We can now revert commit c81a47a692494fb36c3e169e07f34ef6686612fc because erlef/setup-beam v1.17.3 has recovered its original behavior in https://github.com/erlef/setup-beam/commit/951dd857ae77719ae708c8feb0d9224247a5ac43 --- .github/workflows/container.yml | 2 +- tools/make-binaries | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index d9810d370..8d6056f17 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -49,7 +49,7 @@ jobs: sudo apt-get -qq install python3-dev texinfo unzip - name: Install erlang/OTP - uses: erlef/setup-beam@v1.17.2 + uses: erlef/setup-beam@v1 with: otp-version: ${{ env.OTP_VSN }} elixir-version: ${{ env.ELIXIR_VSN }} diff --git a/tools/make-binaries b/tools/make-binaries index d91cd8610..0c63b7c16 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -350,8 +350,8 @@ add_otp_path() then # For github runners to build for non-native systems: # https://github.com/erlef/setup-beam#environment-variables - native_otp_bin="$INSTALL_DIR_FOR_OTP" - native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR" + native_otp_bin="$INSTALL_DIR_FOR_OTP/bin" + native_elixir_bin="$INSTALL_DIR_FOR_ELIXIR/bin" export PATH="$native_elixir_bin:$PATH" fi export PATH="$native_otp_bin:$PATH" From 79eca7c098934e53722d7ec5b7d4f4db579a65ad Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 24 Feb 2024 17:40:03 +0100 Subject: [PATCH 0454/1302] Update version number from the unreleased 24.01 to 24.02 --- CHANGELOG.md | 2 +- ejabberd.doap | 10 +++++----- src/ejabberd.erl | 10 +++++----- src/ejabberd_ctl.erl | 4 ++-- src/ejabberd_oauth.erl | 2 +- src/ejabberd_options_doc.erl | 2 +- src/ejabberd_xmlrpc.erl | 2 +- src/mod_admin_extra.erl | 6 +++--- src/mod_muc_admin.erl | 6 +++--- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaca3d2dd..8a80bcf40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Version 24.01 +## Version 24.02 Core: - Support SASL2 and Bind2 diff --git a/ejabberd.doap b/ejabberd.doap index 6821e2890..39ab85a4f 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -633,7 +633,7 @@ 0.3.0 - 24.01 + 24.02 @@ -642,7 +642,7 @@ 0.4.0 - 24.01 + 24.02 @@ -705,7 +705,7 @@ 0.4.0 - 24.01 + 24.02 @@ -723,7 +723,7 @@ 0.4.0 - 24.01 + 24.02 @@ -741,7 +741,7 @@ 0.3.0 - 24.01 + 24.02 diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 49ae4912f..54506e7a3 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -39,11 +39,11 @@ -protocol({xep, 243, '1.0'}). -protocol({xep, 270, '1.0'}). -protocol({xep, 368, '1.1.0'}). --protocol({xep, 386, '0.3.0', '24.01', "", ""}). --protocol({xep, 388, '0.4.0', '24.01', "", ""}). --protocol({xep, 424, '0.4.0', '24.01', "", ""}). --protocol({xep, 440, '0.4.0', '24.01', "", ""}). --protocol({xep, 474, '0.3.0', '24.01', "", ""}). +-protocol({xep, 386, '0.3.0', '24.02', "", ""}). +-protocol({xep, 388, '0.4.0', '24.02', "", ""}). +-protocol({xep, 424, '0.4.0', '24.02', "", ""}). +-protocol({xep, 440, '0.4.0', '24.02', "", ""}). +-protocol({xep, 474, '0.3.0', '24.02', "", ""}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 783b290ed..7fc73f57a 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -1120,14 +1120,14 @@ get_commands_spec() -> "don't attempt to execute it using any other API frontend."}, #ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia], desc = "Show information of Mnesia system (only ejabberdctl)", - note = "renamed in 24.01", + note = "renamed in 24.02", longdesc = "This command is exclusive for the ejabberdctl command-line script, " "don't attempt to execute it using any other API frontend."}, #ejabberd_commands{name = print_sql_schema, tags = [ejabberdctl, sql], desc = "Print SQL schema for the given RDBMS (only ejabberdctl)", longdesc = "This command is exclusive for the ejabberdctl command-line script, " "don't attempt to execute it using any other API frontend.", - note = "added in 24.01", + note = "added in 24.02", args = [{db_type, string}, {db_version, string}, {new_schema, string}], args_desc = ["Database type: pgsql | mysql | sqlite", "Your database version: 16.1, 8.2.0...", diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index febf35a5a..08c1f0f7f 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -95,7 +95,7 @@ get_commands_spec() -> desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", module = ?MODULE, function = oauth_issue_token, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args = [{jid, string}, {ttl, integer}, {scopes, {list, {scope, binary}}}], policy = restricted, args_example = ["user@server.com", 3600, ["connected_users_number", "muc_online_rooms"]], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index c0c1c5383..742afcec7 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1361,7 +1361,7 @@ doc() -> "prepared statements. The option is valid for PostgreSQL and MySQL.")}}, {sql_flags, #{value => "[mysql_alternative_upsert]", - note => "added in 24.01", + note => "added in 24.02", desc => ?T("This option accepts a list of SQL flags, and is empty by default. " "'mysql_alternative_upsert' forces the alternative upsert implementation in MySQL.")}}, diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index 176a6af8e..84a724423 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -321,7 +321,7 @@ format_arg({array, [{struct, Elements}]}, when is_list(Elements) -> FormattedList = format_args(Elements, ElementsDef), list_to_tuple(FormattedList); -%% New ejabberd 24.01 +%% New ejabberd 24.02 format_arg({struct, Elements}, {tuple, ElementsDef}) when is_list(Elements) -> diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 57555dc43..4c42c882f 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -428,7 +428,7 @@ get_commands_spec() -> desc = "Set presence of a session", module = ?MODULE, function = set_presence, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args = [{user, binary}, {host, binary}, {resource, binary}, {type, binary}, {show, binary}, {status, binary}, @@ -516,7 +516,7 @@ get_commands_spec() -> desc = "Add an item to a user's roster (supports ODBC)", module = ?MODULE, function = add_rosteritem, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args = [{localuser, binary}, {localhost, binary}, {user, binary}, {host, binary}, {nick, binary}, {groups, {list, {group, binary}}}, @@ -717,7 +717,7 @@ get_commands_spec() -> desc = "Create a Shared Roster Group", module = ?MODULE, function = srg_create, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args = [{group, binary}, {host, binary}, {label, binary}, {description, binary}, {display, {list, {group, binary}}}], args_rename = [{name, label}], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 705ff4009..7c1e56548 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -316,7 +316,7 @@ get_commands_spec() -> "`password` and `message` can be set to `none`.", module = ?MODULE, function = send_direct_invitation, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args_desc = ["Room name", "MUC service", "Password, or `none`", "Reason text, or `none`", "List of users JIDs"], args_example = [<<"room1">>, <<"muc.example.com">>, @@ -365,7 +365,7 @@ get_commands_spec() -> desc = "Subscribe to a MUC conference", module = ?MODULE, function = subscribe_room, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args_desc = ["User JID", "a user's nick", "the room to subscribe", "list of nodes"], args_example = ["tom@localhost", "Tom", "room1@conference.localhost", @@ -406,7 +406,7 @@ get_commands_spec() -> "`subscribe_room_many_max_users`)", module = ?MODULE, function = subscribe_room_many, version = 1, - note = "updated in 24.01", + note = "updated in 24.02", args_desc = ["Users JIDs and nicks", "the room to subscribe", "nodes separated by commas: `,`"], From 4146f198142fee8898cfa35dc764168e467b1caf Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 24 Feb 2024 17:53:11 +0100 Subject: [PATCH 0455/1302] Update man page --- man/ejabberd.yml.5 | 106 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index a165c07a7..4e5f2f4e1 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 01/22/2024 +.\" Date: 02/24/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "01/22/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "02/24/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,7 +82,7 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.01/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.02/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" @@ -1705,7 +1705,7 @@ An SQL database name\&. For SQLite this must be a full path to a database file\& \fIejabberd\fR\&. .RE .sp -\fINote\fR about the next option: added in 24\&.01: +\fINote\fR about the next option: added in 24\&.02: .PP \fBsql_flags\fR: \fI[mysql_alternative_upsert]\fR .RS 4 @@ -3070,7 +3070,7 @@ List of accounts that are allowed to use this service\&. Default value: \fBExamples:\fR .RS 4 .sp -This example configuration will serve the files from the local directory \fI/var/www\fR in the address \fIhttp://example\&.org:5280/pub/archive/\fR\&. In this example a new content type \fIogg\fR is defined, \fIpng\fR is redefined, and \fIjpg\fR definition is deleted: +This example configuration will serve the files from the local directory \fI/var/www\fR in the address \fIhttp://example\&.org:5280/pub/content/\fR\&. In this example a new content type \fIogg\fR is defined, \fIpng\fR is redefined, and \fIjpg\fR definition is deleted: .sp .if n \{\ .RS 4 @@ -3083,7 +3083,7 @@ listen: module: ejabberd_http request_handlers: \&.\&.\&. - /pub/archive: mod_http_fileserver + /pub/content: mod_http_fileserver \&.\&.\&. \&.\&.\&. @@ -3575,6 +3575,96 @@ When this option is disabled, for each individual subscriber a separa mucsub mes \fIfalse\fR\&. .RE .RE +.SS "mod_matrix_gw" +.sp +Matrix gateway\&. +.sp +This module is available since ejabberd 24\&.02\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBhost\fR: \fIHost\fR +.RS 4 +This option defines the Jabber IDs of the service\&. If the +\fIhost\fR +option is not specified, the Jabber ID will be the hostname of the virtual host with the prefix +\fI"matrix\&."\fR\&. The keyword +\fI@HOST@\fR +is replaced with the real virtual host name\&. +.RE +.PP +\fBkey\fR: \fIstring()\fR +.RS 4 +Value of the matrix signing key, in base64\&. +.RE +.PP +\fBkey_name\fR: \fIstring()\fR +.RS 4 +Name of the matrix signing key\&. +.RE +.PP +\fBmatrix_domain\fR: \fIDomain\fR +.RS 4 +Specify a domain in the Matrix federation\&. The keyword +\fI@HOST@\fR +is replaced with the hostname\&. The default value is +\fI@HOST@\fR\&. +.RE +.PP +\fBmatrix_id_as_jid\fR: \fItrue | false\fR +.RS 4 +If set to +\fIfalse\fR, all packets failing to be delivered via an XMPP server\-to\-server connection will then be routed to the Matrix gateway by translating a Jabber ID +\fIuser@matrixdomain\&.tld\fR +to a Matrix user identifier +\fI@user:matrixdomain\&.tld\fR\&. When set to +\fItrue\fR, messages must be explicitly sent to the matrix gateway service Jabber ID to be routed to a remote Matrix server\&. In this case, to send a message to Matrix user +\fI@user:matrixdomain\&.tld\fR, the client must send a message to the JID +\fIuser%\fR\fImatrixdomain\&.tld@matrix\&.myxmppdomain\fR\fI\&.tld\fR, where +\fImatrix\&.myxmppdomain\&.tld\fR +is the JID of the gateway service as set by the +\fIhost\fR +option\&. The default is +\fIfalse\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 8448 + module: ejabberd_http + tls: true + request_handlers: + "/_matrix": mod_matrix_gw + +modules: + mod_matrix_gw: + key_name: "key1" + key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + matrix_id_as_jid: true +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_metrics" .sp This module sends events to external backend (by now only grapherl is supported)\&. Supported events are: @@ -7831,13 +7921,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 23\&.10\&.118\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 24\&.02\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.01/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.02/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From cb9053cd79470c745da6fbe16bbbd13512a84483 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 11:37:09 +0100 Subject: [PATCH 0456/1302] Makefile.in: Inform what build tools support some tasks --- Makefile.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index acad927fd..98953ce4f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -635,7 +635,7 @@ help: @echo " scripts Prepare ejabberd start scripts" @echo " deps Get and configure dependencies" @echo " src Compile dependencies and ejabberd" - @echo " update Update dependencies' source code" + @echo " update Update dependencies source code" @echo " clean Clean binary files" @echo " distclean Clean completely the development files" @echo "" @@ -655,10 +655,10 @@ help: @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" @echo "" - @echo " dialyzer Run Dialyzer static analyzer" + @echo " dialyzer Run Dialyzer static analyzer [rebar3]" @echo " hooks Run hooks validator" - @echo " test Run Common Tests suite" - @echo " xref Run cross reference analysis" + @echo " test Run Common Tests suite [rebar3]" + @echo " xref Run cross reference analysis [rebar3]" #. #' From bb349d60856a0a7776655d98e69aa6a620cb906c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 23 Feb 2024 12:44:39 +0100 Subject: [PATCH 0457/1302] Update "make edoc" to use ExDoc --- .github/workflows/runtime.yml | 2 ++ Makefile.in | 26 ++++++++++++++++++++++---- mix.exs | 30 +++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 5835fc3a7..f530f4c3a 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -341,6 +341,8 @@ jobs: - run: make xref + - run: make edoc + - name: Run rel run: | make rel diff --git a/Makefile.in b/Makefile.in index 98953ce4f..19d796955 100644 --- a/Makefile.in +++ b/Makefile.in @@ -133,6 +133,8 @@ ifeq "$(REBAR_VER)" "6" CONFIGURE_DEPS=(cd deps/eimp; ./configure) EBINDIR=$(DEPSDIR)/ejabberd/ebin XREFOPTIONS=graph + EDOCPRE=MIX_ENV=edoc + EDOCTASK=docs --proglang erlang CLEANARG=--deps ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" @@ -238,9 +240,25 @@ translations: doap: tools/generate-doap.sh -edoc: - $(ERL) -noinput +B -eval \ - 'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.' +#. +#' edoc +# + +edoc: edoc_files edoc_compile + $(EDOCPRE) $(REBAR) $(EDOCTASK) + +edoc_compile: deps + $(EDOCPRE) $(REBAR) compile + +edoc_files: _build/edoc/docs.md _build/edoc/logo.png + +_build/edoc/docs.md: edoc_compile + echo "For much more detailed and complete ejabberd documentation, " \ + "go to the [ejabberd Docs](https://docs.ejabberd.im/) site." \ + > _build/edoc/docs.md + +_build/edoc/logo.png: edoc_compile + wget https://docs.ejabberd.im/static/shared/images/footer_logo_e.png -O _build/edoc/logo.png #. #' copy-files @@ -650,7 +668,7 @@ help: @echo " relive Start a live ejabberd in _build/relive/" @echo "" @echo " doap Generate DOAP file" - @echo " edoc Generate edoc documentation (unused)" + @echo " edoc Generate EDoc documentation [mix]" @echo " options Generate ejabberd_option.erl" @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" diff --git a/mix.exs b/mix.exs index 28fa171e8..631e0e728 100644 --- a/mix.exs +++ b/mix.exs @@ -3,6 +3,7 @@ defmodule Ejabberd.MixProject do def project do [app: :ejabberd, + source_url: "https://github.com/processone/ejabberd", version: version(), description: description(), elixir: elixir_required_version(), @@ -17,6 +18,7 @@ defmodule Ejabberd.MixProject do language: :erlang, releases: releases(), package: package(), + docs: docs(), deps: deps()] end @@ -103,7 +105,7 @@ defmodule Ejabberd.MixProject do [{:base64url, "~> 1.0"}, {:cache_tab, "~> 1.0"}, {:eimp, "~> 1.0"}, - {:ex_doc, ">= 0.0.0", only: :dev}, + {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, ">= 1.1.18"}, {:fast_xml, ">= 1.1.51"}, {:fast_yaml, "~> 1.0"}, @@ -161,6 +163,7 @@ defmodule Ejabberd.MixProject do for {:true, app} <- [{config(:pam), :epam}, {config(:lua), :luerl}, {config(:redis), :eredis}, + {Mix.env() == :edoc, :ex_doc}, {if_version_below(~c"22", true), :lager}, {config(:mysql), :p1_mysql}, {config(:sip), :esip}, @@ -350,6 +353,31 @@ defmodule Ejabberd.MixProject do release end + defp docs do + [ + main: "readme", + logo: "_build/edoc/logo.png", + source_ref: "master", + extra_section: "", # No need for Pages section name, it's the only one + api_reference: false, # API section has just Elixir, hide it + filter_modules: "aaaaa", # Module section has just Elixir modules, hide them + extras: [ + "README.md": [title: "Readme"], + "COMPILE.md": [title: "Compile and Install"], + "CONTAINER.md": [title: "Container Image"], + "CONTRIBUTING.md": [title: "Contributing"], + "CONTRIBUTORS.md": [title: "Contributors"], + "CODE_OF_CONDUCT.md": [title: "Code of Conduct"], + "CHANGELOG.md": [title: "ChangeLog"], + "COPYING": [title: "Copying License"], + "_build/edoc/docs.md": [title: "⟹ ejabberd Docs"] + ], + groups_for_extras: [ + "": Path.wildcard("*.md") ++ ["COPYING"], + "For more documentation": "_build/edoc/docs.md" + ] + ] + end end defmodule Mix.Tasks.Compile.Asn1 do From 146d958fc057acdb4aa40e267f360470095b0224 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 12:42:56 +0100 Subject: [PATCH 0458/1302] CHANGELOG.md: Update to match the newest commits --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a80bcf40..395cfeebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Version 24.02 Core: +- Added Matrix gateway in `mod_matrix_gw` - Support SASL2 and Bind2 - Support tls-server-end-point channel binding and sasl2 codec - Support tls-exporter channel binding @@ -11,12 +12,13 @@ Core: - mod_carboncopy: Teach how to interact with bind2 inline requests Other: -- configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135)) - ejabberdctl: Fix startup problem when having set `EJABBERD_OPTS` and logger options -- ejabberdctl: Set EJABBERD_OPTS back to "", and use previous flags as example +- ejabberdctl: Set EJABBERD_OPTS back to `""`, and use previous flags as example - eldap: Change logic for `eldap tls_verify=soft` and `false` - eldap: Don't set `fail_if_no_peer_cert` for eldap ssl client connections +- Ignore hints when checking for chat states - mod_mam: Support XEP-0424 Message Retraction +- mod_mam: Fix XEP-0425: Message Moderation with SQL storage - mod_ping: Support XEP-0198 pings when stream management is enabled - mod_pubsub: Normalize pubsub `max_items` node options on read - mod_pubsub: PEP nodetree: Fix reversed logic in node fixup function @@ -28,7 +30,18 @@ SQL: - Update SQL schema files for MAM's XEP-0424 - New option [`sql_flags`](https://docs.ejabberd.im/admin/configuration/toplevel/#sql-flags): right now only useful to enable `mysql_alternative_upsert` +Installers and Container: +- Container: Add ability to ignore failures in execution of `CTL_ON_*` commands +- Container: Update to Erlang/OTP 26.2, Elixir 1.16.1 and Alpine 3.19 +- Container: Update this custom ejabberdctl to match the main one +- make-binaries: Bump OpenSSL 3.2.1, Erlang/OTP 26.2.2, Elixir 1.16.1 +- make-binaries: Bump many dependency versions + Commands API: +- `print_sql_schema`: New command available in ejabberdctl command-line script +- ejabberdctl: Rework temporary node name generation +- ejabberdctl: Print argument description, examples and note in help +- ejabberdctl: Document exclusive ejabberdctl commands like all the others - Commands: Add a new [`muc_sub`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#muc-sub) tag to all the relevant commands - Commands: Improve syntax of many commands documentation - Commands: Use list arguments in many commands that used separators @@ -38,22 +51,30 @@ Commands API: - ejabberd_xmlrpc: Fix support for restuple error response - mod_http_api: When no specific API version is requested, use the latest -Rebar3/Elixir/Mix: +Compilation with Rebar3/Elixir/Mix: +- Fix compilation with Erlang/OTP 27: don't use the reserved word 'maybe' +- configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135)) - Add observer and runtime_tools in releases when `--enable-tools` +- Update "make translations" to reduce build requirements +- Use Luerl 1.0 for Erlang 20, 1.1.1 for 21-26, and temporary fork for 27 - Makefile: Add `install-rel` and `uninstall-rel` - Makefile: Rename `make rel` to `make prod` -- ejabberdctl: Detect problem running iex or etop and show explanation +- Makefile: Update `make edoc` to use ExDoc, requires mix +- Makefile: No need to use `escript` to run rebar|rebar3|mix +- configure: If `--with-rebar=rebar3` but rebar3 not system-installed, use local one - configure: Use Mix or Rebar3 by default instead of Rebar2 to compile ejabberd +- ejabberdctl: Detect problem running iex or etop and show explanation - Rebar3: Include Elixir files when making a release - Rebar3: Workaround to fix protocol consolidation - Rebar3: Add support to compile Elixir dependencies - Rebar3: Compile explicitly our Elixir files when `--enable-elixir` -- Rebar3: Provide proper path to iex +- Rebar3: Provide proper path to `iex` +- Rebar/Rebar3: Update binaries to work with Erlang/OTP 24-27 - Rebar/Rebar3: Remove Elixir as a rebar dependency -- Rebar/Rebar3: Update binaries to work with Erlang/OTP 23-26 +- Rebar3/Mix: If `dev` profile/environment, enable tools automatically - Elixir: Fix compiling ejabberd as a dependency ([#4128](https://github.com/processone/ejabberd/issues/4128)) - Elixir: Fix ejabberdctl start/live when installed -- Elixir: Fix: FORMATTER ERROR: bad return value ([#4087](https://github.com/processone/ejabberd/issues/4087)) +- Elixir: Fix: `FORMATTER ERROR: bad return value` ([#4087](https://github.com/processone/ejabberd/issues/4087)) - Elixir: Fix: Couldn't find file `Elixir Hex API` - Mix: Enable stun by default when `vars.config` not found - Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128)) From 95e9a63dcb95996887a22848ed8583b571d500da Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 16:12:39 +0100 Subject: [PATCH 0459/1302] CI: Don't run CT with OTP 27 until "Peer certificate rejected" gets solved --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 815780cf6..46585b205 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25', '26', '27.0-rc1'] + otp: ['20.0', '25', '26'] runs-on: ubuntu-20.04 services: redis: From 0f43a7f36b8ea4809455e24dbfe7c53f75c5f8bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 26 Feb 2024 17:26:30 +0100 Subject: [PATCH 0460/1302] Update deps --- mix.lock | 12 ++++++------ rebar.config | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mix.lock b/mix.lock index 98973ebf7..cde78ed67 100644 --- a/mix.lock +++ b/mix.lock @@ -5,10 +5,10 @@ "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, - "esip": {:hex, :esip, "1.0.51", "e57a50e86e86667f4ed0a58d69d9ccf9c63ef45d40e78a7bfa377b7020d09eda", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.11", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a66bb62ce9c67207341efb3e6f91072f3f611452ab22f57197a61b03e96f9456"}, + "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:hex, :fast_tls, "1.1.18", "1f8edd5ae29ccfb7f7f9e44b93e530324586bcca87b850b85e77722e6899fadd", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d98c90c088eb90b20a2586def040ea00cbc04d9430b6755a1824383cf0e6dcaf"}, + "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, @@ -18,18 +18,18 @@ "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.4", "29563475afa9b8a2add1b7a9c8fb68d06ca7737648f28398e04461f008b69521", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29"}, - "mqtree": {:hex, :mqtree, "1.0.15", "bc54d8b88698fdaebc1e27a9ac43688b927e3dbc05bd5cee4057e69a89a8cf17", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "294ac43c9b3d372e24eeea56c259e19c655522dcff64a55c401a639663b9d829"}, + "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.24", "73995e54224483f42e8b8f50be1d9fbb45dc391b5ddb88bd4ec57f3c63ddbfac", [:rebar3], [{:xmpp, "1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "4d2be56310a490c44bda853b870cc510e3fca54103fc3029f0d4415a54c271f4"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, - "stun": {:hex, :stun, "1.2.11", "89ae34da50cb8d179724d389d4f8000cf3c248fc9cdead97b681a5c1973f13e2", [:rebar3], [{:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b4d9f980af3d89653b405e0a3ee0463677e7fdaa4f824efe5b8ad4a753604ff3"}, + "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.8.0", "8e1380a2dc3092c9442207749c0fbc508d801a7484c6b743e62e397ce161eae3", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.18", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "5ecc613bcb39b3ef58ecdc10c698dced5f5fb186357f500a59e984b9b7dcd447"}, + "xmpp": {:hex, :xmpp, "1.8.1", "134a350dbc6e2e99512fb38669191c1d1c134b3b6836f4c6740801882afa650a", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "32d4a308a6613e4e4155fa4e82eccdc8833096d0fa8d99c8d428c800611e59d3"}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index de4ebf951..b29da967d 100644 --- a/rebar.config +++ b/rebar.config @@ -30,10 +30,10 @@ {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.51"}}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.52"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.18"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.19"}}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -70,21 +70,21 @@ {if_version_above, "26", {luerl, ".*", {git, "https://github.com/processone/luerl", {branch, "otp27"}}} }}, - {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.15"}}}, + {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.23"}}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.24"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.25"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.11"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.8.0"}}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.12"}}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.8.1"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. From b04b16825f97538fc1e5786cd85dbf26551ca8f4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 17:15:07 +0100 Subject: [PATCH 0461/1302] Revert "CI: Don't run CT with OTP 27 until "Peer certificate rejected" gets solved" This recent fix in fast_tls solves the problem: https://github.com/processone/fast_tls/commit/528d9759d6bec714115b5042827726d03e4de0ed This reverts commit 95e9a63dcb95996887a22848ed8583b571d500da. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46585b205..815780cf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25', '26'] + otp: ['20.0', '25', '26', '27.0-rc1'] runs-on: ubuntu-20.04 services: redis: From 11ff7d1ff9703d7753c1aefd1192731aa4975d3b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 16:26:24 +0100 Subject: [PATCH 0462/1302] Update Italian translation (thanks to Ermete Melchiorre) --- priv/msgs/it.msg | 404 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 378 insertions(+), 26 deletions(-) diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index ccf6a0c2c..218815bdc 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -3,40 +3,80 @@ %% To improve translations please read: %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ -{" (Add * to the end of field to match substring)"," Riempire il modulo per la ricerca di utenti Jabber corrispondenti ai criteri (Aggiungere * alla fine del campo per la ricerca di una sottostringa"}. +{" (Add * to the end of field to match substring)"," (Aggiungere * alla fine del campo per far corrispondere la sottostringa)"}. {" has set the subject to: "," ha modificato l'oggetto in: "}. +{"# participants","# partecipanti"}. +{"A description of the node","Una descrizione del nodo"}. {"A friendly name for the node","Un nome comodo per il nodo"}. -{"A password is required to enter this room","Per entrare in questa stanza è prevista una password"}. +{"A password is required to enter this room","Per entrare in questa stanza è necessaria una password"}. +{"A Web Page","Un Pagina Web"}. +{"Accept","Accettare"}. {"Access denied by service policy","Accesso impedito dalle politiche del servizio"}. +{"Access model","Modello di accesso"}. +{"Account doesn't exist","L'account non esiste"}. {"Action on user","Azione sull'utente"}. -{"Add Jabber ID","Aggiungere un Jabber ID (Jabber ID)"}. -{"Add New","Aggiungere nuovo"}. -{"Add User","Aggiungere un utente"}. +{"Add a hat to a user","Aggiungere un cappello a un utente"}. +{"Add Jabber ID","Aggiungere un Jabber ID"}. +{"Add New","Aggiungere Nuovo"}. +{"Add User","Aggiungere un Utente"}. {"Administration of ","Amministrazione di "}. {"Administration","Amministrazione"}. -{"Administrator privileges required","Necessari i privilegi di amministratore"}. +{"Administrator privileges required","Sono richiesti privilegi di amministratore"}. {"All activity","Tutta l'attività"}. -{"All Users","Tutti gli utenti"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","Consentire a questo Jabber ID l'iscrizione a questo nodo pubsub?"}. +{"All Users","Tutti gli Utenti"}. +{"Allow subscription","Consenti iscrizione"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","Consentire a questo ID Jabber di iscriversi a questo nodo pubsub?"}. +{"Allow this person to register with the room?","Permettere a questa persona di registrarsi con la stanza?"}. {"Allow users to change the subject","Consentire agli utenti di cambiare l'oggetto"}. {"Allow users to query other users","Consentire agli utenti query verso altri utenti"}. -{"Allow users to send invites","Consentire agli utenti l'invio di inviti"}. -{"Allow users to send private messages","Consentire agli utenti l'invio di messaggi privati"}. +{"Allow users to send invites","Consenti agli utenti di inviare inviti"}. +{"Allow users to send private messages","Consenti agli utenti di inviare messaggi privati"}. {"Allow visitors to change nickname","Consentire ai visitatori di cambiare il nickname"}. {"Allow visitors to send private messages to","Consentire agli ospiti l'invio di messaggi privati a"}. {"Allow visitors to send status text in presence updates","Consentire ai visitatori l'invio di testo sullo stato in aggiornamenti sulla presenza"}. {"Allow visitors to send voice requests","Consentire agli ospiti l'invio di richieste di parola"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un gruppo LDAP associato che definisce l'appartenenza alla sala; deve trattarsi di un nome distinto LDAP in base a una definizione di gruppo specifica dell'implementazione o della distribuzione."}. {"Announcements","Annunci"}. +{"Answer associated with a picture","Risposta associata ad un'immagine"}. +{"Answer associated with a video","Risposta associata a un video"}. +{"Answer associated with speech","Risposta associata al discorso"}. +{"Answer to a question","Risposta a una domanda"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","Chiunque nei gruppi di elenco specificati può iscriversi e recuperare elementi"}. +{"Anyone may associate leaf nodes with the collection","Chiunque può associare i nodi foglia alla raccolta"}. +{"Anyone may publish","Chiunque può pubblicare"}. +{"Anyone may subscribe and retrieve items","Chiunque può iscriversi e recuperare elementi"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","Chiunque abbia un abbonamento di presenza di entrambi o di può sottoscrivere e recuperare elementi"}. +{"Anyone with Voice","Chiunque abbia la Voce"}. +{"Anyone","Chiunque"}. +{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","A quanto pare il tuo account non ha diritti di amministrazione su questo server. Controlla come concedere i diritti di amministratore in: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Aprile"}. +{"Attribute 'channel' is required for this request","Per questa richiesta è richiesto l'attributo 'canale'"}. +{"Attribute 'id' is mandatory for MIX messages","L'attributo 'id' è obbligatorio per i messaggi MIX"}. +{"Attribute 'jid' is not allowed here","L'attributo \"jid\" non è consentito qui"}. +{"Attribute 'node' is not allowed here","L'attributo \"nodo\" non è consentito qui"}. +{"Attribute 'to' of stanza that triggered challenge","Attributo \"a\" della strofa che ha innescato la sfida"}. {"August","Agosto"}. -{"Backup Management","Gestione dei salvataggi"}. -{"Backup to File at ","Salvataggio sul file "}. -{"Backup","Salvare"}. +{"Automatic node creation is not enabled","La creazione automatica del nodo non è abilitata"}. +{"Backup Management","Gestione dei Backup"}. +{"Backup of ~p","Backup di ~p"}. +{"Backup to File at ","Backup su file in "}. +{"Backup","Backup"}. {"Bad format","Formato non valido"}. {"Birthday","Compleanno"}. +{"Both the username and the resource are required","Sono richiesti sia il nome utente che la risorsa"}. +{"Bytestream already activated","Bytestream già attivato"}. +{"Cannot remove active list","Impossibile rimuovere l'elenco attivo"}. +{"Cannot remove default list","Impossibile rimuovere l'elenco predefinito"}. {"CAPTCHA web page","Pagina web CAPTCHA"}. -{"Change Password","Modificare la password"}. +{"Challenge ID","ID Sfida"}. +{"Change Password","Cambiare la password"}. {"Change User Password","Cambiare la password dell'utente"}. +{"Changing password is not allowed","Non è consentito modificare la password"}. +{"Changing role/affiliation is not allowed","Non è consentito cambiare ruolo/affiliazione"}. +{"Channel already exists","Canale già esistente"}. +{"Channel does not exist","Il canale non esiste"}. +{"Channel JID","Canale JID"}. +{"Channels","Canali"}. {"Characters not allowed:","Caratteri non consentiti:"}. {"Chatroom configuration modified","Configurazione della stanza modificata"}. {"Chatroom is created","La stanza è creata"}. @@ -46,39 +86,59 @@ {"Chatrooms","Stanze"}. {"Choose a username and password to register with this server","Scegliere un nome utente e una password per la registrazione con questo server"}. {"Choose storage type of tables","Selezionare una modalità di conservazione delle tabelle"}. -{"Choose whether to approve this entity's subscription.","Scegliere se approvare l'iscrizione per questa entità"}. +{"Choose whether to approve this entity's subscription.","Scegliere se approvare l'iscrizione per questa entità."}. {"City","Città"}. +{"Client acknowledged more stanzas than sent by server","Il client ha riconosciuto più stanze di quelle inviate dal server"}. {"Commands","Comandi"}. {"Conference room does not exist","La stanza per conferenze non esiste"}. {"Configuration of room ~s","Configurazione per la stanza ~s"}. {"Configuration","Configurazione"}. {"Connected Resources:","Risorse connesse:"}. +{"Contact Addresses (normally, room owner or owners)","Indirizzi di contatto (normalmente, proprietario o proprietari della stanza)"}. +{"Contrib Modules","Moduli di Contributo"}. {"Country","Paese"}. {"CPU Time:","Tempo CPU:"}. +{"Current Discussion Topic","Argomento di discussione attuale"}. +{"Database failure","Errore del database"}. +{"Database Tables at ~p","Tabelle del database a ~p"}. {"Database Tables Configuration at ","Configurazione delle tabelle del database su "}. {"Database","Database"}. {"December","Dicembre"}. {"Default users as participants","Definire per default gli utenti come partecipanti"}. +{"Delete content","Elimina contenuto"}. {"Delete message of the day on all hosts","Eliminare il messaggio del giorno (MOTD) su tutti gli host"}. {"Delete message of the day","Eliminare il messaggio del giorno (MOTD)"}. -{"Delete Selected","Eliminare gli elementi selezionati"}. +{"Delete Selected","Elimina Selezionato"}. +{"Delete table","Elimina tabella"}. {"Delete User","Eliminare l'utente"}. {"Deliver event notifications","Inviare notifiche degli eventi"}. {"Deliver payloads with event notifications","Inviare il contenuto del messaggio con la notifica dell'evento"}. {"Description:","Descrizione:"}. -{"Disc only copy","Copia su disco soltanto"}. +{"Disc only copy","Copia solo su disco"}. +{"'Displayed groups' not added (they do not exist!): ","'Gruppi visualizzati' non aggiunti (non esistono!): "}. +{"Displayed:","Visualizzato:"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Non rivelare la tua password a nessuno, nemmeno agli amministratori del server XMPP."}. {"Dump Backup to Text File at ","Trascrivere il salvataggio sul file di testo "}. {"Dump to Text File","Trascrivere su file di testo"}. +{"Duplicated groups are not allowed by RFC6121","I gruppi duplicati non sono consentiti da RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Specifica dinamicamente una risposta dell'editore dell'elemento"}. {"Edit Properties","Modificare le proprietà"}. {"Either approve or decline the voice request.","Approva oppure respingi la richiesta di parola."}. +{"ejabberd HTTP Upload service","Servizio di Caricamento HTTP di ejabberd"}. {"ejabberd MUC module","Modulo MUC per ejabberd"}. +{"ejabberd Multicast service","Servizio Multicast ejabberd"}. {"ejabberd Publish-Subscribe module","Modulo Pubblicazione/Iscrizione (PubSub) per ejabberd"}. {"ejabberd SOCKS5 Bytestreams module","Modulo SOCKS5 Bytestreams per ejabberd"}. {"ejabberd vCard module","Modulo vCard per ejabberd"}. {"ejabberd Web Admin","Amministrazione web ejabberd"}. +{"ejabberd","ejabberd"}. {"Elements","Elementi"}. +{"Email Address","Indirizzo di Posta Elettronica"}. {"Email","E-mail"}. -{"Enable logging","Abilitare i log"}. +{"Enable hats","Abilitare i cappelli"}. +{"Enable logging","Abilitare la registrazione"}. +{"Enable message archiving","Abilita l'archiviazione dei messaggi"}. +{"Enabling push without 'node' attribute is not supported","L'abilitazione del push senza l'attributo 'nodo' non è supportata"}. {"End User Session","Terminare la sessione dell'utente"}. {"Enter nickname you want to register","Immettere il nickname che si vuole registrare"}. {"Enter path to backup file","Immettere il percorso del file di salvataggio"}. @@ -86,30 +146,55 @@ {"Enter path to jabberd14 spool file","Immettere il percorso del file di spool di jabberd14"}. {"Enter path to text file","Immettere il percorso del file di testo"}. {"Enter the text you see","Immettere il testo visibile"}. +{"Erlang XMPP Server","Server XMPP Erlang"}. {"Error","Errore"}. {"Exclude Jabber IDs from CAPTCHA challenge","Escludi degli ID Jabber dal passaggio CAPTCHA"}. +{"Export all tables as SQL queries to a file:","Esporta tutte le tabelle come query SQL in un file:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Esportare i dati degli utenti di un host in file PIEFXIS (XEP-0227):"}. +{"External component failure","Guasto del componente esterno"}. +{"External component timeout","Timeout del componente esterno"}. +{"Failed to activate bytestream","Impossibile attivare il flusso di byte"}. {"Failed to extract JID from your voice request approval","Impossibile estrarre il JID dall'approvazione della richiesta di parola"}. +{"Failed to map delegated namespace to external component","Impossibile mappare lo spazio dei nomi delegato al componente esterno"}. +{"Failed to parse HTTP response","Impossibile analizzare la risposta HTTP"}. +{"Failed to process option '~s'","Impossibile elaborare l'opzione '~s'"}. {"Family Name","Cognome"}. +{"FAQ Entry","Inserimento delle domande frequenti"}. {"February","Febbraio"}. +{"File larger than ~w bytes","File più grande di ~w byte"}. +{"Fill in the form to search for any matching XMPP User","Compila il modulo per cercare qualsiasi utente XMPP corrispondente"}. {"Friday","Venerdì"}. +{"From ~ts","Da ~ts"}. {"From","Da"}. +{"Full List of Room Admins","Elenco Completo degli Amministratori delle Stanze"}. +{"Full List of Room Owners","Elenco Completo dei Proprietari delle Stanze"}. {"Full Name","Nome completo"}. +{"Get List of Online Users","Ottieni L'elenco degli Utenti Online"}. +{"Get List of Registered Users","Ottieni L'elenco degli Utenti Registrati"}. {"Get Number of Online Users","Ottenere il numero di utenti online"}. {"Get Number of Registered Users","Ottenere il numero di utenti registrati"}. +{"Get Pending","Ottieni in sospeso"}. {"Get User Last Login Time","Ottenere la data di ultimo accesso dell'utente"}. {"Get User Password","Ottenere la password dell'utente"}. {"Get User Statistics","Ottenere le statistiche dell'utente"}. +{"Given Name","Nome di battesimo"}. {"Grant voice to this person?","Dare parola a questa persona?"}. {"Group","Gruppo"}. +{"Groups that will be displayed to the members","Gruppi che verranno visualizzati ai membri"}. {"Groups","Gruppi"}. {"has been banned","è stata/o bandita/o"}. {"has been kicked because of a system shutdown","è stato espulso a causa dello spegnimento del sistema"}. {"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. {"has been kicked because the room has been changed to members-only","è stato espulso per la limitazione della stanza ai soli membri"}. {"has been kicked","è stata/o espulsa/o"}. +{"Hat title","Titolo del Cappello"}. +{"Hat URI","URI Cappello"}. +{"Hats limit exceeded","Limite di cappelli superato"}. +{"Host unknown","Host sconosciuto"}. {"Host","Host"}. +{"HTTP File Upload","Caricamento file HTTP"}. +{"Idle connection","Connessione inattiva"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se qui non vedi l'immagine CAPTCHA, visita la pagina web."}. {"Import Directory","Importare una directory"}. {"Import File","Importare un file"}. @@ -119,22 +204,50 @@ {"Import users data from jabberd14 spool directory:","Importare i dati utenti da directory di spool di jabberd14:"}. {"Import Users from Dir at ","Importare utenti dalla directory "}. {"Import Users From jabberd14 Spool Files","Importare utenti da file di spool di jabberd14"}. +{"Improper domain part of 'from' attribute","Parte del dominio non corretta dell'attributo 'da'"}. {"Improper message type","Tipo di messaggio non corretto"}. +{"Incoming s2s Connections:","Connessioni s2s in Entrata:"}. +{"Incorrect CAPTCHA submit","Invio CAPTCHA errato"}. +{"Incorrect data form","Modulo dati errato"}. {"Incorrect password","Password non esatta"}. +{"Incorrect value of 'action' attribute","Valore errato dell'attributo 'azione'"}. +{"Incorrect value of 'action' in data form","Valore errato di 'azione' nel modulo dati"}. +{"Incorrect value of 'path' in data form","Valore errato di 'percorso' nel modulo dati"}. +{"Installed Modules:","Moduli installati:"}. +{"Install","Installare"}. +{"Insufficient privilege","Privilegio insufficiente"}. +{"Internal server error","Errore interno del server"}. +{"Invalid 'from' attribute in forwarded message","Attributo 'da' non valido nel messaggio inoltrato"}. +{"Invalid node name","Nome del nodo non valido"}. +{"Invalid 'previd' value","Valore 'previd' non valido"}. +{"Invitations are not allowed in this conference","Non sono ammessi inviti a questa conferenza"}. {"IP addresses","Indirizzi IP"}. {"is now known as","è ora conosciuta/o come"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Non è consentito inviare messaggi di errore alla stanza. Il partecipante (~s) ha inviato un messaggio di errore (~s) ed è stato espulso dalla stanza"}. {"It is not allowed to send private messages of type \"groupchat\"","Non è consentito l'invio di messaggi privati di tipo \"groupchat\""}. {"It is not allowed to send private messages to the conference","Non è consentito l'invio di messaggi privati alla conferenza"}. {"Jabber ID","Jabber ID (Jabber ID)"}. {"January","Gennaio"}. +{"JID normalization denied by service policy","Normalizzazione JID negata dalla politica del servizio"}. +{"JID normalization failed","La normalizzazione JID non è riuscita"}. +{"Joined MIX channels of ~ts","Canali MIX uniti di ~ts"}. +{"Joined MIX channels:","Canali MIX uniti:"}. {"joins the room","entra nella stanza"}. {"July","Luglio"}. {"June","Giugno"}. +{"Just created","Appena creato"}. +{"Label:","Etichetta:"}. {"Last Activity","Ultima attività"}. {"Last login","Ultimo accesso"}. +{"Last message","Ultimo messaggio"}. {"Last month","Ultimo mese"}. {"Last year","Ultimo anno"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","I bit meno significativi dell'hash di testo SHA-256 devono corrispondere all'etichetta esadecimale"}. {"leaves the room","esce dalla stanza"}. +{"List of rooms","Elenco delle stanze"}. +{"List of users with hats","Elenco degli utenti con cappelli"}. +{"List users with hats","Elenca gli utenti con cappelli"}. +{"Logging","Registrazione"}. {"Low level update script","Script di aggiornamento di basso livello"}. {"Make participants list public","Rendere pubblica la lista dei partecipanti"}. {"Make room CAPTCHA protected","Rendere la stanza protetta da CAPTCHA"}. @@ -143,41 +256,104 @@ {"Make room password protected","Rendere la stanza protetta da password"}. {"Make room persistent","Rendere la stanza persistente"}. {"Make room public searchable","Rendere la sala visibile al pubblico"}. +{"Malformed username","Nome utente malformato"}. +{"MAM preference modification denied by service policy","Modifica delle preferenze MAM negata dalla policy del servizio"}. {"March","Marzo"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Numero massimo di elementi da persistere o `max` per nessun limite specifico diverso da quello massimo imposto dal server"}. {"Max payload size in bytes","Dimensione massima del contenuto del messaggio in byte"}. +{"Maximum file size","Dimensione massima del file"}. +{"Maximum Number of History Messages Returned by Room","Numero Massimo di Messaggi di Cronologia Restituiti dalla Stanza"}. +{"Maximum number of items to persist","Numero massimo di elementi da persistere"}. {"Maximum Number of Occupants","Numero massimo di occupanti"}. {"May","Maggio"}. +{"Members not added (inexistent vhost!): ","Membri non aggiunti (vhost inesistente!): "}. {"Membership is required to enter this room","Per entrare in questa stanza è necessario essere membro"}. {"Members:","Membri:"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorizza la tua password, oppure scrivila su un foglio riposto in un luogo sicuro. In XMPP non esiste un modo automatico per recuperare la password se la dimentichi."}. {"Memory","Memoria"}. +{"Mere Availability in XMPP (No Show Value)","Mera disponibilità in XMPP (Nessun Valore Mostrato)"}. {"Message body","Corpo del messaggio"}. +{"Message not found in forwarded payload","Messaggio non trovato nel payload inoltrato"}. +{"Messages from strangers are rejected","I messaggi provenienti da sconosciuti vengono rifiutati"}. +{"Messages of type headline","Messaggi di tipo headline"}. +{"Messages of type normal","Messaggi di tipo normale"}. {"Middle Name","Altro nome"}. {"Minimum interval between voice requests (in seconds)","Intervallo minimo fra due richieste di parola (in secondi)"}. {"Moderator privileges required","Necessari i privilegi di moderatore"}. +{"Moderator","Moderatore/Moderatrice"}. +{"Moderators Only","Solo i Moderatori"}. {"Modified modules","Moduli modificati"}. +{"Module failed to handle the query","Il modulo non è riuscito a gestire la query"}. {"Monday","Lunedì"}. +{"Multicast","Multicast"}. +{"Multiple elements are not allowed by RFC6121","Più elementi non sono consentiti da RFC6121"}. +{"Multi-User Chat","Chat Multiutente"}. +{"Name in the rosters where this group will be displayed","Nome nei roster in cui verrà visualizzato questo gruppo"}. {"Name","Nome"}. {"Name:","Nome:"}. +{"Natural Language for Room Discussions","Linguaggio Naturale per le Discussioni in Sala"}. +{"Natural-Language Room Name","Nome della Stanza in Linguaggio Naturale"}. +{"Neither 'jid' nor 'nick' attribute found","Né l'attributo 'jid' né quello 'nick' sono stati trovati"}. +{"Neither 'role' nor 'affiliation' attribute found","Non sono stati trovati né gli attributi 'ruolo' né 'affiliazione'"}. {"Never","Mai"}. {"New Password:","Nuova password:"}. +{"Nickname can't be empty","Il soprannome non può essere vuoto"}. {"Nickname Registration at ","Registrazione di un nickname su "}. {"Nickname ~s does not exist in the room","Il nickname ~s non esiste nella stanza"}. -{"Nickname","Nickname"}. +{"Nickname","Soprannome"}. +{"No address elements found","Nessun elemento dell'indirizzo trovato"}. +{"No addresses element found","Nessun elemento degli indirizzi trovato"}. +{"No 'affiliation' attribute found","Nessun attributo 'affiliazione' trovato"}. +{"No available resource found","Nessuna risorsa disponibile trovata"}. {"No body provided for announce message","Nessun corpo fornito per il messaggio di annuncio"}. +{"No child elements found","Non sono stati trovati elementi figlio"}. +{"No data form found","Nessun modulo dati trovato"}. {"No Data","Nessuna informazione"}. +{"No features available","Nessuna funzionalità disponibile"}. +{"No element found","Nessun elemento trovato"}. +{"No hook has processed this command","Nessun hook ha elaborato questo comando"}. +{"No info about last activity found","Nessuna informazione sull'ultima attività trovata"}. +{"No 'item' element found","Nessun elemento 'item' trovato"}. +{"No items found in this query","Nessun elemento trovato in questa query"}. {"No limit","Nessun limite"}. -{"Node ID","ID del nodo"}. +{"No module is handling this query","Nessun modulo gestisce questa query"}. +{"No node specified","Nessun nodo specificato"}. +{"No 'password' found in data form","Nessuna 'password' trovata nel modulo dati"}. +{"No 'password' found in this query","Nessuna \"password\" trovata in questa query"}. +{"No 'path' found in data form","Nessun 'percorso' trovato nel modulo dati"}. +{"No pending subscriptions found","Nessuna sottoscrizione in attesa trovata"}. +{"No privacy list with this name found","Nessun elenco di privacy con questo nome trovato"}. +{"No private data found in this query","Non sono stati trovati dati privati in questa query"}. +{"No running node found","Nessun nodo in esecuzione trovato"}. +{"No services available","Nessun servizio disponibile"}. +{"No statistics found for this item","Nessuna statistica trovata per questa voce"}. +{"No 'to' attribute found in the invitation","Nessun attributo 'a' trovato nell'invito"}. +{"Nobody","Nessuno"}. +{"Node already exists","Il nodo esiste già"}. +{"Node ID","ID del Nodo"}. +{"Node index not found","Indice del nodo non trovato"}. {"Node not found","Nodo non trovato"}. +{"Node ~p","Nodo ~p"}. +{"Node","Nodo"}. +{"Nodeprep has failed","Nodeprep non è riuscito"}. {"Nodes","Nodi"}. -{"None","Nessuno"}. +{"None","Niente"}. +{"Not allowed","Non consentito"}. {"Not Found","Non trovato"}. +{"Not subscribed","Non sottoscritto"}. {"Notify subscribers when items are removed from the node","Notificare gli iscritti quando sono eliminati degli elementi dal nodo"}. {"Notify subscribers when the node configuration changes","Notificare gli iscritti quando la configurazione del nodo cambia"}. {"Notify subscribers when the node is deleted","Notificare gli iscritti quando il nodo è cancellato"}. {"November","Novembre"}. +{"Number of answers required","Numero di risposte richieste"}. {"Number of occupants","Numero di presenti"}. +{"Number of Offline Messages","Numero di messaggi offline"}. {"Number of online users","Numero di utenti online"}. {"Number of registered users","Numero di utenti registrati"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Numero di secondi dopo i quali eliminare automaticamente gli elementi o `max` per nessun limite specifico diverso da quello massimo imposto dal server"}. +{"Occupants are allowed to invite others","Gli occupanti possono invitare altri"}. +{"Occupants are allowed to query others","Gli occupanti possono interrogare gli altri"}. +{"Occupants May Change the Subject","Gli Occupanti Possono Cambiare il Soggetto"}. {"October","Ottobre"}. {"Offline Messages","Messaggi offline"}. {"Offline Messages:","Messaggi offline:"}. @@ -186,50 +362,85 @@ {"Online Users:","Utenti connessi:"}. {"Online Users","Utenti online"}. {"Online","Online"}. +{"Only admins can see this","Solo gli amministratori possono vedere questo"}. +{"Only collection node owners may associate leaf nodes with the collection","Solo i proprietari dei nodi di raccolta possono associare i nodi foglia alla collezione"}. {"Only deliver notifications to available users","Inviare le notifiche solamente agli utenti disponibili"}. +{"Only or tags are allowed","Sono consentiti solo i tag o "}. +{"Only element is allowed in this query","In questa query è consentito solo l'elemento "}. +{"Only members may query archives of this room","Solo i membri possono interrogare gli archivi di questa stanza"}. {"Only moderators and participants are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori e ai partecipanti"}. {"Only moderators are allowed to change the subject in this room","La modifica dell'oggetto di questa stanza è consentita soltanto ai moderatori"}. +{"Only moderators are allowed to retract messages","Solo i moderatori possono ritirare i messaggi"}. {"Only moderators can approve voice requests","Soltanto i moderatori possono approvare richieste di parola"}. {"Only occupants are allowed to send messages to the conference","L'invio di messaggi alla conferenza è consentito soltanto ai presenti"}. {"Only occupants are allowed to send queries to the conference","L'invio di query alla conferenza è consentito ai soli presenti"}. +{"Only publishers may publish","Solo gli editori possono pubblicare"}. {"Only service administrators are allowed to send service messages","L'invio di messaggi di servizio è consentito solamente agli amministratori del servizio"}. +{"Only those on a whitelist may associate leaf nodes with the collection","Solo chi fa parte di una whitelist può associare i nodi foglia alla collezione"}. +{"Only those on a whitelist may subscribe and retrieve items","Solo chi fa parte di una whitelist può sottoscrivere e recuperare le voci"}. {"Organization Name","Nome dell'organizzazione"}. {"Organization Unit","Unità dell'organizzazione"}. +{"Other Modules Available:","Altri Moduli Disponibili:"}. {"Outgoing s2s Connections","Connessioni s2s in uscita"}. {"Outgoing s2s Connections:","Connessioni s2s in uscita:"}. {"Owner privileges required","Necessari i privilegi di proprietario"}. +{"Packet relay is denied by service policy","Il relay dei pacchetti è negato dalla politica di servizio"}. {"Packet","Pacchetto"}. +{"Participant ID","ID Partecipante"}. +{"Participant","Partecipante"}. {"Password Verification","Verifica della password"}. {"Password Verification:","Verifica della password:"}. {"Password","Password"}. {"Password:","Password:"}. {"Path to Dir","Percorso della directory"}. {"Path to File","Percorso del file"}. +{"Payload semantic type information","Informazioni sul tipo semantico del payload"}. {"Pending","Pendente"}. -{"Period: ","Periodo:"}. +{"Period: ","Periodo: "}. {"Persist items to storage","Conservazione persistente degli elementi"}. +{"Persistent","Persistente"}. +{"Ping query is incorrect","La query ping non è corretta"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","N.B.: Queste opzioni comportano il salvataggio solamente del database interno Mnesia. Se si sta utilizzando il modulo ODBC, è necessario salvare anche il proprio database SQL separatamente."}. {"Please, wait for a while before sending new voice request","Attendi qualche istante prima di inviare una nuova richiesta di parola"}. {"Pong","Pong"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","Il possesso dell'attributo 'chiedi' non è consentito da RFC6121"}. {"Present real Jabber IDs to","Rendere visibile il Jabber ID reale a"}. +{"Previous session not found","Sessione precedente non trovata"}. +{"Previous session PID has been killed","Il PID della sessione precedente è stato ucciso"}. +{"Previous session PID has exited","Il PID della sessione precedente è terminato"}. +{"Previous session PID is dead","Il PID della sessione precedente è morto"}. +{"Previous session timed out","La sessione precedente è scaduta"}. {"private, ","privato, "}. +{"Public","Pubblico"}. +{"Publish model","Pubblica modello"}. {"Publish-Subscribe","Pubblicazione-Iscrizione"}. {"PubSub subscriber request","Richiesta di iscrizione per PubSub"}. {"Purge all items when the relevant publisher goes offline","Cancella tutti gli elementi quando chi li ha pubblicati non è più online"}. +{"Push record not found","Record push non trovato"}. {"Queries to the conference members are not allowed in this room","In questa stanza non sono consentite query ai membri della conferenza"}. +{"Query to another users is forbidden","La richiesta ad altri utenti è vietata"}. {"RAM and disc copy","Copia in memoria (RAM) e su disco"}. {"RAM copy","Copia in memoria (RAM)"}. {"Really delete message of the day?","Si conferma l'eliminazione del messaggio del giorno (MOTD)?"}. +{"Receive notification from all descendent nodes","Ricevere una notifica da tutti i nodi discendenti"}. +{"Receive notification from direct child nodes only","Ricevere notifiche solo dai nodi figli diretti"}. +{"Receive notification of new items only","Ricevere una notifica solo per le nuove voce"}. +{"Receive notification of new nodes only","Ricevere solo la notifica di nuovi nodi"}. {"Recipient is not in the conference room","Il destinatario non è nella stanza per conferenze"}. +{"Register an XMPP account","Registra un account XMPP"}. {"Registered Users","Utenti registrati"}. {"Registered Users:","Utenti registrati:"}. {"Register","Registra"}. {"Remote copy","Copia remota"}. -{"Remove All Offline Messages","Eliminare tutti i messaggi offline"}. -{"Remove User","Eliminare l'utente"}. +{"Remove a hat from a user","Rimuovere un cappello da un utente"}. +{"Remove All Offline Messages","Eliminare Tutti i Messaggi Offline"}. +{"Remove User","Rimuovere l'utente"}. {"Remove","Eliminare"}. {"Replaced by new connection","Sostituito da una nuova connessione"}. +{"Request has timed out","La richiesta è scaduta"}. +{"Request is ignored","La richiesta viene ignorata"}. +{"Requested role","Ruolo richiesto"}. {"Resources","Risorse"}. {"Restart Service","Riavviare il servizio"}. {"Restart","Riavviare"}. @@ -238,34 +449,53 @@ {"Restore binary backup immediately:","Recuperare un salvataggio binario adesso:"}. {"Restore plain text backup immediately:","Recuperare un salvataggio come semplice testo adesso:"}. {"Restore","Recuperare"}. +{"Roles and Affiliations that May Retrieve Member List","Ruoli e Affiliazioni che Possono Recuperare L'elenco dei Membri"}. +{"Roles for which Presence is Broadcasted","Ruoli per i quali viene Trasmessa la Presenza"}. +{"Roles that May Send Private Messages","Ruoli che possono inviare messaggi privati"}. {"Room Configuration","Configurazione della stanza"}. {"Room creation is denied by service policy","La creazione di stanze è impedita dalle politiche del servizio"}. {"Room description","Descrizione della stanza"}. {"Room Occupants","Presenti nella stanza"}. +{"Room terminates","La stanza termina"}. {"Room title","Titolo della stanza"}. {"Roster groups allowed to subscribe","Gruppi roster abilitati alla registrazione"}. +{"Roster of ~ts","Roster di ~ts"}. {"Roster size","Dimensione della lista dei contatti"}. +{"Roster:","Lista dei contatti:"}. {"RPC Call Error","Errore di chiamata RPC"}. {"Running Nodes","Nodi attivi"}. +{"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. {"Saturday","Sabato"}. {"Script check","Verifica dello script"}. +{"Search from the date","Cerca dalla data"}. {"Search Results for ","Risultati della ricerca per "}. +{"Search the text","Cerca nel testo"}. +{"Search until the date","Cerca fino alla data"}. {"Search users in ","Cercare utenti in "}. +{"Select All","Seleziona tutto"}. {"Send announcement to all online users on all hosts","Inviare l'annuncio a tutti gli utenti online su tutti gli host"}. {"Send announcement to all online users","Inviare l'annuncio a tutti gli utenti online"}. {"Send announcement to all users on all hosts","Inviare l'annuncio a tutti gli utenti su tutti gli host"}. {"Send announcement to all users","Inviare l'annuncio a tutti gli utenti"}. {"September","Settembre"}. {"Server:","Server:"}. +{"Service list retrieval timed out","Il recupero dell'elenco dei servizi è scaduto"}. +{"Session state copying timed out","La copia dello stato della sessione è scaduta"}. {"Set message of the day and send to online users","Impostare il messaggio del giorno (MOTD) ed inviarlo agli utenti online"}. {"Set message of the day on all hosts and send to online users","Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli utenti online"}. {"Shared Roster Groups","Gruppi di liste di contatti comuni"}. {"Show Integral Table","Mostrare la tabella integrale"}. {"Show Ordinary Table","Mostrare la tabella normale"}. {"Shut Down Service","Terminare il servizio"}. +{"SOCKS5 Bytestreams","SOCKS5 flussi di byte"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Alcuni client XMPP possono memorizzare la tua password nel computer, ma dovresti farlo solo sul tuo computer personale per motivi di sicurezza."}. +{"Sources Specs:","Sorgenti Specifiche:"}. {"Specify the access model","Specificare il modello di accesso"}. {"Specify the event message type","Specificare il tipo di messaggio di evento"}. {"Specify the publisher model","Definire il modello di pubblicazione"}. +{"Stanza id is not valid","L'id della stanza non è valido"}. +{"Stanza ID","Stanza ID"}. +{"Statically specify a replyto of the node owner(s)","Specificare staticamente una risposta del proprietario(i) del nodo"}. {"Statistics of ~p","Statistiche di ~p"}. {"Statistics","Statistiche"}. {"Stop","Arrestare"}. @@ -273,64 +503,186 @@ {"Storage Type","Tipo di conservazione"}. {"Store binary backup:","Conservare un salvataggio binario:"}. {"Store plain text backup:","Conservare un salvataggio come semplice testo:"}. +{"Stream management is already enabled","La gestione del flusso è già abilitata"}. +{"Stream management is not enabled","La gestione del flusso non è abilitata"}. {"Subject","Oggetto"}. {"Submit","Inviare"}. {"Submitted","Inviato"}. {"Subscriber Address","Indirizzo dell'iscritta/o"}. +{"Subscribers may publish","I sottoscrittori possono pubblicare"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Le richieste di sottoscrizione devono essere approvate e solo i sottoscrittori possono recuperare le voci"}. {"Subscription","Iscrizione"}. +{"Subscriptions are not allowed","Le sottoscrizioni non sono consentite"}. {"Sunday","Domenica"}. +{"Text associated with a picture","Testo associato a un'immagine"}. +{"Text associated with a sound","Testo associato a un suono"}. +{"Text associated with a video","Testo associato a un video"}. +{"Text associated with speech","Testo associato al parlato"}. {"That nickname is already in use by another occupant","Il nickname è già in uso all'interno della conferenza"}. {"That nickname is registered by another person","Questo nickname è registrato da un'altra persona"}. +{"The account already exists","L'account esiste già"}. +{"The account was not unregistered","L'account non era non registrato"}. +{"The body text of the last received message","Il corpo del testo dell'ultimo messaggio ricevuto"}. {"The CAPTCHA is valid.","Il CAPTCHA è valido."}. {"The CAPTCHA verification has failed","La verifica del CAPTCHA ha avuto esito negativo"}. +{"The captcha you entered is wrong","Il captcha che hai inserito è sbagliato"}. +{"The child nodes (leaf or collection) associated with a collection","I nodi figlio (foglia o raccolta) associati a una raccolta"}. {"The collections with which a node is affiliated","Le collezioni a cui è affiliato un nodo"}. +{"The DateTime at which a leased subscription will end or has ended","Il DateTime in cui un abbonamento in leasing terminerà o è terminato"}. +{"The datetime when the node was created","La dataora in cui è stato creato il nodo"}. +{"The default language of the node","La lingua predefinita del nodo"}. +{"The feature requested is not supported by the conference","La funzionalità richiesta non è supportata dalla conferenza"}. +{"The JID of the node creator","Il JID del creatore del nodo"}. +{"The JIDs of those to contact with questions","I JID di coloro da contattare per domande"}. +{"The JIDs of those with an affiliation of owner","I JID di coloro che hanno un'affiliazione di proprietario"}. +{"The JIDs of those with an affiliation of publisher","I JID di coloro che hanno un'affiliazione di editore"}. +{"The list of all online users","L'elenco di tutti gli utenti online"}. +{"The list of all users","L'elenco di tutti gli utenti"}. +{"The list of JIDs that may associate leaf nodes with a collection","L'elenco dei JID che possono associare i nodi foglia a una collezione"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Il numero massimo di nodi figli che possono essere associati a una collezione, o `max` per non avere un limite specifico, se non quello imposto dal server"}. +{"The minimum number of milliseconds between sending any two notification digests","Il numero minimo di millisecondi tra l'invio di due digest di notifica qualsiasi"}. +{"The name of the node","Il nome del nodo"}. +{"The node is a collection node","Il nodo è un nodo di raccolta"}. +{"The node is a leaf node (default)","Il nodo è un nodo foglia (predefinito)"}. +{"The NodeID of the relevant node","Il NodeID del nodo rilevante"}. +{"The number of pending incoming presence subscription requests","Il numero di richieste di sottoscrizione di presenza in entrata in sospeso"}. +{"The number of subscribers to the node","Il numero di abbonati al nodo"}. +{"The number of unread or undelivered messages","Il numero di messaggi non letti o non consegnati"}. +{"The password contains unacceptable characters","La password contiene caratteri non accettabili"}. {"The password is too weak","La password è troppo debole"}. {"the password is","la password è"}. +{"The password of your XMPP account was successfully changed.","La password del tuo account XMPP è stata modificata con successo."}. +{"The password was not changed","La password non è stata modificata"}. +{"The passwords are different","Le password sono diverse"}. +{"The presence states for which an entity wants to receive notifications","Gli stati di presenza per i quali un'entità desidera ricevere le notifiche"}. +{"The query is only allowed from local users","La query è consentita solo da utenti locali"}. +{"The query must not contain elements","La query non deve contenere elementi "}. +{"The room subject can be modified by participants","L'oggetto della stanza potrà essere modificato dai partecipanti"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Le informazioni sul tipo semantico dei dati nel nodo, solitamente specificate dallo spazio dei nomi del payload (se presente)"}. +{"The sender of the last received message","Il mittente dell'ultimo messaggio ricevuto"}. +{"The stanza MUST contain only one element, one element, or one element","La stanza DEVE contenere solo un elemento , un elemento o un elemento "}. +{"The subscription identifier associated with the subscription request","L'identificatore di sottoscrizione associato alla richiesta di sottoscrizione"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","L'URL di una trasformazione XSL che può essere applicata ai payload per generare un elemento del corpo del messaggio appropriato."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","L'URL di una trasformazione XSL che può essere applicata al formato del payload per generare un risultato valido di Data Forms che il client può visualizzare utilizzando un motore di rendering Data Forms generico"}. +{"There was an error changing the password: ","Si è verificato un errore durante la modifica della password: "}. {"There was an error creating the account: ","Si è verificato un errore nella creazione dell'account: "}. {"There was an error deleting the account: ","Si è verificato un errore nella cancellazione dell'account: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Questo è insensibile alle maiuscole: macbeth è lo stesso che MacBeth e Macbeth."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Questa pagina consente di registrare un account XMPP in questo server XMPP. Il tuo JID (Jabber ID) sarà nel formato: nomeutente@server. Si prega di leggere attentamente le istruzioni per compilare correttamente i campi."}. +{"This page allows to unregister an XMPP account in this XMPP server.","Questa pagina consente di annullare la registrazione di un account XMPP in questo server XMPP."}. {"This room is not anonymous","Questa stanza non è anonima"}. +{"This service can not process the address: ~s","Questo servizio non può elaborare l'indirizzo: ~s"}. {"Thursday","Giovedì"}. {"Time delay","Ritardo"}. +{"Timed out waiting for stream resumption","Timed out in attesa della ripresa dello stream"}. {"Time","Ora"}. +{"To register, visit ~s","Per registrarsi, visita ~s"}. +{"To ~ts","A ~ts"}. {"To","A"}. +{"Token TTL","Gettone TTL"}. +{"Too many active bytestreams","Troppi bytestream attivi"}. {"Too many CAPTCHA requests","Troppe richieste CAPTCHA"}. +{"Too many child elements","Troppi elementi figlio"}. +{"Too many elements","Troppi elementi "}. +{"Too many elements","Troppi elementi "}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Troppe (~p) autenticazioni non riuscite da questo indirizzo IP (~s). L'indirizzo verrà sbloccato alle ~s UTC"}. +{"Too many receiver fields were specified","Sono stati specificati troppi campi del ricevitore"}. +{"Too many users in this conference","Troppi utenti in questa conferenza"}. +{"Total rooms","Stanze totali"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. {"Transactions Aborted:","Transazioni abortite:"}. {"Transactions Committed:","Transazioni avvenute:"}. {"Transactions Logged:","Transazioni con log:"}. {"Transactions Restarted:","Transazioni riavviate:"}. +{"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}. {"Tuesday","Martedì"}. {"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}. +{"Unable to register route on existing local domain","Impossibile registrare il percorso sul dominio locale esistente"}. {"Unauthorized","Non autorizzato"}. +{"Unexpected action","Azione inaspettata"}. +{"Unexpected error condition: ~p","Condizione di errore imprevisto: ~p"}. +{"Uninstall","Disinstallare"}. +{"Unregister an XMPP account","Disregistrare un account XMPP"}. {"Unregister","Elimina"}. +{"Unselect All","Deseleziona Tutto"}. +{"Unsupported element","Elemento non supportato"}. +{"Unsupported version","Versione non supportata"}. {"Update message of the day (don't send)","Aggiornare il messaggio del giorno (MOTD) (non inviarlo)"}. {"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}. +{"Update ~p","Aggiorna ~p"}. {"Update plan","Piano di aggiornamento"}. {"Update script","Script di aggiornamento"}. +{"Update specs to get modules source, then install desired ones.","Aggiorna le specifiche per ottenere il sorgente dei moduli, quindi installa quelli desiderati."}. +{"Update Specs","Aggiorna Specifiche"}. {"Update","Aggiornare"}. +{"Upgrade","Aggiornamento"}. {"Uptime:","Tempo dall'avvio:"}. +{"URL for Archived Discussion Logs","URL per i Registri delle Discussioni Archiviati"}. +{"User already exists","L'utente esiste già"}. {"User JID","JID utente"}. +{"User (jid)","Utente (jid)"}. {"User Management","Gestione degli utenti"}. +{"User removed","Utente rimosso"}. +{"User session not found","Sessione utente non trovata"}. +{"User session terminated","Sessione utente terminata"}. +{"User ~ts","Utente ~ts"}. {"Username:","Nome utente:"}. {"Users are not allowed to register accounts so quickly","Non è consentito agli utenti registrare account così rapidamente"}. {"Users Last Activity","Ultima attività degli utenti"}. {"Users","Utenti"}. {"User","Utente"}. {"Validate","Validare"}. +{"Value 'get' of 'type' attribute is not allowed","Il valore 'get' dell'attributo 'type' non è consentito"}. +{"Value of '~s' should be boolean","Il valore di '~s' dovrebbe essere booleano"}. +{"Value of '~s' should be datetime string","Il valore di '~s' deve essere una stringa dataora"}. +{"Value of '~s' should be integer","Il valore di '~s' dovrebbe essere un intero"}. +{"Value 'set' of 'type' attribute is not allowed","Il valore 'set' dell'attributo 'type' non è consentito"}. {"vCard User Search","Ricerca di utenti per vCard"}. +{"View joined MIX channels","Visualizza i canali MIX uniti"}. +{"View Queue","Visualizza Coda"}. +{"View Roster","Visualizza il Roster"}. {"Virtual Hosts","Host Virtuali"}. {"Visitors are not allowed to change their nicknames in this room","Non è consentito ai visitatori cambiare il nickname in questa stanza"}. {"Visitors are not allowed to send messages to all occupants","Non è consentito ai visitatori l'invio di messaggi a tutti i presenti"}. +{"Visitor","Visitatore"}. {"Voice request","Richiesta di parola"}. {"Voice requests are disabled in this conference","In questa conferenza le richieste di parola sono escluse"}. {"Wednesday","Mercoledì"}. +{"When a new subscription is processed and whenever a subscriber comes online","Quando viene elaborato una nuova sottoscrizione e ogni volta che una sottoscrizione entra in linea"}. +{"When a new subscription is processed","Quando viene elaborata una nuova sottoscrizione"}. {"When to send the last published item","Quando inviare l'ultimo elemento pubblicato"}. -{"Whether to allow subscriptions","Consentire iscrizioni?"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Se un'entità vuole ricevere un corpo del messaggio XMPP inoltre al formato del payload"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Se un'entità desidera ricevere i digest (aggregazioni) di notifiche o tutte le notifiche individualmente"}. +{"Whether an entity wants to receive or disable notifications","Se un'entità desidera ricevere o disabilitare le notifiche"}. +{"Whether owners or publisher should receive replies to items","Se i proprietari o l'editore dovrebbero ricevere le risposte alle voci"}. +{"Whether the node is a leaf (default) or a collection","Se il nodo è una foglia (impostazione predefinita) o una collezione"}. +{"Whether to allow subscriptions","Se consentire le iscrizioni"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","Se rendere temporanee tutte le sottoscrizioni, in base alla presenza della sottoscrizione"}. +{"Whether to notify owners about new subscribers and unsubscribes","Se notificare ai proprietari le nuove sottoscrizioni e le cancellazioni"}. +{"Who can send private messages","Chi può inviare messaggi privati"}. +{"Who may associate leaf nodes with a collection","Chi può associare i nodi foglia a una collezione"}. +{"Wrong parameters in the web formulary","Parametri errati nel formulario web"}. +{"Wrong xmlns","xmlns errati"}. +{"XMPP Account Registration","Registrazione dell'account XMPP"}. +{"XMPP Domains","Domini XMPP"}. +{"XMPP Show Value of Away","XMPP Mostra Valore di Assenza"}. +{"XMPP Show Value of Chat","XMPP Mostra il Valore della Chat"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Mostra Valore di DND (Non Disturbare)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Mostra Valore di XA (Extended Away)"}. +{"XMPP URI of Associated Publish-Subscribe Node","URI XMPP del Nodo di Pubblicazione-Sottoscrizione Associato"}. +{"You are being removed from the room because of a system shutdown","Stai per essere rimosso dalla stanza a causa di un arresto del sistema"}. +{"You are not allowed to send private messages","Non ti è consentito inviare messaggi privati"}. +{"You are not joined to the channel","Non si è connessi al canale"}. +{"You can later change your password using an XMPP client.","È possibile modificare successivamente la tua password utilizzando un client XMPP."}. {"You have been banned from this room","Sei stata/o bandita/o da questa stanza"}. +{"You have joined too many conferences","Hai partecipato a troppe conferenze"}. {"You must fill in field \"Nickname\" in the form","Si deve riempire il campo \"Nickname\" nel modulo"}. {"You need a client that supports x:data and CAPTCHA to register","La registrazione richiede un client che supporti x:data e CAPTCHA"}. {"You need a client that supports x:data to register the nickname","Per registrare il nickname è necessario un client che supporti x:data"}. {"You need an x:data capable client to search","Per effettuare ricerche è necessario un client che supporti x:data"}. {"Your active privacy list has denied the routing of this stanza.","In base alla tua attuale lista privacy questa stanza è stata esclusa dalla navigazione."}. -{"Your contact offline message queue is full. The message has been discarded.","La coda dei messaggi offline del contatto è piena. Il messaggio è stato scartato"}. +{"Your contact offline message queue is full. The message has been discarded.","La coda dei messaggi offline del contatto è piena. Il messaggio è stato scartato."}. {"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s"}. +{"Your XMPP account was successfully registered.","Il tuo account XMPP è stato registrato con successo."}. +{"Your XMPP account was successfully unregistered.","L'account XMPP è stato disregistrato con successo."}. +{"You're not allowed to create nodes","Non ti è consentito creare nodi"}. From a8e3af3586bf54596dfcdbad0a12b7813ed9023f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Feb 2024 16:27:17 +0100 Subject: [PATCH 0463/1302] Update Japanese translation (thanks to Mako N) --- priv/msgs/ja.msg | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/priv/msgs/ja.msg b/priv/msgs/ja.msg index feda5992f..ddabad44a 100644 --- a/priv/msgs/ja.msg +++ b/priv/msgs/ja.msg @@ -4,7 +4,7 @@ %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ {" (Add * to the end of field to match substring)"," (* を最後に付けると部分文字列にマッチします)"}. -{" has set the subject to: "," は件名を設定しました: "}. +{" has set the subject to: "," は題を設定しました: "}. {"A description of the node","ノードの説明"}. {"A friendly name for the node","ノードのフレンドリネーム"}. {"A password is required to enter this room","このチャットルームに入るにはパスワードが必要です"}. @@ -36,6 +36,7 @@ {"Anyone","誰にでも"}. {"April","4月"}. {"August","8月"}. +{"Automatic node creation is not enabled","ノードの自動作成は有効になっていません"}. {"Backup Management","バックアップ管理"}. {"Backup of ~p","バックアップ: ~p"}. {"Backup to File at ","ファイルにバックアップ: "}. @@ -44,11 +45,13 @@ {"Birthday","誕生日"}. {"Both the username and the resource are required","ユーザー名とリソースの両方が必要"}. {"CAPTCHA web page","CAPTCHA ウェブページ"}. +{"Challenge ID","チャレンジ ID"}. {"Change Password","パスワードを変更"}. {"Change User Password","パスワードを変更"}. {"Changing password is not allowed","パスワード変更の権限がありません"}. {"Channel already exists","チャンネルは既に存在します"}. {"Channel does not exist","チャンネルは存在しません"}. +{"Channel JID","チャンネル ID"}. {"Channels","チャンネル"}. {"Characters not allowed:","使用できない文字:"}. {"Chatroom configuration modified","チャットルームの設定が変更されました"}. @@ -70,6 +73,7 @@ {"Country","国"}. {"CPU Time:","CPU時間:"}. {"Current Discussion Topic","現在の話題"}. +{"Database failure","データーベース障害"}. {"Database Tables at ~p","データーベーステーブル: ~p"}. {"Database Tables Configuration at ","データーベーステーブル設定 "}. {"Database","データーベース"}. @@ -117,8 +121,11 @@ {"Export all tables as SQL queries to a file:","すべてのテーブルをSQL形式でファイルにエクスポート: "}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","ホストのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. +{"External component failure","外部コンポーネントの障害"}. +{"External component timeout","外部コンポーネントのタイムアウト"}. {"Failed to extract JID from your voice request approval","発言権要求の承認から JID を取り出すことに失敗しました"}. {"Failed to parse HTTP response","HTTP 応答のパースに失敗しました"}. +{"Failed to process option '~s'","オプション '~s' の処理に失敗しました"}. {"Family Name","姓"}. {"February","2月"}. {"Fill in the form to search for any matching XMPP User","XMPP ユーザーを検索するには欄に入力してください"}. @@ -128,8 +135,11 @@ {"Full List of Room Admins","チャットルーム管理者の一覧"}. {"Full List of Room Owners","チャットルーム主宰者の一覧"}. {"Full Name","氏名"}. +{"Get List of Online Users","オンラインユーザーの一覧を取得"}. +{"Get List of Registered Users","登録ユーザーの一覧を取得"}. {"Get Number of Online Users","オンラインユーザー数を取得"}. {"Get Number of Registered Users","登録ユーザー数を取得"}. +{"Get Pending","保留中の取得"}. {"Get User Last Login Time","最終ログイン時間を取得"}. {"Get User Password","パスワードを取得"}. {"Get User Statistics","ユーザー統計を取得"}. @@ -158,8 +168,12 @@ {"Incoming s2s Connections:","内向き s2s コネクション:"}. {"Incorrect data form","データ形式が違います"}. {"Incorrect password","パスワードが違います"}. +{"Installed Modules:","インストールされているモジュール:"}. +{"Install","インストール"}. +{"Insufficient privilege","権限が不十分です"}. {"Internal server error","内部サーバーエラー"}. {"Invalid node name","無効なノード名です"}. +{"Invalid 'previd' value","無効な 'previd' の値です"}. {"Invitations are not allowed in this conference","この会議では、招待はできません"}. {"IP addresses","IP アドレス"}. {"is now known as","は名前を変更しました: "}. @@ -168,6 +182,7 @@ {"It is not allowed to send private messages to the conference","この会議にプライベートメッセージを送信することはできません"}. {"Jabber ID","Jabber ID"}. {"January","1月"}. +{"JID normalization failed","JID の正規化に失敗しました"}. {"joins the room","がチャットルームに参加しました"}. {"July","7月"}. {"June","6月"}. @@ -216,14 +231,22 @@ {"Nickname Registration at ","ニックネーム登録: "}. {"Nickname ~s does not exist in the room","ニックネーム ~s はこのチャットルームにいません"}. {"Nickname","ニックネーム"}. +{"No address elements found","アドレス要素が見つかりません"}. +{"No addresses element found","アドレス要素が見つかりません"}. {"No body provided for announce message","アナウンスメッセージはありませんでした"}. +{"No child elements found","子要素が見つかりません"}. {"No Data","データなし"}. +{"No element found"," 要素が見つかりません"}. +{"No 'item' element found","'item' 要素が見つかりません"}. {"No limit","制限なし"}. +{"No 'password' found in this query","このクエリに 'password' が見つかりません"}. +{"No services available","利用できるサービスはありません"}. {"Node already exists","ノードは既に存在しています"}. {"Node ID","ノードID"}. {"Node not found","ノードが見つかりません"}. {"Node ~p","ノード ~p"}. {"Nodes","ノード"}. +{"Node","ノード"}. {"None","なし"}. {"Not Found","見つかりません"}. {"Notify subscribers when items are removed from the node","アイテムがノードから消された時に購読者へ通知する"}. @@ -245,6 +268,8 @@ {"Online Users:","オンラインユーザー:"}. {"Online","オンライン"}. {"Only deliver notifications to available users","有効なユーザーにのみ告知を送信する"}. +{"Only or tags are allowed"," タグまたは タグのみが許可されます"}. +{"Only element is allowed in this query","このクエリでは 要素のみが許可されます"}. {"Only members may query archives of this room","メンバーのみがこのルームのアーカイブを取得できます"}. {"Only moderators and participants are allowed to change the subject in this room","モデレーターと参加者のみがチャットルームの件名を変更できます"}. {"Only moderators are allowed to change the subject in this room","モデレーターのみがチャットルームの件名を変更できます"}. @@ -258,6 +283,7 @@ {"Outgoing s2s Connections:","外向き s2s コネクション:"}. {"Owner privileges required","主宰者の権限が必要です"}. {"Packet","パケット"}. +{"Participant ID","参加者 ID"}. {"Participant","参加者"}. {"Password Verification","パスワード (確認)"}. {"Password Verification:","パスワード (確認):"}. @@ -274,11 +300,14 @@ {"Please, wait for a while before sending new voice request","新しい発言権の要求を送るまで少し間をおいてください"}. {"Pong","Pong"}. {"Present real Jabber IDs to","本当の Jabber ID を公開"}. +{"Previous session not found","前のセッションが見つかりません"}. {"private, ","プライベート、"}. +{"Public","公開"}. {"Publish-Subscribe","Publish-Subscribe"}. {"PubSub subscriber request","PubSub 購読者のリクエスト"}. {"Purge all items when the relevant publisher goes offline","公開者がオフラインになるときに、すべてのアイテムを削除"}. {"Queries to the conference members are not allowed in this room","このチャットルームでは、会議のメンバーへのクエリーは禁止されています"}. +{"Query to another users is forbidden","他のユーザーへのクエリは禁止されています"}. {"RAM and disc copy","RAM, ディスクコピー"}. {"RAM copy","RAM コピー"}. {"Really delete message of the day?","本当にお知らせメッセージを削除しますか ?"}. @@ -292,6 +321,7 @@ {"Remove User","ユーザーを削除"}. {"Remove","削除"}. {"Replaced by new connection","新しいコネクションによって置き換えられました"}. +{"Request is ignored","リクエストは無視されます"}. {"Resources","リソース"}. {"Restart Service","サービスを再起動"}. {"Restart","再起動"}. @@ -330,6 +360,7 @@ {"Show Integral Table","累積の表を表示"}. {"Show Ordinary Table","通常の表を表示"}. {"Shut Down Service","サービスを停止"}. +{"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. {"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","XMPP クライアントはコンピューターにパスワードを記憶できます。コンピューターが安全であると信頼できる場合にのみ、この機能を使用してください。"}. {"Specify the access model","アクセスモデルを設定する"}. {"Specify the event message type","イベントメッセージ種別を設定"}. @@ -377,6 +408,7 @@ {"Too many CAPTCHA requests","CAPTCHA 要求が多すぎます"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。"}. {"Too many unacked stanzas","多くのスタンザが応答していません"}. +{"Too many users in this conference","この会議にはユーザーが多すぎます"}. {"Total rooms","チャットルーム数"}. {"To","To"}. {"Traffic rate limit is exceeded","トラフィックレートの制限を超えました"}. @@ -388,8 +420,12 @@ {"Tuesday","火曜日"}. {"Unable to generate a CAPTCHA","CAPTCHA を生成できません"}. {"Unauthorized","認証されていません"}. +{"Unexpected action","予期しないアクション"}. +{"Unexpected error condition: ~p","予期しないエラー状態: ~p"}. +{"Uninstall","アンインストール"}. {"Unregister an XMPP account","XMPP アカウントを削除"}. {"Unregister","削除"}. +{"Unselect All","すべての選択を解除"}. {"Unsupported version","対応していないバージョン"}. {"Update message of the day (don't send)","お知らせメッセージを更新 (送信しない)"}. {"Update message of the day on all hosts (don't send)","全ホストのお知らせメッセージを更新 (送信しない)"}. @@ -397,12 +433,15 @@ {"Update ~p","更新 ~p"}. {"Update script","スクリプトの更新"}. {"Update","更新"}. +{"Upgrade","アップグレード"}. {"Uptime:","起動時間:"}. {"User already exists","ユーザーは既に存在しています"}. {"User (jid)","ユーザー (JID)"}. {"User JID","ユーザー JID"}. {"User Management","ユーザー管理"}. {"User removed","ユーザーを削除しました"}. +{"User session not found","ユーザーセッションが見つかりません"}. +{"User session terminated","ユーザーセッションが終了しました"}. {"User ~ts","ユーザー ~ts"}. {"Username:","ユーザー名:"}. {"Users are not allowed to register accounts so quickly","それほど速くアカウントを登録することはできません"}. @@ -410,6 +449,8 @@ {"Users","ユーザー"}. {"User","ユーザー"}. {"Validate","検証"}. +{"Value of '~s' should be boolean","'~s' の値はブール値である必要があります"}. +{"Value of '~s' should be integer","'~s' の値は整数である必要があります"}. {"vCard User Search","vCard検索"}. {"View Queue","キューを表示"}. {"View Roster","名簿を表示"}. @@ -420,11 +461,15 @@ {"Voice requests are disabled in this conference","この会議では、発言権の要求はできません"}. {"Voice request","発言権を要求"}. {"Wednesday","水曜日"}. +{"When a new subscription is processed","新しい購読が処理されるとき"}. {"When to send the last published item","最後の公開アイテムを送信するタイミングで"}. {"Whether to allow subscriptions","購読を許可するかどうか"}. +{"Who can send private messages","誰がプライベートメッセージを送れるか"}. {"XMPP Account Registration","XMPP アカウント登録"}. {"XMPP Domains","XMPP ドメイン"}. {"You are being removed from the room because of a system shutdown","システムシャットダウンのためチャットルームから削除されました"}. +{"You are not allowed to send private messages","プライベートメッセージを送信する権限がありません"}. +{"You are not joined to the channel","チャンネルに参加していません"}. {"You can later change your password using an XMPP client.","あなたは後で XMPP クライアントを使用してパスワードを変更できます。"}. {"You have been banned from this room","あなたはこのチャットルームから締め出されています"}. {"You must fill in field \"Nickname\" in the form","フォームの\"ニックネーム\"欄を入力する必要があります"}. From e3e8b9afb405bab25850666ff3542337a54dadea Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Feb 2024 09:54:44 +0100 Subject: [PATCH 0464/1302] COMPILE.md: Update text about OTP release to match current behaviour Also keep this document direct, short and introductory, there's a already a section in the ejabberd Docs site with all the details. --- COMPILE.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/COMPILE.md b/COMPILE.md index 80e932d65..4609ac318 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -91,12 +91,7 @@ Instead of installing ejabberd in the system, you can build an OTP release that includes all necessary to run ejabberd in a subdirectory: ./configure - make rel - -Or, if you have Elixir available and plan to develop Elixir code: - - ./configure --with-rebar=mix - make dev + make prod Check the full list of targets: From d29ef595146371574507592217897aefe4aa0f64 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Feb 2024 10:03:01 +0100 Subject: [PATCH 0465/1302] Update links to SQL files in the man page --- man/ejabberd.yml.5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 4e5f2f4e1..a055935eb 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -1154,7 +1154,7 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/23\&.10/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/24\&.02/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR From 654601ff23262a5ef20740a5638be68c0d713bf5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Feb 2024 09:56:28 +0100 Subject: [PATCH 0466/1302] Set version to 24.02 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f4c1a48ca..a128e6878 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 23.10` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.02` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From ad67710f7e79654f448e21173ea51b74b60eaade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 27 Feb 2024 18:59:14 +0100 Subject: [PATCH 0467/1302] Make config reload update new logger settings --- src/ejabberd_logger.erl | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 33906d2ae..38818e354 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -29,7 +29,7 @@ -export([start/0, get/0, set/1, get_log_path/0, flush/0]). -export([convert_loglevel/1, loglevels/0, set_modules_fully_logged/1]). -ifndef(LAGER). --export([progress_filter/2]). +-export([progress_filter/2, config_reloaded/0]). -endif. %% Deprecated functions -export([restart/0, reopen_log/0, rotate_log/0]). @@ -266,6 +266,7 @@ start(Level) -> EjabberdLog = get_log_path(), Dir = filename:dirname(EjabberdLog), ErrorLog = filename:join([Dir, "error.log"]), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), LogRotateCount = get_integer_env(log_rotate_count, 1), LogBurstLimitWindowTime = get_integer_env(log_burst_limit_window_time, 1000), @@ -330,6 +331,27 @@ get_default_handlerid() -> restart() -> ok. +-spec config_reloaded() -> ok. +config_reloaded() -> + LogRotateSize = ejabberd_option:log_rotate_size(), + LogRotateCount = ejabberd_option:log_rotate_count(), + LogBurstLimitWindowTime = ejabberd_option:log_burst_limit_window_time(), + LogBurstLimitCount = ejabberd_option:log_burst_limit_count(), + lists:foreach( + fun(Handler) -> + case logger:get_handler_config(Handler) of + {ok, #{config := Config}} -> + Config2 = Config#{ + max_no_bytes => LogRotateSize, + max_no_files => LogRotateCount, + burst_limit_window_time => LogBurstLimitWindowTime, + burst_limit_max_count => LogBurstLimitCount}, + logger:update_handler_config(Handler, config, Config2); + _ -> + ok + end + end, [ejabberd_log, error_log]). + progress_filter(#{level:=info,msg:={report,#{label:={_,progress}}}} = Event, _) -> case get() of debug -> From 06675e4fb2b05a6d2d2b63ae26492b60286dc80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 28 Feb 2024 12:01:14 +0100 Subject: [PATCH 0468/1302] Improve validation of arguments in mod_muc_admin commands This adds validation to couple command where they were missing and catch passing unknown hostnames. --- src/mod_muc_admin.erl | 228 ++++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 87 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 7c1e56548..2d945dfa4 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -491,7 +491,7 @@ get_commands_spec() -> %%% muc_online_rooms(ServiceArg) -> - Hosts = find_services(ServiceArg), + Hosts = find_services_validate(ServiceArg, <<"serverhost">>), lists:flatmap( fun(Host) -> [<> @@ -500,7 +500,7 @@ muc_online_rooms(ServiceArg) -> muc_online_rooms_by_regex(ServiceArg, Regex) -> {_, P} = re:compile(Regex), - Hosts = find_services(ServiceArg), + Hosts = find_services_validate(ServiceArg, <<"serverhost">>), lists:flatmap( fun(Host) -> [build_summary_room(Name, RoomHost, Pid) @@ -537,9 +537,9 @@ muc_register_nick(Nick, FromBinary, Service) -> end catch error:{invalid_domain, _} -> - throw({error, "Invalid 'service'"}); + throw({error, "Invalid value of 'service'"}); error:{unregistered_route, _} -> - throw({error, "Invalid 'service'"}); + throw({error, "Unknown host in 'service'"}); error:{bad_jid, _} -> throw({error, "Invalid 'jid'"}); _ -> @@ -564,8 +564,10 @@ get_user_rooms(User, Server) -> end, ejabberd_option:hosts()). get_user_subscriptions(User, Server) -> + User2 = validate_muc(User, <<"user">>), + Server2 = validate_host(Server, <<"host">>), Services = find_services(global), - UserJid = jid:make(jid:nodeprep(User), jid:nodeprep(Server)), + UserJid = jid:make(User2, Server2), lists:flatmap( fun(ServerHost) -> {ok, Rooms} = mod_muc:get_subscribed_rooms(ServerHost, UserJid), @@ -779,34 +781,24 @@ create_room(Name1, Host1, ServerHost) -> create_room_with_opts(Name1, Host1, ServerHost, []). create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> - case {jid:nodeprep(Name1), jid:nodeprep(Host1), jid:nodeprep(ServerHost1)} of - {error, _, _} -> - throw({error, "Invalid 'name'"}); - {_, error, _} -> - throw({error, "Invalid 'host'"}); - {_, _, error} -> - throw({error, "Invalid 'serverhost'"}); - {Name, Host, ServerHost} -> - case get_room_pid(Name, Host) of - room_not_found -> - %% Get the default room options from the muc configuration - DefRoomOpts = mod_muc_opt:default_room_options(ServerHost), - %% Change default room options as required - FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts], - RoomOpts = lists:ukeymerge(1, - lists:keysort(1, FormattedRoomOpts), - lists:keysort(1, DefRoomOpts)), - case mod_muc:create_room(Host, Name, RoomOpts) of - ok -> - ok; - {error, _} -> - throw({error, "Unable to start room"}) - end; - invalid_service -> - throw({error, "Invalid 'service'"}); - _ -> - throw({error, "Room already exists"}) - end + ServerHost = validate_host(ServerHost1, <<"serverhost">>), + case get_room_pid_validate(Name1, Host1, <<"name">>, <<"host">>) of + {room_not_found, Name, Host} -> + %% Get the default room options from the muc configuration + DefRoomOpts = mod_muc_opt:default_room_options(ServerHost), + %% Change default room options as required + FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts], + RoomOpts = lists:ukeymerge(1, + lists:keysort(1, FormattedRoomOpts), + lists:keysort(1, DefRoomOpts)), + case mod_muc:create_room(Host, Name, RoomOpts) of + ok -> + ok; + {error, _} -> + throw({error, "Unable to start room"}) + end; + _ -> + throw({error, "Room already exists"}) end. %% Create the room only in the database. @@ -820,21 +812,12 @@ muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> %% If the room has participants, they are not notified that the room was destroyed; %% they will notice when they try to chat and receive an error that the room doesn't exist. destroy_room(Name1, Service1) -> - case {jid:nodeprep(Name1), jid:nodeprep(Service1)} of - {error, _} -> - throw({error, "Invalid 'name'"}); - {_, error} -> - throw({error, "Invalid 'service'"}); - {Name, Service} -> - case get_room_pid(Name, Service) of - room_not_found -> - throw({error, "Room doesn't exists"}); - invalid_service -> - throw({error, "Invalid 'service'"}); - Pid -> - mod_muc_room:destroy(Pid), - ok - end + case get_room_pid_validate(Name1, Service1, <<"name">>, <<"service">>) of + {room_not_found, _, _} -> + throw({error, "Room doesn't exists"}); + {Pid, _, _} -> + mod_muc_room:destroy(Pid), + ok end. destroy_room({N, H, SH}) -> @@ -1101,8 +1084,8 @@ act_on_room(_Method, list, _) -> %%---------------------------- get_room_occupants(Room, Host) -> - case get_room_pid(Room, Host) of - Pid when is_pid(Pid) -> get_room_occupants(Pid); + case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> get_room_occupants(Pid); _ -> throw({error, room_not_found}) end. @@ -1117,8 +1100,8 @@ get_room_occupants(Pid) -> maps:to_list(S#state.users)). get_room_occupants_number(Room, Host) -> - case get_room_pid(Room, Host) of - Pid when is_pid(Pid )-> + case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid )-> {ok, #{occupants_number := N}} = mod_muc_room:get_info(Pid), N; _ -> @@ -1194,12 +1177,10 @@ send_direct_invitation(FromJid, UserJid, Msg) -> %% For example: %% `change_room_option(<<"testroom">>, <<"conference.localhost">>, <<"title">>, <<"Test Room">>)' change_room_option(Name, Service, OptionString, ValueString) -> - case get_room_pid(Name, Service) of - room_not_found -> + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {room_not_found, _, _} -> throw({error, "Room not found"}); - invalid_service -> - throw({error, "Invalid 'service'"}); - Pid -> + {Pid, _, _} -> {Option, Value} = format_room_option(OptionString, ValueString), change_room_option(Pid, Option, Value) end. @@ -1293,8 +1274,20 @@ parse_nodes([<<"subscribers">> | Rest], Acc) -> parse_nodes(_, _) -> throw({error, "Invalid 'subscribers' - unknown node name used"}). +-spec get_room_pid_validate(binary(), binary(), binary(), binary()) -> + {pid() | room_not_found, binary(), binary()}. +get_room_pid_validate(Name, Service, NameArg, ServiceArg) -> + Name2 = validate_room(Name, NameArg), + {ServerHost, Service2} = validate_muc2(Service, ServiceArg), + case mod_muc:unhibernate_room(ServerHost, Service2, Name2) of + error -> + {room_not_found, Name2, Service2}; + {ok, Pid} -> + {Pid, Name2, Service2} + end. + %% @doc Get the Pid of an existing MUC room, or 'room_not_found'. --spec get_room_pid(binary(), binary()) -> pid() | room_not_found | invalid_service. +-spec get_room_pid(binary(), binary()) -> pid() | room_not_found | invalid_service | unknown_service. get_room_pid(Name, Service) -> try get_room_serverhost(Service) of ServerHost -> @@ -1308,7 +1301,7 @@ get_room_pid(Name, Service) -> error:{invalid_domain, _} -> invalid_service; error:{unregistered_route, _} -> - invalid_service + unknown_service end. room_diagnostics(Name, Service) -> @@ -1330,7 +1323,7 @@ room_diagnostics(Name, Service) -> error:{invalid_domain, _} -> invalid_service; error:{unregistered_route, _} -> - invalid_service + unknown_service end. %% It is required to put explicitly all the options because @@ -1375,8 +1368,8 @@ change_option(Option, Value, Config) -> %%---------------------------- get_room_options(Name, Service) -> - case get_room_pid(Name, Service) of - Pid when is_pid(Pid) -> get_room_options(Pid); + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> get_room_options(Pid); _ -> [] end. @@ -1401,8 +1394,8 @@ get_options(Config) -> %% [{JID::string(), Domain::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations(Name, Service) -> - case get_room_pid(Name, Service) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), Affiliations = maps:to_list(StateData#state.affiliations), @@ -1417,8 +1410,8 @@ get_room_affiliations(Name, Service) -> end. get_room_history(Name, Service) -> - case get_room_pid(Name, Service) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:get_state(Pid) of {ok, StateData} -> History = p1_queue:to_list((StateData#state.history)#lqueue.queue), @@ -1442,15 +1435,15 @@ get_room_history(Name, Service) -> %% @doc Get affiliation of a user in the room Name@Service. get_room_affiliation(Name, Service, JID) -> - case get_room_pid(Name, Service) of - Pid when is_pid(Pid) -> - %% Get the PID of the online room, then request its state - {ok, StateData} = mod_muc_room:get_state(Pid), - UserJID = jid:decode(JID), - mod_muc_room:get_affiliation(UserJID, StateData); - _ -> - throw({error, "The room does not exist."}) - end. + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> + %% Get the PID of the online room, then request its state + {ok, StateData} = mod_muc_room:get_state(Pid), + UserJID = jid:decode(JID), + mod_muc_room:get_affiliation(UserJID, StateData); + _ -> + throw({error, "The room does not exist."}) + end. %%---------------------------- %% Change Room Affiliation @@ -1474,8 +1467,8 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> _ -> throw({error, "Invalid affiliation"}) end, - case get_room_pid(Name, Service) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> %% Get the PID for the online room so we can get the state of the room case mod_muc_room:change_item(Pid, jid:decode(JID), affiliation, Affiliation, <<"">>) of {ok, _} -> @@ -1485,10 +1478,8 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> {error, _} -> throw({error, "Unable to perform change"}) end; - room_not_found -> - throw({error, "Room doesn't exists"}); - invalid_service -> - throw({error, "Invalid 'service'"}) + _ -> + throw({error, "Room doesn't exists"}) end. %%% @@ -1506,8 +1497,8 @@ subscribe_room(User, Nick, Room, NodeList) -> try jid:decode(User) of UserJID1 -> UserJID = jid:replace_resource(UserJID1, <<"modmucadmin">>), - case get_room_pid(Name, Host) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of + {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:subscribe( Pid, UserJID, Nick, NodeList) of {ok, SubscribedNodes} -> @@ -1544,8 +1535,8 @@ unsubscribe_room(User, Room) -> #jid{luser = Name, lserver = Host} when Name /= <<"">> -> try jid:decode(User) of UserJID -> - case get_room_pid(Name, Host) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of + {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:unsubscribe(Pid, UserJID) of ok -> ok; @@ -1565,8 +1556,8 @@ unsubscribe_room(User, Room) -> end. get_subscribers(Name, Host) -> - case get_room_pid(Name, Host) of - Pid when is_pid(Pid) -> + case get_room_pid_validate(Name, Host, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> {ok, JIDList} = mod_muc_room:get_subscribers(Pid), [jid:encode(jid:remove_resource(J)) || J <- JIDList]; _ -> @@ -1577,11 +1568,74 @@ get_subscribers(Name, Host) -> %% Utils %%---------------------------- +-spec validate_host(Name :: binary(), ArgName::binary()) -> binary(). +validate_host(Name, ArgName) -> + case jid:nameprep(Name) of + error -> + throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + Name2 -> + case lists:member(Name2, ejabberd_config:get_myhosts()) of + false -> + throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}); + _ -> + Name2 + end + end. + +-spec validate_muc(Name :: binary(), ArgName::binary()) -> binary(). +validate_muc(Name, ArgName) -> + case jid:nameprep(Name) of + error -> + throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + Name2 -> + try get_room_serverhost(Name2) of + _ -> Name2 + catch + error:{invalid_domain, _} -> + throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}); + error:{unregistered_route, _} -> + throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}) + end + end. + +-spec validate_muc2(Name :: binary(), ArgName::binary()) -> {binary(), binary()}. +validate_muc2(Name, ArgName) -> + case jid:nameprep(Name) of + error -> + throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + Name2 -> + try get_room_serverhost(Name2) of + Host -> {Host, Name2} + catch + error:{invalid_domain, _} -> + throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}); + error:{unregistered_route, _} -> + throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}) + end + end. + +-spec validate_room(Name :: binary(), ArgName :: binary()) -> binary(). +validate_room(Name, ArgName) -> + case jid:nodeprep(Name) of + error -> + throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + Name2 -> + Name2 + end. + find_service(global) -> global; find_service(ServerHost) -> hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)). +find_services_validate(Global, _Name) when Global == global; + Global == <<"global">> -> + find_services(Global); +find_services_validate(Service, Name) -> + case validate_muc(Service, Name) of + Service2 -> find_services(Service2) + end. + find_services(Global) when Global == global; Global == <<"global">> -> lists:flatmap( From c69884fa3f52f67596b459ea7d9aad711a7c4165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 28 Feb 2024 12:03:42 +0100 Subject: [PATCH 0469/1302] Config reload should update loger settings part 2 --- src/ejabberd_config.erl | 3 +++ src/ejabberd_logger.erl | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 376a458e0..bd9480513 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -111,6 +111,9 @@ reload() -> ejabberd_hooks:run(host_down, [Host]) end, DelHosts), ejabberd_hooks:run(config_reloaded, []), + % logger is started too early to be able to use hooks, so + % we need to call it separately + ejabberd_logger:config_reloaded(), delete_host_options(DelHosts), ?INFO_MSG("Configuration reloaded successfully", []); Err -> diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 38818e354..e57fd8146 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -27,9 +27,9 @@ %% API -export([start/0, get/0, set/1, get_log_path/0, flush/0]). --export([convert_loglevel/1, loglevels/0, set_modules_fully_logged/1]). +-export([convert_loglevel/1, loglevels/0, set_modules_fully_logged/1, config_reloaded/0]). -ifndef(LAGER). --export([progress_filter/2, config_reloaded/0]). +-export([progress_filter/2]). -endif. %% Deprecated functions -export([restart/0, reopen_log/0, rotate_log/0]). @@ -185,6 +185,9 @@ restart() -> application:stop(lager), start(Level). +config_reloaded() -> + ok. + reopen_log() -> ok. @@ -266,7 +269,6 @@ start(Level) -> EjabberdLog = get_log_path(), Dir = filename:dirname(EjabberdLog), ErrorLog = filename:join([Dir, "error.log"]), - ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), LogRotateSize = get_integer_env(log_rotate_size, 10*1024*1024), LogRotateCount = get_integer_env(log_rotate_count, 1), LogBurstLimitWindowTime = get_integer_env(log_burst_limit_window_time, 1000), From aac0e782d0cef0a4ce0947e4733c348e36bfa1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 28 Feb 2024 12:24:50 +0100 Subject: [PATCH 0470/1302] Don't use ejabberd_config:get_myhosts() --- src/mod_muc_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 2d945dfa4..d15138605 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1574,7 +1574,7 @@ validate_host(Name, ArgName) -> error -> throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); Name2 -> - case lists:member(Name2, ejabberd_config:get_myhosts()) of + case lists:member(Name2, ejabberd_option:hosts()) of false -> throw({error, <<"Unknown host passed in '",ArgName/binary,"'">>}); _ -> From 716f5de517201f7f180880e17377149b1a4ad7da Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 28 Feb 2024 11:36:57 +0100 Subject: [PATCH 0471/1302] mod_matrix_gw: Fix support for @HOST@ in matrix_domain option (#4167) --- src/mod_matrix_gw.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 17426a289..0e7010913 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -860,7 +860,7 @@ depends(_Host, _Opts) -> mod_opt_type(host) -> econf:host(); mod_opt_type(matrix_domain) -> - econf:binary(); + econf:host(); mod_opt_type(key_name) -> econf:binary(); mod_opt_type(key) -> @@ -875,7 +875,7 @@ mod_opt_type(persist) -> econf:bool(). mod_options(Host) -> - [{matrix_domain, <<"@HOST@">>}, + [{matrix_domain, Host}, {host, <<"matrix.", Host/binary>>}, {key_name, <<"">>}, {key, {<<"">>, <<"">>}}, From 91cca3cff3d63eb6d49977fa5b9fd11bf549714d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 28 Feb 2024 11:31:16 +0100 Subject: [PATCH 0472/1302] mod_conversejs: Simplify support for @HOST@ in default_domain option (#4167) --- src/mod_conversejs.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 6824bdd37..a5682e50a 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -54,8 +54,7 @@ process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> ExtraOptions = get_auth_options(Host) ++ get_register_options(Host) ++ get_extra_options(Host), - DomainRaw = gen_mod:get_module_opt(Host, ?MODULE, default_domain), - Domain = misc:expand_keyword(<<"@HOST@">>, DomainRaw, Host), + Domain = mod_conversejs_opt:default_domain(Host), Script = get_file_url(Host, conversejs_script, <>, <<"https://cdn.conversejs.org/dist/converse.min.js">>), @@ -238,12 +237,12 @@ mod_opt_type(conversejs_script) -> mod_opt_type(conversejs_css) -> econf:binary(); mod_opt_type(default_domain) -> - econf:binary(). + econf:host(). -mod_options(_) -> +mod_options(Host) -> [{bosh_service_url, auto}, {websocket_url, auto}, - {default_domain, <<"@HOST@">>}, + {default_domain, Host}, {conversejs_resources, undefined}, {conversejs_options, []}, {conversejs_script, auto}, From a29955ff96ef739a9bbada50daac69d739333427 Mon Sep 17 00:00:00 2001 From: Melvin Keskin Date: Sun, 3 Mar 2024 21:05:57 +0100 Subject: [PATCH 0473/1302] mod_mam: Document that XEP-0441 is implemented as well --- src/mod_mam.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index cde711f01..fae766558 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1598,7 +1598,9 @@ mod_doc() -> #{desc => ?T("This module implements " "https://xmpp.org/extensions/xep-0313.html" - "[XEP-0313: Message Archive Management]. " + "[XEP-0313: Message Archive Management] and " + "https://xmpp.org/extensions/xep-0441.html" + "[XEP-0441: Message Archive Management Preferences]. " "Compatible XMPP clients can use it to store their " "chat history on the server."), opts => From c0055b7a7f80c8cf6cf2e50a535f255e1901450d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 5 Mar 2024 09:57:58 +0100 Subject: [PATCH 0474/1302] mod_muc_log: Support allowpm introduced in 2bd61ab --- src/mod_muc_log.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index f86812f7f..fa2c47c22 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -583,7 +583,7 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset, "class=\"nav\" href=\"~ts\">>">>, [Date, Date_prev, Date_next]), - case {htmlize(Room#room.subject_author), + case {htmlize(prepare_subject_author(Room#room.subject_author)), htmlize(Room#room.subject)} of {<<"">>, <<"">>} -> ok; @@ -783,6 +783,12 @@ roomconfig_to_string(Options, Lang, FileFormat) -> (htmlize(tr(Lang, misc:atom_to_binary(T)), FileFormat))/binary, "\"">>; + allowpm -> + <<"
", + OptText/binary, ": \"", + (htmlize(tr(Lang, misc:atom_to_binary(T)), + FileFormat))/binary, + "\"
">>; _ -> <<"\"", T/binary, "\"">> end end, @@ -898,6 +904,11 @@ get_room_occupants(RoomJIDString) -> [] end. +prepare_subject_author({Nick, _}) -> + Nick; +prepare_subject_author(SA) -> + SA. + -spec get_room_state(binary(), binary()) -> {ok, mod_muc_room:state()} | error. get_room_state(RoomName, MucService) -> From 281515cd9f3bc1795239c098efbfd35afaa43a91 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 5 Mar 2024 11:49:18 +0100 Subject: [PATCH 0475/1302] mod_muc_log: Hide join/leave lines, add method to show them --- priv/css/muc.css | 4 +++- priv/js/muc.js | 11 +++++++++++ src/mod_muc_log.erl | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/priv/css/muc.css b/priv/css/muc.css index 48b7a19fa..b81ad5b52 100644 --- a/priv/css/muc.css +++ b/priv/css/muc.css @@ -16,12 +16,14 @@ a.roomjid {color: #336699; font-size: 24px; font-weight: bold; font-family: sans div.logdate {color: #663399; font-size: 20px; font-weight: bold; font-family: sans-serif; letter-spacing: 2px; border-bottom: #224466 solid 1pt; margin-left:80pt; margin-top:20px;} div.roomsubject {color: #336699; font-size: 18px; font-family: sans-serif; margin-left: 80pt; margin-bottom: 10px;} div.rc {color: #336699; font-size: 12px; font-family: sans-serif; margin-left: 50%; text-align: right; background: #f3f6f9; border-bottom: 1px solid #336699; border-right: 4px solid #336699;} -div.rct {font-weight: bold; background: #e3e6e9; padding-right: 10px;} +div.rct {font-weight: bold; background: #e3e6e9; padding-right: 10px; cursor: pointer;} +div.rct:hover {text-decoration: underline;} div.rcos {padding-right: 10px;} div.rcoe {color: green;} div.rcod {color: red;} div.rcoe:after {content: ": v";} div.rcod:after {content: ": x";} div.rcot:after {} +div.jl {display: none;} .legend {width: 100%; margin-top: 30px; border-top: #224466 solid 1pt; padding: 10px 0px 10px 0px; text-align: left; font-family: monospace; letter-spacing: 2px;} .w3c {position: absolute; right: 10px; width: 60%; text-align: right; font-family: monospace; letter-spacing: 1px;} diff --git a/priv/js/muc.js b/priv/js/muc.js index 9acd0dcfa..7f7de5c39 100644 --- a/priv/js/muc.js +++ b/priv/js/muc.js @@ -6,3 +6,14 @@ function sh(e) { document.getElementById(e).style.display='none'; } } +// Show/Hide join/leave elements +function jlf() { + var es = document.getElementsByClassName('jl'); + for (var i = 0; i < es.length; i++) { + if (es[i].style.display === 'block') { + es[i].style.display = 'none'; + } else { + es[i].style.display = 'block'; + } + } +} diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index fa2c47c22..b678961b5 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -443,11 +443,13 @@ add_message_to_log(Nick1, Message, RoomJID, Opts, {_, _, Microsecs} = Now, STimeUnique = io_lib:format("~ts.~w", [STime, Microsecs]), + maybe_print_jl(open, F, Message, FileFormat), fw(F, io_lib:format("[~ts] ", [STimeUnique, STimeUnique, STimeUnique, STime]) ++ Text, FileFormat), + maybe_print_jl(close, F, Message, FileFormat), file:close(F), ok. @@ -598,6 +600,7 @@ put_header(F, Room, Date, CSSFile, Lang, Hour_offset, RoomOccupants = roomoccupants_to_string(Occupants, FileFormat), put_room_occupants(F, RoomOccupants, Lang, FileFormat), + put_occupants_join_leave(F, Lang), Time_offset_str = case Hour_offset < 0 of true -> io_lib:format("~p", [Hour_offset]); false -> io_lib:format("+~p", [Hour_offset]) @@ -663,6 +666,35 @@ put_room_occupants(F, RoomOccupants, Lang, [Now2, RoomOccupants]), fw(F, <<"">>). +put_occupants_join_leave(F, Lang) -> + fw(F, <<"
">>), + fw(F, + <<"
~ts
">>, + [tr(Lang, ?T("Show Occupants Join/Leave"))]), + fw(F, <<"
">>). + +maybe_print_jl(_Direction, _F, _Message, plaintext) -> + ok; +maybe_print_jl(Direction, F, Message, html) -> + PrintJl = case Message of + join -> true; + leave -> true; + {leave, _} -> true; + _ -> false + end, + case PrintJl of + true -> print_jl(Direction, F); + false -> ok + end. + +print_jl(Direction, F) -> + String = case Direction of + open -> "
"; + close -> "
" + end, + fw(F, io_lib:format(String, [])). + htmlize(S1) -> htmlize(S1, html). htmlize(S1, plaintext) -> From 356ee22cc5f42a656864127986638496d59dfd64 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Feb 2024 16:36:41 +0100 Subject: [PATCH 0476/1302] Change method from 1bf80e8 to determine if Elixir supports default_formatter function Dialyzer when using Elixir complains about undefined function module_info. --- src/ejabberd_logger.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index e57fd8146..e9fb7ada7 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -368,7 +368,7 @@ progress_filter(Event, _) -> console_template() -> case (false /= code:is_loaded('Elixir.Logger')) andalso - lists:keymember(default_formatter, 1, 'Elixir.Logger':module_info(exports)) of + 'Elixir.System':version() >= <<"1.15">> of true -> [date, " ", time, " [", level, "] ", message, "\n"]; false -> From cd30538130d54237307f1ebcb7b7a375658376bf Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Feb 2024 18:30:28 +0100 Subject: [PATCH 0477/1302] Fix "ejabberdctl iexlive" after "make prod" when using Elixir The version number in ejabberd is 24.02, but Elixir requires semver, that is, 24.2. Use the version() function, which does the conversion. --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 631e0e728..fcd30b093 100644 --- a/mix.exs +++ b/mix.exs @@ -264,7 +264,7 @@ defmodule Ejabberd.MixProject do config_dir: config(:config_dir), logs_dir: config(:logs_dir), spool_dir: config(:spool_dir), - vsn: config(:vsn), + vsn: version(), iexpath: config(:iexpath), erl: config(:erl), epmd: config(:epmd), From 2378ea9c076bf12edf1ec5d0a86a7c0261869f81 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Feb 2024 16:43:04 +0100 Subject: [PATCH 0478/1302] Dialyzer: Replace re:mp dirty workaround from 0bbc255 with other less dirty --- Makefile.in | 2 -- include/logger.hrl | 2 -- src/acl.erl | 18 +++++++++--------- src/misc.erl | 4 ++++ src/mod_shared_roster_ldap.erl | 2 +- tools/opt_types.sh | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Makefile.in b/Makefile.in index 19d796955..def29dd46 100644 --- a/Makefile.in +++ b/Makefile.in @@ -579,9 +579,7 @@ Makefile: Makefile.in ifeq "$(REBAR_VER)" "3" dialyzer: - find src/*_opt.erl -type f \! -regex ".*git.*" -exec sed -i 's/re:mp/ tuple/g' {} \; $(REBAR) dialyzer - find src/*_opt.erl -type f \! -regex ".*git.*" -exec sed -i 's/ tuple/re:mp/g' {} \; else deps := $(wildcard $(DEPSDIR)/*/ebin) diff --git a/include/logger.hrl b/include/logger.hrl index 20bd50c09..5518f5a1a 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -62,7 +62,5 @@ _ -> 'Elixir.Logger':bare_log(error, io_lib:format(Format, Args), [?MODULE]) end). --type re_mp() :: {re_pattern, _, _, _, _}. % Copied from re.erl - %% Uncomment if you want to debug p1_fsm/gen_fsm %%-define(DBGFSM, true). diff --git a/src/acl.erl b/src/acl.erl index b1f12c82f..88ea0119e 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -39,14 +39,14 @@ -type acl_rule() :: {user, {binary(), binary()} | binary()} | {server, binary()} | {resource, binary()} | - {user_regexp, {re_mp(), binary()} | re_mp()} | - {server_regexp, re_mp()} | - {resource_regexp, re_mp()} | - {node_regexp, {re_mp(), re_mp()}} | - {user_glob, {re_mp(), binary()} | re_mp()} | - {server_glob, re_mp()} | - {resource_glob, re_mp()} | - {node_glob, {re_mp(), re_mp()}} | + {user_regexp, {misc:re_mp(), binary()} | misc:re_mp()} | + {server_regexp, misc:re_mp()} | + {resource_regexp, misc:re_mp()} | + {node_regexp, {misc:re_mp(), misc:re_mp()}} | + {user_glob, {misc:re_mp(), binary()} | misc:re_mp()} | + {server_glob, misc:re_mp()} | + {resource_glob, misc:re_mp()} | + {node_glob, {misc:re_mp(), misc:re_mp()}} | {shared_group, {binary(), binary()} | binary()} | {ip, ip_mask()}. -type access() :: [{action(), [access_rule()]}]. @@ -348,7 +348,7 @@ node_validator(UV, SV) -> %%%=================================================================== %%% Aux %%%=================================================================== --spec match_regexp(iodata(), re_mp()) -> boolean(). +-spec match_regexp(iodata(), misc:re_mp()) -> boolean(). match_regexp(Data, RegExp) -> re:run(Data, RegExp) /= nomatch. diff --git a/src/misc.erl b/src/misc.erl index 52a2fb15f..26c3c4c2d 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -54,6 +54,10 @@ -include_lib("xmpp/include/xmpp.hrl"). -include_lib("kernel/include/file.hrl"). +%% Copied from erlang/otp/lib/stdlib/src/re.erl +-type re_mp() :: {re_pattern, _, _, _, _}. +-export_type([re_mp/0]). + -type distance_cache() :: #{{string(), string()} => non_neg_integer()}. -spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}. diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 2e294140b..0cabc0809 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -72,7 +72,7 @@ user_desc = <<"">> :: binary(), user_uid = <<"">> :: binary(), uid_format = <<"">> :: binary(), - uid_format_re :: undefined | re_mp(), + uid_format_re :: undefined | misc:re_mp(), filter = <<"">> :: binary(), ufilter = <<"">> :: binary(), rfilter = <<"">> :: binary(), diff --git a/tools/opt_types.sh b/tools/opt_types.sh index 71d69a5d4..5c8761f82 100755 --- a/tools/opt_types.sh +++ b/tools/opt_types.sh @@ -320,9 +320,9 @@ spec(ip_mask, 0, _, _) -> spec(port, 0, _, _) -> erl_types:t_from_range(1, 65535); spec(re, A, _, _) when A == 0; A == 1 -> - t_remote(re, mp); + t_remote(misc, re_mp); spec(glob, A, _, _) when A == 0; A == 1 -> - t_remote(re, mp); + t_remote(misc, re_mp); spec(path, 0, _, _) -> erl_types:t_binary(); spec(binary_sep, 1, _, _) -> From 5bb94f1d9e68fdd3f88683855e4adc3d4b805dc2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Feb 2024 16:08:13 +0100 Subject: [PATCH 0479/1302] Result of running "make options" --- src/mod_muc_opt.erl | 2 +- src/mod_pubsub_opt.erl | 2 +- src/mod_shared_roster_ldap_opt.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl index 0d397abbb..d4550da1a 100644 --- a/src/mod_muc_opt.erl +++ b/src/mod_muc_opt.erl @@ -212,7 +212,7 @@ ram_db_type(Opts) when is_map(Opts) -> ram_db_type(Host) -> gen_mod:get_module_opt(Host, mod_muc, ram_db_type). --spec regexp_room_id(gen_mod:opts() | global | binary()) -> <<>> | re:mp(). +-spec regexp_room_id(gen_mod:opts() | global | binary()) -> <<>> | misc:re_mp(). regexp_room_id(Opts) when is_map(Opts) -> gen_mod:get_opt(regexp_room_id, Opts); regexp_room_id(Host) -> diff --git a/src/mod_pubsub_opt.erl b/src/mod_pubsub_opt.erl index cb3c014b9..612abf35b 100644 --- a/src/mod_pubsub_opt.erl +++ b/src/mod_pubsub_opt.erl @@ -39,7 +39,7 @@ default_node_config(Opts) when is_map(Opts) -> default_node_config(Host) -> gen_mod:get_module_opt(Host, mod_pubsub, default_node_config). --spec force_node_config(gen_mod:opts() | global | binary()) -> [{re:mp(),[{atom(),atom() | integer()}]}]. +-spec force_node_config(gen_mod:opts() | global | binary()) -> [{misc:re_mp(),[{atom(),atom() | integer()}]}]. force_node_config(Opts) when is_map(Opts) -> gen_mod:get_opt(force_node_config, Opts); force_node_config(Host) -> diff --git a/src/mod_shared_roster_ldap_opt.erl b/src/mod_shared_roster_ldap_opt.erl index 3833f24f2..d4657222e 100644 --- a/src/mod_shared_roster_ldap_opt.erl +++ b/src/mod_shared_roster_ldap_opt.erl @@ -118,7 +118,7 @@ ldap_memberattr_format(Opts) when is_map(Opts) -> ldap_memberattr_format(Host) -> gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_memberattr_format). --spec ldap_memberattr_format_re(gen_mod:opts() | global | binary()) -> 'undefined' | re:mp(). +-spec ldap_memberattr_format_re(gen_mod:opts() | global | binary()) -> 'undefined' | misc:re_mp(). ldap_memberattr_format_re(Opts) when is_map(Opts) -> gen_mod:get_opt(ldap_memberattr_format_re, Opts); ldap_memberattr_format_re(Host) -> From 6542a70c05a292b248365a0eec290a0e30a2291f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 5 Mar 2024 13:05:05 +0100 Subject: [PATCH 0480/1302] Dialyzer: First set alias definitions and then use them in @type --- lib/ejabberd/config/ejabberd_module.ex | 9 +++++---- lib/ejabberd/config/validator/validation.ex | 6 +++--- lib/ejabberd/config/validator/validator_attrs.ex | 7 ++++--- lib/ejabberd/config/validator/validator_dependencies.ex | 2 ++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/ejabberd/config/ejabberd_module.ex b/lib/ejabberd/config/ejabberd_module.ex index 57fd8303c..2d413ab56 100644 --- a/lib/ejabberd/config/ejabberd_module.ex +++ b/lib/ejabberd/config/ejabberd_module.ex @@ -7,12 +7,13 @@ defmodule Ejabberd.Config.EjabberdModule do the already existing Elixir.Module. """ - @type t :: %{module: atom, attrs: [Attr.t]} - - defstruct [:module, :attrs] - alias Ejabberd.Config.EjabberdModule alias Ejabberd.Config.Validation + alias Ejabberd.Config.Attr + + @type t :: %{module: atom, attrs: [Attr.attr]} + + defstruct [:module, :attrs] @doc """ Given a list of modules / single module diff --git a/lib/ejabberd/config/validator/validation.ex b/lib/ejabberd/config/validator/validation.ex index af582676e..227a3545f 100644 --- a/lib/ejabberd/config/validator/validation.ex +++ b/lib/ejabberd/config/validator/validation.ex @@ -3,12 +3,12 @@ defmodule Ejabberd.Config.Validation do Module used to validate a list of modules. """ - @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} - @type mod_validation_result :: {:ok, EjabberdModule.t} | {:error, EjabberdModule.t, map} - alias Ejabberd.Config.EjabberdModule alias Ejabberd.Config.Validator + @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} + @type mod_validation_result :: {:ok, EjabberdModule.t} | {:error, EjabberdModule.t, map} + @doc """ Given a module or a list of modules it runs validators on them and returns {:ok, mod} or {:error, mod, errors}, for each diff --git a/lib/ejabberd/config/validator/validator_attrs.ex b/lib/ejabberd/config/validator/validator_attrs.ex index 6a85c068d..bdce4f679 100644 --- a/lib/ejabberd/config/validator/validator_attrs.ex +++ b/lib/ejabberd/config/validator/validator_attrs.ex @@ -3,11 +3,12 @@ defmodule Ejabberd.Config.Validator.Attrs do Validator module used to validate attributes. """ - # TODO: Duplicated from validator.ex !!! - @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} - import Ejabberd.Config.ValidatorUtility alias Ejabberd.Config.Attr + alias Ejabberd.Config.EjabberdModule + + # TODO: Duplicated from validator.ex !!! + @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} @doc """ Given a module (with the form used for validation) diff --git a/lib/ejabberd/config/validator/validator_dependencies.ex b/lib/ejabberd/config/validator/validator_dependencies.ex index d44c8a136..4eb466663 100644 --- a/lib/ejabberd/config/validator/validator_dependencies.ex +++ b/lib/ejabberd/config/validator/validator_dependencies.ex @@ -4,6 +4,8 @@ defmodule Ejabberd.Config.Validator.Dependencies do with the @dependency annotation. """ + alias Ejabberd.Config.EjabberdModule + # TODO: Duplicated from validator.ex !!! @type mod_validation :: {[EjabberdModule.t], EjabberdModule.t, map} import Ejabberd.Config.ValidatorUtility From c7f76944f3dc555dbd311f49478a0f72dc96fc2b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 5 Mar 2024 12:48:26 +0100 Subject: [PATCH 0481/1302] Dialyzer: Update Elixir code to satisfy dialyzer warnings --- lib/ejabberd/config/attr.ex | 2 +- lib/ejabberd/config/config.ex | 7 ++----- lib/ejabberd/config/ejabberd_hook.ex | 1 - lib/ejabberd/config/ejabberd_module.ex | 1 - lib/ejabberd/config/opts_formatter.ex | 8 ++++---- lib/ejabberd/config/validator/validator_attrs.ex | 6 +++--- lib/ejabberd/config/validator/validator_utility.ex | 3 --- lib/ejabberd/config_util.ex | 2 +- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/lib/ejabberd/config/attr.ex b/lib/ejabberd/config/attr.ex index 9d17b157d..85d19191b 100644 --- a/lib/ejabberd/config/attr.ex +++ b/lib/ejabberd/config/attr.ex @@ -41,7 +41,7 @@ defmodule Ejabberd.Config.Attr do """ @spec validate([attr]) :: [{:ok, attr}] | [{:error, attr, atom()}] def validate(attrs) when is_list(attrs), do: Enum.map(attrs, &valid_attr?/1) - def validate(attr), do: validate([attr]) |> List.first + def validate(attr), do: validate([attr]) @doc """ Returns the type of an attribute, given its name. diff --git a/lib/ejabberd/config/config.ex b/lib/ejabberd/config/config.ex index df508fb4d..a8805e612 100644 --- a/lib/ejabberd/config/config.ex +++ b/lib/ejabberd/config/config.ex @@ -105,11 +105,8 @@ defmodule Ejabberd.Config do Code.eval_file(file_path) |> extract_and_store_module_name() # Getting start/0 config - Ejabberd.Config.Store.get(:module_name) - |> case do - nil -> IO.puts "[ ERR ] Configuration module not found." - [module] -> call_start_func_and_store_data(module) - end + [module] = Ejabberd.Config.Store.get(:module_name) + call_start_func_and_store_data(module) # Fetching git modules and install them get_modules_parsed_in_order() diff --git a/lib/ejabberd/config/ejabberd_hook.ex b/lib/ejabberd/config/ejabberd_hook.ex index 8b7858d23..5f9de4aa0 100644 --- a/lib/ejabberd/config/ejabberd_hook.ex +++ b/lib/ejabberd/config/ejabberd_hook.ex @@ -13,7 +13,6 @@ defmodule Ejabberd.Config.EjabberdHook do @doc """ Register a hook to ejabberd. """ - @spec start(EjabberdHook.t) :: none def start(%EjabberdHook{hook: hook, opts: opts, fun: fun}) do host = Keyword.get(opts, :host, :global) priority = Keyword.get(opts, :priority, 50) diff --git a/lib/ejabberd/config/ejabberd_module.ex b/lib/ejabberd/config/ejabberd_module.ex index 2d413ab56..6d5b1e467 100644 --- a/lib/ejabberd/config/ejabberd_module.ex +++ b/lib/ejabberd/config/ejabberd_module.ex @@ -30,7 +30,6 @@ defmodule Ejabberd.Config.EjabberdModule do a git attribute and tries to fetch the repo, then, it install them through :ext_mod.install/1 """ - @spec fetch_git_repos([EjabberdModule.t]) :: none() def fetch_git_repos(modules) do modules |> Enum.filter(&is_git_module?/1) diff --git a/lib/ejabberd/config/opts_formatter.ex b/lib/ejabberd/config/opts_formatter.ex index 67887fa4f..3d3db926f 100644 --- a/lib/ejabberd/config/opts_formatter.ex +++ b/lib/ejabberd/config/opts_formatter.ex @@ -14,15 +14,12 @@ defmodule Ejabberd.Config.OptsFormatter do Look at how Config.get_ejabberd_opts/0 is constructed for more informations. """ - @spec format_opts_for_ejabberd([{atom(), any()}]) :: list() + @spec format_opts_for_ejabberd(map) :: list() def format_opts_for_ejabberd(opts) do opts |> format_attrs_for_ejabberd end - defp format_attrs_for_ejabberd(opts) when is_list(opts), - do: (Enum.map opts, &format_attrs_for_ejabberd/1) - defp format_attrs_for_ejabberd({:listeners, mods}), do: {:listen, format_listeners_for_ejabberd(mods)} @@ -32,6 +29,9 @@ defmodule Ejabberd.Config.OptsFormatter do defp format_attrs_for_ejabberd({key, opts}) when is_atom(key), do: {key, opts} + defp format_attrs_for_ejabberd(opts), + do: (Enum.map opts, &format_attrs_for_ejabberd/1) + defp format_mods_for_ejabberd(mods) do Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} -> {mod, attrs[:opts]} diff --git a/lib/ejabberd/config/validator/validator_attrs.ex b/lib/ejabberd/config/validator/validator_attrs.ex index bdce4f679..e0e133b61 100644 --- a/lib/ejabberd/config/validator/validator_attrs.ex +++ b/lib/ejabberd/config/validator/validator_attrs.ex @@ -18,9 +18,9 @@ defmodule Ejabberd.Config.Validator.Attrs do @spec validate(mod_validation) :: mod_validation def validate({modules, mod, errors}) do errors = Enum.reduce mod.attrs, errors, fn(attr, err) -> - case Attr.validate(attr) do - {:ok, _attr} -> err - {:error, attr, cause} -> put_error(err, :attribute, {attr, cause}) + case Attr.validate([attr]) do + [{:ok, _attr}] -> err + [{:error, attr, cause}] -> put_error(err, :attribute, {attr, cause}) end end diff --git a/lib/ejabberd/config/validator/validator_utility.ex b/lib/ejabberd/config/validator/validator_utility.ex index 17805f748..6047618b6 100644 --- a/lib/ejabberd/config/validator/validator_utility.ex +++ b/lib/ejabberd/config/validator/validator_utility.ex @@ -4,8 +4,6 @@ defmodule Ejabberd.Config.ValidatorUtility do Imports utility functions for working with validation structures. """ - alias Ejabberd.Config.EjabberdModule - @doc """ Inserts an error inside the errors collection, for the given key. If the key doesn't exists then it creates an empty collection @@ -22,7 +20,6 @@ defmodule Ejabberd.Config.ValidatorUtility do Given a list of modules it extracts and returns a list of the module names (which are Elixir.Module). """ - @spec extract_module_names(EjabberdModule.t) :: [atom] def extract_module_names(modules) when is_list(modules) do modules |> Enum.map(&Map.get(&1, :module)) diff --git a/lib/ejabberd/config_util.ex b/lib/ejabberd/config_util.ex index 6592104a2..71d854f15 100644 --- a/lib/ejabberd/config_util.ex +++ b/lib/ejabberd/config_util.ex @@ -7,7 +7,7 @@ defmodule Ejabberd.ConfigUtil do @doc """ Returns true when the config file is based on elixir. """ - @spec is_elixir_config(list) :: boolean + @spec is_elixir_config(binary) :: boolean def is_elixir_config(filename) when is_list(filename) do is_elixir_config(to_string(filename)) end From f5c8406353f1ec660e53250547e0670d15eda476 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 Feb 2024 16:46:05 +0100 Subject: [PATCH 0482/1302] Dialyzer: Add support to run Dialyzer with Mix --- Makefile.in | 13 ++++++++++--- mix.exs | 14 ++++++++++++++ mix.lock | 2 ++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index def29dd46..7073e46d5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -577,10 +577,16 @@ Makefile: Makefile.in #' dialyzer # -ifeq "$(REBAR_VER)" "3" +ifeq "$(REBAR_VER)" "6" # Mix +dialyzer: + MIX_ENV=test $(REBAR) dialyzer + +else +ifeq "$(REBAR_VER)" "3" # Rebar3 dialyzer: $(REBAR) dialyzer -else + +else # Rebar2 deps := $(wildcard $(DEPSDIR)/*/ebin) dialyzer/erlang.plt: @@ -620,6 +626,7 @@ dialyzer: erlang_plt deps_plt ejabberd_plt --get_warnings -o dialyzer/error.log ebin; \ status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi endif +endif #. #' test @@ -671,7 +678,7 @@ help: @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" @echo "" - @echo " dialyzer Run Dialyzer static analyzer [rebar3]" + @echo " dialyzer Run Dialyzer static analyzer" @echo " hooks Run hooks validator" @echo " test Run Common Tests suite [rebar3]" @echo " xref Run cross reference analysis [rebar3]" diff --git a/mix.exs b/mix.exs index fcd30b093..d349dc370 100644 --- a/mix.exs +++ b/mix.exs @@ -16,6 +16,7 @@ defmodule Ejabberd.MixProject do aliases: [test: "test --no-start"], start_permanent: Mix.env() == :prod, language: :erlang, + dialyzer: dialyzer(), releases: releases(), package: package(), docs: docs(), @@ -52,6 +53,17 @@ defmodule Ejabberd.MixProject do ++ cond_included_apps()] end + defp dialyzer do + [ + plt_add_apps: [ + :mnesia, :odbc, :os_mon, :stdlib, + :eredis, :luerl, + :cache_tab, :eimp, :epam, :esip, :ezlib, :mqtree, + :p1_acme, :p1_mysql, :p1_oauth2, :p1_pgsql, :pkix, + :sqlite3, :stun, :xmpp], + ] + end + defp if_version_above(ver, okResult) do if :erlang.system_info(:otp_release) > ver do okResult @@ -104,6 +116,7 @@ defmodule Ejabberd.MixProject do defp deps do [{:base64url, "~> 1.0"}, {:cache_tab, "~> 1.0"}, + {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, ">= 1.1.18"}, @@ -164,6 +177,7 @@ defmodule Ejabberd.MixProject do {config(:lua), :luerl}, {config(:redis), :eredis}, {Mix.env() == :edoc, :ex_doc}, + {Mix.env() == :test, :dialyxir}, {if_version_below(~c"22", true), :lager}, {config(:mysql), :p1_mysql}, {config(:sip), :esip}, diff --git a/mix.lock b/mix.lock index cde78ed67..d519bfd75 100644 --- a/mix.lock +++ b/mix.lock @@ -1,10 +1,12 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, + "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, + "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, From 2c1b0bb77b7619f3cd8a383f5d9f5ac88b00e11e Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Mar 2024 01:09:53 +0100 Subject: [PATCH 0483/1302] Use shorthands provided by docs.ejabberd.im Makefile, fix markdown syntax --- src/ejabberd_admin.erl | 12 ++++++------ src/ejabberd_options_doc.erl | 8 ++++---- src/mod_admin_extra.erl | 4 ++-- src/mod_private.erl | 3 +-- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 4d89e09d2..e482960d3 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -300,7 +300,7 @@ get_commands_spec() -> #ejabberd_commands{name = import_prosody, tags = [mnesia, sql], desc = "Import data from Prosody", longdesc = "Note: this requires ejabberd to be " - "[compiled with `--enable-lua`](http://localhost:8098/admin/installation/#configure) " + "compiled with `./configure --enable-lua` " "(which installs the `luerl` library).", module = prosody2ejabberd, function = from_dir, args_desc = ["Full path to the Prosody data directory"], @@ -371,7 +371,7 @@ get_commands_spec() -> longdesc = "Configure the modules to use SQL, then call this command. " "After correctly exported the database of a vhost, " "you may want to delete from mnesia with " - "the http://./#delete-mnesia[delete_mnesia] command.", + "the _`delete_mnesia`_ API.", module = ejd2sql, function = export, args_desc = ["Vhost", "Full path to the destination SQL file"], args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"], @@ -407,7 +407,7 @@ get_commands_spec() -> "binary backup file the internal Mnesia " "database. This will consume a lot of memory if " "you have a large database, you may prefer " - "http://./#install-fallback[install_fallback].", + "_`install_fallback`_ API.", module = ?MODULE, function = restore_mnesia, args_desc = ["Full path to the backup file"], args_example = ["/var/lib/ejabberd/database.backup"], @@ -430,8 +430,8 @@ get_commands_spec() -> "recommended for big databases, as it will " "consume much time, memory and processor. In " "that case it's preferable to use " - "http://./#backup[backup] and " - "http://./#install-fallback[install_fallback].", + "_`backup`_ API and " + "_`install_fallback`_ API.", module = ?MODULE, function = load_mnesia, args_desc = ["Full path to the text file"], args_example = ["/var/lib/ejabberd/database.txt"], @@ -454,7 +454,7 @@ get_commands_spec() -> "start. This means that, after running this " "command, you have to restart ejabberd. This " "command requires less memory than " - "http://./#restore[restore].", + "_`restore`_ API.", module = ?MODULE, function = install_fallback_mnesia, args_desc = ["Full path to the fallback file"], args_example = ["/var/lib/ejabberd/database.fallback"], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 742afcec7..8d5394441 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -417,7 +417,7 @@ doc() -> "one of these root CA certificates and should contain the " "corresponding JID(s) in 'subjectAltName' field. " "There is no default value."), "", - ?T("You can use http://../toplevel/#host-config[host_config] to specify this option per-vhost."), "", + ?T("You can use _`host_config`_ to specify this option per-vhost."), "", ?T("To set a specific file per listener, use the listener's http://../listen-options/#cafile[cafile] option. Please notice that 'c2s_cafile' overrides the listener's 'cafile' option."), "" ]}}, {c2s_ciphers, @@ -462,7 +462,7 @@ doc() -> desc => [?T("Path to a file of CA root certificates. " "The default is to use system defined file if possible."), "", - ?T("For server connections, this 'ca_file' option is overridden by the http://../toplevel/#s2s-cafile[s2s_cafile] option."), "" + ?T("For server connections, this 'ca_file' option is overridden by the _`s2s_cafile`_ option."), "" ]}}, {captcha_cmd, #{value => ?T("Path | ModuleName"), @@ -1164,8 +1164,8 @@ doc() -> desc => [?T("A path to a file with CA root certificates that will " "be used to authenticate s2s connections. If not set, " - "the value of http://../toplevel/#ca-file[ca_file] will be used."), "", - ?T("You can use http://../toplevel/#host-config[host_config] to specify this option per-vhost."), "" + "the value of _`ca_file`_ will be used."), "", + ?T("You can use _`host_config`_ to specify this option per-vhost."), "" ]}}, {s2s_ciphers, #{value => "[Cipher, ...]", diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 4c42c882f..886606b02 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -802,7 +802,7 @@ get_commands_spec() -> #ejabberd_commands{name = send_stanza_c2s, tags = [stanza], desc = "Send a stanza from an existing C2S session", longdesc = "`user`@`host`/`resource` must be an existing C2S session." - " As an alternative, use http://./#send-stanza[send_stanza] instead.", + " As an alternative, use _`send_stanza`_ API instead.", module = ?MODULE, function = send_stanza_c2s, args = [{user, binary}, {host, binary}, {resource, binary}, {stanza, binary}], args_example = [<<"admin">>, <<"myserver.com">>, <<"bot">>, @@ -1730,7 +1730,7 @@ mod_doc() -> ?T("- 'pushroster': (and 'pushroster-all')"), ?T("The roster file must be placed, if using Windows, on the " "directory where you installed ejabberd: " - "C:/Program Files/ejabberd or similar. If you use other " + "`C:/Program Files/ejabberd` or similar. If you use other " "Operating System, place the file on the same directory where " "the .beam files are installed. See below an example roster file."), ?T("- 'srg-create':"), diff --git a/src/mod_private.erl b/src/mod_private.erl index 5eb0970e1..82c6653a6 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -127,8 +127,7 @@ mod_doc() -> "[XEP-0048: Bookmarks])."), "", ?T("It also implements the bookmark conversion described in " "https://xmpp.org/extensions/xep-0402.html[XEP-0402: PEP Native Bookmarks]" - ", see the command " - "https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#bookmarks-to-pep[bookmarks_to_pep].")], + ", see the command _`bookmarks_to_pep`_ API.")], opts => [{db_type, #{value => "mnesia | sql", From 80b06da56b9134d357fea11bd0212f02ced6ac76 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Mar 2024 18:09:17 +0100 Subject: [PATCH 0484/1302] Download GPL 2 in markdown format Downloaded file https://www.gnu.org/licenses/old-licenses/gpl-2.0.md from https://www.gnu.org/licenses/old-licenses/gpl-2.0.html --- COPYING.md | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 COPYING.md diff --git a/COPYING.md b/COPYING.md new file mode 100644 index 000000000..d1412480c --- /dev/null +++ b/COPYING.md @@ -0,0 +1,361 @@ +# GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +## Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at +all. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work +based on the Program" means either the Program or any derivative work +under copyright law: that is to say, a work containing the Program or +a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee +is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +**1.** You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Program or any +portion of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + +**a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + + +**b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + + +**c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such interactive +use in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and telling +the user how to view a copy of this License. (Exception: if the +Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + +**a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; or, + + +**b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete machine-readable +copy of the corresponding source code, to be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + + +**c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and +will automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +**6.** Each time you redistribute the Program (or any work based on +the Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +**7.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this +License, they do not excuse you from the conditions of this License. +If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Program at all. For +example, if a patent license would not permit royalty-free +redistribution of the Program by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**8.** If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**9.** The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +**10.** If you wish to incorporate parts of the Program into other +free programs whose distribution conditions are different, write to +the author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +**NO WARRANTY** + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper +mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type `show c' + for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than \`show w' and +\`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +[GNU Lesser General Public +License](https://www.gnu.org/licenses/lgpl.html) instead of this +License. From fcc5f00e75339b009a4e0535f0eecfbe40d37813 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Mar 2024 18:10:39 +0100 Subject: [PATCH 0485/1302] Copy from COPYING the sentence about OpenSSL additional permission --- COPYING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/COPYING.md b/COPYING.md index d1412480c..917ab0169 100644 --- a/COPYING.md +++ b/COPYING.md @@ -1,3 +1,6 @@ +As a special exception, the authors give permission to link this program +with the OpenSSL library and distribute the resulting binary. + # GNU GENERAL PUBLIC LICENSE Version 2, June 1991 From 1639471250b4c8ead55c1699bab43efecd3bd8c3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Mar 2024 17:16:39 +0100 Subject: [PATCH 0486/1302] Fix crash running export2sql when MAM enabled but MUC disabled Problem introduced in cfc393a --- src/ejd2sql.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl index 1400ba0da..090bb32f0 100644 --- a/src/ejd2sql.erl +++ b/src/ejd2sql.erl @@ -74,7 +74,10 @@ export(Server, Output) -> close_output(Output, IO). export(Server, Output, mod_mam = M1) -> - MucServices = gen_mod:get_module_opt_hosts(Server, mod_muc), + MucServices = case gen_mod:is_loaded(Server, mod_muc) of + true -> gen_mod:get_module_opt_hosts(Server, mod_muc); + false -> [] + end, [export2(MucService, Output, M1, M1) || MucService <- MucServices], export2(Server, Output, M1, M1); export(Server, Output, mod_pubsub = M1) -> From 8f20dd8425aa0543feeee0b679041a6c5c7bd882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 18 Mar 2024 12:42:03 +0100 Subject: [PATCH 0487/1302] Test retractons in mam_tests --- test/mam_tests.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/mam_tests.erl b/test/mam_tests.erl index c726bcbe7..66af9dc8d 100644 --- a/test/mam_tests.erl +++ b/test/mam_tests.erl @@ -431,12 +431,21 @@ set_default(Config, Default) -> send_messages(Config, Range) -> Peer = ?config(peer, Config), + send_message_extra(Config, 0, <<"to-retract-1">>, []), lists:foreach( - fun(N) -> + fun + (1) -> + send_message_extra(Config, 1, <<"retraction-1">>, [#message_retract{id = <<"to-retract-1">>}]); + (N) -> Body = xmpp:mk_text(integer_to_binary(N)), send(Config, #message{to = Peer, body = Body}) end, Range). +send_message_extra(Config, N, Id, Sub) -> + Peer = ?config(peer, Config), + Body = xmpp:mk_text(integer_to_binary(N)), + send(Config, #message{id = Id, to = Peer, body = Body, sub_els = Sub}). + recv_messages(Config, Range) -> Peer = ?config(peer, Config), lists:foreach( @@ -448,7 +457,7 @@ recv_messages(Config, Range) -> xmpp:get_subtag(Msg, #mam_archived{}), #stanza_id{by = BareMyJID} = xmpp:get_subtag(Msg, #stanza_id{}) - end, Range). + end, [0 | Range]). recv_archived_messages(Config, From, To, QID, Range) -> MyJID = my_jid(Config), From 537aac24f7243c5292e51b648f46c153bc35ad1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 18 Mar 2024 14:54:32 +0100 Subject: [PATCH 0488/1302] Improve detection of types in odbc This should fix issue with dialyzer on erlang 26.2.3+ --- mix.exs | 17 ++++++++++++++++- rebar.config | 1 + rebar.config.script | 24 ++++++++++++++++++++++++ src/ejabberd_sql.erl | 8 ++------ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index d349dc370..3f3845cf7 100644 --- a/mix.exs +++ b/mix.exs @@ -80,6 +80,20 @@ defmodule Ejabberd.MixProject do end end + defp if_type_exported(module, typeDef, okResult) do + try do + {:ok, concrete} = :dialyzer_utils.get_core_from_beam(:code.which(module)) + {:ok, types} = :dialyzer_utils.get_record_and_type_info(concrete) + if Maps.has_key(types, typeDef) do + okResult + else + [] + end + rescue + _ -> [] + end + end + defp erlc_options do # Use our own includes + includes from all dependencies includes = ["include", deps_include()] @@ -97,7 +111,8 @@ defmodule Ejabberd.MixProject do if_version_below(~c"24", [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ if_version_below(~c"24", [{:d, :OTP_BELOW_24}]) ++ - if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) + if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++ + if_type_exported(:odbc, {:opaque, :connection_reference, 0}, [{:d, :ODBC_HAS_TYPES}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] end diff --git a/rebar.config b/rebar.config index b29da967d..bdf6c6088 100644 --- a/rebar.config +++ b/rebar.config @@ -152,6 +152,7 @@ {if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATEWAY_WORKAROUND'}}, {if_var_true, sip, {d, 'SIP'}}, {if_var_true, stun, {d, 'STUN'}}, + {if_type_exported, {odbc, {opaque, connection_reference, 0}}, {d, 'ODBC_HAS_TYPES'}}, {src_dirs, [src, {if_rebar3, sql}, {if_var_true, tools, tools}]}]}. diff --git a/rebar.config.script b/rebar.config.script index bc1e060db..7d7fa864c 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -151,6 +151,30 @@ ProcessVars = fun F([], Acc) -> false -> F(Tail, Acc) end; + F([{Type, {Mod, TypeDef}, Value} | Tail], Acc) when + Type == if_type_exported orelse + Type == if_type_not_exported -> + try + {ok, Concrete} = dialyzer_utils:get_core_from_beam(code:which(Mod)), + {ok, Types} = dialyzer_utils:get_record_and_type_info(Concrete), + maps:get(TypeDef, Types, undefined) + of + undefined when Type == if_type_not_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + undefined -> + F(Tail, Acc); + _ when Type == if_type_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + _ -> + F(Tail, Acc) + catch _:_ -> + if + Type == if_type_not_exported -> + F(Tail, ProcessSingleVar(F, Value, Acc)); + true -> + F(Tail, Acc) + end + end; F([Other1 | Tail1], Acc) -> F(Tail1, [F(Other1, []) | Acc]); F(Val, Acc) when is_tuple(Val) -> diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 58580c182..5e5549c45 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -67,12 +67,8 @@ -export([connecting/2, connecting/3, session_established/2, session_established/3]). --ifdef(OTP_RELEASE). - -if(?OTP_RELEASE >= 27). - -type(odbc_connection_reference() :: odbc:connection_reference()). - -else. - -type(odbc_connection_reference() :: pid()). - -endif. +-ifdef(ODBC_HAS_TYPES). + -type(odbc_connection_reference() :: odbc:connection_reference()). -else. -type(odbc_connection_reference() :: pid()). -endif. From c5437235f3942214a0122bee63ed44903e8be9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 20 Mar 2024 10:57:12 +0100 Subject: [PATCH 0489/1302] Make mod_vcard_xupdate send hash after avatar get set for first time We need to remove info about empty photo in session stored presence after avatar get set, otherwise as we don't modify presences like that in mod_vcard_xupdate, we will send presence with updated hash. This fixes issue #4182 --- src/ejabberd_c2s.erl | 14 +++++++++++++- src/ejabberd_sm.erl | 10 ++++++++++ src/mod_vcard_xupdate.erl | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index c1273d0dc..2ec5baaf0 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -52,7 +52,8 @@ -export([get_presence/1, set_presence/2, resend_presence/1, resend_presence/2, open_session/1, call/3, cast/2, send/2, close/1, close/2, stop_async/1, reply/2, copy_state/2, set_timeout/2, route/2, format_reason/2, - host_up/1, host_down/1, send_ws_ping/1, bounce_message_queue/2]). + host_up/1, host_down/1, send_ws_ping/1, bounce_message_queue/2, + reset_vcard_xupdate_resend_presence/1]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -108,6 +109,10 @@ resend_presence(Pid) -> resend_presence(Pid, To) -> route(Pid, {resend_presence, To}). +-spec reset_vcard_xupdate_resend_presence(pid()) -> boolean(). +reset_vcard_xupdate_resend_presence(Pid) -> + route(Pid, reset_vcard_xupdate_resend_presence). + -spec close(pid()) -> ok; (state()) -> state(). close(Ref) -> @@ -246,6 +251,13 @@ process_info(#{lserver := LServer} = State, {route, Packet}) -> true -> State1 end; +process_info(State, reset_vcard_xupdate_resend_presence) -> + case maps:get(pres_last, State, error) of + error -> State; + Pres -> + Pres2 = xmpp:remove_subtag(Pres, #vcard_xupdate{}), + process_self_presence(State#{pres_last => Pres2}, Pres2) + end; process_info(#{jid := JID} = State, {resend_presence, To}) -> case maps:get(pres_last, State, error) of error -> State; diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 1ff539ad1..b986d5400 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -56,6 +56,7 @@ get_vh_session_number/1, get_vh_by_backend/1, force_update_presence/1, + reset_vcard_xupdate_resend_presence/1, connected_users/0, connected_users_number/0, user_resources/2, @@ -926,6 +927,15 @@ force_update_presence({LUser, LServer}) -> end, Ss). +-spec reset_vcard_xupdate_resend_presence({binary(), binary()}) -> ok. +reset_vcard_xupdate_resend_presence({LUser, LServer}) -> + Mod = get_sm_backend(LServer), + Ss = get_sessions(Mod, LUser, LServer), + lists:foreach( + fun(#session{sid = {_, Pid}}) -> + ejabberd_c2s:reset_vcard_xupdate_resend_presence(Pid) + end, Ss). + -spec get_sm_backend(binary()) -> module(). get_sm_backend(Host) -> diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index e6a26864c..0b6367684 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -97,7 +97,7 @@ user_send_packet(Acc) -> -spec vcard_set(iq()) -> iq(). vcard_set(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) -> ets_cache:delete(?VCARD_XUPDATE_CACHE, {LUser, LServer}, ejabberd_cluster:get_nodes()), - ejabberd_sm:force_update_presence({LUser, LServer}), + ejabberd_sm:reset_vcard_xupdate_resend_presence({LUser, LServer}), IQ; vcard_set(Acc) -> Acc. From 9ab60935a448bd5f66a984db47cde9aed1202cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 21 Mar 2024 10:58:06 +0100 Subject: [PATCH 0490/1302] Add update_primary_key command to sql schema updater --- src/ejabberd_sql_schema.erl | 129 +++++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index c1a3c0168..a4eaea0b2 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -394,6 +394,29 @@ get_current_version(Host, Module, Schemas) -> Version end. +sqlite_table_copy(Host, SchemaInfo, Table) -> + ejabberd_sql:sql_transaction(Host, + fun() -> + TableName = Table#sql_table.name, + NewTableName = <<"new_", TableName/binary>>, + NewTable = Table#sql_table{name = NewTableName}, + create_table_t(SchemaInfo, NewTable), + SQL2 = <<"INSERT INTO ", NewTableName/binary, + " SELECT * FROM ", TableName/binary>>, + ?INFO_MSG("Copying table ~s to ~s:~n~s~n", + [TableName, NewTableName, SQL2]), + ejabberd_sql:sql_query_t(SQL2), + SQL3 = <<"DROP TABLE ", TableName/binary>>, + ?INFO_MSG("Droping old table ~s:~n~s~n", + [TableName, SQL2]), + ejabberd_sql:sql_query_t(SQL3), + SQL4 = <<"ALTER TABLE ", NewTableName/binary, + " RENAME TO ", TableName/binary>>, + ?INFO_MSG("Renameing table ~s to ~s:~n~s~n", + [NewTableName, TableName, SQL4]), + ejabberd_sql:sql_query_t(SQL4) + end). + format_type(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of text -> <<"text">>; @@ -697,24 +720,26 @@ format_create_table(#sql_schema_info{db_type = mysql} = SchemaInfo, Table) -> Table#sql_table.indices). create_table(Host, SchemaInfo, Table) -> - ejabberd_sql:sql_query( - Host, - fun() -> - SQLs = format_create_table(SchemaInfo, Table), - ?INFO_MSG("Creating table ~s:~n~s~n", - [Table#sql_table.name, SQLs]), - lists:foreach( - fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, SQLs), - case Table#sql_table.post_create of - undefined -> - ok; - F when is_function(F, 1) -> - PostSQLs = F(SchemaInfo), - lists:foreach( - fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, - PostSQLs) - end - end). + ejabberd_sql:sql_query(Host, + fun() -> + create_table_t(SchemaInfo, Table) + end). + +create_table_t(SchemaInfo, Table) -> + SQLs = format_create_table(SchemaInfo, Table), + ?INFO_MSG("Creating table ~s:~n~s~n", + [Table#sql_table.name, SQLs]), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, SQLs), + case Table#sql_table.post_create of + undefined -> + ok; + F when is_function(F, 1) -> + PostSQLs = F(SchemaInfo), + lists:foreach( + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, + PostSQLs) + end. create_tables(Host, Module, SchemaInfo, Schema) -> lists:foreach( @@ -969,6 +994,74 @@ do_update_schema(Host, Module, SchemaInfo, Schema) -> ok end end; + ({update_primary_key, TableName, Columns1}) -> + Columns = + case ejabberd_sql:use_new_schema() of + true -> + Columns1; + false -> + lists:delete( + <<"server_host">>, Columns1) + end, + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Index} = + lists:keysearch( + Columns, #sql_index.columns, Table#sql_table.indices), + Res = + case SchemaInfo#sql_schema_info.db_type of + sqlite -> + sqlite_table_copy(Host, SchemaInfo, Table); + pgsql -> + TableName = Table#sql_table.name, + SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP CONSTRAINT ", + TableName/binary,"_pkey, ", + "ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + Index#sql_index.columns), + <<");">>], + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Update primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query( + Host, + fun(_DBType, _DBVersion) -> + ejabberd_sql:sql_query_t(SQL) + end); + mysql -> + TableName = Table#sql_table.name, + SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP PRIMARY KEY, " + "ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + lists:map( + fun(Col) -> + format_mysql_index_column(Table, Col) + end, Index#sql_index.columns)), + <<");">>], + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Update primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query( + Host, + fun(_DBType, _DBVersion) -> + ejabberd_sql:sql_query_t(SQL) + end) + end, + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; ({drop_index, TableName, Columns1}) -> Columns = case ejabberd_sql:use_new_schema() of From 3ca2d99a47b4eadbb72b9db88ea65e8977cb26c9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Mar 2024 12:31:17 +0100 Subject: [PATCH 0491/1302] COPYING: Move the markdown-formatted file to Docs git, keep plaintext file here --- COPYING.md | 364 ----------------------------------------------------- 1 file changed, 364 deletions(-) delete mode 100644 COPYING.md diff --git a/COPYING.md b/COPYING.md deleted file mode 100644 index 917ab0169..000000000 --- a/COPYING.md +++ /dev/null @@ -1,364 +0,0 @@ -As a special exception, the authors give permission to link this program -with the OpenSSL library and distribute the resulting binary. - -# GNU GENERAL PUBLIC LICENSE - -Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -## Preamble - -The licenses for most software are designed to take away your freedom -to share and change it. By contrast, the GNU General Public License is -intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - -To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the software, or if you modify it. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - -We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - -Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, -we want its recipients to know that what they have is not the -original, so that any problems introduced by others will not reflect -on the original authors' reputations. - -Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at -all. - -The precise terms and conditions for copying, distribution and -modification follow. - -## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -**0.** This License applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work -based on the Program" means either the Program or any derivative work -under copyright law: that is to say, a work containing the Program or -a portion of it, either verbatim or with modifications and/or -translated into another language. (Hereinafter, translation is -included without limitation in the term "modification".) Each licensee -is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the Program -(independent of having been made by running the Program). Whether that -is true depends on what the Program does. - -**1.** You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a -fee. - -**2.** You may modify your copy or copies of the Program or any -portion of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - -**a)** You must cause the modified files to carry prominent notices -stating that you changed the files and the date of any change. - - -**b)** You must cause any work that you distribute or publish, that in -whole or in part contains or is derived from the Program or any part -thereof, to be licensed as a whole at no charge to all third parties -under the terms of this License. - - -**c)** If the modified program normally reads commands interactively -when run, you must cause it, when started running for such interactive -use in the most ordinary way, to print or display an announcement -including an appropriate copyright notice and a notice that there is -no warranty (or else, saying that you provide a warranty) and that -users may redistribute the program under these conditions, and telling -the user how to view a copy of this License. (Exception: if the -Program itself is interactive but does not normally print such an -announcement, your work based on the Program is not required to print -an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -**3.** You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - -**a)** Accompany it with the complete corresponding machine-readable -source code, which must be distributed under the terms of Sections 1 -and 2 above on a medium customarily used for software interchange; or, - - -**b)** Accompany it with a written offer, valid for at least three -years, to give any third party, for a charge no more than your cost of -physically performing source distribution, a complete machine-readable -copy of the corresponding source code, to be distributed under the -terms of Sections 1 and 2 above on a medium customarily used for -software interchange; or, - - -**c)** Accompany it with the information you received as to the offer -to distribute corresponding source code. (This alternative is allowed -only for noncommercial distribution and only if you received the -program in object code or executable form with such an offer, in -accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - -**4.** You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt otherwise -to copy, modify, sublicense or distribute the Program is void, and -will automatically terminate your rights under this License. However, -parties who have received copies, or rights, from you under this -License will not have their licenses terminated so long as such -parties remain in full compliance. - -**5.** You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - -**6.** Each time you redistribute the Program (or any work based on -the Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - -**7.** If, as a consequence of a court judgment or allegation of -patent infringement or for any other reason (not limited to patent -issues), conditions are imposed on you (whether by court order, -agreement or otherwise) that contradict the conditions of this -License, they do not excuse you from the conditions of this License. -If you cannot distribute so as to satisfy simultaneously your -obligations under this License and any other pertinent obligations, -then as a consequence you may not distribute the Program at all. For -example, if a patent license would not permit royalty-free -redistribution of the Program by all those who receive copies directly -or indirectly through you, then the only way you could satisfy both it -and this License would be to refrain entirely from distribution of the -Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -**8.** If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - -**9.** The Free Software Foundation may publish revised and/or new -versions of the General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Program does not specify a -version number of this License, you may choose any version ever -published by the Free Software Foundation. - -**10.** If you wish to incorporate parts of the Program into other -free programs whose distribution conditions are different, write to -the author to ask for permission. For software which is copyrighted by -the Free Software Foundation, write to the Free Software Foundation; -we sometimes make exceptions for this. Our decision will be guided by -the two goals of preserving the free status of all derivatives of our -free software and of promoting the sharing and reuse of software -generally. - -**NO WARRANTY** - -**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - -END OF TERMS AND CONDITIONS - -## How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - -To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - one line to give the program's name and an idea of what it does. - Copyright (C) yyyy name of author - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -Also add information on how to contact you by electronic and paper -mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details - type `show w'. This is free software, and you are welcome - to redistribute it under certain conditions; type `show c' - for details. - -The hypothetical commands \`show w' and \`show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than \`show w' and -\`show c'; they could even be mouse-clicks or menu items--whatever -suits your program. - -You should also get your employer (if you work as a programmer) or -your school, if any, to sign a "copyright disclaimer" for the program, -if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright - interest in the program `Gnomovision' - (which makes passes at compilers) written - by James Hacker. - - signature of Ty Coon, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, -you may consider it more useful to permit linking proprietary -applications with the library. If this is what you want to do, use the -[GNU Lesser General Public -License](https://www.gnu.org/licenses/lgpl.html) instead of this -License. From 7269d171a7efd6c46d483a7e9eec799da9991813 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Mar 2024 12:58:48 +0100 Subject: [PATCH 0492/1302] README.md: Fix markdown syntax and add docs badge --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aed86a486..15817f666 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,11 @@ - + - + + +

From 059a028c0c3591a01a320af617183c2389984c18 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 24 Mar 2024 15:50:31 +0100 Subject: [PATCH 0493/1302] CONTRIBUTING.md: Fix markdown --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b1fdffc3..72619891b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ If you would like to implement a new feature then consider what kind of change i * **Major Changes** that you wish to contribute to the project should be discussed first in an [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. * **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] - as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr). + as a Pull Request. See the section about [Pull Request Submission Guidelines](#pull-request-submission-guidelines). ## Issue Submission Guidelines From 208f798b0754a7d2d724065879f62fbbc2ccb90a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 06:17:51 +0000 Subject: [PATCH 0494/1302] Bump softprops/action-gh-release from 1 to 2 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/installers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index a298c4531..b820dd6d9 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -78,7 +78,7 @@ jobs: with: name: ejabberd-packages - name: Draft Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: draft: true files: ejabberd-packages/* From 703e98ec45db9b57200c3d203824d4f198a7c0af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 06:23:17 +0000 Subject: [PATCH 0495/1302] Bump ex_doc from 0.31.1 to 0.31.2 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.31.1 to 0.31.2. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.31.1...v0.31.2) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index d519bfd75..c2ff98cdd 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, - "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, + "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, @@ -18,8 +18,8 @@ "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "luerl": {:hex, :luerl, "1.1.1", "083518e437586f6631150d39c4bff242ed2ec80cb14a3299a0c2628f07a2ff7f", [:rebar3], [], "hexpm", "e17ef246a7ff876ec90e68792a39708979416004d4eacfe8a7643206b9470773"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.4", "29563475afa9b8a2add1b7a9c8fb68d06ca7737648f28398e04461f008b69521", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f4ed47ecda66de70dd817698a703f8816daa91272e7e45812469498614ae8b29"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, From 49ca325845facc139209ef0bd2c93584d11dceb2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Mar 2024 13:27:11 +0100 Subject: [PATCH 0496/1302] make-binaries: Bump to OTP 26.2.3, Elixir 1.16.2, Expat 2.6.2 --- tools/make-binaries | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 0c63b7c16..6b2e9fec6 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,12 +67,12 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.26.0' termcap_vsn='1.3.1' -expat_vsn='2.6.0' +expat_vsn='2.6.2' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.2.1' -otp_vsn='26.2.2' -elixir_vsn='1.16.1' +otp_vsn='26.2.3' +elixir_vsn='1.16.2' pam_vsn='1.5.2' png_vsn='1.6.42' jpeg_vsn='9f' From a1b76ac0a93aeccb70b7c58962f4a67993e2f82b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Mar 2024 13:31:33 +0100 Subject: [PATCH 0497/1302] Container: Update to Elixir 1.16.2 --- .github/container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 24029d2b6..f9a97e0af 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,7 +1,7 @@ #' Define default build variables ## specifc ARGs for METHOD='direct' ARG OTP_VSN='26.2' -ARG ELIXIR_VSN='1.16.1' +ARG ELIXIR_VSN='1.16.2' ## specifc ARGs for METHOD='package' ARG ALPINE_VSN='3.19' ## general ARGs From 50c74ab82115bfd4f2bb5f221970722291676b17 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Mar 2024 20:49:08 +0100 Subject: [PATCH 0498/1302] Fix code:lib_dir call to work with Erlang/OTP 27.0-rc2 --- src/ejabberd.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 54506e7a3..ff62e3b47 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -160,11 +160,11 @@ exit_or_halt(Reason, StartFlag) -> get_module_file(App, Mod) -> BaseName = atom_to_list(Mod), - case code:lib_dir(App, ebin) of + case code:lib_dir(App) of {error, _} -> BaseName; Dir -> - filename:join([Dir, BaseName ++ ".beam"]) + filename:join([Dir, "ebin", BaseName ++ ".beam"]) end. module_name([Dir, _, <> | _] = Mod) when H >= 65, H =< 90 -> From f50b5e1a584ee2e3e1bcf24b967ded1489f09968 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 1 Apr 2024 14:07:51 +0200 Subject: [PATCH 0499/1302] Fix typo in elixir code that breaks 537aac2 when using mix --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 3f3845cf7..96d74691b 100644 --- a/mix.exs +++ b/mix.exs @@ -84,7 +84,7 @@ defmodule Ejabberd.MixProject do try do {:ok, concrete} = :dialyzer_utils.get_core_from_beam(:code.which(module)) {:ok, types} = :dialyzer_utils.get_record_and_type_info(concrete) - if Maps.has_key(types, typeDef) do + if Map.has_key?(types, typeDef) do okResult else [] From dbf0fb8e8610aaff79c48675dd6f600303263db5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 6 Mar 2024 12:51:46 +0100 Subject: [PATCH 0500/1302] Runtime: Run Dialyzer in Mix jobs --- .github/workflows/runtime.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index f530f4c3a..11953ad5b 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -334,13 +334,14 @@ jobs: ./autogen.sh ./configure --with-rebar=mix \ --prefix=/tmp/ejabberd \ - --enable-all \ - --disable-odbc + --enable-all mix deps.get make - run: make xref + - run: make dialyzer + - run: make edoc - name: Run rel From 735516ed379c162658ab9c000d1febb3de5e22dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 4 Apr 2024 11:05:29 +0200 Subject: [PATCH 0501/1302] Don't start sql connection pools for unknown hosts It's possible to trigger that by passing wrong domain to some ctl commands, and if you don't have default credentials for sql connections, you get repeating errors when this pools tries to reconnect from error. --- src/ejabberd_sql_sup.erl | 54 ++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index ccbc2374c..984f59f62 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -37,30 +37,36 @@ start(Host) -> case is_started(Host) of true -> ok; false -> - App = case ejabberd_option:sql_type(Host) of - mysql -> p1_mysql; - pgsql -> p1_pgsql; - sqlite -> sqlite3; - _ -> odbc - end, - ejabberd:start_app(App), - Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE), - start => {ejabberd_sql_sup, start_link, [Host]}, - restart => transient, - shutdown => infinity, - type => supervisor, - modules => [?MODULE]}, - case supervisor:start_child(ejabberd_db_sup, Spec) of - {ok, _} -> - ejabberd_sql_schema:start(Host), - ok; - {error, {already_started, Pid}} -> - %% Wait for the supervisor to fully start - _ = supervisor:count_children(Pid), - ok; - {error, Why} = Err -> - ?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]), - Err + case lists:member(Host, ejabberd_option:hosts()) of + false -> + ?WARNING_MSG("Rejecting start of sql worker for unknown host: ~ts", [Host]), + {error, invalid_host}; + true -> + App = case ejabberd_option:sql_type(Host) of + mysql -> p1_mysql; + pgsql -> p1_pgsql; + sqlite -> sqlite3; + _ -> odbc + end, + ejabberd:start_app(App), + Spec = #{id => gen_mod:get_module_proc(Host, ?MODULE), + start => {ejabberd_sql_sup, start_link, [Host]}, + restart => transient, + shutdown => infinity, + type => supervisor, + modules => [?MODULE]}, + case supervisor:start_child(ejabberd_db_sup, Spec) of + {ok, _} -> + ejabberd_sql_schema:start(Host), + ok; + {error, {already_started, Pid}} -> + %% Wait for the supervisor to fully start + _ = supervisor:count_children(Pid), + ok; + {error, Why} = Err -> + ?ERROR_MSG("Failed to start ~ts: ~p", [?MODULE, Why]), + Err + end end end. From 7c76f2b764723f92b2ffb596f8b5d65311162f62 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Apr 2024 13:32:32 +0200 Subject: [PATCH 0502/1302] Handle the "approved" attribute. As feature isn't implemented, discard it (#4188) Reference: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-items-approved Additionally, when roster contains unknown attribute, discard it and show a warning --- src/prosody2ejabberd.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 2ed6ea8e5..193314f50 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -322,8 +322,13 @@ convert_roster_item(LUser, LServer, JIDstring, LuaList) -> [R#roster{name = Name}]; ({<<"persist">>, false}, _) -> []; - (_, []) -> - [] + ({<<"approved">>, _}, [R]) -> + [R]; + (A, [R]) -> + io:format("Warning: roster of user ~ts@~ts includes unknown " + "attribute:~n ~p~nand that one is discarded.~n", + [LUser, LServer, A]), + [R] end, [InitR], LuaList) catch _:{bad_jid, _} -> [] From d6d8bce0e497f41105256c543530054ccdf69f9a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 3 Apr 2024 21:24:39 +0200 Subject: [PATCH 0503/1302] If INET_DIST_INTERFACE is IPv6, set required option (thanks to Stu Tomlinson)(#4189) --- .github/container/ejabberdctl.template | 3 +++ ejabberdctl.template | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index e7e112ffa..2d64e135e 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -79,6 +79,9 @@ fi if [ -n "$INET_DIST_INTERFACE" ] ; then INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then + if [ $(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l) -eq 7 ] ; then + INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" + fi ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi fi diff --git a/ejabberdctl.template b/ejabberdctl.template index f558e93b2..28c31c054 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -78,6 +78,9 @@ fi if [ -n "$INET_DIST_INTERFACE" ] ; then INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then + if [ $(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l) -eq 7 ] ; then + INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" + fi ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" fi fi From 426e33d3a67302518adb9b778dca448ff71166c9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Apr 2024 15:53:25 +0200 Subject: [PATCH 0504/1302] Fix previous commit: shellcheck reported a warning --- .github/container/ejabberdctl.template | 2 +- ejabberdctl.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 2d64e135e..fdfbedf10 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -79,7 +79,7 @@ fi if [ -n "$INET_DIST_INTERFACE" ] ; then INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then - if [ $(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l) -eq 7 ] ; then + if [ "$(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l)" -eq 7 ] ; then INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" fi ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" diff --git a/ejabberdctl.template b/ejabberdctl.template index 28c31c054..4eea168fb 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -78,7 +78,7 @@ fi if [ -n "$INET_DIST_INTERFACE" ] ; then INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) if [ -n "$INET_DIST_INTERFACE2" ] ; then - if [ $(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l) -eq 7 ] ; then + if [ "$(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l)" -eq 7 ] ; then INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp" fi ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" From 61ad808d26987a14dac9679bb76e3c896c38ce7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 11 Apr 2024 12:15:30 +0200 Subject: [PATCH 0505/1302] Fix validation of user field in get_user_subscriptions command I made it test for valid muc room, instead of just valid username. This should fix issue #4197 --- src/mod_muc_admin.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index d15138605..732644873 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -564,7 +564,7 @@ get_user_rooms(User, Server) -> end, ejabberd_option:hosts()). get_user_subscriptions(User, Server) -> - User2 = validate_muc(User, <<"user">>), + User2 = validate_user(User, <<"user">>), Server2 = validate_host(Server, <<"host">>), Services = find_services(global), UserJid = jid:make(User2, Server2), @@ -1582,6 +1582,15 @@ validate_host(Name, ArgName) -> end end. +-spec validate_user(Name :: binary(), ArgName::binary()) -> binary(). +validate_user(Name, ArgName) -> + case jid:nodeprep(Name) of + error -> + throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + Name2 -> + Name2 + end. + -spec validate_muc(Name :: binary(), ArgName::binary()) -> binary(). validate_muc(Name, ArgName) -> case jid:nameprep(Name) of From ef5a435480c31934ee01690fc55b4ef81c3a8e43 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Apr 2024 11:17:34 +0200 Subject: [PATCH 0506/1302] sql_server option: Mention in what version it was last updated --- src/ejabberd_options_doc.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 8d5394441..23b489084 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1379,6 +1379,7 @@ doc() -> "or 'ram' if the latter is not set.")}}, {sql_server, #{value => ?T("Host"), + note => "improved in 23.04", desc => ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " "'mssql' or 'odbc' this can also be an ODBC connection string. " From ba0be28d455523330438d93ed7b798f0a1f8b7a3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Apr 2024 13:50:06 +0200 Subject: [PATCH 0507/1302] CONTAINER.md: live in podman doesn't strictly require EJABBERD_BYPASS_WARNINGS --- CONTAINER.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTAINER.md b/CONTAINER.md index 1ea18519c..a277360a9 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -299,7 +299,7 @@ docker buildx build \ It's also possible to use podman instead of docker, just notice: - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile - It mentions that `healthcheck` is not supported by the Open Container Initiative image format -- If you want to start with command `live`, add environment variable `EJABBERD_BYPASS_WARNINGS=true` +- to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true` ```bash podman build \ -t ejabberd \ @@ -313,6 +313,8 @@ podman exec eja1 ejabberdctl status podman exec -it eja1 sh podman stop eja1 + +podman run --name eja1 -it -e EJABBERD_BYPASS_WARNINGS=true -p 5222:5222 localhost/ejabberd live ``` ### Package build for `arm64` From b6a0c7e57f7e45b756833754400ec754de81242a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Apr 2024 18:24:54 +0200 Subject: [PATCH 0508/1302] mod_configure: Retract 'Get User Password' command to update XEP-0133 1.3.0 https://github.com/xsf/xeps/pull/1314 --- src/mod_configure.erl | 44 +++++-------------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/src/mod_configure.erl b/src/mod_configure.erl index f73471e4c..b5f45b532 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 133, '1.1'}). +-protocol({xep, 133, '1.3.0', '13.10', "complete", ""}). -behaviour(gen_mod). @@ -133,8 +133,6 @@ get_local_identity(Acc, _From, _To, Node, Lang) -> ?INFO_COMMAND(?T("Delete User"), Lang); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_COMMAND(?T("End User Session"), Lang); - ?NS_ADMINL(<<"get-user-password">>) -> - ?INFO_COMMAND(?T("Get User Password"), Lang); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_COMMAND(?T("Change User Password"), Lang); ?NS_ADMINL(<<"get-user-lastlogin">>) -> @@ -219,8 +217,6 @@ get_local_features(Acc, From, ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"end-user-session">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); - ?NS_ADMINL(<<"get-user-password">>) -> - ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"change-user-password">>) -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); ?NS_ADMINL(<<"get-user-lastlogin">>) -> @@ -447,8 +443,6 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To, ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"end-user-session">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); - ?NS_ADMINL(<<"get-user-password">>) -> - ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"change-user-password">>) -> ?ITEMS_RESULT(Allow, LNode, {error, Err}); ?NS_ADMINL(<<"get-user-lastlogin">>) -> @@ -490,8 +484,6 @@ get_local_items(_Host, [<<"user">>], Server, Lang) -> (?NS_ADMINX(<<"delete-user">>))), ?NODE(?T("End User Session"), (?NS_ADMINX(<<"end-user-session">>))), - ?NODE(?T("Get User Password"), - (?NS_ADMINX(<<"get-user-password">>))), ?NODE(?T("Change User Password"), (?NS_ADMINX(<<"change-user-password">>))), ?NODE(?T("Get User Last Login Time"), @@ -1009,16 +1001,6 @@ get_form(_Host, ?NS_ADMINL(<<"end-user-session">>), label = tr(Lang, ?T("Jabber ID")), required = true, var = <<"accountjid">>}]}}; -get_form(_Host, ?NS_ADMINL(<<"get-user-password">>), - Lang) -> - {result, - #xdata{title = tr(Lang, ?T("Get User Password")), - type = form, - fields = [?HFIELD(), - #xdata_field{type = 'jid-single', - label = tr(Lang, ?T("Jabber ID")), - var = <<"accountjid">>, - required = true}]}}; get_form(_Host, ?NS_ADMINL(<<"change-user-password">>), Lang) -> {result, @@ -1324,23 +1306,6 @@ set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>), ejabberd_sm:kick_user(JID#jid.luser, JID#jid.lserver, R) end, {result, undefined}; -set_form(From, Host, - ?NS_ADMINL(<<"get-user-password">>), Lang, XData) -> - AccountString = get_value(<<"accountjid">>, XData), - JID = jid:decode(AccountString), - User = JID#jid.luser, - Server = JID#jid.lserver, - true = Server == Host orelse - get_permission_level(From) == global, - Password = ejabberd_auth:get_password(User, Server), - true = is_binary(Password), - {result, - #xdata{type = form, - fields = [?HFIELD(), - ?XFIELD('jid-single', ?T("Jabber ID"), - <<"accountjid">>, AccountString), - ?XFIELD('text-single', ?T("Password"), - <<"password">>, Password)]}}; set_form(From, Host, ?NS_ADMINL(<<"change-user-password">>), _Lang, XData) -> AccountString = get_value(<<"accountjid">>, XData), @@ -1570,6 +1535,7 @@ mod_options(_) -> []. mod_doc() -> #{desc => ?T("The module provides server configuration functionality via " - "https://xmpp.org/extensions/xep-0050.html" - "[XEP-0050: Ad-Hoc Commands]. This module requires " - "_`mod_adhoc`_ to be loaded.")}. + "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " + "Implements many commands as defined in " + "https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]. " + "This module requires _`mod_adhoc`_ to be loaded.")}. From ed6762b8ee6467e5cb1eaba81ca4ad0a91485fd5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Apr 2024 11:31:55 +0200 Subject: [PATCH 0509/1302] Don't name integer and string results in API examples (#4198) Since ejabberd 24.02, mod_http_api does not name any results, previously integer and strings were named, but other results were not. And ejabberdctl obviously does not name results either. Only ejabberd_xmlrpc does. Consequently, the documentation should not name results in the examples. --- src/ejabberd_commands_doc.erl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 7d59bcc78..646eafe90 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -247,13 +247,8 @@ json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput) -> {200, [?STR(Text1)]}; {{_, restuple}, {_, Text2}} -> {500, [?STR(Text2)]}; - {{_, {list, _}}, _} -> - {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; - {{_, {tuple, _}}, _} -> - {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)}; - {{Name0, _}, _} -> - {200, [Indent, ?OP_L("{"), ?STR_A(Name0), ?OP_L(": "), - json_gen(ResultDesc, Result, Indent, HTMLOutput), ?OP_L("}")]} + {{_, _}, _} -> + {200, json_gen(ResultDesc, Result, Indent, HTMLOutput)} end, CodeStr = case Code of 200 -> <<" 200 OK">>; From a29d54c4973f34e6eae9f4e3cd0fe8f0690536bf Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Apr 2024 12:15:41 +0200 Subject: [PATCH 0510/1302] Comment ERTS_VSN variable in ejabberdctl when not used (#4194) That variable is not used in "make relive" and "make install", so let's comment the variable in ejabberdctl when preparing that file for those targets. --- Makefile.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.in b/Makefile.in index 7073e46d5..103fa38a7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -396,6 +396,7 @@ ejabberdctl.relive: -e "s*{{spool_dir}}*${SPOOL_DIR}*g" \ -e "s*{{bindir}}*${BINDIR}*g" \ -e "s*{{libdir}}*${relivelibdir}${ELIXIR_LIBDIR}*g" \ + -e "s*ERTS_VSN*# ERTS_VSN*g" \ -e "s*{{iexpath}}*${IEX}*g" \ -e "s*{{erl}}*${ERL}*g" \ -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ @@ -420,6 +421,7 @@ ejabberdctl.example: vars.config -e "s*{{spool_dir}}*${SPOOLDIR}*g" \ -e "s*{{bindir}}*${BINDIR}*g" \ -e "s*{{libdir}}*${LIBDIR}${ELIXIR_LIBDIR}*g" \ + -e "s*ERTS_VSN*# ERTS_VSN*g" \ -e "s*{{iexpath}}*${IEX}*g" \ -e "s*{{erl}}*${ERL}*g" \ -e "s*{{epmd}}*${EPMD}*g" ejabberdctl.template \ From 4fd83631ec2fd2284dff37520341eb29667ad9ae Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Apr 2024 16:37:47 +0200 Subject: [PATCH 0511/1302] Mention in what ejabberd version was added update_sql_schema option --- src/ejabberd_options_doc.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 23b489084..b005111a4 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -924,6 +924,7 @@ doc() -> [binary:part(ejabberd_config:version(), {0,5})]}}}, {update_sql_schema, #{value => "true | false", + note => "added in 23.10", desc => ?T("Allow ejabberd to update SQL schema. " "The default value is 'true'.")}}, From 051bf2968a2f3d1f414b049daecaf1e959ff3b9a Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Apr 2024 17:52:42 +0200 Subject: [PATCH 0512/1302] Runtime: Test 27.0-rc1 instead of 27, as port_compiler still fails in rc2 --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 11953ad5b..02511156d 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20', '25', '26', '27'] + otp: ['20', '25', '26', '27.0-rc1'] rebar: ['rebar', 'rebar3'] runs-on: ubuntu-22.04 container: From 0fdf58b1ecb152b26b894c21735d86edcdc37845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 17 Apr 2024 14:01:21 +0200 Subject: [PATCH 0513/1302] Add ability to use additional custom errors when parsing options --- src/econf.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/econf.erl b/src/econf.erl index 595c24bac..8501356e9 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -197,6 +197,11 @@ format_error(eimp_error) -> format("ejabberd is built without image converter support", []); format_error({mqtt_codec, Reason}) -> mqtt_codec:format_error(Reason); +format_error({external_module_error, Module, Error}) -> + try Module:format_error(Error) + catch _:_ -> + format("Invalid value", []) + end; format_error(Reason) -> yconf:format_error(Reason). From 22f0d44fede6ce3fcf5e9f2ab78efc3d143cdb54 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 10 Apr 2024 16:59:08 +0200 Subject: [PATCH 0514/1302] make-binaries: Bump Linux-PAM to 1.6.1 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 6b2e9fec6..eb4752ecd 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -73,7 +73,7 @@ yaml_vsn='0.2.5' ssl_vsn='3.2.1' otp_vsn='26.2.3' elixir_vsn='1.16.2' -pam_vsn='1.5.2' +pam_vsn='1.6.1' png_vsn='1.6.42' jpeg_vsn='9f' webp_vsn='1.3.2' From 017f609a516548b1a4a785313419219b20705a39 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 10 Apr 2024 17:40:04 +0200 Subject: [PATCH 0515/1302] make-binaries: Bump OpenSSL to 3.3.0 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index eb4752ecd..4e5339105 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,7 +70,7 @@ termcap_vsn='1.3.1' expat_vsn='2.6.2' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.2.1' +ssl_vsn='3.3.0' otp_vsn='26.2.3' elixir_vsn='1.16.2' pam_vsn='1.6.1' From 697e5b8b6c4a40d33df1a7705df4b1a7fb1dac87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:57:41 +0000 Subject: [PATCH 0516/1302] Bump peaceiris/actions-gh-pages from 3 to 4 Bumps [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) from 3 to 4. - [Release notes](https://github.com/peaceiris/actions-gh-pages/releases) - [Changelog](https://github.com/peaceiris/actions-gh-pages/blob/main/CHANGELOG.md) - [Commits](https://github.com/peaceiris/actions-gh-pages/compare/v3...v4) --- updated-dependencies: - dependency-name: peaceiris/actions-gh-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 815780cf6..f7315f79c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -204,7 +204,7 @@ jobs: - name: Upload test logs if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: publish_dir: _build/test exclude_assets: '.github,lib,plugins' From e973360aa738f36e637ee4065e5022314c900384 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 15 Apr 2024 13:16:57 +0200 Subject: [PATCH 0517/1302] Test: Fix some minor compilation warnings - This QueryID is unnecessary since d3aeed8, due to d6e9e03 - DB variable was added in d32a0ce to detect riak and disable some tests, but support for Riak was removed later in 3f7d9e3. - MyJID was added in 5b863c2 but was never actually used - recv2 return should be checked --- test/mam_tests.erl | 2 +- test/offline_tests.erl | 4 +--- test/vcard_tests.erl | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/test/mam_tests.erl b/test/mam_tests.erl index 66af9dc8d..ed384cfa1 100644 --- a/test/mam_tests.erl +++ b/test/mam_tests.erl @@ -492,7 +492,7 @@ send_query(Config, #mam_query{xmlns = NS} = Query) -> maybe_recv_iq_result(Config, NS, I), I. -recv_fin(Config, I, QueryID, NS, IsComplete) when NS == ?NS_MAM_1; NS == ?NS_MAM_2 -> +recv_fin(Config, I, _QueryID, NS, IsComplete) when NS == ?NS_MAM_1; NS == ?NS_MAM_2 -> ct:comment("Receiving fin iq for namespace '~s'", [NS]), #iq{type = result, id = I, sub_els = [#mam_fin{xmlns = NS, diff --git a/test/offline_tests.erl b/test/offline_tests.erl index 4bd11a257..4ab366f05 100644 --- a/test/offline_tests.erl +++ b/test/offline_tests.erl @@ -142,7 +142,7 @@ unsupported_iq(Config) -> %%%=================================================================== %%% Master-slave tests %%%=================================================================== -master_slave_cases(DB) -> +master_slave_cases(_DB) -> {offline_master_slave, [sequence], [master_slave_test(flex), master_slave_test(send_all), @@ -233,8 +233,6 @@ mucsub_mam_slave(Config) -> gen_mod:update_module(Server, mod_mam, #{user_mucsub_from_muc_archive => true}), Room = suite:muc_room_jid(Config), - MyJID = my_jid(Config), - MyJIDBare = jid:remove_resource(MyJID), ok = mam_tests:set_default(Config, always), #presence{} = send_recv(Config, #presence{}), send(Config, #presence{type = unavailable}), diff --git a/test/vcard_tests.erl b/test/vcard_tests.erl index 955077460..4ee94ddcc 100644 --- a/test/vcard_tests.erl +++ b/test/vcard_tests.erl @@ -119,7 +119,7 @@ xupdate_master(Config) -> sub_els = [#vcard_xupdate{hash = ImgHash}]} = recv_presence(Config), #iq{type = result, sub_els = []} = send_recv(Config, #iq{type = set, sub_els = [#vcard_temp{}]}), - ?recv2(#presence{from = MyJID, type = available, + {_, _} = ?recv2(#presence{from = MyJID, type = available, sub_els = [#vcard_xupdate{hash = undefined}]}, #presence{from = Peer, type = unavailable}), disconnect(Config). From 9c8ae96f250698bf923f714c172ab10ff97ef8fb Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Apr 2024 12:56:21 +0200 Subject: [PATCH 0518/1302] Define base64url dependency only when using rebar2 and Erlang<24 base64url is not called directly in ejabberd code since e227940, it is only required by p1_acme and erlang-jose 1.11.1 erlang-jose 1.11.1 defines base64url dependency as an hex package, and rebar2 is not able to download it. For that reason, when compiling ejabberd with rebar2, we provide a rebar2-friendly base64url dependency definition. That way when jose is downloaded, that dependency is already available. --- .github/workflows/runtime.yml | 2 +- mix.exs | 5 ++--- rebar.config | 5 ++++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 02511156d..1255f9d63 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -193,7 +193,7 @@ jobs: run: | sed -i "s|^modules:|modules:\n 'ModPresenceDemo': {}|g" ejabberd.yml.example cat ejabberd.yml.example - sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config + sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config cat rebar.config - name: Compile diff --git a/mix.exs b/mix.exs index 96d74691b..1fe6528db 100644 --- a/mix.exs +++ b/mix.exs @@ -44,7 +44,7 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, - :base64url, :fast_tls, :fast_xml, :fast_yaml, :jiffy, :jose, + :fast_tls, :fast_xml, :fast_yaml, :jiffy, :jose, :p1_utils, :stringprep, :syntax_tools, :yconf] ++ cond_apps(), included_applications: [:mnesia, :os_mon, @@ -129,8 +129,7 @@ defmodule Ejabberd.MixProject do end defp deps do - [{:base64url, "~> 1.0"}, - {:cache_tab, "~> 1.0"}, + [{:cache_tab, "~> 1.0"}, {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, diff --git a/rebar.config b/rebar.config index bdf6c6088..f9951f4f7 100644 --- a/rebar.config +++ b/rebar.config @@ -22,7 +22,10 @@ %%% Dependencies %%% -{deps, [{base64url, ".*", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}}, +{deps, [{if_not_rebar3, + {if_version_below, "24", + {base64url, ".*", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} + }}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, {if_var_true, pam, From 64cd794c0242e66d98693e93d8e15c0afdc49b0a Mon Sep 17 00:00:00 2001 From: LeszekBlazewski Date: Fri, 19 Apr 2024 16:14:05 +0200 Subject: [PATCH 0519/1302] fix: invalid CTL_ON_CREATE usage in docker-compose example --- CONTAINER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index a277360a9..d52f4255c 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -227,7 +227,7 @@ abort start, this can be disabled by prefixing commands with `!` Example usage (or check the [full example](#customized-example)): ```yaml environment: - - CTL_ON_CREATE=\! register admin localhost asd + - CTL_ON_CREATE=! register admin localhost asd - CTL_ON_START=stats registeredusers ; check_password admin localhost asd ; status @@ -435,7 +435,7 @@ services: environment: - ERLANG_NODE_ARG=ejabberd@main - ERLANG_COOKIE=dummycookie123 - - CTL_ON_CREATE=\! register admin localhost asd + - CTL_ON_CREATE=! register admin localhost asd replica: image: ghcr.io/processone/ejabberd From 44bafa478e6da939413ba67d6b2470d27fea2bf0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 19 Apr 2024 13:47:06 +0200 Subject: [PATCH 0520/1302] New ban commands use private storage to keep ban information (#4201) --- src/mod_admin_extra.erl | 179 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 6 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 886606b02..003d2b5de 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -49,7 +49,8 @@ % Accounts set_password/3, check_password_hash/4, delete_old_users/1, - delete_old_users_vhost/2, ban_account/3, check_password/3, + delete_old_users_vhost/2, check_password/3, + ban_account/3, ban_account_v2/3, get_ban_details/2, unban_account/2, % vCard set_nickname/3, get_vcard/3, @@ -85,6 +86,7 @@ -include("mod_roster.hrl"). -include("mod_privacy.hrl"). -include("ejabberd_sm.hrl"). +-include_lib("xmpp/include/scram.hrl"). -include_lib("xmpp/include/xmpp.hrl"). %%% @@ -236,8 +238,10 @@ get_commands_spec() -> "New password for user"], result = {res, rescode}, result_example = ok}, + #ejabberd_commands{name = ban_account, tags = [accounts], desc = "Ban an account: kick sessions and set random password", + longdesc = "This simply sets a random password.", module = ?MODULE, function = ban_account, args = [{user, binary}, {host, binary}, {reason, binary}], args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>], @@ -245,6 +249,53 @@ get_commands_spec() -> "Reason for banning user"], result = {res, rescode}, result_example = ok}, + #ejabberd_commands{name = ban_account, tags = [accounts], + desc = "Ban an account", + longdesc = "This command kicks the account sessions, " + "sets a random password, and stores ban details in the " + "account private storage. " + "This command requires mod_private to be enabled. " + "Check also _`get_ban_details`_ API " + "and `_unban_account`_ API.", + module = ?MODULE, function = ban_account_v2, + version = 2, + note = "improved in 24.xx", + args = [{user, binary}, {host, binary}, {reason, binary}], + args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>], + args_desc = ["User name to ban", "Server name", + "Reason for banning user"], + result = {res, rescode}, + result_example = ok}, + #ejabberd_commands{name = get_ban_details, tags = [accounts], + desc = "Get ban details about an account", + longdesc = "Check _`ban_account`_ API.", + module = ?MODULE, function = get_ban_details, + version = 2, + note = "added in 24.xx", + args = [{user, binary}, {host, binary}], + args_example = [<<"attacker">>, <<"myserver.com">>], + args_desc = ["User name to unban", "Server name"], + result = {ban_details, {list, + {detail, {tuple, [{name, string}, + {value, string} + ]}} + }}, + result_example = [{"reason", "Spamming other users"}, + {"bandate", "2024-04-22T09:16:47.975312Z"}, + {"lastdate", "2024-04-22T08:39:12Z"}, + {"lastreason", "Connection reset by peer"}]}, + #ejabberd_commands{name = unban_account, tags = [accounts], + desc = "Revert the ban from an account: set back the old password", + longdesc = "Check _`ban_account`_ API.", + module = ?MODULE, function = unban_account, + version = 2, + note = "added in 24.xx", + args = [{user, binary}, {host, binary}], + args_example = [<<"gooduser">>, <<"myserver.com">>], + args_desc = ["User name to unban", "Server name"], + result = {res, rescode}, + result_example = ok}, + #ejabberd_commands{name = num_resources, tags = [session], desc = "Get the number of resources of a user", module = ?MODULE, function = num_resources, @@ -905,7 +956,7 @@ set_password(User, Host, Password) -> check_password(User, Host, Password) -> ejabberd_auth:check_password(User, <<>>, Host, Password). -%% Copied some code from ejabberd_commands.erl +%% Copied some code from ejabberd_commands.erln check_password_hash(User, Host, PasswordHash, HashMethod) -> AccountPass = ejabberd_auth:get_password_s(User, Host), Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end, @@ -978,7 +1029,7 @@ delete_or_not(LUser, LServer, TimeStamp_oldest) -> end. %% -%% Ban account +%% Ban account v0 ban_account(User, Host, ReasonText) -> Reason = prepare_reason(ReasonText), @@ -1014,6 +1065,119 @@ prepare_reason([Reason]) -> prepare_reason(Reason) when is_binary(Reason) -> Reason. +%% +%% Ban account v2 + +ban_account_v2(User, Host, ReasonText) -> + case is_banned(User, Host) of + true -> + account_was_already_banned; + false -> + ban_account_v2_b(User, Host, ReasonText) + end. + +ban_account_v2_b(User, Host, ReasonText) -> + Reason = prepare_reason(ReasonText), + Pass = ejabberd_auth:get_password_s(User, Host), + Last = get_last(User, Host), + BanDate = xmpp_util:encode_timestamp(erlang:timestamp()), + BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate), + ok = private_set2(User, Host, BanPrivateXml), + ok = set_random_password_v2(User, Host), + kick_sessions(User, Host, Reason), + ok. + +set_random_password_v2(User, Server) -> + NewPass = p1_rand:get_string(), + ok = ejabberd_auth:set_password(User, Server, NewPass). + +build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate) -> + PassEls = build_pass_els(Pass), + #xmlel{name = <<"banned">>, + attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}], + children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]}, + #xmlel{name = <<"password">>, attrs = [], children = PassEls}, + #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]}, + #xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]}, + #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]} + ]}. + +build_pass_els(Pass) when is_binary(Pass) -> + [{xmlcdata, Pass}]; +build_pass_els(#scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt, + hash = Hash, + iterationcount = IterationCount}) -> + [#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]}, + #xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]}, + #xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]}, + #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]}, + #xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]} + ]. + +%% +%% Get ban details + +get_ban_details(User, Host) -> + [El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>), + Reason = fxml:get_subtag_cdata(El, <<"reason">>), + LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>), + LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>), + BanDate = fxml:get_subtag_cdata(El, <<"bandate">>), + [{"reason", Reason}, + {"bandate", BanDate}, + {"lastdate", LastDate}, + {"lastreason", LastReason} + ]. + +is_banned(User, Host) -> + case lists:keyfind("bandate", 1, get_ban_details(User, Host)) of + {_, BanDate} when BanDate /= <<>> -> + true; + _ -> + false + end. + +%% +%% Unban account + +unban_account(User, Host) -> + case is_banned(User, Host) of + false -> + account_was_not_banned; + true -> + unban_account2(User, Host) + end. + +unban_account2(User, Host) -> + OldPass = get_oldpass(User, Host), + ok = ejabberd_auth:set_password(User, Host, OldPass), + UnBanPrivateXml = build_unban_xmlel(), + private_set2(User, Host, UnBanPrivateXml). + +get_oldpass(User, Host) -> + [El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>), + Pass = fxml:get_subtag(El, <<"password">>), + get_pass(Pass). + +get_pass(#xmlel{children = [{xmlcdata, Pass}]}) -> + Pass; +get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) -> + StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>), + ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>), + Salt = fxml:get_subtag_cdata(Pass, <<"salt">>), + Hash = fxml:get_subtag_cdata(Pass, <<"hash">>), + IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>), + #scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt, + hash = binary_to_existing_atom(Hash, latin1), + iterationcount = binary_to_integer(IterationCount)}. + +build_unban_xmlel() -> + #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}]}. + %%% %%% Sessions %%% @@ -1515,11 +1679,14 @@ set_last(User, Server, Timestamp, Status) -> %% Cluth private_get(Username, Host, Element, Ns) -> - ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]}, - Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host), - [{Ns, ElementXml}]), + Els = private_get2(Username, Host, Element, Ns), binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))). +private_get2(Username, Host, Element, Ns) -> + ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]}, + mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host), + [{Ns, ElementXml}]). + private_set(Username, Host, ElementString) -> case fxml_stream:parse_element(ElementString) of {error, Error} -> From ee9d4d56a95cca1ee1adc7393e08bf327d53eb15 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Apr 2024 10:46:14 +0200 Subject: [PATCH 0521/1302] Update Jose to the new 1.11.10 for Erlang/OTP > 23 --- .github/workflows/runtime.yml | 5 +++++ mix.exs | 3 ++- rebar.config | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 1255f9d63..d6c2010a7 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -329,6 +329,11 @@ jobs: sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config cat rebar.config + - name: Unlock Jose dependency on older Erlang + if: matrix.otp < 24 + run: | + mix deps.unlock jose + - name: Compile run: | ./autogen.sh diff --git a/mix.exs b/mix.exs index 1fe6528db..6fc177e3c 100644 --- a/mix.exs +++ b/mix.exs @@ -138,7 +138,6 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:jiffy, "~> 1.1.1"}, - {:jose, "~> 1.11.5"}, {:mqtree, "~> 1.0"}, {:p1_acme, "~> 1.0"}, {:p1_oauth2, "~> 0.6"}, @@ -168,6 +167,8 @@ defmodule Ejabberd.MixProject do {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, + {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, + {if_version_below(~c"24", true), {:jose, "1.11.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua) and if_version_below(~c"27", true), {:luerl, "~> 1.1.1"}}, diff --git a/rebar.config b/rebar.config index f9951f4f7..3a7c41d8c 100644 --- a/rebar.config +++ b/rebar.config @@ -44,8 +44,8 @@ {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.1.0"}}} % for R19 and below }, - {if_version_above, "20", - {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.5"}}}, + {if_version_above, "23", + {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.10"}}}, {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} }, {if_version_below, "22", From 0345f798bdbc40ec8386856021e44991aa08f1f2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Apr 2024 16:31:36 +0200 Subject: [PATCH 0522/1302] Update p1_acme to git, to support Jose 1.11.10 and Ipv6 support (#4170) --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 6fc177e3c..3589a323f 100644 --- a/mix.exs +++ b/mix.exs @@ -139,7 +139,7 @@ defmodule Ejabberd.MixProject do {:idna, "~> 6.0"}, {:jiffy, "~> 1.1.1"}, {:mqtree, "~> 1.0"}, - {:p1_acme, "~> 1.0"}, + {:p1_acme, git: "https://github.com/processone/p1_acme", branch: "master"}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, diff --git a/rebar.config b/rebar.config index 3a7c41d8c..6515f0425 100644 --- a/rebar.config +++ b/rebar.config @@ -74,7 +74,7 @@ {luerl, ".*", {git, "https://github.com/processone/luerl", {branch, "otp27"}}} }}, {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, - {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.22"}}}, + {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {branch, "master"}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.23"}}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, From 1a63443e02cc77757adca41297938ebbb1ecf8ed Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Apr 2024 12:56:21 +0200 Subject: [PATCH 0523/1302] rebar.config.script: Support relaxed dependency version (#4192) Instead of adding another element to the tuple, let's reuse the second element, which was used only by rebar2, and always has ".*" as value. --- rebar.config.script | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index 7d7fa864c..3deb6b894 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -219,20 +219,35 @@ AppendList2 = fun(Append) -> end end, -Rebar3DepsFilter = +% Convert our rich deps syntax to rebar2 format: +% https://github.com/rebar/rebar/wiki/Dependency-management +Rebar2DepsFilter = fun(DepsList, GitOnlyDeps) -> - lists:map(fun({DepName, _, {git, _, {tag, Version}}} = Dep) -> - case lists:member(DepName, GitOnlyDeps) of - true -> - Dep; - _ -> - {DepName, Version} - end; - (Dep) -> - Dep + lists:map(fun({DepName, _HexVersion, Source}) -> + {DepName, ".*", Source} end, DepsList) end, +% Convert our rich deps syntax to rebar3 version definition format: +% https://rebar3.org/docs/configuration/dependencies/#dependency-version-handling +% https://hexdocs.pm/elixir/Version.html +Rebar3DepsFilter = +fun(DepsList, GitOnlyDeps) -> + lists:map(fun({DepName, HexVersion, {git, _, {tag, GitVersion}} = Source}) -> + case {lists:member(DepName, GitOnlyDeps), HexVersion == ".*"} of + {true, _} -> + {DepName, ".*", Source}; + {false, true} -> + {DepName, GitVersion}; + {false, false} -> + {DepName, HexVersion} + end; + ({DepName, _HexVersion, Source}) -> + {DepName, ".*", Source} + end, DepsList) +end, + + DepAlts = fun("esip") -> ["esip", "p1_sip"]; ("xmpp") -> ["xmpp", "p1_xmpp"]; ("fast_xml") -> ["fast_xml", "p1_xml"]; @@ -423,6 +438,8 @@ Rules = [ ProcessRelx, [], []}, {[deps], [floating_deps], true, ProcessFloatingDeps, [], []}, + {[deps], [gitonly_deps], (not IsRebar3), + Rebar2DepsFilter, [], []}, {[deps], [gitonly_deps], IsRebar3, Rebar3DepsFilter, [], []}, {[deps], SystemDeps /= false, From 0407c566059a20dd18479d70e87107e55a63cd32 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 19 Apr 2024 02:05:57 +0200 Subject: [PATCH 0524/1302] rebar.config: Update deps version to rebar3's relaxed versioning --- rebar.config | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/rebar.config b/rebar.config index 6515f0425..e2c58cfb1 100644 --- a/rebar.config +++ b/rebar.config @@ -24,71 +24,71 @@ {deps, [{if_not_rebar3, {if_version_below, "24", - {base64url, ".*", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} + {base64url, "~> 1.0", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} }}, - {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, - {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, + {cache_tab, "~> 1.0.30", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, + {eimp, "~> 1.0.22", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, {if_var_true, pam, - {epam, ".*", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, + {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, - {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, + {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.52"}}}}, + {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.52"}}}}, {if_var_true, zlib, - {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.19"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, - {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, - {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, + {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.19"}}}, + {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, + {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, + {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_above, "19", - {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}}, - {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.1.0"}}} % for R19 and below + {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}}, + {jiffy, "1.1.0", {git, "https://github.com/davisp/jiffy", {tag, "1.1.0"}}} % for R19 and below }, {if_version_above, "23", - {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.10"}}}, - {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} + {jose, "~> 1.11.10", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.10"}}}, + {jose, "1.11.1", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}} }, {if_version_below, "22", - {lager, ".*", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}} + {lager, "~> 3.9.1", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}} }, %% Lua, rebar, OTP 20: 1.0 git tag {if_var_true, lua, {if_not_rebar3, {if_version_below, "21", - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} + {luerl, "1.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} }}}, %% Lua, rebar3, OTP 20: 1.0.0 hex package {if_var_true, lua, {if_rebar3, {if_version_below, "21", - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} + {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} }}}, %% Lua, any rebar, OTP 21-26: 1.1.1 git tag / 1.1.1 hex package {if_var_true, lua, {if_version_above, "20", {if_version_below, "27", - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.1.1"}}} + {luerl, "~> 1.1.1", {git, "https://github.com/rvirding/luerl", {tag, "1.1.1"}}} }}}, %% Lua, any rebar, OTP 27: temporary otp27 branch from fork {if_var_true, lua, {if_version_above, "26", {luerl, ".*", {git, "https://github.com/processone/luerl", {branch, "otp27"}}} }}, - {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, + {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {branch, "master"}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.23"}}}}, - {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, + {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.25"}}}}, - {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, - {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, + {p1_pgsql, "~> 1.1.15", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.25"}}}}, + {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, + {pkix, "~> 1.0.9", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, {if_var_true, sqlite, - {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, - {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, + {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, + {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.12"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.8.1"}}}, - {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} + {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.12"}}}}, + {xmpp, "~> 1.8.1", {git, "https://github.com/processone/xmpp", {tag, "1.8.1"}}}, + {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. {gitonly_deps, [ejabberd_po]}. From e9fbfaf53664127b06495feb1d92b5dbb3e4ac32 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Apr 2024 17:36:32 +0200 Subject: [PATCH 0525/1302] Update Luerl to 1.2.0 when OTP>=20, simplifies commit a09f222 --- mix.exs | 5 +---- rebar.config | 23 +++-------------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/mix.exs b/mix.exs index 3589a323f..a97b61443 100644 --- a/mix.exs +++ b/mix.exs @@ -170,10 +170,7 @@ defmodule Ejabberd.MixProject do {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, {if_version_below(~c"24", true), {:jose, "1.11.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, - {config(:lua) and if_version_below(~c"27", true), - {:luerl, "~> 1.1.1"}}, - {config(:lua) and if_version_above(~c"26", true), - {:luerl, git: "https://github.com/processone/luerl", branch: "otp27"}}, + {config(:lua), {:luerl, "~> 1.2.0"}}, {config(:mysql), {:p1_mysql, ">= 1.0.23" }}, {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, diff --git a/rebar.config b/rebar.config index e2c58cfb1..bacf4a79c 100644 --- a/rebar.config +++ b/rebar.config @@ -51,27 +51,10 @@ {if_version_below, "22", {lager, "~> 3.9.1", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}} }, - %% Lua, rebar, OTP 20: 1.0 git tag {if_var_true, lua, - {if_not_rebar3, - {if_version_below, "21", - {luerl, "1.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} - }}}, - %% Lua, rebar3, OTP 20: 1.0.0 hex package - {if_var_true, lua, - {if_rebar3, - {if_version_below, "21", - {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} - }}}, - %% Lua, any rebar, OTP 21-26: 1.1.1 git tag / 1.1.1 hex package - {if_var_true, lua, - {if_version_above, "20", {if_version_below, "27", - {luerl, "~> 1.1.1", {git, "https://github.com/rvirding/luerl", {tag, "1.1.1"}}} - }}}, - %% Lua, any rebar, OTP 27: temporary otp27 branch from fork - {if_var_true, lua, - {if_version_above, "26", - {luerl, ".*", {git, "https://github.com/processone/luerl", {branch, "otp27"}}} + {if_version_below, "21", + {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}}, + {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {branch, "master"}}}, From 6cfef4f476e79c19d6d7c93073c670ad58faf9b0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Apr 2024 16:29:11 +0200 Subject: [PATCH 0526/1302] mix.lock: Update to latest versions --- mix.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.lock b/mix.lock index c2ff98cdd..7aa1acdd4 100644 --- a/mix.lock +++ b/mix.lock @@ -8,21 +8,21 @@ "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, - "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, + "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, - "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, - "luerl": {:hex, :luerl, "1.1.1", "083518e437586f6631150d39c4bff242ed2ec80cb14a3299a0c2628f07a2ff7f", [:rebar3], [], "hexpm", "e17ef246a7ff876ec90e68792a39708979416004d4eacfe8a7643206b9470773"}, + "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, + "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:hex, :p1_acme, "1.0.22", "b40a8031ef0f4592e97e6a8e08e53dbd31a2198cb8377b249f0caea4f8025a1d", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.5", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "c2b25a7b295a435dac4f278a73d8417ff2b0020c45e1683504e8692ef03e2057"}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "2d049fa25d66f97a08b58ba8e07a751ed60ff9c4", [branch: "master"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, From da8c9f33572eb6c6ccff8f9fe71cd7b629bae575 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Apr 2024 13:45:14 +0200 Subject: [PATCH 0527/1302] rebar.lock: Track it, now that rebar3 uses loose dependency versioning This is recommended in Rebar3 documentation: [Lock files] should always be checked into source control. https://rebar3.org/docs/configuration/dependencies/#lock-files --- .gitignore | 1 - rebar.lock | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 rebar.lock diff --git a/.gitignore b/.gitignore index 4e8bc1de3..5c63117ef 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ /_build /database/ /.rebar -/rebar.lock /log/ Mnesia.nonode@nohost/ # Binaries created with tools/make-{binaries,installers,packages}: diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 000000000..a1cc16216 --- /dev/null +++ b/rebar.lock @@ -0,0 +1,87 @@ +{"1.2.0", +[{<<"base64url">>,{pkg,<<"base64url">>,<<"1.0.1">>},1}, + {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.30">>},0}, + {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.22">>},0}, + {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, + {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.52">>},0}, + {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.12">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.19">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.51">>},0}, + {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.36">>},0}, + {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.1">>},0}, + {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, + {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, + {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.16">>},0}, + {<<"p1_acme">>, + {git,"https://github.com/processone/p1_acme", + {ref,"b4d0900eabb208c493ae3958bc545151bb19b90e"}}, + 0}, + {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.23">>},0}, + {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.25">>},0}, + {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, + {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.9">>},0}, + {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.14">>},0}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.12">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.1">>},0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. +[ +{pkg_hash,[ + {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, + {<<"cache_tab">>, <<"6D35EECFB65FBE5FC85988503A27338D32DE01243F3FC8EA3EE7161AF08725A4">>}, + {<<"eimp">>, <<"FA9B376EF0B50E8455DB15C7C11DEA4522C6902E04412288AAB436D26335F6EB">>}, + {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, + {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, + {<<"esip">>, <<"A2840287C493A4280E6FBA57A257706843B025C315875E38B03FD07190E22DBA">>}, + {<<"ezlib">>, <<"FFE906BA10D03AAEE7977E1E0E81D9FFC3BB8B47FB9CD8E2E453507A2E56221F">>}, + {<<"fast_tls">>, <<"F52731A4B35259FA06CF23E2A0732920AD9EFCE7C3D68377F129A474998747BB">>}, + {<<"fast_xml">>, <<"A7F8C6942591632309099386D5C339C89997AC2BBDD1216F6C196DEE6D7828A9">>}, + {<<"fast_yaml">>, <<"65413A34A570FD4E205A460BA602E4EE7A682F35C22D2E1C839025DBF515105C">>}, + {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"jiffy">>, <<"ACA10F47AA91697BF24AB9582C74E00E8E95474C7EF9F76D4F1A338D0F5DE21B">>}, + {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, + {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, + {<<"mqtree">>, <<"F8F8B4971E4CA94313BA9BCAAA1AA1077DAABA5E3FD3468FFB491420A4CC3593">>}, + {<<"p1_mysql">>, <<"9B4E98F1F01E2CC4A759F611F0F015365E65C5F61EA453AA4BDFE950D20A5F11">>}, + {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, + {<<"p1_pgsql">>, <<"F59B7FAA457FADF0C2713AC335202F41CA1B06B7C4926925B3CB0BC6F0578601">>}, + {<<"p1_utils">>, <<"2D39B5015A567BBD2CC7033EEB93A7C60D8C84EFE1EF69A3473FAA07FA268187">>}, + {<<"pkix">>, <<"EB20B2715D71A23B4FE7E754DAE9281A964B51113D0BBA8ADF9DA72BF9D65AC2">>}, + {<<"sqlite3">>, <<"F9EA0CFF8540865FDFDB7E24EEF34DC46677364B1C070896E99B5BF08C8A7FD7">>}, + {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, + {<<"stun">>, <<"A65DF67A8AAAECB6A94D687977B2E9F161820819910CB97BBE26A3525356525B">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"134A350DBC6E2E99512FB38669191C1D1C134B3B6836F4C6740801882AFA650A">>}, + {<<"yconf">>, <<"E22998B3D7728270BDD06162A9515BD142B14FAE8927CBDBD3EF639C32AA6F7A">>}]}, +{pkg_hash_ext,[ + {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, + {<<"cache_tab">>, <<"6D8A5E00D8F84C42627706A6DBEDB02E34D58495F3ED61935C8475CA0531CDA0">>}, + {<<"eimp">>, <<"B3B9FFB1D9A5F4A2BA88AC418A819164932D9A9D3A2FC3D32CA338CE855C4392">>}, + {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, + {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, + {<<"esip">>, <<"6F00165395900500AA262CE0297162D93931C78C1464D89FD0EDC6E3D6BC011F">>}, + {<<"ezlib">>, <<"30E94355FB42260AAB6E12582CB0C56BF233515E655C8AEAF48760E7561E4EBB">>}, + {<<"fast_tls">>, <<"DB34322C8782D4C5139CCB80709D8EC8C38089B44262EDD0C2F660AC495BD389">>}, + {<<"fast_xml">>, <<"7FCE41B7D1A4BA438A2D7A088DABE74A3CA0739F1AF2ABCB77E62DAF43E0409A">>}, + {<<"fast_yaml">>, <<"1ABE8F758FC2A86B08EDFF80BBC687CFD41EBC1412CFEC0EF4A0ACFCD032052F">>}, + {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"jiffy">>, <<"62E1F0581C3C19C33A725C781DFA88410D8BFF1BBAFC3885A2552286B4785C4C">>}, + {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, + {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, + {<<"mqtree">>, <<"C87D1C95575DB65AF29B795C9DAA3BED43F5C1BF84072A74469659BCF53594EB">>}, + {<<"p1_mysql">>, <<"9CEF98265FB287374E7B64BF4022C4DF5EC66196CA31CF4BED73C67C45BF824E">>}, + {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, + {<<"p1_pgsql">>, <<"B654C1E07D3E775B626B407E7696CACFD88D17BE83E7168B9D89C3832D913DE7">>}, + {<<"p1_utils">>, <<"9219214428F2C6E5D3187FF8EB9A8783695C2427420BE9A259840E07ADA32847">>}, + {<<"pkix">>, <<"DAAB2C09CDD4EDA05C9B45A5C00E994A1A5F27634929E1377E2E59B707103E3A">>}, + {<<"sqlite3">>, <<"85054B6CA297343C159ED6794A473FF2C8EEABD854B6FE02F711C0BFD373CE86">>}, + {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, + {<<"stun">>, <<"A2055032B6D338D0454142004BCB12FAFB0C64AB1F273F1D0C6923EBBC8EDE40">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"32D4A308A6613E4E4155FA4E82ECCDC8833096D0FA8D99C8D428C800611E59D3">>}, + {<<"yconf">>, <<"7FF2AB24D3C9833842716B9AAAA01A8F96641A7695CBB701B03445C4DEF01117">>}]} +]. From 73bcabd2faea5c219a47ad3613efdb8233fd6680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 23 Apr 2024 14:33:27 +0200 Subject: [PATCH 0528/1302] Replace muc.example.com with conference.example.com in commands docs Second form matches what we use by default, and we seen some confusion between those two when people try to adopt commands from docs to real usage. --- src/mod_muc_admin.erl | 64 +++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 732644873..a0f62f145 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -98,9 +98,9 @@ get_commands_spec() -> policy = admin, module = ?MODULE, function = muc_online_rooms, args_desc = ["MUC service, or `global` for all"], - args_example = ["muc.example.com"], + args_example = ["conference.example.com"], result_desc = "List of rooms", - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, @@ -110,11 +110,11 @@ get_commands_spec() -> policy = admin, module = ?MODULE, function = muc_online_rooms_by_regex, args_desc = ["MUC service, or `global` for all", - "Regex pattern for room name"], - args_example = ["muc.example.com", "^prefix"], + "Regex pattern for room name"], + args_example = ["conference.example.com", "^prefix"], result_desc = "List of rooms with summary", - result_example = [{"room1@muc.example.com", "true", 10}, - {"room2@muc.example.com", "false", 10}], + result_example = [{"room1@conference.example.com", "true", 10}, + {"room2@conference.example.com", "false", 10}], args = [{service, binary}, {regex, binary}], args_rename = [{host, service}], result = {rooms, {list, {room, {tuple, @@ -126,7 +126,7 @@ get_commands_spec() -> desc = "Register a nick to a User JID in a MUC service", module = ?MODULE, function = muc_register_nick, args_desc = ["Nick", "User JID", "Service"], - args_example = [<<"Tim">>, <<"tim@example.org">>, <<"muc.example.org">>], + args_example = [<<"Tim">>, <<"tim@example.org">>, <<"conference.example.org">>], args = [{nick, binary}, {jid, binary}, {service, binary}], args_rename = [{host, service}], result = {res, rescode}}, @@ -134,7 +134,7 @@ get_commands_spec() -> desc = "Unregister the nick registered by that account in the MUC service", module = ?MODULE, function = muc_unregister_nick, args_desc = ["User JID", "MUC service"], - args_example = [<<"tim@example.org">>, <<"muc.example.org">>], + args_example = [<<"tim@example.org">>, <<"conference.example.org">>], args = [{jid, binary}, {service, binary}], args_rename = [{host, service}], result = {res, rescode}}, @@ -143,7 +143,7 @@ get_commands_spec() -> desc = "Create a MUC room name@service in host", module = ?MODULE, function = create_room, args_desc = ["Room name", "MUC service", "Server host"], - args_example = ["room1", "muc.example.com", "example.com"], + args_example = ["room1", "conference.example.com", "example.com"], args = [{name, binary}, {service, binary}, {host, binary}], result = {res, rescode}}, @@ -151,7 +151,7 @@ get_commands_spec() -> desc = "Destroy a MUC room", module = ?MODULE, function = destroy_room, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], args = [{name, binary}, {service, binary}], result = {res, rescode}}, #ejabberd_commands{name = create_rooms_file, tags = [muc], @@ -169,7 +169,7 @@ get_commands_spec() -> "The syntax of `subscribers` is: `JID:Nick:Node:Node2:Node3,JID:Nick:Node`.", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], - args_example = ["room1", "muc.example.com", "localhost", + args_example = ["room1", "conference.example.com", "localhost", [{"members_only","true"}, {"affiliations", "owner:bob@example.com,member:peter@example.com"}, {"subscribers", "bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages"}]], @@ -197,9 +197,9 @@ get_commands_spec() -> " The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_unused_list, args_desc = ["MUC service, or `global` for all", "Number of days"], - args_example = ["muc.example.com", 31], + args_example = ["conference.example.com", 31], result_desc = "List of unused rooms", - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}, {days, integer}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, @@ -210,9 +210,9 @@ get_commands_spec() -> " The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_unused_destroy, args_desc = ["MUC service, or `global` for all", "Number of days"], - args_example = ["muc.example.com", 31], + args_example = ["conference.example.com", 31], result_desc = "List of unused rooms that has been destroyed", - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}, {days, integer}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, @@ -222,9 +222,9 @@ get_commands_spec() -> longdesc = "The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_empty_list, args_desc = ["MUC service, or `global` for all"], - args_example = ["muc.example.com"], + args_example = ["conference.example.com"], result_desc = "List of empty rooms", - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, @@ -233,9 +233,9 @@ get_commands_spec() -> longdesc = "The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_empty_destroy, args_desc = ["MUC service, or `global` for all"], - args_example = ["muc.example.com"], + args_example = ["conference.example.com"], result_desc = "List of empty rooms that have been destroyed", - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, @@ -245,7 +245,7 @@ get_commands_spec() -> module = ?MODULE, function = get_user_rooms, args_desc = ["Username", "Server host"], args_example = ["tom", "example.com"], - result_example = ["room1@muc.example.com", "room2@muc.example.com"], + result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{user, binary}, {host, binary}], result = {rooms, {list, {room, string}}}}, #ejabberd_commands{name = get_user_subscriptions, tags = [muc, muc_sub], @@ -254,7 +254,7 @@ get_commands_spec() -> module = ?MODULE, function = get_user_subscriptions, args_desc = ["Username", "Server host"], args_example = ["tom", "example.com"], - result_example = [{"room1@muc.example.com", "Tommy", ["mucsub:config"]}], + result_example = [{"room1@conference.example.com", "Tommy", ["mucsub:config"]}], args = [{user, binary}, {host, binary}], result = {rooms, {list, @@ -270,7 +270,7 @@ get_commands_spec() -> desc = "Get the list of occupants of a MUC room", module = ?MODULE, function = get_room_occupants, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], result_desc = "The list of occupants with JID, nick and affiliation", result_example = [{"user1@example.com/psi", "User 1", "owner"}], args = [{name, binary}, {service, binary}], @@ -286,7 +286,7 @@ get_commands_spec() -> desc = "Get the number of occupants of a MUC room", module = ?MODULE, function = get_room_occupants_number, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], result_desc = "Number of room occupants", result_example = 7, args = [{name, binary}, {service, binary}], @@ -302,7 +302,7 @@ get_commands_spec() -> module = ?MODULE, function = send_direct_invitation, args_desc = ["Room name", "MUC service", "Password, or `none`", "Reason text, or `none`", "Users JIDs separated with `:` characters"], - args_example = [<<"room1">>, <<"muc.example.com">>, + args_example = [<<"room1">>, <<"conference.example.com">>, <<>>, <<"Check this out!">>, "user2@localhost:user3@example.com"], args = [{name, binary}, {service, binary}, {password, binary}, @@ -319,7 +319,7 @@ get_commands_spec() -> note = "updated in 24.02", args_desc = ["Room name", "MUC service", "Password, or `none`", "Reason text, or `none`", "List of users JIDs"], - args_example = [<<"room1">>, <<"muc.example.com">>, + args_example = [<<"room1">>, <<"conference.example.com">>, <<>>, <<"Check this out!">>, ["user2@localhost", "user3@example.com"]], args = [{name, binary}, {service, binary}, {password, binary}, @@ -330,7 +330,7 @@ get_commands_spec() -> desc = "Change an option in a MUC room", module = ?MODULE, function = change_room_option, args_desc = ["Room name", "MUC service", "Option name", "Value to assign"], - args_example = ["room1", "muc.example.com", "members_only", "true"], + args_example = ["room1", "conference.example.com", "members_only", "true"], args = [{name, binary}, {service, binary}, {option, binary}, {value, binary}], result = {res, rescode}}, @@ -338,7 +338,7 @@ get_commands_spec() -> desc = "Get options from a MUC room", module = ?MODULE, function = get_room_options, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], result_desc = "List of room options tuples with name and value", result_example = [{"members_only", "true"}], args = [{name, binary}, {service, binary}], @@ -434,7 +434,7 @@ get_commands_spec() -> desc = "List subscribers of a MUC conference", module = ?MODULE, function = get_subscribers, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], result_desc = "The list of users that are subscribed to that room", result_example = ["user2@example.com", "user3@example.com"], args = [{name, binary}, {service, binary}], @@ -443,7 +443,7 @@ get_commands_spec() -> desc = "Change an affiliation in a MUC room", module = ?MODULE, function = set_room_affiliation, args_desc = ["Room name", "MUC service", "User JID", "Affiliation to set"], - args_example = ["room1", "muc.example.com", "user2@example.com", "member"], + args_example = ["room1", "conference.example.com", "user2@example.com", "member"], args = [{name, binary}, {service, binary}, {jid, binary}, {affiliation, binary}], result = {res, rescode}}, @@ -451,7 +451,7 @@ get_commands_spec() -> desc = "Get the list of affiliations of a MUC room", module = ?MODULE, function = get_room_affiliations, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], result_desc = "The list of affiliations with username, domain, affiliation and reason", result_example = [{"user1", "example.com", member, "member"}], args = [{name, binary}, {service, binary}], @@ -467,7 +467,7 @@ get_commands_spec() -> desc = "Get affiliation of a user in MUC room", module = ?MODULE, function = get_room_affiliation, args_desc = ["Room name", "MUC service", "User JID"], - args_example = ["room1", "muc.example.com", "user1@example.com"], + args_example = ["room1", "conference.example.com", "user1@example.com"], result_desc = "Affiliation of the user", result_example = member, args = [{name, binary}, {service, binary}, {jid, binary}], @@ -477,7 +477,7 @@ get_commands_spec() -> note = "added in 23.04", module = ?MODULE, function = get_room_history, args_desc = ["Room name", "MUC service"], - args_example = ["room1", "muc.example.com"], + args_example = ["room1", "conference.example.com"], args = [{name, binary}, {service, binary}], result = {history, {list, {entry, {tuple, From 832588f861c121be2535039ba22109c0222c9cbc Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Apr 2024 16:15:34 +0200 Subject: [PATCH 0529/1302] mix.lock: Update to get p1_acme fix in ACME IPv6 --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 7aa1acdd4..fb8dc7fac 100644 --- a/mix.lock +++ b/mix.lock @@ -22,7 +22,7 @@ "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "2d049fa25d66f97a08b58ba8e07a751ed60ff9c4", [branch: "master"]}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "b4d0900eabb208c493ae3958bc545151bb19b90e", [branch: "master"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, From a550a1c55cba809268db975d310dea522c65222f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Mar 2024 17:50:08 +0100 Subject: [PATCH 0530/1302] Fix documentation links to new URLs generated by MkDocs --- src/mod_conversejs.erl | 4 ++-- src/mod_host_meta.erl | 2 +- src/mod_http_api.erl | 2 +- src/mod_http_upload.erl | 2 +- src/mod_register_web.erl | 2 +- src/mod_sip.erl | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index a5682e50a..3ae7d9747 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -256,9 +256,9 @@ mod_doc() -> ?T("Several options were improved in ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers]."), "", + "http://../listen-options/#request_handlers[request_handlers]."), "", ?T("Make sure either 'mod_bosh' or 'ejabberd_http_ws' " - "http://../listen-options/#request-handlers[request_handlers] " + "http://../listen-options/#request_handlers[request_handlers] " "are enabled."), "", ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " "by default they point to the public Converse client.") diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index e1f7f141b..43efdcf9e 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -214,7 +214,7 @@ mod_doc() -> ?T("This module is available since ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers]."), "", + "http://../listen-options/#request_handlers[request_handlers]."), "", ?T("Notice it only works if ejabberd_http has tls enabled.")], example => ["listen:", diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 50efb2859..77055eafa 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -538,7 +538,7 @@ mod_doc() -> "commands using JSON data."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers]."), "", + "http://../listen-options/#request_handlers[request_handlers]."), "", ?T("To use a specific API version N, when defining the URL path " "in the request_handlers, add a 'vN'. " "For example: '/api/v2: mod_http_api'"), "", diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 036be03f0..fba6cf7cd 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -234,7 +234,7 @@ mod_doc() -> "another URL from which that file can later be downloaded."), "", ?T("In order to use this module, it must be enabled " "in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers].")], + "http://../listen-options/#request_handlers[request_handlers].")], opts => [{host, #{desc => ?T("Deprecated. Use 'hosts' instead.")}}, diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 0a84e170c..f50b6f2f3 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -625,7 +625,7 @@ mod_doc() -> "important to include the last / character in the URL, " "otherwise the subpages URL will be incorrect."), "", ?T("This module is enabled in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers], " + "http://../listen-options/#request_handlers[request_handlers], " "no need to enable in 'modules'."), ?T("The module depends on _`mod_register`_ where all the " "configuration is performed.")], diff --git a/src/mod_sip.erl b/src/mod_sip.erl index cc9e62744..16390ff72 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -383,7 +383,7 @@ mod_doc() -> ?T("NOTE: It is not enough to just load this module. " "You should also configure listeners and DNS records " "properly. For details see the section about the " - "http://../listen/#ejabberd-sip[ejabberd_sip] listen module " + "http://../listen/#ejabberd_sip[ejabberd_sip] listen module " "in the ejabberd Documentation.")], opts => [{always_record_route, From 68c0c4d94ff2e91c25b48afb5f66c3e1cc1ea34f Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Mar 2024 10:40:27 +0100 Subject: [PATCH 0531/1302] Update doc headers to MkDocs and mention ejabberd version used --- src/ejabberd_commands_doc.erl | 12 ++++++------ src/ejabberd_doc.erl | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 646eafe90..485e7f68e 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -492,9 +492,9 @@ generate_md_output(File, RegExp, Languages, Cmds) -> end, Cmds2), Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), - Header = <<"---\ntitle: Administration API reference\ntoc: true\nmenu: API Reference\norder: 1\n" - "// Autogenerated with 'ejabberdctl gen_markdown_doc_for_commands'\n---\n\n" - "This section describes API of ejabberd.\n">>, + Version = ejabberd_config:version(), + Header = <<"# Administration API reference\n\n" + "This section describes API of ejabberd ", Version/binary, ".\n\n">>, Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), {ok, Fh} = file:open(File, [write]), io:format(Fh, "~ts~ts", [Header, Out]), @@ -502,9 +502,9 @@ generate_md_output(File, RegExp, Languages, Cmds) -> ok. generate_tags_md(File) -> - Header = <<"---\ntitle: API Tags\ntoc: true\nmenu: API Tags\norder: 2\n" - "// Autogenerated with 'ejabberdctl gen_markdown_doc_for_tags'\n---\n\n" - "This section enumerates the tags and their associated API.\n">>, + Version = ejabberd_config:version(), + Header = <<"# API Tags\n\n" + "This section enumerates the API tags of ejabberd ", Version/binary, ".\n\n">>, Tags = make_tags(false), {ok, Fh} = file:open(File, [write]), io:format(Fh, "~ts~ts", [Header, Tags]), diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index c5398350f..3ea95f91b 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -72,10 +72,11 @@ man(Lang) -> catch _:undef -> [] end end, ejabberd_config:callback_modules(all)), + Version = ejabberd_config:version(), Options = ["TOP LEVEL OPTIONS", "-----------------", - tr(Lang, ?T("This section describes top level options of ejabberd.")), + tr(Lang, <<"This section describes top level options of ejabberd ", Version/binary, ".">>), io_lib:nl()] ++ lists:flatmap( fun(Opt) -> @@ -95,7 +96,7 @@ man(Lang) -> "MODULES", "-------", "[[modules]]", - tr(Lang, ?T("This section describes options of all ejabberd modules.")), + tr(Lang, <<"This section describes modules options of ejabberd ", Version/binary, ".">>), io_lib:nl()] ++ lists:flatmap( fun({M, Descr, DocOpts, Backends, Example}) -> @@ -114,7 +115,7 @@ man(Lang) -> "LISTENERS", "-------", "[[listeners]]", - tr(Lang, ?T("This section describes options of all ejabberd listeners.")), + tr(Lang, <<"This section describes listeners options of ejabberd ", Version/binary, ".">>), io_lib:nl(), "TODO"], AsciiData = From 15e3add909f09234ce2ba3e4050298f8dd22b1a9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Mar 2024 11:35:49 +0100 Subject: [PATCH 0532/1302] CHANGELOG.md: Fix markdown syntax to work with MkDocs --- CHANGELOG.md | 262 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 395cfeebb..c5083cb36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Version 24.02 -Core: +#### Core: + - Added Matrix gateway in `mod_matrix_gw` - Support SASL2 and Bind2 - Support tls-server-end-point channel binding and sasl2 codec @@ -11,7 +12,8 @@ Core: - [`negotiation_timeout`](https://docs.ejabberd.im/admin/configuration/toplevel/#negotiation-timeout): Increase default value from 30s to 2m - mod_carboncopy: Teach how to interact with bind2 inline requests -Other: +#### Other: + - ejabberdctl: Fix startup problem when having set `EJABBERD_OPTS` and logger options - ejabberdctl: Set EJABBERD_OPTS back to `""`, and use previous flags as example - eldap: Change logic for `eldap tls_verify=soft` and `false` @@ -24,20 +26,23 @@ Other: - mod_pubsub: PEP nodetree: Fix reversed logic in node fixup function - mod_pubsub: Only care about PEP bookmarks options when creating node from scratch -SQL: +#### SQL: + - MySQL: Support `sha256_password` auth plugin - ejabberd_sql_schema: Use the first unique index as a primary key - Update SQL schema files for MAM's XEP-0424 - New option [`sql_flags`](https://docs.ejabberd.im/admin/configuration/toplevel/#sql-flags): right now only useful to enable `mysql_alternative_upsert` -Installers and Container: +#### Installers and Container: + - Container: Add ability to ignore failures in execution of `CTL_ON_*` commands - Container: Update to Erlang/OTP 26.2, Elixir 1.16.1 and Alpine 3.19 - Container: Update this custom ejabberdctl to match the main one - make-binaries: Bump OpenSSL 3.2.1, Erlang/OTP 26.2.2, Elixir 1.16.1 - make-binaries: Bump many dependency versions -Commands API: +#### Commands API: + - `print_sql_schema`: New command available in ejabberdctl command-line script - ejabberdctl: Rework temporary node name generation - ejabberdctl: Print argument description, examples and note in help @@ -51,7 +56,7 @@ Commands API: - ejabberd_xmlrpc: Fix support for restuple error response - mod_http_api: When no specific API version is requested, use the latest -Compilation with Rebar3/Elixir/Mix: +#### Compilation with Rebar3/Elixir/Mix: - Fix compilation with Erlang/OTP 27: don't use the reserved word 'maybe' - configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135)) - Add observer and runtime_tools in releases when `--enable-tools` @@ -82,7 +87,8 @@ Compilation with Rebar3/Elixir/Mix: ## Version 23.10 -Compilation: +#### Compilation: + - Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum - CI: Update tests to Erlang/OTP 26 and recent Elixir - Move Xref and Dialyzer options from workflows to `rebar.config` @@ -95,7 +101,8 @@ Compilation: - Elixir: When building OTP release with mix, keep `ERLANG_NODE=ejabberd@localhost` - `ejabberdctl`: Pass `ERLANG_OPTS` when calling `erl` to parse the `INET_DIST_INTERFACE` ([#4066](https://github.com/processone/ejabberd/issues/#4066) -Commands: +#### Commands: + - `create_room_with_opts`: Fix typo and move examples to `args_example` ([#4080](https://github.com/processone/ejabberd/issues/#4080)) - `etop`: Let `ejabberdctl etop` work in a release (if `observer` application is available) - `get_roster`: Command now returns groups in a list instead of newlines ([#4088](https://github.com/processone/ejabberd/issues/#4088)) @@ -105,7 +112,8 @@ Commands: - `ejabberdctl`: Support `policy=user` in the help and return proper arguments - `ejabberdctl`: Document how to stop a debug shell: control+g -Container: +#### Container: + - Dockerfile: Add missing dependency for mssql databases - Dockerfile: Reorder stages and steps for consistency - Dockerfile: Use Alpine as base for `METHOD=package` @@ -113,7 +121,8 @@ Container: - Dockerfile: Provide specific OTP and elixir vsn for direct compilation - Halt ejabberd if a command in `CTL_ON_` fails during ejabberd startup -Core: +#### Core: + - `auth_external_user_exists_check`: New option ([#3377](https://github.com/processone/ejabberd/issues/#3377)) - `gen_mod`: Extend `gen_mod` API to simplify hooks and IQ handlers registration - `gen_mod`: Add shorter forms for `gen_mod` hook/`iq_handler` API @@ -128,14 +137,15 @@ Core: - Web Admin: In roster page move the `AddJID` textbox to top ([#4067](https://github.com/processone/ejabberd/issues/#4067)) - Web Admin: Show a warning when visiting webadmin with non-privileged account ([#4089](https://github.com/processone/ejabberd/issues/#4089)) -Docs: +#### Docs: + - Example configuration: clarify 5223 tls options; specify s2s shaper - Make sure that `policy=user` commands have `host` instead of `server` arg in docs - Improve syntax of many command descriptions for the Docs site - Move example Perl extauth script from ejabberd git to Docs site - Remove obsolete example files, and add link in Docs to the archived copies -Installers (`make-binaries`): +#### Installers (`make-binaries`): - Bump Erlang/OTP version to 26.1.1, and other dependencies - Remove outdated workaround - Don't build Linux-PAM examples @@ -147,7 +157,8 @@ Installers (`make-binaries`): - Set kernel version for all builds - Let curl fail on HTTP errors -Modules: +#### Modules: + - `mod_muc_log`: Add trailing backslash to URLs shown in disco info - `mod_muc_occupantid`: New module with support for XEP-0421 Occupant Id ([#3397](https://github.com/processone/ejabberd/issues/#3397)) - `mod_muc_rtbl`: Better error handling in ([#4050](https://github.com/processone/ejabberd/issues/#4050)) @@ -165,7 +176,8 @@ Modules: - `mod_register_web`: Make redirect to page that end with `/` ([#3177](https://github.com/processone/ejabberd/issues/#3177)) - `mod_shared_roster_ldap`: Don't crash in `get_member_jid` on empty output ([#3614](https://github.com/processone/ejabberd/issues/#3614)) -MUC: +#### MUC: + - Add support to register nick in a room ([#3455](https://github.com/processone/ejabberd/issues/#3455)) - Convert `allow_private_message` MUC room option to `allowpm` ([#3736](https://github.com/processone/ejabberd/issues/#3736)) - Update xmpp version to send `roomconfig_changesubject` in disco#info ([#4085](https://github.com/processone/ejabberd/issues/#4085)) @@ -178,7 +190,8 @@ MUC: - Remove existing role information for users that are kicked from room ([#4035](https://github.com/processone/ejabberd/issues/#4035)) - Expand rule "mucsub subscribers are members in members only rooms" to more places -SQL: +#### SQL: + - Add ability to force alternative upsert implementation in mysql - Properly parse mysql version even if it doesn't have type tag - Use prepared statement with mysql @@ -190,7 +203,8 @@ SQL: ## Version 23.04 -General: +#### General: + - New `s2s_out_bounce_packet` hook - Re-allow anonymous connection for connection without client certificates ([#3985](https://github.com/processone/ejabberd/issues/3985)) - Stop `ejabberd_system_monitor` before stopping node @@ -203,13 +217,15 @@ General: - `mod_pubsub`: Pubsub xdata fields `max_item/item_expira/children_max` use `max` not `infinity` - `mod_vcard_xupdate`: Invalidate `vcard_xupdate` cache on all nodes when vcard is updated -Admin: +#### Admin: + - `ext_mod`: Improve support for loading `*.so` files from `ext_mod` dependencies - Improve output in `gen_html_doc_for_commands` command - Fix ejabberdctl output formatting ([#3979](https://github.com/processone/ejabberd/issues/3979)) - Log HTTP handler exceptions -MUC: +#### MUC: + - New command `get_room_history` - Persist `none` role for outcasts - Try to populate room history from mam when unhibernating @@ -218,7 +234,8 @@ MUC: - Store state in db in `mod_muc:create_room()` - Make subscribers members by default -SQL schemas: +#### SQL schemas: + - Fix a long standing bug in new schema migration - `update_sql` command: Many improvements in new schema migration - `update_sql` command: Add support to migrate MySQL too @@ -227,14 +244,16 @@ SQL schemas: - Remove unnecessary indexes - New SQL schema migrate fix -MS SQL: +#### MS SQL: + - MS SQL schema fixes - Add `new` schema for MS SQL - Add MS SQL support for new schema migration - Minor MS SQL improvements - Fix MS SQL error caused by `ORDER BY` in subquery -SQL Tests: +#### SQL Tests: + - Add support for running tests on MS SQL - Add ability to run tests on upgraded DB - Un-deprecate `ejabberd_config:set_option/2` @@ -242,7 +261,8 @@ SQL Tests: - Correct README for creating test docker MS SQL DB - Fix TSQLlint warnings in MSSQL test script -Testing: +#### Testing: + - Fix Shellcheck warnings in shell scripts - Fix Remark-lint warnings - Fix Prospector and Pylint warnings in test `extauth.py` @@ -250,14 +270,14 @@ Testing: - Test only with oldest OTP supported (20.0), newest stable (25.3) and bleeding edge (26.0-rc2) - Upload Common Test logs as artifact in case of failure -`ecs` container image: +#### `ecs` container image: - Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 - Add `tini` as runtime init - Set `ERLANG_NODE` fixed to `ejabberd@localhost` - Upload images as artifacts to Github Actions - Publish tag images automatically to ghcr.io -`ejabberd` container image: +#### `ejabberd` container image: - Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14 - Add `METHOD` to build container using packages ([#3983](https://github.com/processone/ejabberd/issues/3983)) - Add `tini` as runtime init @@ -267,14 +287,16 @@ Testing: - Expose only `HOME` volume, it contains all the required subdirs - ejabberdctl: Don't use `.../releases/COOKIE`, it's no longer included -Installers: +#### Installers: + - make-binaries: Bump versions, e.g. erlang/otp to 25.3 - make-binaries: Fix building with erlang/otp v25.x - make-packages: Fix for installers workflow, which didn't find lynx ## Version 23.01 -General: +#### General: + - Add `misc:uri_parse/2` to allow declaring default ports for protocols - CAPTCHA: Add support to define module instead of path to script - Clustering: Handle `mnesia_system_event mnesia_up` when other node joins this ([#3842](https://github.com/processone/ejabberd/issues/3842)) @@ -294,7 +316,8 @@ General: - PubSub: Expose the `pubsub#type` field in `disco#info` query to the node ([#3914](https://github.com/processone/ejabberd/issues/3914)) - Translations: Update German translation -Admin: +#### Admin: + - `api_permissions`: Fix option crash when doesn't have `who:` section - `log_modules_fully`: New option to list modules that will log everything - `outgoing_s2s_families`: Changed option's default to IPv6, and fall back to IPv4 @@ -305,13 +328,15 @@ Admin: - Silent warning in OTP24 about not specified `cacerts` in SQL connections - Fix compilation warnings with Elixir 1.14 -DOAP: +#### DOAP: + - Support extended `-protocol` erlang attribute - Add extended RFCs and XEP details to some protocol attributes - `tools/generate-doap.sh`: New script to generate DOAP file, add `make doap` ([#3915](https://github.com/processone/ejabberd/issues/3915)) - `ejabberd.doap`: New DOAP file describing ejabberd supported protocols -MQTT: +#### MQTT: + - Add MQTT bridge module - Add support for certificate authentication in MQTT bridge - Implement reload in MQTT bridge @@ -320,7 +345,8 @@ MQTT: - `mqtt_publish`: New hook for MQTT publish event - `mqtt_(un)subscribe`: New hooks for MQTT subscribe & unsubscribe events -VSCode: +#### VSCode: + - Improve `.devcontainer` to use use devcontainer image and `.vscode` - Add `.vscode` files to instruct VSCode how to run ejabberd - Add Erlang LS default configuration @@ -328,7 +354,8 @@ VSCode: ## Version 22.10 -Core: +#### Core: + - Add `log_burst_limit_*` options ([#3865](https://github.com/processone/ejabberd/issues/3865)) - Support `ERL_DIST_PORT` option to work without epmd - Auth JWT: Catch all errors from `jose_jwt:verify` and log debugging details ([#3890](https://github.com/processone/ejabberd/issues/3890)) @@ -346,7 +373,8 @@ Core: - `mod_shared_roster_ldap`: Update roster_get hook to use `#roster_item{}` - `prosody2ejabberd`: Fix parsing of scram password from prosody -MIX: +#### MIX: + - Fix MIX's filter_nodes - Return user jid on join - `mod_mix_pam`: Add new MIX namespaces to disco features @@ -361,7 +389,8 @@ MIX: - `mod_roster`: Adapt to change of mix_annotate type to boolean in roster_query - `mod_shared_roster`: Fix wrong hook type `#roster{}` (now `#roster_item{}`) -MUC: +#### MUC: + - Store role, and use it when joining a moderated room ([#3330](https://github.com/processone/ejabberd/issues/3330)) - Don't persist `none` role ([#3330](https://github.com/processone/ejabberd/issues/3330)) - Allow MUC service admins to bypass max_user_conferences limitation @@ -374,7 +403,8 @@ MUC: - Export `mod_muc_admin:get_room_pid/2` - Export function for getting room diagnostics -SQL: +#### SQL: + - Handle errors reported from begin/commit inside transaction - Make connection close errors bubble up from inside sql transaction - Make first sql reconnect wait shorter time @@ -386,7 +416,8 @@ SQL: - Update mysql library - Catch mysql connection being close earlier -Build: +#### Build: + - `make all`: Generate start scripts here, not in `make install` ([#3821](https://github.com/processone/ejabberd/issues/3821)) - `make clean`: Improve this and "distclean" - `make deps`: Ensure deps configuration is ran when getting deps ([#3823](https://github.com/processone/ejabberd/issues/3823)) @@ -399,7 +430,8 @@ Build: - Remove unused macro definitions detected by rebar3_hank - Remove unused header files which content is already in xmpp library -Container: +#### Container: + - Get ejabberd-contrib sources to include them - Copy `.ejabberd-modules` directory if available - Do not clone repo inside container build @@ -409,7 +441,8 @@ Container: - Set a less frequent healthcheck to reduce CPU usage ([#3826](https://github.com/processone/ejabberd/issues/3826)) - Fix build instructions, add more podman examples -Installers: +#### Installers: + - make-binaries: Include CAPTCHA script with release - make-binaries: Edit rebar.config more carefully - make-binaries: Fix linking of EIMP dependencies @@ -423,14 +456,16 @@ Installers: - make-installers: Override code on upgrade - make-installers: Apply cosmetic changes -External modules: +#### External modules: + - ext_mod: Support managing remote nodes in the cluster - ext_mod: Handle correctly when COMMIT.json not found - Don't bother with COMMIT.json user-friendly feature in automated user case - Handle not found COMMIT.json, for example in GH Actions - Add WebAdmin page for managing external modules -Workflows Actions: +#### Workflows Actions: + - Update workflows to Erlang 25 - Update workflows: Ubuntu 18 is deprecated and 22 is added - CI: Remove syntax_tools from applications, as fast_xml fails Dialyzer @@ -438,42 +473,42 @@ Workflows Actions: ## Version 22.05 -Core +#### Core - C2S: Don't expect that socket will be available in `c2s_terminated` hook - Event handling process hook tracing - Guard against `erlang:system_info(logical_processors)` not always returning a number - `domain_balancing`: Allow for specifying `type` only, without specifying `component_number` -MQTT +#### MQTT - Add TLS certificate authentication for MQTT connections - Fix login when generating client id, keep connection record (#3593) - Pass property name as expected in mqtt_codec (fixes login using MQTT 5) - Support MQTT subscriptions spread over the cluster (#3750) -MUC +#### MUC - Attach meta field with real jid to mucsub subscription events - Handle user removal - Stop empty MUC rooms 30 seconds after creation - `default_room_options`: Update options configurable - `subscribe_room_many_max_users`: New option in `mod_muc_admin` -mod_conversejs +#### mod_conversejs - Improved options to support `@HOST@` and `auto` values - Set `auth` and `register` options based on ejabberd configuration - `conversejs_options`: New option - `conversejs_resources`: New option -PubSub +#### PubSub - `mod_pubsub`: Allow for limiting `item_expire` value - `mod_pubsub`: Unsubscribe JID on whitelist removal - `node_pep`: Add config-node and multi-items features (#3714) -SQL +#### SQL - Improve compatibility with various db engine versions - Sync old-to-new schema script with reality (#3790) - Slight improvement in MSSQL testing support, but not yet complete -Other Modules +#### Other Modules - `auth_jwt`: Checking if an user is active in SM for a JWT authenticated user (#3795) - `mod_configure`: Implement Get List of Registered/Online Users from XEP-0133 - `mod_host_meta`: New module to serve host-meta files, see XEP-0156 @@ -486,7 +521,7 @@ Other Modules - `mod_shared_roster`: Normalize JID on unset_presence (#3752) - `mod_stun_disco`: Fix parsing of IPv6 listeners -Dependencies +#### Dependencies - autoconf: Supported from 2.59 to the new 2.71 - fast_tls: Update to 1.1.14 to support OpenSSL 3 - jiffy: Update to 1.1.1 to support Erlang/OTP 25.0-rc1 @@ -496,7 +531,7 @@ Dependencies - rebar3: Updated binary to work from Erlang/OTP 22 to 25 - `make update`: Fix when used with rebar 3.18 -Compile +#### Compile - `mix release`: Copy `include/` files for ejabberd, deps and otp, in `mix.exs` - `rebar3 release`: Fix ERTS path in `ejabberdctl` - `configure.ac`: Set default ejabberd version number when not using git @@ -505,7 +540,7 @@ Compile - `tools/make-binaries`: New script for building Linux binaries - `tools/make-installers`: New script for building command line installers -Start +#### Start - New `make relive` similar to `ejabberdctl live` without installing - `ejabberdctl`: Fix some warnings detected by ShellCheck - `ejabberdctl`: Mention in the help: `etop`, `ping` and `started`/`stopped` @@ -513,7 +548,7 @@ Start - `mix.exs`: Add `-boot` and `-boot_var` in `ejabberdctl` instead of adding `vm.args` - `tools/captcha.sh`: Fix some warnings detected by ShellCheck -Commands +#### Commands - Accept more types of ejabberdctl commands arguments as JSON-encoded - `delete_old_mam_messages_batch`: New command with rate limit - `delete_old_messages_batch`: New command with rate limit @@ -525,7 +560,7 @@ Commands - `stop|restart`: Terminate ejabberd_sm before everything else to ensure sessions closing (#3641) - `subscribe_room_many`: New command -Translations +#### Translations - Updated Catalan - Updated French - Updated German @@ -533,7 +568,7 @@ Translations - Updated Portuguese (Brazil) - Updated Spanish -Workflows +#### Workflows - CI: Publish CT logs and Cover on failure to an external GH Pages repo - CI: Test shell scripts using ShellCheck (#3738) - Container: New workflow to build and publish containers @@ -543,12 +578,12 @@ Workflows ## Version 21.12 -Commands +#### Commands - `create_room_with_opts`: Fixed when using SQL storage - `change_room_option`: Add missing fields from config inside `mod_muc_admin:change_options` - piefxis: Fixed arguments of all commands -Modules +#### Modules - mod_caps: Don't forget caps on XEP-0198 resumption - mod_conversejs: New module to serve a simple page for Converse.js - mod_http_upload_quota: Avoid `max_days` race @@ -563,7 +598,7 @@ Modules - mod_register_web: Handle unknown host gracefully - mod_register_web: Use mod_register configured restrictions -PubSub +#### PubSub - Add `delete_expired_pubsub_items` command - Add `delete_old_pubsub_items` command - Optimize publishing on large nodes (SQL) @@ -574,7 +609,7 @@ PubSub - node_flat: Avoid catch-all clauses for RSM - node_flat_sql: Avoid catch-all clauses for RSM -SQL +#### SQL - Use `INSERT ... ON CONFLICT` in SQL_UPSERT for PostgreSQL >= 9.5 - mod_mam export: assign MUC entries to the MUC service - MySQL: Fix typo when creating index @@ -582,7 +617,7 @@ SQL - PgSQL: Add missing SQL migration for table `push_session` - PgSQL: Fix `vcard_search` definition in pgsql new schema -Other +#### Other - `captcha-ng.sh`: "sort -R" command not POSIX, added "shuf" and "cat" as fallback - Make s2s connection table cleanup more robust - Update export/import of scram password to XEP-0227 1.1 @@ -590,7 +625,7 @@ Other ## Version 21.07 -Compilation +#### Compilation - Add rebar3 3.15.2 binary - Add support for mix to: `./configure --enable-rebar=mix` - Improved `make rel` to work with rebar3 and mix @@ -602,14 +637,16 @@ Compilation - Added experimental support for GitHub Codespaces - Switch test service from TravisCI to GitHub Actions -Commands: +#### Commands: + - Display extended error message in ejabberdctl - Remove SMP option from ejabberdctl.cfg, `-smp` was removed in OTP 21 - `create_room`: After creating room, store in DB if it's persistent - `help`: Major changes in its usage and output - `srg_create`: Update to use `label` parameter instead of `name` -Modules: +#### Modules: + - ejabberd_listener: New `send_timeout` option - mod_mix: Improvements to update to 0.14.1 - mod_muc_room: Don't leak owner JIDs @@ -626,7 +663,8 @@ Modules: - WebAdmin: New simple pages to view mnesia tables information and content - WebSocket: Fix typos -SQL: +#### SQL: + - MySQL Backend Patch for scram-sha512 - SQLite: When exporting for SQLite, use its specific escape options - SQLite: Minor fixes for new_sql_schema support @@ -636,14 +674,16 @@ SQL: ## Version 21.04 -API Commands: +#### API Commands: + - `add_rosteritem/...`: Add argument guards to roster commands - `get_user_subscriptions`: New command for MUC/Sub - `remove_mam_for_user_with_peer`: Fix when removing room archive - `send_message`: Fix bug introduced in ejabberd 21.01 - `set_vcard`: Return modules errors -Build and setup: +#### Build and setup: + - Allow ejabberd to be compatible as a dependency for an Erlang project using rebar3 - CAPTCHA: New question/answer-based CAPTCHA script - `--enable-lua`: new configure option for luerl instead of --enable-tools @@ -651,14 +691,16 @@ Build and setup: - Update `sql_query` record to handle the Erlang/OTP 24 compiler reports - Updated dependencies to fix Dialyzer warnings -Miscellaneous: +#### Miscellaneous: + - CAPTCHA: Update `FORM_TYPE` from captcha to register - LDAP: fix eldap certificate verification - MySQL: Fix for "specified key was too long" - Translations: updated the Esperanto, Greek, and Japanese translations - Websocket: Fix PONG responses -Modules: +#### Modules: + - `mod_block_strangers`: If stanza is type error, allow it passing - `mod_caps`: Don't request roster when not needed - `mod_caps`: Skip reading roster in one more case @@ -674,7 +716,8 @@ Modules: ## Version 21.01 -Miscellaneous changes: +#### Miscellaneous changes: + - `log_rotate_size` option: Fix handling of ‘infinity’ value - `mod_time`: Fix invalid timezone - Auth JWT: New `check_decoded_jwt` hook runs the default JWT verifier @@ -689,7 +732,8 @@ Miscellaneous changes: - Stun: Block loopback addresses by default - Several documentation fixes and clarifications -Commands: +#### Commands: + - `decide_room`: Use better fallback value for room activity time when skipping room - `delete_old_message`: Fix when using sqlite spool table - `module_install`: Make ext_mod compile module with debug_info flags @@ -697,13 +741,15 @@ Commands: - `send_message`: Don’t include empty in messages - `set_room_affiliation`: Validate affiliations -Running: +#### Running: + - Docker: New `Dockerfile` and `devcontainer.json` - New `ejabberdctl foreground-quiet` - Systemd: Allow for listening on privileged ports - Systemd: Integrate nicely with systemd -Translations: +#### Translations: + - Moved gettext PO files to a new `ejabberd-po` repository - Improved several translations: Catalan, Chinese, German, Greek, Indonesian, Norwegian, Portuguese (Brazil), Spanish. @@ -716,13 +762,13 @@ Translations: - start_room: new hook runs when a room process is started - check_decoded_jwt: new hook to check decoded JWT after success authentication -* Admin +#### Admin - Docker: Fix DB initialization - New sql_odbc_driver option: choose the mssql ODBC driver - Rebar3: Fully supported. Enable with `./configure --with-rebar=/path/to/rebar3` - systemd: start ejabberd in foreground -* Modules: +#### Modules: - MAM: Make sure that jid used as base in mam xml_compress is bare - MAM: Support for MAM Flipped Pages - MUC: Always show MucSub subscribers nicks @@ -744,7 +790,7 @@ Translations: ## Version 20.07 -* Changes in this version +#### Changes in this version - Add support for using unix sockets in listeners. - Make this version compatible with erlang R23 - Make room permissions checks more strict for subscribers @@ -771,7 +817,7 @@ Translations: ## Version 20.03 -* Changes in this version +#### Changes in this version - Add support of ssl connection when connection to mysql database (configured with `sql_ssl: true` option) - Experimental support for cockroachdb when configured @@ -791,7 +837,7 @@ Translations: ## Version 20.02 -* Changes in this version +#### Changes in this version - Fix problems when trying to use string format with unicode values directly in xmpp nodes - Add missing oauth_client table declaration in lite.new.sql @@ -809,7 +855,7 @@ Translations: ## Version 20.01 -* New features +#### New features - Implement OAUTH authentication in mqtt - Make logging infrastructure use new logger introduced in Erlang (requires OTP22) @@ -824,7 +870,7 @@ Translations: - Generate man page automatically - Implement copy feature in mod_carboncopy -* Fixes +#### Fixes - Make webadmin work with configurable paths - Fix handling of result in xmlrpc module - Make webadmin work even when accessed through not declared domain @@ -842,18 +888,18 @@ Translations: ## Version 19.09 -* Admin +#### Admin - The minimum required Erlang/OTP version is now 19.3 - Fix API call using OAuth (#2982) - Rename MUC command arguments from Host to Service (#2976) -* Webadmin +#### Webadmin - Don't treat 'Host' header as a virtual XMPP host (#2989) - Fix some links to Guide in WebAdmin and add new ones (#3003) - Use select fields to input host in WebAdmin Backup (#3000) - Check account auth provided in WebAdmin is a local host (#3000) -* ACME +#### ACME - Improve ACME implementation - Fix IDA support in ACME requests - Fix unicode formatting in ACME module @@ -864,10 +910,10 @@ Translations: - Don't auto request certificate for localhost and IP-like domains - Add listener for ACME challenge in example config -* Authentication +#### Authentication - JWT-only authentication for some users (#3012) -* MUC +#### MUC - Apply default role after revoking admin affiliation (#3023) - Custom exit message is not broadcast (#3004) - Revert "Affiliations other than admin and owner cannot invite to members_only rooms" (#2987) @@ -875,11 +921,11 @@ Translations: - Improve rooms_* commands to accept 'global' as MUC service argument (#2976) - Rename MUC command arguments from Host to Service (#2976) -* SQL +#### SQL - Fix transactions for Microsoft SQL Server (#2978) - Spawn SQL connections on demand only -* Misc +#### Misc - Add support for XEP-0328: JID Prep - Added gsfonts for captcha - Log Mnesia table type on creation @@ -895,12 +941,12 @@ Translations: ## Version 19.08 -* Administration +#### Administration - Improve ejabberd halting procedure - Process unexpected erlang messages uniformly: logging a warning - mod_configure: Remove modules management -* Configuration +#### Configuration - Use new configuration validator - ejabberd_http: Use correct virtual host when consulting trusted_proxies - Fix Elixir modules detection in the configuration file @@ -910,7 +956,7 @@ Translations: - mod_stream_mgmt: Allow flexible timeout format - mod_mqtt: Allow flexible timeout format in session_expiry option -* Misc +#### Misc - Fix SQL connections leakage - New authentication method using JWT tokens - extauth: Add 'certauth' command @@ -925,14 +971,14 @@ Translations: - mod_privacy: Don't attempt to query 'undefined' active list - mod_privacy: Fix race condition -* MUC +#### MUC - Add code for hibernating inactive muc_room processes - Improve handling of unexpected iq in mod_muc_room - Attach mod_muc_room processes to a supervisor - Restore room when receiving message or generic iq for not started room - Distribute routing of MUC messages across all CPU cores -* PubSub +#### PubSub - Fix pending nodes retrieval for SQL backend - Check access_model when publishing PEP - Remove deprecated pubsub plugins @@ -940,7 +986,7 @@ Translations: ## Version 19.05 -* Admin +#### Admin - The minimum required Erlang/OTP version is now 19.1 - Provide a suggestion when unknown command, module, option or request handler is detected - Deprecate some listening options: captcha, register, web_admin, http_bind and xmlrpc @@ -951,19 +997,19 @@ Translations: - Improve request_handlers validator - Fix syntax in example Elixir config file -* Auth +#### Auth - Correctly support cache tags in ejabberd_auth - Don't process failed EXTERNAL authentication by mod_fail2ban - Don't call to mod_register when it's not loaded - Make anonymous auth don't {de}register user when there are other resources -* Developer +#### Developer - Rename listening callback from start/2 to start/3 - New hook called when room gets destroyed: room_destroyed - New hooks for tracking mucsub subscriptions changes: muc_subscribed, muc_unsubscribed - Make static hooks analyzer working again -* MUC +#### MUC - Service admins are allowed to recreate room even if archive is nonempty - New option user_mucsub_from_muc_archive - Avoid late arrival of get_disco_item response @@ -972,7 +1018,7 @@ Translations: - Make get_subscribed_rooms work even for non-persistant rooms - Allow non-moderator subscribers to get list of room subscribers -* Offline +#### Offline - New option bounce_groupchat: make it not bounce mucsub/groupchat messages - New option use_mam_for_storage: fetch data from mam instead of spool table - When applying limit of max msgs in spool check only spool size @@ -984,27 +1030,27 @@ Translations: - Return correct value from count_offline_messages with mam storage option - Make mod_offline put msg ignored by mam in spool when mam storage is on -* SQL: +#### SQL: - Add SQL schemas for MQTT tables - Report better errors on SQL terms decode failure - Fix PostgreSQL compatibility in mod_offline_sql:remove_old_messages - Fix handling of list arguments on pgsql - Preliminary support for SQL in process_rosteritems command -* Tests +#### Tests - Add tests for user mucsub mam from muc mam - Add tests for offline with mam storage - Add tests for offline use_mam_for_storage - Initial Docker environment to run ejabberd test suite - Test offline:use_mam_for_storage, mam:user_mucsub_from_muc_archive used together -* Websocket +#### Websocket - Add WebSockets support to mod_mqtt - Return "Bad request" error when origin in websocket connection doesn't match - Fix RFC6454 violation on websocket connection when validating Origin header - Origin header validation on websocket connection -* Other modules +#### Other modules - mod_adhoc: Use xml:lang from stanza when it's missing in element - mod_announce: Add 'sessionid' attribute when required - mod_bosh: Don't put duplicate polling attribute in bosh payload @@ -1017,14 +1063,14 @@ Translations: ## Version 19.02 -* Admin +#### Admin - Fix in configure.ac the Erlang/OTP version: from 17.5 to 19.0 - reload_config command: Fix crash when sql_pool_size option is used - reload_config command: Fix crash when SQL is not configured - rooms_empty_destroy command: Several fixes to behave more conservative - Fix serverhost->host parameter name for muc_(un)register_nick API -* Configuration +#### Configuration - Allow specifying tag for listener for api_permission purposes - Change default ciphers to intermediate - Define default ciphers/protocol_option in example config @@ -1034,29 +1080,29 @@ Translations: - mod_muc: New option access_mam to restrict who can modify that room option - mod_offline: New option store_groupchat to allow storing group chat messages -* Core +#### Core - Add MQTT protocol support - Fix (un)setting of priority - Use OTP application startup infrastructure for starting dependencies - Improve starting order of several dependencies -* MAM +#### MAM - mod_mam_mnesia/sql: Improve check for empty archive - disallow room creation if archive not empty and clear_archive_on_room_destroy is false - allow check if archive is empty for or user or room - Additional checks for database failures -* MUC +#### MUC - Make sure that room_destroyed is called even when some code throws in terminate - Update muc room state after adding extra access field to it - MUC/Sub: Send mucsub subscriber notification events with from set to room jid -* Shared Roster +#### Shared Roster - Don't perform roster push for non-local contacts - Handle versioning result when shared roster group has remote account - Fix SQL queries -* Miscelanea +#### Miscelanea - CAPTCHA: Add no-store hint to CAPTCHA challenge stanzas - HTTP: Reject http_api request with malformed Authentication header - mod_carboncopy: Don't lose carbons on presence change or session resumption @@ -1071,7 +1117,7 @@ Translations: ## Version 18.12 -* MAM data store compression -* Proxy protocol support -* MUC Self-Ping optimization (XEP-0410) -* Bookmarks conversion (XEP-0411) +- MAM data store compression +- Proxy protocol support +- MUC Self-Ping optimization (XEP-0410) +- Bookmarks conversion (XEP-0411) From 569f0b303bc6db7541894c57d8e7d4278d53bc30 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Mar 2024 13:24:07 +0100 Subject: [PATCH 0533/1302] Use same module shorthand in API documentation than in module docs --- src/ejabberd_commands_doc.erl | 6 +++--- src/mod_muc_admin.erl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 485e7f68e..a8093efcb 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -364,7 +364,7 @@ make_tags(HTMLOutput) -> -dialyzer({no_match, gen_tags/2}). gen_tags({TagName, Commands}, HTMLOutput) -> - [?TAG(h1, TagName) | [?TAG(p, ?RAW("* *`"++C++"`*")) || C <- Commands]]. + [?TAG(h1, TagName) | [?TAG(p, ?RAW("* _`"++C++"`_")) || C <- Commands]]. gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, args=Args, args_desc=ArgsDesc, note=Note, definer=Definer, @@ -395,14 +395,14 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, [?TAG(dl, [gen_param(RName, Type, ResultDesc, HTMLOutput)])] end end, - TagsText = ?RAW(string:join(["*`"++atom_to_list(Tag)++"`*" || Tag <- Tags], ", ")), + TagsText = ?RAW(string:join(["_`"++atom_to_list(Tag)++"`_" || Tag <- Tags], ", ")), IsDefinerMod = case Definer of unknown -> false; _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) end, ModuleText = case IsDefinerMod of true -> - [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW("*`"++atom_to_list(Definer)++"`*"))]; + [?TAG(h2, <<"Module:">>), ?TAG(p, ?RAW("_`"++atom_to_list(Definer)++"`_"))]; false -> [] end, diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index a0f62f145..59ccb3804 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -380,7 +380,7 @@ get_commands_spec() -> desc = "Subscribe several users to a MUC conference", note = "added in 22.05", longdesc = "This command accepts up to 50 users at once " - "(this is configurable with the *`mod_muc_admin`* option " + "(this is configurable with the _`mod_muc_admin`_ option " "`subscribe_room_many_max_users`)", module = ?MODULE, function = subscribe_room_many, args_desc = ["Users JIDs and nicks", @@ -402,7 +402,7 @@ get_commands_spec() -> #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], desc = "Subscribe several users to a MUC conference", longdesc = "This command accepts up to 50 users at once " - "(this is configurable with the *`mod_muc_admin`* option " + "(this is configurable with the _`mod_muc_admin`_ option " "`subscribe_room_many_max_users`)", module = ?MODULE, function = subscribe_room_many, version = 1, From 8be6dc77583700d216e5b43801149eff26fcd5d2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Mar 2024 12:38:03 +0100 Subject: [PATCH 0534/1302] Use new shorthand to provide URLs relative to ejabberd Docs --- src/ejabberd_admin.erl | 2 +- src/ejabberd_oauth.erl | 14 +++++++------- src/ejabberd_options_doc.erl | 33 ++++++++++++++++----------------- src/ejabberd_xmlrpc.erl | 3 +-- src/mod_admin_update_sql.erl | 2 +- src/mod_block_strangers.erl | 4 ++-- src/mod_conversejs.erl | 4 ++-- src/mod_host_meta.erl | 2 +- src/mod_http_api.erl | 4 ++-- src/mod_http_upload.erl | 2 +- src/mod_mix.erl | 4 +--- src/mod_mqtt.erl | 2 +- src/mod_muc.erl | 5 ++--- src/mod_offline.erl | 4 ++-- src/mod_register.erl | 2 +- src/mod_register_web.erl | 4 ++-- src/mod_shared_roster_ldap.erl | 13 ++++++------- src/mod_sip.erl | 2 +- 18 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index e482960d3..d8b1e2d1c 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -131,7 +131,7 @@ get_commands_spec() -> "only on log files generated by some modules.\n" "This can be useful when an external tool is " "used for log rotation. See " - "[Log Files](https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files).", + "_`../../admin/guide/troubleshooting.md#log-files|Log Files`_.", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 08c1f0f7f..026597f00 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -81,7 +81,7 @@ get_commands_spec() -> [ #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", + desc = "Issue an _`oauth.md|OAuth`_ token for the given jid", module = ?MODULE, function = oauth_issue_token, args = [{jid, string},{ttl, integer}, {scopes, string}], policy = restricted, @@ -92,7 +92,7 @@ get_commands_spec() -> result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token for the given jid", + desc = "Issue an _`oauth.md|OAuth`_ optionredir token for the given jid", module = ?MODULE, function = oauth_issue_token, version = 1, note = "updated in 24.02", @@ -105,7 +105,7 @@ get_commands_spec() -> result = {result, {tuple, [{token, string}, {scopes, {list, {scope, string}}}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], - desc = "List [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) tokens, user, scope, and seconds to expire (only Mnesia)", + desc = "List _`oauth.md|OAuth`_ tokens, user, scope, and seconds to expire (only Mnesia)", longdesc = "List OAuth tokens, their user and scope, and how many seconds remain until expirity", module = ?MODULE, function = oauth_list_tokens, args = [], @@ -113,7 +113,7 @@ get_commands_spec() -> result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}} }, #ejabberd_commands{name = oauth_revoke_token, tags = [oauth], - desc = "Revoke authorization for an [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) token", + desc = "Revoke authorization for an _`oauth.md|OAuth`_ token", note = "changed in 22.05", module = ?MODULE, function = oauth_revoke_token, args = [{token, binary}], @@ -122,7 +122,7 @@ get_commands_spec() -> result_desc = "Result code" }, #ejabberd_commands{name = oauth_add_client_password, tags = [oauth], - desc = "Add [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id with password grant type", + desc = "Add _`oauth.md|OAuth`_ client_id with password grant type", module = ?MODULE, function = oauth_add_client_password, args = [{client_id, binary}, {client_name, binary}, @@ -131,7 +131,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_add_client_implicit, tags = [oauth], - desc = "Add [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id with implicit grant type", + desc = "Add _`oauth.md|OAuth`_ client_id with implicit grant type", module = ?MODULE, function = oauth_add_client_implicit, args = [{client_id, binary}, {client_name, binary}, @@ -140,7 +140,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_remove_client, tags = [oauth], - desc = "Remove [OAuth](https://docs.ejabberd.im/developer/ejabberd-api/oauth/) client_id", + desc = "Remove _`oauth.md|OAuth`_ client_id", module = ?MODULE, function = oauth_remove_client, args = [{client_id, binary}], policy = restricted, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index b005111a4..5bb1b3b9b 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -37,13 +37,13 @@ doc() -> #{value => "[Options, ...]", desc => ?T("The option for listeners configuration. See the " - "http://../listen/[Listen Modules] section " + "_`listen.md|Listen Modules`_ section " "for details.")}}, {modules, #{value => "{Module: Options}", desc => ?T("The option for modules configuration. See " - "http://../modules/[Modules] section " + "_`modules.md|Modules`_ section " "for details.")}}, {loglevel, #{value => @@ -221,7 +221,7 @@ doc() -> #{value => "{AccessName: {allow|deny: ACLRules|ACLName}}", desc => ?T("This option defines " - "http://../basic/#access-rules[Access Rules]. " + "_`basic.md#access-rules|Access Rules`_. " "Each access rule is " "assigned a name that can be referenced from other parts " "of the configuration file (mostly from 'access' options of " @@ -255,7 +255,7 @@ doc() -> {acme, #{value => ?T("Options"), desc => - ?T("http://../basic/#acme[ACME] configuration, to automatically " + ?T("_`basic.md#acme|ACME`_ configuration, to automatically " "obtain SSL certificates for the domains served by ejabberd, " "which means that certificate requests and renewals are " "performed to some CA server (aka \"ACME server\") in a fully " @@ -303,8 +303,8 @@ doc() -> #{value => "true | false", desc => ?T("Whether to allow installation of third-party modules or not. " - "See https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib" - "[ejabberd-contrib] documentation section. " + "See _`../../developer/extending-ejabberd/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "documentation section. " "The default value is 'true'.")}}, {allow_multiple_connections, #{value => "true | false", @@ -328,8 +328,7 @@ doc() -> desc => ?T("Define the permissions for API access. Please consult the " "ejabberd Docs web -> For Developers -> ejabberd ReST API -> " - "https://docs.ejabberd.im/developer/ejabberd-api/permissions/" - "[API Permissions].")}}, + "_`../../developer/ejabberd-api/permissions.md|API Permissions`_.")}}, {append_host_config, #{value => "{Host: Options}", desc => @@ -373,7 +372,7 @@ doc() -> note => "improved in 20.01", desc => [?T("The option defines in what format the users passwords " - "are stored, plain text or in http://../authentication/#scram[SCRAM] format:"), "", + "are stored, plain text or in _`authentication.md#scram|SCRAM`_ format:"), "", ?T("* 'plain': The password is stored as plain text " "in the database. This is risky because the passwords " "can be read if your database gets compromised. " @@ -392,7 +391,7 @@ doc() -> {auth_scram_hash, #{value => "sha | sha256 | sha512", desc => - ?T("Hash algorithm that should be used to store password in http://../authentication/#scram[SCRAM] format. " + ?T("Hash algorithm that should be used to store password in _`authentication.md#scram|SCRAM`_ format. " "You shouldn't change this if you already have passwords generated with " "a different algorithm - users that have such passwords will not be able " "to authenticate. The default value is 'sha'.")}}, @@ -418,7 +417,7 @@ doc() -> "corresponding JID(s) in 'subjectAltName' field. " "There is no default value."), "", ?T("You can use _`host_config`_ to specify this option per-vhost."), "", - ?T("To set a specific file per listener, use the listener's http://../listen-options/#cafile[cafile] option. Please notice that 'c2s_cafile' overrides the listener's 'cafile' option."), "" + ?T("To set a specific file per listener, use the listener's _`listen-options.md#cafile|cafile`_ option. Please notice that 'c2s_cafile' overrides the listener's 'cafile' option."), "" ]}}, {c2s_ciphers, #{value => "[Cipher, ...]", @@ -468,7 +467,7 @@ doc() -> #{value => ?T("Path | ModuleName"), note => "improved in 23.01", desc => - ?T("Full path to a script that generates http://../basic/#captcha[CAPTCHA] images. " + ?T("Full path to a script that generates _`basic.md#captcha|CAPTCHA`_ images. " "'@VERSION@' is replaced with ejabberd version number in 'XX.YY' format. " "'@SEMVER@' is replaced with ejabberd version number in semver format " "when compiled with Elixir's mix, or XX.YY format otherwise. " @@ -481,7 +480,7 @@ doc() -> {captcha_limit, #{value => "pos_integer() | infinity", desc => - ?T("Maximum number of http://../basic/#captcha[CAPTCHA] generated images per minute for " + ?T("Maximum number of _`basic.md#captcha|CAPTCHA`_ generated images per minute for " "any given JID. The option is intended to protect the server " "from CAPTCHA DoS. The default value is 'infinity'.")}}, {captcha_host, @@ -491,7 +490,7 @@ doc() -> #{value => ?T("URL | auto | undefined"), note => "improved in 23.04", desc => - ?T("An URL where http://../basic/#captcha[CAPTCHA] requests should be sent. NOTE: you need " + ?T("An URL where _`basic.md#captcha|CAPTCHA`_ requests should be sent. NOTE: you need " "to configure 'request_handlers' for 'ejabberd_http' listener " "as well. " "If set to 'auto', it builds the URL using a 'request_handler' " @@ -696,8 +695,8 @@ doc() -> note => "added in 23.10", desc => ?T("Modules to install from " - "https://docs.ejabberd.im/developer/extending-ejabberd/modules/#ejabberd-contrib" - "[ejabberd-contrib] at start time. " + "_`../../developer/extending-ejabberd/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "at start time. " "The default value is an empty list of modules: '[]'.")}}, {jwt_auth_only_rule, #{value => ?T("AccessName"), @@ -1156,7 +1155,7 @@ doc() -> {s2s_access, #{value => ?T("Access"), desc => - ?T("This http://../basic/#access-rules[Access Rule] defines to " + ?T("This _`basic.md#access-rules|Access Rule`_ defines to " "what remote servers can s2s connections be established. " "The default value is 'all'; no restrictions are applied, it is" " allowed to connect s2s to/from all remote servers.")}}, diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index 84a724423..e0e922166 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -410,6 +410,5 @@ listen_options() -> " method may be removed in a future ejabberd release. You are " "encouraged to define ejabberd_xmlrpc inside request_handlers " "option of ejabberd_http listen module. See the ejabberd " - "documentation for details: https://docs.ejabberd.im/admin/" - "configuration/listen/#ejabberd-xmlrpc", []), + "documentation for details: _`/admin/configuration/listen/#ejabberd-xmlrpc|ejabberd_xmlrpc listener`_.", []), []. diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 2f9e1e92e..85d039495 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -632,6 +632,6 @@ mod_doc() -> #{desc => ?T("This module can be used to update existing SQL database " "from the default to the new schema. Check the section " - "http://../database/#default-and-new-schemas[Default and New Schemas] for details. " + "_`database.md#default-and-new-schemas|Default and New Schemas`_ for details. " "Please note that only MS SQL, MySQL, and PostgreSQL are supported. " "When the module is loaded use _`update_sql`_ API.")}. diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index c8ea332f0..c7a436c24 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -301,6 +301,6 @@ mod_doc() -> desc => ?T("Whether to generate CAPTCHA or not in response to " "messages from strangers. See also section " - "http://../#captcha" - "[CAPTCHA] of the Configuration Guide. " + "_`basic.md#captcha|CAPTCHA`_" + " of the Configuration Guide. " "The default value is 'false'.")}}]}. diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 3ae7d9747..f2dcf43b8 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -256,9 +256,9 @@ mod_doc() -> ?T("Several options were improved in ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request_handlers[request_handlers]."), "", + "_`listen-options.md#request_handlers|request_handlers`_."), "", ?T("Make sure either 'mod_bosh' or 'ejabberd_http_ws' " - "http://../listen-options/#request_handlers[request_handlers] " + "_`listen-options.md#request_handlers|request_handlers`_ " "are enabled."), "", ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " "by default they point to the public Converse client.") diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 43efdcf9e..7be3e81fe 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -214,7 +214,7 @@ mod_doc() -> ?T("This module is available since ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request_handlers[request_handlers]."), "", + "_`listen-options.md#request_handlers|request_handlers`_."), "", ?T("Notice it only works if ejabberd_http has tls enabled.")], example => ["listen:", diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 77055eafa..5a61ad1c3 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -534,11 +534,11 @@ mod_options(_) -> mod_doc() -> #{desc => [?T("This module provides a ReST interface to call " - "https://docs.ejabberd.im/developer/ejabberd-api[ejabberd API] " + "_`../../developer/ejabberd-api/index.md|ejabberd API`_ " "commands using JSON data."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request_handlers[request_handlers]."), "", + "_`listen-options.md#request_handlers|request_handlers`_."), "", ?T("To use a specific API version N, when defining the URL path " "in the request_handlers, add a 'vN'. " "For example: '/api/v2: mod_http_api'"), "", diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index fba6cf7cd..7cf404946 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -234,7 +234,7 @@ mod_doc() -> "another URL from which that file can later be downloaded."), "", ?T("In order to use this module, it must be enabled " "in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request_handlers[request_handlers].")], + "_`listen-options.md#request_handlers|request_handlers`_.")], opts => [{host, #{desc => ?T("Deprecated. Use 'hosts' instead.")}}, diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 7066911b9..57e78dbc2 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -108,9 +108,7 @@ mod_doc() -> "the MIX protocol is going to replace the MUC protocol " "in the future (see _`mod_muc`_)."), "", ?T("To learn more about how to use that feature, you can refer to " - "our tutorial: https://docs.ejabberd.im/tutorials/mix-010/" - "[Getting started with XEP-0369: Mediated Information " - "eXchange (MIX) v0.1]."), "", + "our tutorial: _`../../tutorials/mix-010.md|Getting started with MIX`_"), "", ?T("The module depends on _`mod_mam`_.")], opts => [{access_create, diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index 6f9563c64..974b03549 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -282,7 +282,7 @@ listen_options() -> mod_doc() -> #{desc => ?T("This module adds " - "https://docs.ejabberd.im/admin/guide/mqtt/[support for the MQTT] " + "_`../guide/mqtt/index.md|support for the MQTT`_ " "protocol version '3.1.1' and '5.0'. Remember to configure " "'mod_mqtt' in 'modules' and 'listen' sections."), opts => diff --git a/src/mod_muc.erl b/src/mod_muc.erl index d3f37b3d4..216c1277b 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1732,7 +1732,7 @@ mod_doc() -> ?T("When a user tries to join a room where they have no " "affiliation (not owner, admin or member), the room " "requires them to fill a CAPTCHA challenge (see section " - "http://../#captcha[CAPTCHA] " + "_`basic.md#captcha|CAPTCHA`_ " "in order to accept their join in the room. " "The default value is 'false'.")}}, {description, @@ -1824,8 +1824,7 @@ mod_doc() -> #{value => "true | false", desc => ?T("Allow users to subscribe to room events as described in " - "https://docs.ejabberd.im/developer/xmpp-clients-bots/extensions/muc-sub/" - "[Multi-User Chat Subscriptions]. " + "_`../../developer/xmpp-clients-bots/extensions/muc-sub.md|Multi-User Chat Subscriptions`_. " "The default value is 'false'.")}}, {title, #{value => ?T("Room Title"), diff --git a/src/mod_offline.erl b/src/mod_offline.erl index ff48b162a..f626d8d24 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1247,8 +1247,8 @@ mod_doc() -> "are currently open."), "", ?T("NOTE: 'ejabberdctl' has a command to " "delete expired messages (see chapter " - "https://docs.ejabberd.im/admin/guide/managing" - "[Managing an ejabberd server] in online documentation.")], + "_`../guide/managing.md|Managing an ejabberd server`_ " + "in online documentation.")], opts => [{access_max_user_messages, #{value => ?T("AccessName"), diff --git a/src/mod_register.erl b/src/mod_register.erl index 8ffb512d6..0e595991f 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -671,7 +671,7 @@ mod_doc() -> {captcha_protected, #{value => "true | false", desc => - ?T("Protect registrations with http://../basic/#captcha[CAPTCHA]. " + ?T("Protect registrations with _`basic.md#captcha|CAPTCHA`_. " "The default is 'false'.")}}, {ip_access, #{value => ?T("AccessName"), diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index f50b6f2f3..76d84d068 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -615,7 +615,7 @@ mod_doc() -> ?T("- Register a new account on the server."), "", ?T("- Change the password from an existing account on the server."), "", ?T("- Unregister an existing account on the server."), "", - ?T("This module supports http://../basic/#captcha[CAPTCHA] " + ?T("This module supports _`basic.md#captcha|CAPTCHA`_ " "to register a new account. " "To enable this feature, configure the " "top-level _`captcha_cmd`_ and " @@ -625,7 +625,7 @@ mod_doc() -> "important to include the last / character in the URL, " "otherwise the subpages URL will be incorrect."), "", ?T("This module is enabled in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request_handlers[request_handlers], " + "_`listen-options.md#request_handlers|request_handlers`_, " "no need to enable in 'modules'."), ?T("The module depends on _`mod_register`_ where all the " "configuration is performed.")], diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 0cabc0809..bad252e9f 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -678,10 +678,10 @@ mod_doc() -> ?T("- Connection parameters: The module also accepts the " "connection parameters, all of which default to the top-level " "parameter of the same name, if unspecified. " - "See http://../ldap/#ldap-connection[LDAP Connection] " + "See _`ldap.md#ldap-connection|LDAP Connection`_ " "section for more information about them."), "", - ?T("Check also the http://../ldap/#ldap-examples" - "[Configuration examples] section to get details about " + ?T("Check also the _`ldap.md#ldap-examples|Configuration examples`_ " + "section to get details about " "retrieving the roster, " "and configuration examples including Flat DIT and Deep DIT.")], opts => @@ -710,13 +710,13 @@ mod_doc() -> "name of roster entries (usually full names of people in " "the roster). See also the parameters 'ldap_userdesc' and " "'ldap_useruid'. For more information check the LDAP " - "http://../ldap/#filters[Filters] section.")}}, + "_`ldap.md#filters|Filters`_ section.")}}, {ldap_filter, #{desc => ?T("Additional filter which is AND-ed together " "with \"User Filter\" and \"Group Filter\". " "For more information check the LDAP " - "http://../ldap/#filters[Filters] section.")}}, + "_`ldap.md#filters|Filters`_ section.")}}, %% Attributes: {ldap_groupattr, #{desc => @@ -774,8 +774,7 @@ mod_doc() -> #{desc => ?T("A regex for extracting user ID from the value of the " "attribute named by 'ldap_memberattr'. Check the LDAP " - "http://../ldap/#control-parameters" - "[Control Parameters] section.")}}, + "_`ldap.md#control-parameters|Control Parameters`_ section.")}}, {ldap_auth_check, #{value => "true | false", desc => diff --git a/src/mod_sip.erl b/src/mod_sip.erl index 16390ff72..e8b7cb2f8 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -383,7 +383,7 @@ mod_doc() -> ?T("NOTE: It is not enough to just load this module. " "You should also configure listeners and DNS records " "properly. For details see the section about the " - "http://../listen/#ejabberd_sip[ejabberd_sip] listen module " + "_`listen.md#ejabberd_sip|ejabberd_sip`_ listen module " "in the ejabberd Documentation.")], opts => [{always_record_route, From 71b9db688a945222c912996f2800edc003ccbc7f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Mar 2024 20:22:20 +0100 Subject: [PATCH 0535/1302] Fix markdown indentation of JSON examples --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index a8093efcb..6a97ccc0c 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -237,7 +237,7 @@ json_gen({_Name, {list, ElDesc}}, List, Indent, HTMLOutput) -> [?OP_L("["), ?BR, Indent2, list_join_with(Res, [?OP_L(","), ?BR, Indent2]), ?BR, Indent, ?OP_L("]")]. json_call(Name, ArgsDesc, Values, ResultDesc, Result, HTMLOutput) -> - {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<" ">>, <<"~~~ json\n">>} end, + {Indent, Preamble} = if HTMLOutput -> {<<"">>, []}; true -> {<<"">>, <<"~~~ json\n">>} end, {Code, ResultStr} = case {ResultDesc, Result} of {{_, rescode}, V} when V == true; V == ok -> {200, [?STR_L("")]}; From effcf0b7cffbefa9a68eb41aee3e67ed9674490c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 20 Mar 2024 15:31:22 +0100 Subject: [PATCH 0536/1302] Update markdown text for version notes, to work with mkdocs --- src/ejabberd_commands_doc.erl | 2 +- src/ejabberd_doc.erl | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 6a97ccc0c..bf9ac32f3 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -87,7 +87,7 @@ md_tag(h2, V) -> md_tag(strong, V) -> [<<"*">>, V, <<"*">>]; md_tag('div', V) -> - [<<"
">>, V, <<"
">>]; + [<<"*Note* about the next option: ">>, V]; md_tag(_, V) -> V. diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 3ea95f91b..389be0f81 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -152,7 +152,7 @@ opts_to_man(Lang, Backends) -> end, Backends). opt_to_man(Lang, {Option, Options}, Level) -> - [format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++ + [format_option(Lang, Option, Options)|format_versions(Lang, Options)++format_desc(Lang, Options)] ++ format_example(Level, Lang, Options); opt_to_man(Lang, {Option, Options, Children}, Level) -> [format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++ @@ -163,16 +163,17 @@ opt_to_man(Lang, {Option, Options, Children}, Level) -> lists:keysort(1, Children))]) ++ [io_lib:nl()|format_example(Level, Lang, Options)]. -format_option(Lang, Option, #{note := Note, value := Val}) -> - "\n\n_Note_ about the next option: " ++ Note ++ ":\n\n"++ - "*" ++ atom_to_list(Option) ++ "*: 'pass:[" ++ - tr(Lang, Val) ++ "]'::"; format_option(Lang, Option, #{value := Val}) -> "*" ++ atom_to_list(Option) ++ "*: 'pass:[" ++ tr(Lang, Val) ++ "]'::"; format_option(_Lang, Option, #{}) -> "*" ++ atom_to_list(Option) ++ "*::". +format_versions(Lang, #{note := Note}) -> + ["_Note_ about this option: " ++ Note ++ ". "]; +format_versions(_, _) -> + []. + format_desc(Lang, #{desc := Desc}) -> tr_multi(Lang, Desc). From 27bd8852a1d501d16ee31a6316b749cb27e7cac4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Mar 2024 11:06:09 +0100 Subject: [PATCH 0537/1302] Update name of API section --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index bf9ac32f3..bbb4bffc5 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -493,7 +493,7 @@ generate_md_output(File, RegExp, Languages, Cmds) -> Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), Version = ejabberd_config:version(), - Header = <<"# Administration API reference\n\n" + Header = <<"# API Reference\n\n" "This section describes API of ejabberd ", Version/binary, ".\n\n">>, Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), {ok, Fh} = file:open(File, [write]), From cd7eedf589d2ecaf50f7558abc0390501ef920ad Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Mar 2024 11:15:11 +0100 Subject: [PATCH 0538/1302] Update API to mkdocs --- src/ejabberd_commands_doc.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index bbb4bffc5..2b26b4800 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -87,7 +87,7 @@ md_tag(h2, V) -> md_tag(strong, V) -> [<<"*">>, V, <<"*">>]; md_tag('div', V) -> - [<<"*Note* about the next option: ">>, V]; + [<<"*Note* about this command: ">>, V, <<".">>]; md_tag(_, V) -> V. @@ -415,8 +415,8 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, true -> {NoteEl, []} end, - [NotePre, - ?TAG(h1, atom_to_list(Name)), + [?TAG(h1, atom_to_list(Name)), + NotePre, ?TAG(p, ?RAW(Desc)), case LongDesc of "" -> []; From c98302b3c0c0f3c6bca5a683b08538832d6c9ccc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 28 Mar 2024 00:13:28 +0100 Subject: [PATCH 0539/1302] Now modules themselves can have version annotations in 'note' This was already supported in module options, toplevel options and ejabberd_commands. --- src/gen_mod.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 21cb166bc..908755b99 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -74,6 +74,7 @@ -callback mod_opt_type(atom()) -> econf:validator(). -callback mod_options(binary()) -> [{atom(), term()} | atom()]. -callback mod_doc() -> #{desc => binary() | [binary()], + note => string(), opts => [opt_doc()], example => [string()] | [{binary(), [string()]}]}. -callback depends(binary(), opts()) -> [{module(), hard | soft}]. From 7cd580876626448df246c86a50b510ed9f87befb Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Mar 2024 12:50:47 +0100 Subject: [PATCH 0540/1302] Mark toplevel options, commands and modules that changed in latest version --- src/ejabberd_commands_doc.erl | 32 +++++++++++++++++++++++--------- src/ejabberd_doc.erl | 33 ++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 2b26b4800..7b0b46246 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -415,7 +415,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, true -> {NoteEl, []} end, - [?TAG(h1, atom_to_list(Name)), + [?TAG(h1, make_command_name(Name, Note)), NotePre, ?TAG(p, ?RAW(Desc)), case LongDesc of @@ -435,6 +435,19 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, [Name, Ex]))) end. +get_version_mark("") -> + ""; +get_version_mark(Note) -> + [XX, YY | _] = string:tokens(binary_to_list(ejabberd_option:version()), "."), + XXYY = string:join([XX, YY], "."), + case string:find(Note, XXYY) of + nomatch -> ""; + _ -> " 🟤" + end. + +make_command_name(Name, Note) -> + atom_to_list(Name) ++ get_version_mark(Note). + find_commands_definitions() -> lists:flatmap( fun(Mod) -> @@ -492,21 +505,22 @@ generate_md_output(File, RegExp, Languages, Cmds) -> end, Cmds2), Cmds4 = [maybe_add_policy_arguments(Cmd) || Cmd <- Cmds3], Langs = binary:split(Languages, <<",">>, [global]), - Version = ejabberd_config:version(), - Header = <<"# API Reference\n\n" - "This section describes API of ejabberd ", Version/binary, ".\n\n">>, + Version = binary_to_list(ejabberd_config:version()), + Header = ["# API Reference\n\n" + "This section describes API commands of ejabberd ", Version, ". " + "The commands that changed in this version are marked with 🟤.\n\n"], Out = lists:map(fun(C) -> gen_doc(C, false, Langs) end, Cmds4), - {ok, Fh} = file:open(File, [write]), + {ok, Fh} = file:open(File, [write, {encoding, utf8}]), io:format(Fh, "~ts~ts", [Header, Out]), file:close(Fh), ok. generate_tags_md(File) -> - Version = ejabberd_config:version(), - Header = <<"# API Tags\n\n" - "This section enumerates the API tags of ejabberd ", Version/binary, ".\n\n">>, + Version = binary_to_list(ejabberd_config:version()), + Header = ["# API Tags\n\n" + "This section enumerates the API tags of ejabberd ", Version, ". \n\n"], Tags = make_tags(false), - {ok, Fh} = file:open(File, [write]), + {ok, Fh} = file:open(File, [write, {encoding, utf8}]), io:format(Fh, "~ts~ts", [Header, Tags]), file:close(Fh), ok. diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 389be0f81..28a6e1a5c 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -45,7 +45,8 @@ man(Lang) -> #{desc := Descr} = Map -> DocOpts = maps:get(opts, Map, []), Example = maps:get(example, Map, []), - {[{M, Descr, DocOpts, #{example => Example}}|Mods], SubMods}; + Note = maps:get(note, Map, []), + {[{M, Descr, DocOpts, #{example => Example, note => Note}}|Mods], SubMods}; #{opts := DocOpts} -> {ParentMod, Backend} = strip_backend_suffix(M), {Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)}; @@ -72,11 +73,12 @@ man(Lang) -> catch _:undef -> [] end end, ejabberd_config:callback_modules(all)), - Version = ejabberd_config:version(), + Version = binary_to_list(ejabberd_config:version()), Options = ["TOP LEVEL OPTIONS", "-----------------", - tr(Lang, <<"This section describes top level options of ejabberd ", Version/binary, ".">>), + "This section describes top level options of ejabberd " ++ Version ++ ".", + "The options that changed in this version are marked with 🟤.", io_lib:nl()] ++ lists:flatmap( fun(Opt) -> @@ -96,13 +98,15 @@ man(Lang) -> "MODULES", "-------", "[[modules]]", - tr(Lang, <<"This section describes modules options of ejabberd ", Version/binary, ".">>), + "This section describes modules options of ejabberd " ++ Version ++ ".", + "The modules that changed in this version are marked with 🟤.", io_lib:nl()] ++ lists:flatmap( fun({M, Descr, DocOpts, Backends, Example}) -> ModName = atom_to_list(M), + VersionMark = get_version_mark(Example), [io_lib:nl(), - ModName, + lists:flatten([ModName, VersionMark]), lists:duplicate(length(atom_to_list(M)), $~), "[[" ++ ModName ++ "]]", io_lib:nl()] ++ @@ -115,7 +119,7 @@ man(Lang) -> "LISTENERS", "-------", "[[listeners]]", - tr(Lang, <<"This section describes listeners options of ejabberd ", Version/binary, ".">>), + "This section describes listeners options of ejabberd " ++ Version ++ ".", io_lib:nl(), "TODO"], AsciiData = @@ -163,13 +167,24 @@ opt_to_man(Lang, {Option, Options, Children}, Level) -> lists:keysort(1, Children))]) ++ [io_lib:nl()|format_example(Level, Lang, Options)]. -format_option(Lang, Option, #{value := Val}) -> - "*" ++ atom_to_list(Option) ++ "*: 'pass:[" ++ +get_version_mark(#{note := Note}) -> + [XX, YY | _] = string:tokens(binary_to_list(ejabberd_option:version()), "."), + XXYY = string:join([XX, YY], "."), + case string:find(Note, XXYY) of + nomatch -> ""; + _ -> " 🟤" + end; +get_version_mark(_) -> + "". + +format_option(Lang, Option, #{value := Val} = Options) -> + VersionMark = get_version_mark(Options), + "*" ++ atom_to_list(Option) ++ VersionMark ++ "*: 'pass:[" ++ tr(Lang, Val) ++ "]'::"; format_option(_Lang, Option, #{}) -> "*" ++ atom_to_list(Option) ++ "*::". -format_versions(Lang, #{note := Note}) -> +format_versions(_Lang, #{note := Note}) -> ["_Note_ about this option: " ++ Note ++ ". "]; format_versions(_, _) -> []. From da01d932ea11788284f8eca42dfef13ccab9a1b7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Mar 2024 16:38:29 +0100 Subject: [PATCH 0541/1302] Add some text to examples, so docs Makefile can find and update its syntax --- src/ejabberd_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 28a6e1a5c..6a054a583 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -211,7 +211,7 @@ format_example(Level, Lang, #{example := [_|_] = Example}) -> false -> lists:flatmap( fun(Block) -> - ["+", "''''", "+"|Block] + ["+", "*Examples*:", "+"|Block] end, lists:map( fun({Text, Lines}) -> From b70a2fe4cf4e888f57f0d0faedddebc865cbc0af Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 31 Mar 2024 01:03:49 +0100 Subject: [PATCH 0542/1302] Support for version note in modules too --- src/ejabberd_doc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 6a054a583..053e5826c 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -110,6 +110,7 @@ man(Lang) -> lists:duplicate(length(atom_to_list(M)), $~), "[[" ++ ModName ++ "]]", io_lib:nl()] ++ + format_versions(Lang, Example) ++ [io_lib:nl()] ++ tr_multi(Lang, Descr) ++ [io_lib:nl()] ++ opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++ format_example(0, Lang, Example) @@ -184,7 +185,7 @@ format_option(Lang, Option, #{value := Val} = Options) -> format_option(_Lang, Option, #{}) -> "*" ++ atom_to_list(Option) ++ "*::". -format_versions(_Lang, #{note := Note}) -> +format_versions(_Lang, #{note := Note}) when Note /= [] -> ["_Note_ about this option: " ++ Note ++ ". "]; format_versions(_, _) -> []. From 6e5895ce45a8dc465fde45d9d35dd44a6a792cd5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 28 Mar 2024 03:24:28 +0100 Subject: [PATCH 0543/1302] Update some modules doc to use the note field --- src/mod_conversejs.erl | 3 +-- src/mod_host_meta.erl | 2 +- src/mod_matrix_gw.erl | 4 ++-- src/mod_mix.erl | 5 ++--- src/mod_muc_occupantid.erl | 4 ++-- src/mod_muc_rtbl.erl | 4 ++-- src/mod_stun_disco.erl | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index f2dcf43b8..2d3af544e 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -252,8 +252,6 @@ mod_doc() -> #{desc => [?T("This module serves a simple page for the " "https://conversejs.org/[Converse] XMPP web browser client."), "", - ?T("This module is available since ejabberd 21.12."), - ?T("Several options were improved in ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "_`listen-options.md#request_handlers|request_handlers`_."), "", @@ -263,6 +261,7 @@ mod_doc() -> ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " "by default they point to the public Converse client.") ], + note => "added in 21.12 and improved in 22.05", example => [{?T("Manually setup WebSocket url, and use the public Converse client:"), ["listen:", diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 7be3e81fe..484127443 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -211,11 +211,11 @@ mod_doc() -> [?T("This module serves small 'host-meta' files as described in " "https://xmpp.org/extensions/xep-0156.html[XEP-0156: Discovering " "Alternative XMPP Connection Methods]."), "", - ?T("This module is available since ejabberd 22.05."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "_`listen-options.md#request_handlers|request_handlers`_."), "", ?T("Notice it only works if ejabberd_http has tls enabled.")], + note => "added in 22.05", example => ["listen:", " -", diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 0e7010913..46c5dbcfc 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -884,8 +884,8 @@ mod_options(Host) -> mod_doc() -> #{desc => - [?T("https://matrix.org/[Matrix] gateway."), "", - ?T("This module is available since ejabberd 24.02.")], + [?T("https://matrix.org/[Matrix] gateway.")], + note => "added in 24.02", example => ["listen:", " -", diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 57e78dbc2..1c570c37f 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -102,14 +102,13 @@ mod_doc() -> [?T("This module is an experimental implementation of " "https://xmpp.org/extensions/xep-0369.html" "[XEP-0369: Mediated Information eXchange (MIX)]. " - "MIX support was added in ejabberd 16.03 as an " - "experimental feature, updated in 19.02, and is not " - "yet ready to use in production. It's asserted that " + "It's asserted that " "the MIX protocol is going to replace the MUC protocol " "in the future (see _`mod_muc`_)."), "", ?T("To learn more about how to use that feature, you can refer to " "our tutorial: _`../../tutorials/mix-010.md|Getting started with MIX`_"), "", ?T("The module depends on _`mod_mam`_.")], + note => "added in 16.03 and improved in 19.02", opts => [{access_create, #{value => ?T("AccessName"), diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 8e77882ea..fabd69e28 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -120,8 +120,8 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0421.html" "[XEP-0421: Anonymous unique occupant identifiers for MUCs]."), "", ?T("When the module is enabled, the feature is enabled " - "in all semi-anonymous rooms."), "", - ?T("This module is available since ejabberd 23.10.")] + "in all semi-anonymous rooms.")], + note => "added in 23.10" }. depends(_, _) -> diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 3a616fff7..93e13fbb3 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -264,8 +264,8 @@ mod_doc() -> #{desc => [?T("This module implement Real-time blocklists for MUC rooms."), "", ?T("It works by observing remote pubsub node conforming with " - "specification described in https://xmppbl.org/."), "", - ?T("This module is available since ejabberd 23.04.")], + "specification described in https://xmppbl.org/.")], + note => "added in 23.04", opts => [{rtbl_server, #{value => ?T("Domain"), diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index 3021ac217..8d2d6d58c 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -159,8 +159,8 @@ mod_doc() -> ?T("This module allows XMPP clients to discover STUN/TURN services " "and to obtain temporary credentials for using them as per " "https://xmpp.org/extensions/xep-0215.html" - "[XEP-0215: External Service Discovery]. " - "This module is included in ejabberd since version 20.04."), + "[XEP-0215: External Service Discovery]."), + note => "added in 20.04", opts => [{access, #{value => ?T("AccessName"), From 345af5a5352bd6a0a5b36f3851317182f2a99759 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Mar 2024 16:09:50 +0100 Subject: [PATCH 0544/1302] Remove ... in example configuration: it is assumed and reduces verbosity --- src/mod_delegation.erl | 1 - src/mod_http_fileserver.erl | 8 +------- src/mod_http_upload.erl | 8 +------- src/mod_http_upload_quota.erl | 6 +----- src/mod_mqtt_bridge.erl | 4 +--- src/mod_ping.erl | 4 +--- src/mod_pres_counter.erl | 4 +--- src/mod_privilege.erl | 4 +--- src/mod_proxy65.erl | 4 +--- src/mod_pubsub.erl | 8 ++------ src/mod_roster.erl | 4 +--- src/mod_s2s_dialback.erl | 4 +--- src/mod_service_log.erl | 4 +--- src/mod_sip.erl | 4 +--- 14 files changed, 14 insertions(+), 53 deletions(-) diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index f45fea09f..d761b81b4 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -129,7 +129,6 @@ mod_doc() -> " server: sat-pubsub.example.org", "", "modules:", - " ...", " mod_delegation:", " namespaces:", " urn:xmpp:mam:1:", diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index f48bed8fe..19d59c958 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -561,18 +561,13 @@ mod_doc() -> "content type 'ogg' is defined, 'png' is redefined, and 'jpg' " "definition is deleted:"), ["listen:", - " ...", " -", " port: 5280", " module: ejabberd_http", " request_handlers:", - " ...", " /pub/content: mod_http_fileserver", - " ...", - " ...", "", "modules:", - " ...", " mod_http_fileserver:", " docroot: /var/www", " accesslog: /var/log/ejabberd/access.log", @@ -585,5 +580,4 @@ mod_doc() -> " content_types:", " .ogg: audio/ogg", " .png: image/png", - " default_content_type: text/html", - " ..."]}]}. + " default_content_type: text/html"]}]}. diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 7cf404946..f40fc8945 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -384,23 +384,17 @@ mod_doc() -> " street: Elm Street"]}]}}], example => ["listen:", - " ...", " -", " port: 5443", " module: ejabberd_http", " tls: true", " request_handlers:", - " ...", " /upload: mod_http_upload", - " ...", - " ...", "", "modules:", - " ...", " mod_http_upload:", " docroot: /ejabberd/upload", - " put_url: \"https://@HOST@:5443/upload\"", - " ..."]}. + " put_url: \"https://@HOST@:5443/upload\""]}. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 3782079f8..3935588f7 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -134,19 +134,15 @@ mod_doc() -> "to use the quota feature. You can stick to the default names " "and just specify access rules such as those in this example:"), ["shaper_rules:", - " ...", " soft_upload_quota:", " 1000: all # MiB", " hard_upload_quota:", " 1100: all # MiB", - " ...", "", "modules:", - " ...", " mod_http_upload: {}", " mod_http_upload_quota:", - " max_days: 100", - " ..."]}]}. + " max_days: 100"]}]}. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 95d5052e6..8ba69288d 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -178,7 +178,6 @@ mod_doc() -> ?T("It is available since ejabberd 23.01.")], example => ["modules:", - " ...", " mod_mqtt_bridge:", " servers:", " \"mqtt://server.com\":", @@ -189,8 +188,7 @@ mod_doc() -> " \"remoteB\": \"localB\" # changes to 'remoteB' on remote server will be stored as 'localB' on local server", " authentication:", " certfile: \"/etc/ejabberd/mqtt_server.pem\"", - " replication_user: \"mqtt@xmpp.server.com\"", - " ..."], + " replication_user: \"mqtt@xmpp.server.com\""], opts => [{servers, #{value => "{ServerUrl: {publish: [TopicPairs], subscribe: [TopicPairs], authentication: [AuthInfo]}}", diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 4f7aab9ba..2947f1454 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -352,9 +352,7 @@ mod_doc() -> "The default value is 'none'.")}}], example => ["modules:", - " ...", " mod_ping:", " send_pings: true", " ping_interval: 4 min", - " timeout_action: kill", - " ..."]}. + " timeout_action: kill"]}. diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl index efafe2a0f..58029eadc 100644 --- a/src/mod_pres_counter.erl +++ b/src/mod_pres_counter.erl @@ -150,8 +150,6 @@ mod_doc() -> ?T("The time interval. The default value is '1' minute.")}}], example => ["modules:", - " ...", " mod_pres_counter:", " count: 5", - " interval: 30 secs", - " ..."]}. + " interval: 30 secs"]}. diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index ad9362368..7524af181 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -163,15 +163,13 @@ mod_doc() -> "The default value is 'none'.")}}]}], example => ["modules:", - " ...", " mod_privilege:", " roster:", " get: all", " presence:", " managed_entity: all", " message:", - " outgoing: all", - " ..."]}. + " outgoing: all"]}. depends(_, _) -> []. diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 8f0ce6f8d..e606260ea 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -274,7 +274,6 @@ mod_doc() -> " proxyrate: 10240", "", "modules:", - " ...", " mod_proxy65:", " host: proxy1.example.org", " name: \"File Transfer Proxy\"", @@ -284,5 +283,4 @@ mod_doc() -> " access: proxy65_access", " shaper: proxy65_shaper", " recbuf: 10240", - " sndbuf: 10240", - " ..."]}. + " sndbuf: 10240"]}. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 4cd2bd59e..1c6de26df 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4524,7 +4524,6 @@ mod_doc() -> [{?T("Example of configuration that uses flat nodes as default, " "and allows use of flat, hometree and pep nodes:"), ["modules:", - " ...", " mod_pubsub:", " access_createnode: pubsub_createnode", " max_subscriptions_node: 100", @@ -4534,14 +4533,12 @@ mod_doc() -> " max_items: 4", " plugins:", " - flat", - " - pep", - " ..."]}, + " - pep"]}, {?T("Using relational database requires using mod_pubsub with " "db_type 'sql'. Only flat, hometree and pep plugins supports " "SQL. The following example shows previous configuration " "with SQL usage:"), ["modules:", - " ...", " mod_pubsub:", " db_type: sql", " access_createnode: pubsub_createnode", @@ -4549,6 +4546,5 @@ mod_doc() -> " last_item_cache: false", " plugins:", " - flat", - " - pep", - " ..."]} + " - pep"]} ]}. diff --git a/src/mod_roster.erl b/src/mod_roster.erl index b205db2ee..18d69a7ee 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -1410,8 +1410,6 @@ mod_doc() -> ?T("Same as top-level _`cache_life_time`_ option, but applied to this module only.")}}], example => ["modules:", - " ...", " mod_roster:", " versioning: true", - " store_current_id: false", - " ..."]}. + " store_current_id: false"]}. diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 54a7aa10a..620ab1b8c 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -93,14 +93,12 @@ mod_doc() -> "is 'all'.")}}], example => ["modules:", - " ...", " mod_s2s_dialback:", " access:", " allow:", " server: legacy.domain.tld", " server: invalid-cert.example.org", - " deny: all", - " ..."]}. + " deny: all"]}. s2s_in_features(Acc, _) -> [#db_feature{errors = true}|Acc]. diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl index 0239942c4..434798fd9 100644 --- a/src/mod_service_log.erl +++ b/src/mod_service_log.erl @@ -92,9 +92,7 @@ mod_doc() -> "to which stanzas will be forwarded.")}}], example => ["modules:", - " ...", " mod_service_log:", " loggers:", " - xmpp-server.tld", - " - component.domain.tld", - " ..."]}. + " - component.domain.tld"]}. diff --git a/src/mod_sip.erl b/src/mod_sip.erl index e8b7cb2f8..cb77227bf 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -437,7 +437,6 @@ mod_doc() -> "cannot omit \"port\" or \"scheme\").")}}], example => ["modules:", - " ...", " mod_sip:", " always_record_route: false", " record_route: \"sip:example.com;lr\"", @@ -449,7 +448,6 @@ mod_doc() -> " via:", " - tls://sip-tls.example.com:5061", " - tcp://sip-tcp.example.com:5060", - " - udp://sip-udp.example.com:5060", - " ..."]}. + " - udp://sip-udp.example.com:5060"]}. -endif. From d8cdd82bf8a69e1db385b6d4e239d57ce20a900e Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Mar 2024 16:36:47 +0100 Subject: [PATCH 0545/1302] Rewrite vcard example configuration to not require extended format --- src/mod_http_upload.erl | 31 ++++++++++++++++--------------- src/mod_muc.erl | 31 ++++++++++++++++--------------- src/mod_proxy65.erl | 31 ++++++++++++++++--------------- src/mod_pubsub.erl | 31 ++++++++++++++++--------------- src/mod_vcard.erl | 33 ++++++++++++++++++--------------- 5 files changed, 82 insertions(+), 75 deletions(-) diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index f40fc8945..cf5facb56 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -367,21 +367,22 @@ mod_doc() -> "of vCard. Since the representation has no attributes, " "the mapping is straightforward."), example => - [{?T("For example, the following XML representation of vCard:"), - ["", - " Conferences", - " ", - " ", - " Elm Street", - " ", - ""]}, - {?T("will be translated to:"), - ["vcard:", - " fn: Conferences", - " adr:", - " -", - " work: true", - " street: Elm Street"]}]}}], + ["# This XML representation of vCard:", + "# ", + "# Conferences", + "# ", + "# ", + "# Elm Street", + "# ", + "# ", + "# ", + "# is translated to:", + "vcard:", + " fn: Conferences", + " adr:", + " -", + " work: true", + " street: Elm Street"]}}], example => ["listen:", " -", diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 216c1277b..e297d866f 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1654,21 +1654,22 @@ mod_doc() -> "of vCard. Since the representation has no attributes, " "the mapping is straightforward."), example => - [{?T("For example, the following XML representation of vCard:"), - ["", - " Conferences", - " ", - " ", - " Elm Street", - " ", - ""]}, - {?T("will be translated to:"), - ["vcard:", - " fn: Conferences", - " adr:", - " -", - " work: true", - " street: Elm Street"]}]}}, + ["# This XML representation of vCard:", + "# ", + "# Conferences", + "# ", + "# ", + "# Elm Street", + "# ", + "# ", + "# ", + "# is translated to:", + "vcard:", + " fn: Conferences", + " adr:", + " -", + " work: true", + " street: Elm Street"]}}, {cleanup_affiliations_on_start, #{value => "true | false", note => "added in 22.05", diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index e606260ea..9768e9535 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -239,21 +239,22 @@ mod_doc() -> "of vCard. Since the representation has no attributes, " "the mapping is straightforward."), example => - [{?T("For example, the following XML representation of vCard:"), - ["", - " Conferences", - " ", - " ", - " Elm Street", - " ", - ""]}, - {?T("will be translated to:"), - ["vcard:", - " fn: Conferences", - " adr:", - " -", - " work: true", - " street: Elm Street"]}]}}], + ["# This XML representation of vCard:", + "# ", + "# Conferences", + "# ", + "# ", + "# Elm Street", + "# ", + "# ", + "# ", + "# is translated to:", + "vcard:", + " fn: Conferences", + " adr:", + " -", + " work: true", + " street: Elm Street"]}}], example => ["acl:", " admin:", diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 1c6de26df..0fd73cf39 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4504,21 +4504,22 @@ mod_doc() -> "representation of vCard. Since the representation has " "no attributes, the mapping is straightforward."), example => - [{?T("The following XML representation of vCard:"), - ["", - " PubSub Service", - " ", - " ", - " Elm Street", - " ", - ""]}, - {?T("will be translated to:"), - ["vcard:", - " fn: PubSub Service", - " adr:", - " -", - " work: true", - " street: Elm Street"]}]}} + ["# This XML representation of vCard:", + "# ", + "# Conferences", + "# ", + "# ", + "# Elm Street", + "# ", + "# ", + "# ", + "# is translated to:", + "vcard:", + " fn: Conferences", + " adr:", + " -", + " work: true", + " street: Elm Street"]}} ], example => [{?T("Example of configuration that uses flat nodes as default, " diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 43721e38d..9a6a42b23 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -667,18 +667,21 @@ mod_doc() -> "of vCard. Since the representation has no attributes, " "the mapping is straightforward."), example => - [{?T("For example, the following XML representation of vCard:"), - ["", - " Conferences", - " ", - " ", - " Elm Street", - " ", - ""]}, - {?T("will be translated to:"), - ["vcard:", - " fn: Conferences", - " adr:", - " -", - " work: true", - " street: Elm Street"]}]}}]}. + ["# This XML representation of vCard:", + "# ", + "# ", + "# Conferences", + "# ", + "# ", + "# Elm Street", + "# ", + "# ", + "# ", + "# is translated to:", + "# ", + "vcard:", + " fn: Conferences", + " adr:", + " -", + " work: true", + " street: Elm Street"]}}]}. From 45ca11ecd0e3d98c140dd4739b31984b6ab8f8ce Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 3 Apr 2024 22:55:04 +0200 Subject: [PATCH 0546/1302] Improve markdown of generated documentation --- src/mod_admin_extra.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 003d2b5de..fced7c490 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -170,7 +170,7 @@ get_commands_spec() -> #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], desc = "Delete users that didn't log in last days, or that never logged", longdesc = "To protect admin accounts, configure this for example:\n" - "```\n" + "``` yaml\n" "access_rules:\n" " protect_old_users:\n" " - allow: admin\n" @@ -186,7 +186,7 @@ get_commands_spec() -> #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge], desc = "Delete users that didn't log in last days in vhost, or that never logged", longdesc = "To protect admin accounts, configure this for example:\n" - "```\n" + "``` yaml\n" "access_rules:\n" " delete_old_users:\n" " - deny: admin\n" From 7ea2c6eabf21e8b9e7606b7a107c7632da975c60 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 3 Apr 2024 22:55:58 +0200 Subject: [PATCH 0547/1302] Improve markdown of md files --- CONTAINER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index d52f4255c..9fa5492b8 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -249,7 +249,7 @@ For this you can either: Example to connect a local `ejabberdctl` to a containerized ejabberd: 1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: -``` +```sh docker run --name ejabberd -it \ -e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \ -p 5210:5210 -p 5222:5222 \ @@ -260,7 +260,7 @@ docker run --name ejabberd -it \ 4. Now use `ejabberdctl` in your local ejabberd deployment To connect using a local `ejabberd` script: -``` +```sh ERL_DIST_PORT=5210 _build/dev/rel/ejabberd/bin/ejabberd ping ``` From 0b16cbdddef196df869fadcb3c2b61f8d7ba7856 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Apr 2024 00:49:57 +0200 Subject: [PATCH 0548/1302] Remove vcard example config in mod_proxy65, it breaks PDF Docs generation --- src/mod_proxy65.erl | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 9768e9535..5805613c8 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -237,24 +237,7 @@ mod_doc() -> "by some XMPP clients in Service Discovery. The value of " "'vCard' is a YAML map constructed from an XML representation " "of vCard. Since the representation has no attributes, " - "the mapping is straightforward."), - example => - ["# This XML representation of vCard:", - "# ", - "# Conferences", - "# ", - "# ", - "# Elm Street", - "# ", - "# ", - "# ", - "# is translated to:", - "vcard:", - " fn: Conferences", - " adr:", - " -", - " work: true", - " street: Elm Street"]}}], + "the mapping is straightforward.")}}], example => ["acl:", " admin:", From e744665171e06d4e55802ef4ff124c7d15501ce1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Apr 2024 01:18:20 +0200 Subject: [PATCH 0549/1302] Fix links in mix.exs --- mix.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index a97b61443..3e2145f56 100644 --- a/mix.exs +++ b/mix.exs @@ -206,9 +206,9 @@ defmodule Ejabberd.MixProject do "mix.exs", "rebar.config", "rebar.config.script", "vars.config"], maintainers: ["ProcessOne"], licenses: ["GPL-2.0-or-later"], - links: %{"Site" => "https://www.ejabberd.im", - "Documentation" => "http://docs.ejabberd.im", - "Source" => "https://github.com/processone/ejabberd", + links: %{"ejabberd.im" => "https://www.ejabberd.im", + "ejabberd Docs" => "http://docs.ejabberd.im", + "GitHub" => "https://github.com/processone/ejabberd", "ProcessOne" => "http://www.process-one.net/"}] end From 5c6a399f5be86b0161aec4ee06b81bd12d5f433e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 24 Apr 2024 17:23:53 +0200 Subject: [PATCH 0550/1302] Update links to some moved content in docs.ejabberd.im --- COMPILE.md | 2 +- CONTAINER.md | 4 ++-- Makefile.in | 2 +- README.md | 4 ++-- src/ejabberd_web_admin.erl | 2 +- tools/generate-doap.sh | 2 +- tools/make-installers | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/COMPILE.md b/COMPILE.md index 4609ac318..0313d0b87 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -7,7 +7,7 @@ from source code. For a more detailed explanation, please check the ejabberd Docs: [Source Code Installation][docs-source]. -[docs-source]: https://docs.ejabberd.im/admin/installation/#source-code +[docs-source]: https://docs.ejabberd.im/admin/install/source/ Requirements diff --git a/CONTAINER.md b/CONTAINER.md index 9fa5492b8..6fbb12534 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -29,7 +29,7 @@ repository. Check the [differences between `ejabberd` and `ecs` images](https://github.com/processone/docker-ejabberd/blob/master/ecs/HUB-README.md#alternative-image-in-github). If you are using a Windows operating system, check the tutorials mentioned in -[ejabberd Docs > Docker Image](https://docs.ejabberd.im/admin/installation/#docker-image). +[ejabberd Docs > Docker Image](https://docs.ejabberd.im/admin/install/container/#ejabberd-container-image). Start ejabberd @@ -108,7 +108,7 @@ docker exec -it ejabberd ejabberdctl register admin localhost passw0rd ``` Then edit conf/ejabberd.yml and add the ACL as explained in -[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/installation/#administration-account) +[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account) ### Check ejabberd log files diff --git a/Makefile.in b/Makefile.in index 103fa38a7..34728f66a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -258,7 +258,7 @@ _build/edoc/docs.md: edoc_compile > _build/edoc/docs.md _build/edoc/logo.png: edoc_compile - wget https://docs.ejabberd.im/static/shared/images/footer_logo_e.png -O _build/edoc/logo.png + wget https://docs.ejabberd.im/assets/img/footer_logo_e.png -O _build/edoc/logo.png #. #' copy-files diff --git a/README.md b/README.md index 15817f666..b307277ac 100644 --- a/README.md +++ b/README.md @@ -109,14 +109,14 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [erlang]: https://www.erlang.org/ [features]: https://docs.ejabberd.im/admin/introduction/ [fluux]: https://fluux.io/ -[homebrew]: https://docs.ejabberd.im/admin/installation/#homebrew +[homebrew]: https://docs.ejabberd.im/admin/install/homebrew/ [hubecs]: https://hub.docker.com/r/ejabberd/ecs/ [im]: https://ejabberd.im/ [issues]: https://github.com/processone/ejabberd/issues [localization]: https://docs.ejabberd.im/developer/extending-ejabberd/localization/ [mqtt]: https://mqtt.org/ [muc]: xmpp:ejabberd@conference.process-one.net -[osp]: https://docs.ejabberd.im/admin/installation/#operating-system-packages +[osp]: https://docs.ejabberd.im/admin/install/os-package/ [p1contact]: https://www.process-one.net/en/company/contact/ [p1download]: https://www.process-one.net/en/ejabberd/downloads/ [p1home]: https://www.process-one.net/en/ejabberd/ diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index f34887f66..d81bd0f6e 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -625,7 +625,7 @@ maybe_disclaimer_not_admin(MenuItems, AJID, Lang) -> {[_], []} -> [?XREST(?T("Apparently your account has no administration rights in this server. " "Please check how to grant admin rights in: " - "https://docs.ejabberd.im/admin/installation/#administration-account")) + "https://docs.ejabberd.im/admin/install/next-steps/#administration-account")) ]; _ -> [] diff --git a/tools/generate-doap.sh b/tools/generate-doap.sh index fb146599f..74de66134 100755 --- a/tools/generate-doap.sh +++ b/tools/generate-doap.sh @@ -48,7 +48,7 @@ write_doap_head() - + diff --git a/tools/make-installers b/tools/make-installers index ee0ee1ed1..e1c7c47eb 100755 --- a/tools/make-installers +++ b/tools/make-installers @@ -67,7 +67,7 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] home_url='https://www.ejabberd.im' doc_url='https://docs.ejabberd.im' upgrade_url="$doc_url/admin/upgrade/#specific-version-upgrade-notes" -admin_url="$doc_url/admin/installation/#administration-account" +admin_url="$doc_url/admin/install/next-steps/#administration-account" default_code_dir="/opt/$rel_name-$rel_vsn" default_data_dir="/opt/$rel_name" tmp_dir=$(mktemp -d "/tmp/.$rel_name.XXXXXX") From b0afe4946d48f19e3dffc2a114b1dec6c86dc7a6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 13 Feb 2024 13:09:32 +0100 Subject: [PATCH 0551/1302] Add Sync tool to "make relive" with Rebar3 How to use: - Compile ejabberd with Rebar3 - Start ejabberd with "make relive" - Edit some ejabberd source code file - Save the file, and Sync will compile and reload it automatically I've added src_dirs option so Sync doesn't act on dependencies, which would produce many garbage log lines. However, now it only works if the parent directory is named "ejabberd" Sync requires at least Erlang/OTP 21, which introduced the new try-catch syntax to retrieve the stacktrace https://www.erlang.org/patches/otp-21.0 References: https://hex.pm/packages/sync https://github.com/rustyio/sync --- .gitignore | 1 + Makefile.in | 4 +++- rebar.config | 2 ++ rel/relive.config | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5c63117ef..fbb2c2135 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ /priv/bin/captcha*sh /priv/sql /rel/ejabberd +/recompile.log /_build /database/ /.rebar diff --git a/Makefile.in b/Makefile.in index 34728f66a..ec6f911eb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -150,6 +150,7 @@ ifeq ($(REBAR_ENABLE_ELIXIR),true) ELIXIR_LIBDIR_RAW=$(shell elixir -e "IO.puts(:filename.dirname(:code.lib_dir(:elixir)))" -e ":erlang.halt") ELIXIR_LIBDIR=":$(ELIXIR_LIBDIR_RAW)" EXPLICIT_ELIXIR_COMPILE=MIX_ENV=default mix compile.elixir + EXPLICIT_ELIXIR_COMPILE_DEV=MIX_ENV=dev mix compile.elixir PREPARE_ELIXIR_SCRIPTS=$(MKDIR_P) rel/overlays; cp $(ELIXIR_LIBDIR_RAW)/../bin/iex rel/overlays/; cp $(ELIXIR_LIBDIR_RAW)/../bin/elixir rel/overlays/; sed -i 's|ERTS_BIN=$$|ERTS_BIN=$$SCRIPT_PATH/../../erts-{{erts_vsn}}/bin/|' rel/overlays/elixir endif ifeq "$(REBAR_VER)" "3" @@ -170,7 +171,7 @@ endif CLEANARG=--all REBARREL=$(REBAR) as prod tar REBARDEV=$(REBAR) as dev release - RELIVECMD=$(REBAR) relive + RELIVECMD=$(REBAR) as dev relive REL_LIB_DIR = _build/dev/rel/ejabberd/lib COPY_REL_TARGET = dev GET_DEPS_TRANSLATIONS=$(REBAR) as translations $(GET_DEPS) @@ -377,6 +378,7 @@ uninstall-librel: # relive: + $(EXPLICIT_ELIXIR_COMPILE_DEV) $(RELIVECMD) relivelibdir=$(shell pwd)/$(DEPSDIR) diff --git a/rebar.config b/rebar.config index bacf4a79c..46c904f91 100644 --- a/rebar.config +++ b/rebar.config @@ -274,6 +274,7 @@ {copy, "ejabberdctl.cfg.example", "conf/ejabberdctl.cfg"}, {copy, "ejabberd.yml.example", "conf/ejabberd.yml"}]}]}]}, {dev, [{post_hooks, [{release, "rel/setup-dev.sh rebar3"}]}, + {deps, [{if_version_above, "20", sync}]}, {relx, [{debug_info, keep}, {dev_mode, true}, {include_erts, true}, @@ -289,6 +290,7 @@ {alias, [{relive, [{shell, "--apps ejabberd \ --config rel/relive.config \ + --eval sync:go(). \ --script rel/relive.escript \ --name ejabberd@localhost"}]} ]}. diff --git a/rel/relive.config b/rel/relive.config index 7e3901fd4..49da88b79 100644 --- a/rel/relive.config +++ b/rel/relive.config @@ -1,3 +1,4 @@ [{mnesia, [{dir, "_build/relive/database"}]}, + {sync,[{src_dirs, {replace, [{"ejabberd/src", []}]}}]}, {ejabberd, [{config, "_build/relive/conf/ejabberd.yml"}, {log_path, "_build/relive/logs/ejabberd.log"}]}]. From eb975268f43d6df7d7d7b2d7398a6a1f498a2a12 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Apr 2024 16:47:50 +0200 Subject: [PATCH 0552/1302] Add ExSync tool to "make relive" with Mix How to use: - Compile ejabberd with Mix - Start ejabberd with "make relive" - Edit some ejabberd source code file - Save the file, and ExSync will compile and reload it automatically ExSync depends on FileSystem library, which requires inotify-tools, see https://github.com/falood/file_system#system-support References: https://hex.pm/packages/exsync https://github.com/falood/exsync --- config/runtime.exs | 2 ++ mix.exs | 3 +++ mix.lock | 2 ++ 3 files changed, 7 insertions(+) diff --git a/config/runtime.exs b/config/runtime.exs index fb2372ea6..adfc18c06 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -11,3 +11,5 @@ config :ejabberd, log_path: Path.join(rootpath, "logs/ejabberd.log") config :mnesia, dir: Path.join(rootpath, "database/") +config :exsync, + reload_callback: {:ejabberd_admin, :update, []} diff --git a/mix.exs b/mix.exs index 3e2145f56..448555b49 100644 --- a/mix.exs +++ b/mix.exs @@ -164,6 +164,8 @@ defmodule Ejabberd.MixProject do for {:true, dep} <- [{config(:pam), {:epam, "~> 1.0"}}, {Mix.env() == :translations, {:ejabberd_po, git: "https://github.com/processone/ejabberd-po.git"}}, + {Mix.env() == :dev, + {:exsync, "~> 0.2"}}, {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, @@ -180,6 +182,7 @@ defmodule Ejabberd.MixProject do defp cond_apps do for {:true, app} <- [{config(:stun), :stun}, + {Map.has_key?(System.get_env(), "RELIVE"), :exsync}, {config(:tools), :observer}], do: app end diff --git a/mix.lock b/mix.lock index fb8dc7fac..ea5a398b6 100644 --- a/mix.lock +++ b/mix.lock @@ -9,10 +9,12 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, + "exsync": {:hex, :exsync, "0.4.0", "41d1f1975a387ba7983c5b341541d9feaa90b9b654eaf245cde814d26dcea1c5", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "e1c4aed25806f1f9b60c6373a4b3f0e0db06e1d00e111e77eab9f21a997a5eef"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, + "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, From 33c6b3700e9ceafa9966e1f9efe41897d741e2a4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Apr 2024 18:50:44 +0200 Subject: [PATCH 0553/1302] Ammend ejabberd_admin:update/0 from adbccbe to be used by Rebar3 Sync --- src/ejabberd_admin.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index d8b1e2d1c..28215491c 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -603,8 +603,9 @@ update() -> Mods = ejabberd_admin:update_list(), io:format("Updating modules: ~p~n", [Mods]), ejabberd_admin:update("all"), - io:format("Updated modules: ", []), - Mods -- ejabberd_admin:update_list(). + Mods2 = Mods -- ejabberd_admin:update_list(), + io:format("Updated modules: ~p~n", [Mods2]), + ok. %%% %%% Account management From 374f6b8e7dddc48fd5b3228b7e22b4afd867d41d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 24 Apr 2024 19:54:45 +0200 Subject: [PATCH 0554/1302] Improve recent commit so users cannot prevent banning, fixes 44bafa4 (#4201) --- src/mod_admin_extra.erl | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index fced7c490..0353138a5 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1081,17 +1081,22 @@ ban_account_v2_b(User, Host, ReasonText) -> Pass = ejabberd_auth:get_password_s(User, Host), Last = get_last(User, Host), BanDate = xmpp_util:encode_timestamp(erlang:timestamp()), - BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate), + Hash = get_hash_value(User, Host), + BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate, Hash), ok = private_set2(User, Host, BanPrivateXml), ok = set_random_password_v2(User, Host), kick_sessions(User, Host, Reason), ok. +get_hash_value(User, Host) -> + Cookie = misc:atom_to_binary(erlang:get_cookie()), + misc:term_to_base64(crypto:hash(sha256, <>)). + set_random_password_v2(User, Server) -> NewPass = p1_rand:get_string(), ok = ejabberd_auth:set_password(User, Server, NewPass). -build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate) -> +build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) -> PassEls = build_pass_els(Pass), #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}], @@ -1099,7 +1104,8 @@ build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate) -> #xmlel{name = <<"password">>, attrs = [], children = PassEls}, #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]}, #xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]}, - #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]} + #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]}, + #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, Hash}]} ]}. build_pass_els(Pass) when is_binary(Pass) -> @@ -1125,11 +1131,16 @@ get_ban_details(User, Host) -> LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>), LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>), BanDate = fxml:get_subtag_cdata(El, <<"bandate">>), - [{"reason", Reason}, - {"bandate", BanDate}, - {"lastdate", LastDate}, - {"lastreason", LastReason} - ]. + Hash = fxml:get_subtag_cdata(El, <<"hash">>), + case Hash == get_hash_value(User, Host) of + true -> + [{"reason", Reason}, + {"bandate", BanDate}, + {"lastdate", LastDate}, + {"lastreason", LastReason}]; + false -> + [] + end. is_banned(User, Host) -> case lists:keyfind("bandate", 1, get_ban_details(User, Host)) of From 2bfc4b0f5d2b4d3c19a1446177837b65b35faad0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 24 Apr 2024 20:17:02 +0200 Subject: [PATCH 0555/1302] Use "jabber:" namespace that clients cannot use (#4201) As mentioned in: Certain namespaces are reserved in Jabber (namespaces beginning with 'jabber:' or 'http://jabber.org/', as well as 'vcard-temp'). If a user attempts to get or set jabber:iq:private data in a reserved namespace, historically some server implementations have chosen to return an error (commonly "Not Acceptable") to the sender. https://xmpp.org/extensions/xep-0049.html#example-5 --- src/mod_admin_extra.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 0353138a5..9421002c9 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1099,7 +1099,7 @@ set_random_password_v2(User, Server) -> build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) -> PassEls = build_pass_els(Pass), #xmlel{name = <<"banned">>, - attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}], + attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}], children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]}, #xmlel{name = <<"password">>, attrs = [], children = PassEls}, #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]}, @@ -1126,7 +1126,7 @@ build_pass_els(#scram{storedkey = StoredKey, %% Get ban details get_ban_details(User, Host) -> - [El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>), + [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>), Reason = fxml:get_subtag_cdata(El, <<"reason">>), LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>), LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>), @@ -1168,7 +1168,7 @@ unban_account2(User, Host) -> private_set2(User, Host, UnBanPrivateXml). get_oldpass(User, Host) -> - [El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>), + [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>), Pass = fxml:get_subtag(El, <<"password">>), get_pass(Pass). @@ -1187,7 +1187,7 @@ get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) -> iterationcount = binary_to_integer(IterationCount)}. build_unban_xmlel() -> - #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}]}. + #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}]}. %%% %%% Sessions From 94a0aa696706ae73a9190afafe3233be6b09e4db Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 24 Apr 2024 21:02:31 +0200 Subject: [PATCH 0556/1302] Add support to provide ban detail when account logins (#4201) --- src/ejabberd_auth.erl | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 594fc1bd7..c37d4c187 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -236,14 +236,16 @@ check_password_with_authmodule(User, AuthzId, Server, Password) -> -spec check_password_with_authmodule( binary(), binary(), binary(), binary(), binary(), - digest_fun() | undefined) -> false | {true, atom()}. + digest_fun() | undefined) -> false | {false, atom(), binary()} | {true, atom()}. check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> - case jid:nodeprep(AuthzId) of - error -> + case {jid:nodeprep(AuthzId), get_is_banned(LUser, LServer)} of + {error, _} -> false; - LAuthzId -> + {_, {is_banned, BanReason}} -> + {false, 'account-disabled', BanReason}; + {LAuthzId, _} -> untag_stop( lists:foldl( fun(Mod, false) -> @@ -373,10 +375,15 @@ get_password_s(User, Server) -> Password -> Password end. --spec get_password_with_authmodule(binary(), binary()) -> {false | password(), module()}. +-spec get_password_with_authmodule(binary(), binary()) -> + {false | {false, atom(), binary()} | password(), module()}. get_password_with_authmodule(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> + case get_is_banned(LUser, LServer) of + {is_banned, BanReason} -> + {{false, 'account-disabled', BanReason}, module_not_consulted}; + not_banned -> case lists:foldl( fun(M, {error, _}) -> {db_get_password(LUser, LServer, M), M}; @@ -387,6 +394,7 @@ get_password_with_authmodule(User, Server) -> {Password, Module}; {error, Module} -> {false, Module} + end end; _ -> {false, undefined} @@ -567,6 +575,15 @@ backend_type(Mod) -> password_format(LServer) -> ejabberd_option:auth_password_format(LServer). +get_is_banned(User, Server) -> + case mod_admin_extra:get_ban_details(User, Server) of + [] -> + not_banned; + BanDetails -> + {_, ReasonText} = lists:keyfind("reason", 1, BanDetails), + {is_banned, <<"Account is banned: ", ReasonText/binary>>} + end. + %%%---------------------------------------------------------------------- %%% Backend calls %%%---------------------------------------------------------------------- From 5870e2e3780559b9cd300d5c85dac916ea40eb26 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 24 Apr 2024 23:34:53 +0200 Subject: [PATCH 0557/1302] Use updated xmpp library with minor improvement in auth (#4201) --- mix.exs | 2 +- mix.lock | 4 ++-- rebar.config | 2 +- rebar.lock | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mix.exs b/mix.exs index 448555b49..2d8dcf12a 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.8.0"}, + {:xmpp, git: "https://github.com/processone/xmpp", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index ea5a398b6..fb8008d0e 100644 --- a/mix.lock +++ b/mix.lock @@ -33,7 +33,7 @@ "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.8.1", "134a350dbc6e2e99512fb38669191c1d1c134b3b6836f4c6740801882afa650a", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "32d4a308a6613e4e4155fa4e82eccdc8833096d0fa8d99c8d428c800611e59d3"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "ddaaf513366be7f96816046c6e269e2f27684762", []}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.config b/rebar.config index 46c904f91..729e79e40 100644 --- a/rebar.config +++ b/rebar.config @@ -70,7 +70,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.12"}}}}, - {xmpp, "~> 1.8.1", {git, "https://github.com/processone/xmpp", {tag, "1.8.1"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {branch, "master"}}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} ]}. diff --git a/rebar.lock b/rebar.lock index a1cc16216..76865a54d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -27,7 +27,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.12">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.1">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"ddaaf513366be7f96816046c6e269e2f27684762"}}, + 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. [ {pkg_hash,[ @@ -55,7 +58,6 @@ {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, {<<"stun">>, <<"A65DF67A8AAAECB6A94D687977B2E9F161820819910CB97BBE26A3525356525B">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"134A350DBC6E2E99512FB38669191C1D1C134B3B6836F4C6740801882AFA650A">>}, {<<"yconf">>, <<"E22998B3D7728270BDD06162A9515BD142B14FAE8927CBDBD3EF639C32AA6F7A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -82,6 +84,5 @@ {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, {<<"stun">>, <<"A2055032B6D338D0454142004BCB12FAFB0C64AB1F273F1D0C6923EBBC8EDE40">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"32D4A308A6613E4E4155FA4E82ECCDC8833096D0FA8D99C8D428C800611E59D3">>}, {<<"yconf">>, <<"7FF2AB24D3C9833842716B9AAAA01A8F96641A7695CBB701B03445C4DEF01117">>}]} ]. From 395a73bbe01bd756c65f1df1ffe184718faf0df6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 25 Apr 2024 00:22:45 +0200 Subject: [PATCH 0558/1302] Handle case when mod_private is not enabled (#4201) --- src/mod_admin_extra.erl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 9421002c9..e5e5df357 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1126,7 +1126,14 @@ build_pass_els(#scram{storedkey = StoredKey, %% Get ban details get_ban_details(User, Host) -> - [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>), + case private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>) of + [El] -> + get_ban_details(User, Host, El); + [] -> + [] + end. + +get_ban_details(User, Host, El) -> Reason = fxml:get_subtag_cdata(El, <<"reason">>), LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>), LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>), @@ -1694,6 +1701,12 @@ private_get(Username, Host, Element, Ns) -> binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))). private_get2(Username, Host, Element, Ns) -> + case gen_mod:is_loaded(Host, mod_private) of + true -> private_get3(Username, Host, Element, Ns); + false -> [] + end. + +private_get3(Username, Host, Element, Ns) -> ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]}, mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host), [{Ns, ElementXml}]). From 0da767f56830a32b73307b2cd1e0608faddddf76 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 29 Apr 2024 17:17:06 +0200 Subject: [PATCH 0559/1302] ban_account/unban: When mod_private is disabled, return meaningful error (#4201) --- src/mod_admin_extra.erl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index e5e5df357..8616bdd24 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1069,11 +1069,16 @@ prepare_reason(Reason) when is_binary(Reason) -> %% Ban account v2 ban_account_v2(User, Host, ReasonText) -> - case is_banned(User, Host) of - true -> - account_was_already_banned; + case gen_mod:is_loaded(Host, mod_private) of false -> - ban_account_v2_b(User, Host, ReasonText) + mod_private_is_required_but_disabled; + true -> + case is_banned(User, Host) of + true -> + account_was_already_banned; + false -> + ban_account_v2_b(User, Host, ReasonText) + end end. ban_account_v2_b(User, Host, ReasonText) -> @@ -1161,11 +1166,16 @@ is_banned(User, Host) -> %% Unban account unban_account(User, Host) -> - case is_banned(User, Host) of + case gen_mod:is_loaded(Host, mod_private) of false -> - account_was_not_banned; + mod_private_is_required_but_disabled; true -> - unban_account2(User, Host) + case is_banned(User, Host) of + false -> + account_was_not_banned; + true -> + unban_account2(User, Host) + end end. unban_account2(User, Host) -> From b46165740784aafc003b04d6914f8e6b1a510cae Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 30 Apr 2024 15:54:40 +0200 Subject: [PATCH 0560/1302] Bump idna from 6.0.0 to 6.1.1 Bumps [idna](https://github.com/benoitc/erlang-idna) from 6.0.0 to 6.1.1. - [Changelog](https://github.com/benoitc/erlang-idna/blob/master/CHANGELOG) - [Commits](https://github.com/benoitc/erlang-idna/compare/6.0.0...6.1.1) --- updated-dependencies: - dependency-name: idna dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index fb8008d0e..dce26ef1d 100644 --- a/mix.lock +++ b/mix.lock @@ -15,7 +15,7 @@ "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, From f15d5d073e2f08186f6386f95a5b7f840d477398 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 25 Apr 2024 03:51:29 +0200 Subject: [PATCH 0561/1302] configure.ac: When using rebar3, unlock dependencies that are disabled (#4212) --- configure.ac | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/configure.ac b/configure.ac index a128e6878..01afe6dcc 100644 --- a/configure.ac +++ b/configure.ac @@ -333,3 +333,19 @@ AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS) AC_OUTPUT + +AS_CASE([$rebar], + [*rebar3], [ + deps="" + AS_IF([test "x$stun" = "xfalse"], [deps="stun,$deps"]) + AS_IF([test "x$sqlite" = "xfalse"], [deps="sqlite3,$deps"]) + AS_IF([test "x$pgsql" = "xfalse"], [deps="p1_pgsql,$deps"]) + AS_IF([test "x$mysql" = "xfalse"], [deps="p1_mysql,$deps"]) + AS_IF([test "x$zlib" = "xfalse"], [deps="ezlib,$deps"]) + AS_IF([test "x$sip" = "xfalse"], [deps="esip,$deps"]) + AS_IF([test "x$redis" = "xfalse"], [deps="eredis,$deps"]) + AS_IF([test "x$pam" = "xfalse"], [deps="epam,$deps"]) + AS_IF([test "x$deps" = "x"], [], + [echo "unlocking disabled rebar3 dependencies: $deps" + $rebar unlock "$deps"]) + ]) From e9b9159d23af4cdccf1954ada632fb693f47b512 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 29 Apr 2024 17:46:35 +0200 Subject: [PATCH 0562/1302] rebar.config.script: Use port_compiler branch that supports OTP 27.0-rc2 If we provide this plugin in ejabberd, then dependencies will not download the old pc 1.14.0 which doesn't support OTP 27.0-rc2, and will use this one. --- rebar.config.script | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rebar.config.script b/rebar.config.script index 3deb6b894..4b2e8e702 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -417,6 +417,8 @@ GithubConfig = case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of end, Rules = [ + {[plugins], IsRebar3, + AppendList([{pc, ".*", {git, "https://github.com/blt/port_compiler.git", {branch, "otp-27"}}}]), []}, {[provider_hooks], IsRebar3, AppendList([{pre, [ {compile, {asn, compile}}, From c7c3cc10c521aa8dc44242b65cc72fa79ffef805 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 30 Apr 2024 11:11:01 +0200 Subject: [PATCH 0563/1302] mix.exs: Dirty workaround to get port_compiler with OTP 27 support --- mix.exs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 2d8dcf12a..f978fa0ff 100644 --- a/mix.exs +++ b/mix.exs @@ -13,7 +13,9 @@ defmodule Ejabberd.MixProject do erlc_options: erlc_options(), erlc_paths: ["asn1", "src"], # Elixir tests are starting the part of ejabberd they need - aliases: [test: "test --no-start"], + aliases: [ + test: "test --no-start", + "deps.get": ["deps.get", "pc_branch"]], start_permanent: Mix.env() == :prod, language: :erlang, dialyzer: dialyzer(), @@ -409,6 +411,14 @@ defmodule Ejabberd.MixProject do end end +defmodule Mix.Tasks.PcBranch do + use Mix.Task + def run(_) do + command = "find deps -name rebar.config.script -exec sed -i 's/AppendList..pc/AppendList\(\[{pc, {git, \"https:\\/\\/github.com\\/blt\\/port_compiler.git\", {branch, \"otp-27\"}}}/g' {} ';' " + :os.cmd(to_charlist(command)) + end +end + defmodule Mix.Tasks.Compile.Asn1 do use Mix.Task alias Mix.Compilers.Erlang From 13ebe89fdc8530986006c5fe628d9507588f4608 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 30 Apr 2024 13:47:40 +0200 Subject: [PATCH 0564/1302] Remove unused format_status/2 callback that is deprecated in OTP 27 --- src/ejabberd_acme.erl | 5 +---- src/ejabberd_pkix.erl | 6 +----- src/mod_mix.erl | 5 +---- src/mod_mqtt_ws.erl | 5 +---- 4 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 17e083bcb..4e997da9b 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -31,7 +31,7 @@ revoke_certificate/1, list_certificates/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, format_status/2]). + terminate/2, code_change/3]). -include("logger.hrl"). -include("ejabberd_commands.hrl"). @@ -158,9 +158,6 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -format_status(_Opt, Status) -> - Status. - %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl index c3cb2049c..6a9f08e8a 100644 --- a/src/ejabberd_pkix.erl +++ b/src/ejabberd_pkix.erl @@ -34,7 +34,7 @@ -export([ejabberd_started/0, config_reloaded/0, cert_expired/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, format_status/2]). + terminate/2, code_change/3]). -include("logger.hrl"). -define(CALL_TIMEOUT, timer:minutes(1)). @@ -225,10 +225,6 @@ terminate(_Reason, State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. --spec format_status(normal | terminate, list()) -> term(). -format_status(_Opt, Status) -> - Status. - %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 1c570c37f..a776250bd 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -33,7 +33,7 @@ -export([mod_doc/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, format_status/2]). + terminate/2, code_change/3]). %% Hooks -export([process_disco_info/1, process_disco_items/1, @@ -340,9 +340,6 @@ terminate(_Reason, State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -format_status(_Opt, Status) -> - Status. - %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/src/mod_mqtt_ws.erl b/src/mod_mqtt_ws.erl index 4525f9c0a..b9e1623b8 100644 --- a/src/mod_mqtt_ws.erl +++ b/src/mod_mqtt_ws.erl @@ -27,7 +27,7 @@ -export([peername/1, setopts/2, send/2, close/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3, format_status/2]). + terminate/2, code_change/3]). -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). @@ -132,9 +132,6 @@ terminate(_Reason, State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -format_status(_Opt, Status) -> - Status. - %%%=================================================================== %%% Internal functions %%%=================================================================== From f7dc4fa2ac604e46c20ca0353a6cadb76ffdf1c8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 2 May 2024 13:01:40 +0200 Subject: [PATCH 0565/1302] configure.ac: When using rebar3 with old Erlang, unlock some dependencies (#4213) --- configure.ac | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 01afe6dcc..4d3ea7643 100644 --- a/configure.ac +++ b/configure.ac @@ -346,6 +346,15 @@ AS_CASE([$rebar], AS_IF([test "x$redis" = "xfalse"], [deps="eredis,$deps"]) AS_IF([test "x$pam" = "xfalse"], [deps="epam,$deps"]) AS_IF([test "x$deps" = "x"], [], - [echo "unlocking disabled rebar3 dependencies: $deps" + [AC_MSG_NOTICE([unlocking disabled rebar3 dependencies: $deps]) + $rebar unlock "$deps"]) + deps="" + ERLANG_VERSION=m4_esyscmd([erl -noinput -noshell -eval 'erlang:display(list_to_integer(erlang:system_info(otp_release))), halt().']) + AS_IF([test "$ERLANG_VERSION" -lt "21"], [deps="luerl,$deps"]) + AS_IF([test "$ERLANG_VERSION" -lt "22"], [deps="lager,$deps"]) + AS_IF([test "$ERLANG_VERSION" -le "23"], [deps="jose,$deps"]) + AS_IF([test "$ERLANG_VERSION" -ge "27"], [deps="jiffy,$deps"]) + AS_IF([test "x$deps" = "x"], [], + [AC_MSG_NOTICE([unlocking rebar3 dependencies for old Erlang/OTP: $deps]) $rebar unlock "$deps"]) ]) From 025e2a5760c8452b9541da67f6f391ad3e70772a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 2 May 2024 10:33:47 +0200 Subject: [PATCH 0566/1302] CI and Runtime: Don't get explicitly dependencies (#4213) --- .github/workflows/ci.yml | 1 - .github/workflows/runtime.yml | 3 --- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7315f79c..428710cdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,7 +127,6 @@ jobs: --disable-elixir \ --disable-mssql \ --disable-odbc - make update make - run: make install -s diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index d6c2010a7..39c6e8196 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -67,7 +67,6 @@ jobs: --disable-elixir \ --disable-tools \ --disable-odbc - make update make - run: make xref @@ -203,7 +202,6 @@ jobs: --prefix=/tmp/ejabberd \ --enable-all \ --disable-odbc - make update make - run: make xref @@ -340,7 +338,6 @@ jobs: ./configure --with-rebar=mix \ --prefix=/tmp/ejabberd \ --enable-all - mix deps.get make - run: make xref From 392d7ee30bed0efaaa9db54c3c3429b0817f43c1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 29 Apr 2024 17:56:11 +0200 Subject: [PATCH 0567/1302] CI and Runtime: Now ejabberd and dependencies can compile with OTP 27.0-rc3 --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 428710cdd..c553c95d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25', '26', '27.0-rc1'] + otp: ['20.0', '25', '26', '27.0-rc3'] runs-on: ubuntu-20.04 services: redis: diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 39c6e8196..d538d3473 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20', '25', '26', '27.0-rc1'] + otp: ['20', '25', '26', '27.0-rc3'] rebar: ['rebar', 'rebar3'] runs-on: ubuntu-22.04 container: @@ -148,7 +148,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27.0-rc1'] + otp: ['23.0', '25', '26', '27.0-rc3'] elixir: ['1.13.4', '1.15.7', '1.16'] exclude: - otp: '23.0' @@ -157,7 +157,7 @@ jobs: elixir: '1.16' - otp: '26' elixir: '1.13.4' - - otp: '27.0-rc1' + - otp: '27.0-rc3' elixir: '1.13.4' runs-on: ubuntu-20.04 @@ -282,7 +282,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27.0-rc1'] + otp: ['23.0', '25', '26', '27.0-rc3'] elixir: ['1.13.4', '1.15.7', '1.16'] exclude: - otp: '23.0' @@ -291,7 +291,7 @@ jobs: elixir: '1.16' - otp: '26' elixir: '1.13.4' - - otp: '27.0-rc1' + - otp: '27.0-rc3' elixir: '1.13.4' runs-on: ubuntu-20.04 From 9fbc7b96e49c18ef284e6565f18ad79c3a093890 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 2 May 2024 19:10:50 +0200 Subject: [PATCH 0568/1302] Update pkix dependency with minor fixes for compilation warnings --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index f978fa0ff..ba1a690b7 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_acme, git: "https://github.com/processone/p1_acme", branch: "master"}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, - {:pkix, "~> 1.0"}, + {:pkix, git: "https://github.com/processone/pkix"}, {:stringprep, ">= 1.0.26"}, {:xmpp, git: "https://github.com/processone/xmpp", override: true}, {:yconf, "~> 1.0"}] diff --git a/mix.lock b/mix.lock index dce26ef1d..39786fb71 100644 --- a/mix.lock +++ b/mix.lock @@ -29,7 +29,7 @@ "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, - "pkix": {:hex, :pkix, "1.0.9", "eb20b2715d71a23b4fe7e754dae9281a964b51113d0bba8adf9da72bf9d65ac2", [:rebar3], [], "hexpm", "daab2c09cdd4eda05c9b45a5c00e994a1a5f27634929e1377e2e59b707103e3a"}, + "pkix": {:git, "https://github.com/processone/pkix", "15d9ea9b2d5b2b92743cbd32be85327dd87ec868", []}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, diff --git a/rebar.config b/rebar.config index 729e79e40..09da3c176 100644 --- a/rebar.config +++ b/rebar.config @@ -64,7 +64,7 @@ {if_var_true, pgsql, {p1_pgsql, "~> 1.1.15", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.25"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, - {pkix, "~> 1.0.9", {git, "https://github.com/processone/pkix", {tag, "1.0.9"}}}, + {pkix, ".*", {git, "https://github.com/processone/pkix", {branch, "master"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, diff --git a/rebar.lock b/rebar.lock index 76865a54d..23e3d344e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -22,7 +22,10 @@ {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.25">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, - {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.9">>},0}, + {<<"pkix">>, + {git,"https://github.com/processone/pkix", + {ref,"15d9ea9b2d5b2b92743cbd32be85327dd87ec868"}}, + 0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.14">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.12">>},0}, @@ -53,7 +56,6 @@ {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_pgsql">>, <<"F59B7FAA457FADF0C2713AC335202F41CA1B06B7C4926925B3CB0BC6F0578601">>}, {<<"p1_utils">>, <<"2D39B5015A567BBD2CC7033EEB93A7C60D8C84EFE1EF69A3473FAA07FA268187">>}, - {<<"pkix">>, <<"EB20B2715D71A23B4FE7E754DAE9281A964B51113D0BBA8ADF9DA72BF9D65AC2">>}, {<<"sqlite3">>, <<"F9EA0CFF8540865FDFDB7E24EEF34DC46677364B1C070896E99B5BF08C8A7FD7">>}, {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, {<<"stun">>, <<"A65DF67A8AAAECB6A94D687977B2E9F161820819910CB97BBE26A3525356525B">>}, @@ -79,7 +81,6 @@ {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_pgsql">>, <<"B654C1E07D3E775B626B407E7696CACFD88D17BE83E7168B9D89C3832D913DE7">>}, {<<"p1_utils">>, <<"9219214428F2C6E5D3187FF8EB9A8783695C2427420BE9A259840E07ADA32847">>}, - {<<"pkix">>, <<"DAAB2C09CDD4EDA05C9B45A5C00E994A1A5F27634929E1377E2E59B707103E3A">>}, {<<"sqlite3">>, <<"85054B6CA297343C159ED6794A473FF2C8EEABD854B6FE02F711C0BFD373CE86">>}, {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, {<<"stun">>, <<"A2055032B6D338D0454142004BCB12FAFB0C64AB1F273F1D0C6923EBBC8EDE40">>}, From 4697a9295abc56315153e90e9259c1cd028c7106 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 29 Apr 2024 18:28:38 +0200 Subject: [PATCH 0569/1302] Update p1_acme that uses Erlang's json library instead of jiffy when OTP 27 --- mix.lock | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 39786fb71..8fb17e878 100644 --- a/mix.lock +++ b/mix.lock @@ -24,7 +24,7 @@ "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "b4d0900eabb208c493ae3958bc545151bb19b90e", [branch: "master"]}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "167397c07cd3d64d5245d6a7e513baa8ff6bd95d", [branch: "master"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, diff --git a/rebar.lock b/rebar.lock index 23e3d344e..06f9310fc 100644 --- a/rebar.lock +++ b/rebar.lock @@ -16,7 +16,7 @@ {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.16">>},0}, {<<"p1_acme">>, {git,"https://github.com/processone/p1_acme", - {ref,"b4d0900eabb208c493ae3958bc545151bb19b90e"}}, + {ref,"167397c07cd3d64d5245d6a7e513baa8ff6bd95d"}}, 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.23">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, From 0ad1d315e8e1ef62271cfc1dfe874f3e4ab4a8c7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 May 2024 17:07:26 +0200 Subject: [PATCH 0570/1302] Use maps instead of tuple+list+tuple as arguments in jiffy functions --- src/ejabberd_ctl.erl | 2 +- src/ejabberd_oauth.erl | 13 ++++----- src/ejabberd_oauth_rest.erl | 58 ++++++++++++++++++++++++------------- src/ext_mod.erl | 24 +++++++-------- src/mod_conversejs.erl | 3 +- src/mod_http_api.erl | 13 ++++----- src/rest.erl | 2 +- 7 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 7fc73f57a..3a6f984c5 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -374,7 +374,7 @@ format_arg(Arg, {tuple, Elements}) -> list_to_tuple(format_args(Args, Elements)); format_arg(Arg, Format) -> S = unicode:characters_to_binary(Arg, utf8), - JSON = jiffy:decode(S), + JSON = jiffy:decode(S, [return_maps]), mod_http_api:format_arg(JSON, Format). format_arg2(Arg, Parse)-> diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 026597f00..9fd6aee3c 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -721,11 +721,10 @@ process(_Handlers, ExpiresIn end, {ok, VerifiedScope} = oauth2_response:scope(Response), - json_response(200, {[ - {<<"access_token">>, AccessToken}, - {<<"token_type">>, Type}, - {<<"scope">>, str:join(VerifiedScope, <<" ">>)}, - {<<"expires_in">>, Expires}]}); + json_response(200, #{<<"access_token">> => AccessToken, + <<"token_type">> => Type, + <<"scope">> => str:join(VerifiedScope, <<" ">>), + <<"expires_in">> => Expires}); {error, Error} when is_atom(Error) -> json_error(400, <<"invalid_grant">>, Error) end; @@ -762,8 +761,8 @@ json_response(Code, Body) -> %% https://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-5.2 json_error(Code, Error, Reason) -> Desc = json_error_desc(Reason), - Body = {[{<<"error">>, Error}, - {<<"error_description">>, Desc}]}, + Body = #{<<"error">> => Error, + <<"error_description">> => Desc}, json_response(Code, Body). json_error_desc(access_denied) -> <<"Access denied">>; diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl index 9e9bdc17f..e38dceffe 100644 --- a/src/ejabberd_oauth_rest.erl +++ b/src/ejabberd_oauth_rest.erl @@ -50,11 +50,11 @@ store(R) -> case rest:with_retry( post, [ejabberd_config:get_myname(), Path, [], - {[{<<"token">>, R#oauth_token.token}, - {<<"user">>, SJID}, - {<<"scope">>, R#oauth_token.scope}, - {<<"expire">>, R#oauth_token.expire} - ]}], 2, 500) of + #{<<"token">> => R#oauth_token.token, + <<"user">> => SJID, + <<"scope">> => R#oauth_token.scope, + <<"expire">> => R#oauth_token.expire + }], 2, 500) of {ok, Code, _} when Code == 200 orelse Code == 201 -> ok; Err -> @@ -65,14 +65,23 @@ store(R) -> lookup(Token) -> Path = path(<<"lookup">>), case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [], - {[{<<"token">>, Token}]}], + #{<<"token">> => Token}], 2, 500) of - {ok, 200, {Data}} -> - SJID = proplists:get_value(<<"user">>, Data, <<>>), + {ok, 200, Data} -> + SJID = case maps:find(<<"user">>, Data) of + {ok, U} -> U; + error -> <<>> + end, JID = jid:decode(SJID), US = {JID#jid.luser, JID#jid.lserver}, - Scope = proplists:get_value(<<"scope">>, Data, []), - Expire = proplists:get_value(<<"expire">>, Data, 0), + Scope = case maps:find(<<"scope">>, Data) of + {ok, S} -> S; + error -> [] + end, + Expire = case maps:find(<<"expire">>, Data) of + {ok, E} -> E; + error -> 0 + end, {ok, #oauth_token{token = Token, us = US, scope = Scope, @@ -113,11 +122,11 @@ store_client(#oauth_client{client_id = ClientID, case rest:with_retry( post, [ejabberd_config:get_myname(), Path, [], - {[{<<"client_id">>, ClientID}, - {<<"client_name">>, ClientName}, - {<<"grant_type">>, SGrantType}, - {<<"options">>, SOptions} - ]}], 2, 500) of + #{<<"client_id">> => ClientID, + <<"client_name">> => ClientName, + <<"grant_type">> => SGrantType, + <<"options">> => SOptions + }], 2, 500) of {ok, Code, _} when Code == 200 orelse Code == 201 -> ok; Err -> @@ -128,17 +137,26 @@ store_client(#oauth_client{client_id = ClientID, lookup_client(ClientID) -> Path = path(<<"lookup_client">>), case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [], - {[{<<"client_id">>, ClientID}]}], + #{<<"client_id">> => ClientID}], 2, 500) of - {ok, 200, {Data}} -> - ClientName = proplists:get_value(<<"client_name">>, Data, <<>>), - SGrantType = proplists:get_value(<<"grant_type">>, Data, <<>>), + {ok, 200, Data} -> + ClientName = case maps:find(<<"client_name">>, Data) of + {ok, CN} -> CN; + error -> <<>> + end, + SGrantType = case maps:find(<<"grant_type">>, Data) of + {ok, GT} -> GT; + error -> <<>> + end, GrantType = case SGrantType of <<"password">> -> password; <<"implicit">> -> implicit end, - SOptions = proplists:get_value(<<"options">>, Data, <<>>), + SOptions = case maps:find(<<"options">>, Data) of + {ok, O} -> O; + error -> <<>> + end, case misc:base64_to_term(SOptions) of {term, Options} -> {ok, #oauth_client{client_id = ClientID, diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 2c74017b0..6b8344026 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -884,21 +884,21 @@ get_commit_details2(Path) -> end. parse_details(Body) -> - {Contents} = jiffy:decode(Body), + Contents = jiffy:decode(Body, [return_maps]), - {_, {Commit}} = lists:keyfind(<<"commit">>, 1, Contents), - {_, Sha} = lists:keyfind(<<"sha">>, 1, Commit), - {_, CommitHtmlUrl} = lists:keyfind(<<"html_url">>, 1, Commit), + {ok, Commit} = maps:find(<<"commit">>, Contents), + {ok, Sha} = maps:find(<<"sha">>, Commit), + {ok, CommitHtmlUrl} = maps:find(<<"html_url">>, Commit), - {_, {Commit2}} = lists:keyfind(<<"commit">>, 1, Commit), - {_, Message} = lists:keyfind(<<"message">>, 1, Commit2), - {_, {Author}} = lists:keyfind(<<"author">>, 1, Commit2), - {_, AuthorName} = lists:keyfind(<<"name">>, 1, Author), - {_, {Committer}} = lists:keyfind(<<"committer">>, 1, Commit2), - {_, Date} = lists:keyfind(<<"date">>, 1, Committer), + {ok, Commit2} = maps:find(<<"commit">>, Commit), + {ok, Message} = maps:find(<<"message">>, Commit2), + {ok, Author} = maps:find(<<"author">>, Commit2), + {ok, AuthorName} = maps:find(<<"name">>, Author), + {ok, Committer} = maps:find(<<"committer">>, Commit2), + {ok, Date} = maps:find(<<"date">>, Committer), - {_, {Links}} = lists:keyfind(<<"_links">>, 1, Contents), - {_, Html} = lists:keyfind(<<"html">>, 1, Links), + {ok, Links} = maps:find(<<"_links">>, Contents), + {ok, Html} = maps:find(<<"html">>, Links), #{sha => Sha, date => Date, diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 2d3af544e..de1564cbb 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -78,6 +78,7 @@ process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> undefined -> Init2; BoshURL -> [{<<"bosh_service_url">>, BoshURL} | Init2] end, + Init4 = maps:from_list(Init3), {200, [html], [<<"">>, <<"">>, @@ -89,7 +90,7 @@ process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> <<"">>, <<"">>, <<"">>, <<"">>, <<"">>]}; diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 5a61ad1c3..958c71c66 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -197,11 +197,8 @@ perform_call(Command, Args, Req, Version) -> %% Be tolerant to make API more easily usable from command-line pipe. extract_args(<<"\n">>) -> []; extract_args(Data) -> - case jiffy:decode(Data) of - List when is_list(List) -> List; - {List} when is_list(List) -> List; - Other -> [Other] - end. + Maps = jiffy:decode(Data, [return_maps]), + maps:to_list(Maps). % get API version N from last "vN" element in URL path get_api_version(#request{path = Path}) -> @@ -509,9 +506,9 @@ json_response(Code, Body) when is_integer(Code) -> %% message is binary json_error(HTTPCode, JSONCode, Message) -> {HTTPCode, ?HEADER(?CT_JSON), - jiffy:encode({[{<<"status">>, <<"error">>}, - {<<"code">>, JSONCode}, - {<<"message">>, Message}]}) + jiffy:encode(#{<<"status">> => <<"error">>, + <<"code">> => JSONCode, + <<"message">> => Message}) }. log(Call, Args, {Addr, Port}) -> diff --git a/src/rest.erl b/src/rest.erl index 03c1f35b5..381e612be 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -157,7 +157,7 @@ encode_json(Content) -> decode_json(<<>>) -> []; decode_json(<<" ">>) -> []; decode_json(<<"\r\n">>) -> []; -decode_json(Data) -> jiffy:decode(Data). +decode_json(Data) -> jiffy:decode(Data, [return_maps]). custom_headers(Server) -> case ejabberd_option:ext_api_headers(Server) of From 696e42b5b404fcc2a77a4f82c8dd34a12d83cc36 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 May 2024 18:02:36 +0200 Subject: [PATCH 0571/1302] Use json module when Erlang/OTP 27, jiffy with older ones --- mix.exs | 6 +++-- rebar.config | 9 +++---- src/ejabberd_ctl.erl | 2 +- src/ejabberd_oauth.erl | 2 +- src/ext_mod.erl | 2 +- src/misc.erl | 20 ++++++++++++++++ src/mod_bosh.erl | 12 ++++++++-- src/mod_conversejs.erl | 2 +- src/mod_host_meta.erl | 2 +- src/mod_http_api.erl | 10 ++++---- src/mod_matrix_gw.erl | 49 +++++++++++++++++++------------------- src/mod_matrix_gw_room.erl | 12 +++++----- src/mod_matrix_gw_s2s.erl | 4 ++-- src/rest.erl | 4 ++-- test/muc_tests.erl | 2 +- 15 files changed, 84 insertions(+), 54 deletions(-) diff --git a/mix.exs b/mix.exs index ba1a690b7..26e40b1db 100644 --- a/mix.exs +++ b/mix.exs @@ -46,7 +46,7 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, - :fast_tls, :fast_xml, :fast_yaml, :jiffy, :jose, + :fast_tls, :fast_xml, :fast_yaml, :jose, :p1_utils, :stringprep, :syntax_tools, :yconf] ++ cond_apps(), included_applications: [:mnesia, :os_mon, @@ -114,6 +114,7 @@ defmodule Ejabberd.MixProject do if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ if_version_below(~c"24", [{:d, :OTP_BELOW_24}]) ++ if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++ + if_version_below(~c"27", [{:d, :OTP_BELOW_27}]) ++ if_type_exported(:odbc, {:opaque, :connection_reference, 0}, [{:d, :ODBC_HAS_TYPES}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] @@ -139,7 +140,6 @@ defmodule Ejabberd.MixProject do {:fast_xml, ">= 1.1.51"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, - {:jiffy, "~> 1.1.1"}, {:mqtree, "~> 1.0"}, {:p1_acme, git: "https://github.com/processone/p1_acme", branch: "master"}, {:p1_oauth2, "~> 0.6"}, @@ -173,6 +173,7 @@ defmodule Ejabberd.MixProject do {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, {if_version_below(~c"24", true), {:jose, "1.11.1"}}, + {if_version_below(~c"27", true), {:jiffy, "~> 1.1.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.2.0"}}, {config(:mysql), {:p1_mysql, ">= 1.0.23" }}, @@ -185,6 +186,7 @@ defmodule Ejabberd.MixProject do defp cond_apps do for {:true, app} <- [{config(:stun), :stun}, {Map.has_key?(System.get_env(), "RELIVE"), :exsync}, + {if_version_below(~c"27", true), :jiffy}, {config(:tools), :observer}], do: app end diff --git a/rebar.config b/rebar.config index 09da3c176..b1b017843 100644 --- a/rebar.config +++ b/rebar.config @@ -40,9 +40,8 @@ {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, - {if_version_above, "19", - {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}}, - {jiffy, "1.1.0", {git, "https://github.com/davisp/jiffy", {tag, "1.1.0"}}} % for R19 and below + {if_version_below, "27", + {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}} }, {if_version_above, "23", {jose, "~> 1.11.10", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.10"}}}, @@ -131,6 +130,7 @@ {if_version_below, "24", {d, 'SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL'}}, {if_version_below, "24", {d, 'OTP_BELOW_24'}}, {if_version_below, "25", {d, 'OTP_BELOW_25'}}, + {if_version_below, "27", {d, 'OTP_BELOW_27'}}, {if_var_false, debug, no_debug_info}, {if_var_true, debug, debug_info}, {if_var_true, elixir, {d, 'ELIXIR_ENABLED'}}, @@ -206,10 +206,11 @@ {if_version_above, "25", {plt_extra_apps, [asn1, odbc, public_key, stdlib, syntax_tools, - idna, jiffy, jose, + idna, jose, cache_tab, eimp, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_oauth2, p1_utils, pkix, stringprep, xmpp, yconf, + {if_version_below, "27", jiffy}, {if_var_true, pam, epam}, {if_var_true, redis, eredis}, {if_var_true, sip, esip}, diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 3a6f984c5..d42ac2393 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -374,7 +374,7 @@ format_arg(Arg, {tuple, Elements}) -> list_to_tuple(format_args(Args, Elements)); format_arg(Arg, Format) -> S = unicode:characters_to_binary(Arg, utf8), - JSON = jiffy:decode(S, [return_maps]), + JSON = misc:json_decode(S), mod_http_api:format_arg(JSON, Format). format_arg2(Arg, Parse)-> diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 9fd6aee3c..96c664acf 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -755,7 +755,7 @@ json_response(Code, Body) -> {Code, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}, {<<"Cache-Control">>, <<"no-store">>}, {<<"Pragma">>, <<"no-cache">>}], - jiffy:encode(Body)}. + misc:json_encode(Body)}. %% OAauth error are defined in: %% https://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-5.2 diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 6b8344026..3c3182415 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -884,7 +884,7 @@ get_commit_details2(Path) -> end. parse_details(Body) -> - Contents = jiffy:decode(Body, [return_maps]), + Contents = misc:json_decode(Body), {ok, Commit} = maps:find(<<"commit">>, Contents), {ok, Sha} = maps:find(<<"sha">>, Commit), diff --git a/src/misc.erl b/src/misc.erl index 26c3c4c2d..a1cd340a2 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -42,6 +42,7 @@ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4, get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, + json_encode/1, json_decode/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). @@ -58,6 +59,13 @@ -type re_mp() :: {re_pattern, _, _, _, _}. -export_type([re_mp/0]). +-ifdef(OTP_BELOW_27). +-type json_value() :: jiffy:json_value(). +-else. +-type json_value() :: json:encode_value(). +-endif. +-export_type([json_value/0]). + -type distance_cache() :: #{{string(), string()} => non_neg_integer()}. -spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}. @@ -122,6 +130,18 @@ crypto_hmac(Type, Key, Data) -> crypto:mac(hmac, Type, Key, Data). crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL). -endif. +-ifdef(OTP_BELOW_27). +json_encode(Term) -> + jiffy:encode(Term). +json_decode(Bin) -> + jiffy:decode(Bin, [return_maps]). +-else. +json_encode(Term) -> + iolist_to_binary(json:encode(Term)). +json_decode(Bin) -> + json:decode(Bin). +-endif. + %%%=================================================================== %%% API %%%=================================================================== diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index 1a6cfbdf6..9b6e0eeaf 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -154,14 +154,22 @@ get_type(Hdrs) -> depends(_Host, _Opts) -> []. -mod_opt_type(json) -> +-ifdef(OTP_BELOW_27). +mod_opt_type_json() -> econf:and_then( econf:bool(), fun(false) -> false; (true) -> ejabberd:start_app(jiffy), true - end); + end). +-else. +mod_opt_type_json() -> + econf:bool(). +-endif. + +mod_opt_type(json) -> + mod_opt_type_json(); mod_opt_type(max_concat) -> econf:pos_int(unlimited); mod_opt_type(max_inactivity) -> diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index de1564cbb..9017720a4 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -90,7 +90,7 @@ process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> <<"">>, <<"">>, <<"">>, <<"">>, <<"">>]}; diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 484127443..92595069d 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -125,7 +125,7 @@ file_json(Host) -> {200, [html, {<<"Content-Type">>, <<"application/json">>}, {<<"Access-Control-Allow-Origin">>, <<"*">>}], - [jiffy:encode(#{links => BoshList ++ WsList})]}. + [misc:json_encode(#{links => BoshList ++ WsList})]}. get_url(M, bosh, Tls, Host) -> get_url(M, Tls, Host, bosh_service_url, mod_bosh); diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 958c71c66..359f00c24 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -197,7 +197,7 @@ perform_call(Command, Args, Req, Version) -> %% Be tolerant to make API more easily usable from command-line pipe. extract_args(<<"\n">>) -> []; extract_args(Data) -> - Maps = jiffy:decode(Data, [return_maps]), + Maps = misc:json_decode(Data), maps:to_list(Maps). % get API version N from last "vN" element in URL path @@ -492,10 +492,10 @@ invalid_token_response() -> badrequest_response() -> badrequest_response(<<"400 Bad Request">>). badrequest_response(Body) -> - json_response(400, jiffy:encode(Body)). + json_response(400, misc:json_encode(Body)). json_format({Code, Result}) -> - json_response(Code, jiffy:encode(Result)); + json_response(Code, misc:json_encode(Result)); json_format({HTMLCode, JSONErrorCode, Message}) -> json_error(HTMLCode, JSONErrorCode, Message). @@ -506,8 +506,8 @@ json_response(Code, Body) when is_integer(Code) -> %% message is binary json_error(HTTPCode, JSONCode, Message) -> {HTTPCode, ?HEADER(?CT_JSON), - jiffy:encode(#{<<"status">> => <<"error">>, - <<"code">> => JSONCode, + misc:json_encode(#{<<"status">> => <<"error">>, + <<"code">> => JSONCode, <<"message">> => Message}) }. diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 46c5dbcfc..37943ae02 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -75,13 +75,13 @@ process([<<"key">>, <<"v2">>, <<"server">> | _], }}, SJSON = sign_json(Host, JSON), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(SJSON)}; + misc:json_encode(SJSON)}; process([<<"federation">>, <<"v1">>, <<"version">>], #request{method = 'GET', host = _Host} = _Request) -> JSON = #{<<"server">> => #{<<"name">> => <<"ejabberd/mod_matrix_gw">>, <<"version">> => <<"0.1">>}}, {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(JSON)}; + misc:json_encode(JSON)}; process([<<"federation">>, <<"v1">>, <<"query">>, <<"profile">>], #request{method = 'GET', host = _Host} = Request) -> case proplists:get_value(<<"user_id">>, Request#request.q) of @@ -116,7 +116,7 @@ process([<<"federation">>, <<"v1">>, <<"user">>, <<"devices">>, UserID], <<"keys">> => []}], <<"stream_id">> => 1, <<"user_id">> => UserID}, - {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], jiffy:encode(Res)}; + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], misc:json_encode(Res)}; {result, HTTPResult} -> HTTPResult end; @@ -127,7 +127,7 @@ process([<<"federation">>, <<"v1">>, <<"user">>, <<"keys">>, <<"query">>], DeviceKeys2 = maps:map(fun(_Key, _) -> #{} end, DeviceKeys), Res = #{<<"device_keys">> => DeviceKeys2}, {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {ok, _JSON, _Origin} -> {400, [], <<"400 Bad Request: invalid format">>}; {result, HTTPResult} -> @@ -156,8 +156,8 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], ?DEBUG("sign event ~p~n", [SEvent]), ResJSON = #{<<"event">> => SEvent}, mod_matrix_gw_room:join(Host, Origin, RoomID, Sender, UserID), - ?DEBUG("res ~s~n", [jiffy:encode(ResJSON)]), - {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], jiffy:encode(ResJSON)}; + ?DEBUG("res ~s~n", [misc:json_encode(ResJSON)]), + {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], misc:json_encode(ResJSON)}; _ -> {400, [], <<"400 Bad Request: bad event id">>} end; @@ -191,7 +191,7 @@ process([<<"federation">>, <<"v1">>, <<"send">>, _TxnID], end, PDUs), ?DEBUG("send res ~p~n", [Res]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(maps:from_list(Res))}; + misc:json_encode(maps:from_list(Res))}; {ok, _JSON, _Origin} -> {400, [], <<"400 Bad Request: invalid format">>}; {result, HTTPResult} -> @@ -212,7 +212,7 @@ process([<<"federation">>, <<"v1">>, <<"get_missing_events">>, RoomID], ?DEBUG("get_missing_events res ~p~n", [PDUs]), Res = #{<<"events">> => PDUs}, {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {ok, _JSON, _Origin} -> {400, [], <<"400 Bad Request: invalid format">>}; {result, HTTPResult} -> @@ -245,7 +245,7 @@ process([<<"federation">>, <<"v1">>, <<"backfill">>, RoomID], <<"origin_server_ts">> => erlang:system_time(millisecond), <<"pdus">> => PDUs}, {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {result, HTTPResult} -> HTTPResult end; @@ -265,7 +265,7 @@ process([<<"federation">>, <<"v1">>, <<"state_ids">>, RoomID], <<"pdu_ids">> => PDUs}, ?DEBUG("get_state_ids res ~p~n", [Res]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {error, room_not_found} -> {400, [], <<"400 Bad Request: room not found">>}; {error, not_allowed} -> @@ -307,7 +307,7 @@ process([<<"federation">>, <<"v1">>, <<"event">>, EventID], <<"origin_server_ts">> => erlang:system_time(millisecond), <<"pdus">> => [PDU]}, {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)} + misc:json_encode(Res)} end; {result, HTTPResult} -> HTTPResult @@ -324,27 +324,27 @@ process([<<"federation">>, <<"v1">>, <<"make_join">>, RoomID, UserID], Res = #{<<"errcode">> => <<"M_NOT_FOUND">>, <<"error">> => <<"Unknown room">>}, {404, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {error, not_invited} -> Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, <<"error">> => <<"You are not invited to this room">>}, {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {error, {incompatible_version, Ver}} -> Res = #{<<"errcode">> => <<"M_INCOMPATIBLE_ROOM_VERSION">>, <<"error">> => <<"Your homeserver does not support the features required to join this room">>, <<"room_version">> => Ver}, {400, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {ok, Res} -> {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)} + misc:json_encode(Res)} end; _ -> Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, <<"error">> => <<"User not from origin">>}, {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)} + misc:json_encode(Res)} end; {result, HTTPResult} -> HTTPResult @@ -366,23 +366,23 @@ process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], Res = #{<<"errcode">> => <<"M_BAD_REQUEST">>, <<"error">> => Error}, {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {ok, Res} -> ?DEBUG("send_join res: ~p~n", [Res]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)} + misc:json_encode(Res)} end; _ -> Res = #{<<"errcode">> => <<"M_FORBIDDEN">>, <<"error">> => <<"User not from origin">>}, {403, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)} + misc:json_encode(Res)} end; {ok, _JSON, _Origin} -> Res = #{<<"errcode">> => <<"M_BAD_REQUEST">>, <<"error">> => <<"Invalid event format">>}, {400, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], - jiffy:encode(Res)}; + misc:json_encode(Res)}; {result, HTTPResult} -> HTTPResult end; @@ -409,8 +409,7 @@ preprocess_federation_request(Request, DoSignCheck) -> if Request#request.length > 0 -> try - jiffy:decode(Request2#request.data, - [return_maps]) + misc:json_decode(Request2#request.data) catch _:_ -> error @@ -674,7 +673,7 @@ get_pruned_event_id(PrunedEvent) -> encode_canonical_json(JSON) -> JSON2 = sort_json(JSON), - jiffy:encode(JSON2). + misc:json_encode(JSON2). sort_json(#{} = Map) -> Map2 = maps:map(fun(_K, V) -> @@ -736,7 +735,7 @@ sign_json(Host, JSON) -> binary(), [binary()], [{binary(), binary()}], - none | #{atom() | binary() => jiffy:json_value()}, + none | #{atom() | binary() => misc:json_value()}, [any()], [any()]) -> {ok, any()} | {error, any()}. @@ -772,7 +771,7 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, Content = case JSON of none -> <<>>; - _ -> jiffy:encode(JSON) + _ -> misc:json_encode(JSON) end, Request = case Method of diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 18be85b15..77d323f59 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -67,7 +67,7 @@ sender :: binary(), prev_events :: [binary()], origin_server_ts :: integer(), - json :: #{atom() | binary() => jiffy:json_value()}, + json :: #{atom() | binary() => misc:json_value()}, state_map}). -record(data, @@ -510,7 +510,7 @@ handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> ?DEBUG("make_join ~p~n", [MakeJoinRes]), case MakeJoinRes of {ok, {{_, 200, _}, _Headers, Body}} -> - try jiffy:decode(Body, [return_maps]) of + try misc:json_decode(Body) of #{<<"event">> := Event, <<"room_version">> := SRoomVersion} -> case binary_to_room_version(SRoomVersion) of @@ -702,7 +702,7 @@ process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> case SendJoinRes of {ok, {{_, 200, _}, _Headers, Body}} -> try - case jiffy:decode(Body, [return_maps]) of + case misc:json_decode(Body) of #{<<"auth_chain">> := JSONAuthChain, <<"event">> := JSONEvent, <<"state">> := JSONState} = JSON when is_list(JSONAuthChain), @@ -1585,7 +1585,7 @@ process_pdu(Host, Origin, PDU) -> process_missing_events_res(Host, Origin, Pid, RoomID, RoomVersion, {ok, {{_, 200, _}, _Headers, Body}}) -> try - case jiffy:decode(Body, [return_maps]) of + case misc:json_decode(Body) of #{<<"events">> := JSONEvents} when is_list(JSONEvents) -> process_missing_events(Host, Origin, Pid, RoomID, RoomVersion, JSONEvents) end @@ -1673,7 +1673,7 @@ request_room_state(Host, Origin, _Pid, RoomID, RoomVersion, Event) -> case Res of {ok, {{_, 200, _}, _Headers, Body}} -> try - case jiffy:decode(Body, [return_maps]) of + case misc:json_decode(Body) of #{<<"auth_chain">> := JSONAuthChain, <<"pdus">> := JSONState} = _JSON when is_list(JSONAuthChain), is_list(JSONState) -> @@ -1732,7 +1732,7 @@ request_event(Host, Origin, _Pid, RoomID, RoomVersion, EventID) -> case Res of {ok, {{_, 200, _}, _Headers, Body}} -> try - case jiffy:decode(Body, [return_maps]) of + case misc:json_decode(Body) of #{<<"pdus">> := [PDU]} -> Event = json_to_event(PDU, RoomVersion), case check_event_sig_and_hash(Host, Event) of diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index fea528eef..e516ec744 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -342,7 +342,7 @@ handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> case HTTPResult of {{_, 200, _}, _, SJSON} -> try - JSON = jiffy:decode(SJSON, [return_maps]), + JSON = misc:json_decode(SJSON), ?DEBUG("key ~p~n", [JSON]), #{<<"verify_keys">> := VerifyKeys} = JSON, #{KeyID := KeyData} = VerifyKeys, @@ -447,7 +447,7 @@ do_get_matrix_host_port(Data) -> case HTTPRes of {ok, {{_, 200, _}, _Headers, Body}} -> try - case jiffy:decode(Body, [return_maps]) of + case misc:json_decode(Body) of #{<<"m.server">> := Server} -> case binary:split(Server, <<":">>) of [ServerAddr] -> diff --git a/src/rest.erl b/src/rest.erl index 381e612be..7f4883295 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -147,7 +147,7 @@ to_list(V) when is_list(V) -> V. encode_json(Content) -> - case catch jiffy:encode(Content) of + case catch misc:json_encode(Content) of {'EXIT', Reason} -> {error, {invalid_payload, Content, Reason}}; Encoded -> @@ -157,7 +157,7 @@ encode_json(Content) -> decode_json(<<>>) -> []; decode_json(<<" ">>) -> []; decode_json(<<"\r\n">>) -> []; -decode_json(Data) -> jiffy:decode(Data, [return_maps]). +decode_json(Data) -> misc:json_decode(Data). custom_headers(Server) -> case ejabberd_option:ext_api_headers(Server) of diff --git a/test/muc_tests.erl b/test/muc_tests.erl index ec3e2a334..f7e080aee 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -259,7 +259,7 @@ set_room_affiliation(Config) -> RequestURL = "http://" ++ ServerHost ++ ":" ++ integer_to_list(WebPort) ++ "/api/set_room_affiliation", Headers = [{"X-Admin", "true"}], ContentType = "application/json", - Body = jiffy:encode(#{name => RoomName, service => RoomService, jid => jid:encode(PeerJID), affiliation => member}), + Body = misc:json_encode(#{name => RoomName, service => RoomService, jid => jid:encode(PeerJID), affiliation => member}), {ok, {{_, 200, _}, _, _}} = httpc:request(post, {RequestURL, Headers, ContentType, Body}, [], []), #message{id = _, from = RoomJID, to = MyJID, sub_els = [ From 4ec236966a2e8221593c00e7235d2a349fe1be54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 06:23:10 +0000 Subject: [PATCH 0572/1302] Bump exsync from 0.4.0 to 0.4.1 Bumps [exsync](https://github.com/falood/exsync) from 0.4.0 to 0.4.1. - [Changelog](https://github.com/falood/exsync/blob/main/CHANGELOG.md) - [Commits](https://github.com/falood/exsync/compare/v0.4.0...v0.4.1) --- updated-dependencies: - dependency-name: exsync dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 8fb17e878..cb1c64290 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, - "exsync": {:hex, :exsync, "0.4.0", "41d1f1975a387ba7983c5b341541d9feaa90b9b654eaf245cde814d26dcea1c5", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "e1c4aed25806f1f9b60c6373a4b3f0e0db06e1d00e111e77eab9f21a997a5eef"}, + "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, From 38fd9a3ed253f7e54cb872c8bc172f75c4ab747f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 10 May 2024 16:04:28 +0200 Subject: [PATCH 0573/1302] Update URLs of module documentations --- src/ejabberd_web_admin.erl | 11 ++++++----- src/mod_mix_pam.erl | 2 +- src/mod_muc_admin.erl | 4 ++-- src/mod_offline.erl | 2 +- src/mod_roster.erl | 2 +- src/mod_shared_roster.erl | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index d81bd0f6e..742ac262c 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -400,7 +400,7 @@ process_admin(global, #request{path = [], lang = Lang}, AJID) -> MenuItems = get_menu_items(global, cluster, Lang, AJID, 0), Disclaimer = maybe_disclaimer_not_admin(MenuItems, AJID, Lang), make_xhtml((?H1GL((translate:translate(Lang, ?T("Administration"))), <<"">>, - <<"Contents">>)) + <<"Configuration">>)) ++ Disclaimer ++ [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) @@ -477,7 +477,7 @@ process_admin(Host, #request{path = [<<"last-activity">>], list_last_activity(Host, Lang, false, Month); _ -> list_last_activity(Host, Lang, true, Month) end, - PageH1 = ?H1GL(translate:translate(Lang, ?T("Users Last Activity")), <<"modules/#mod-last">>, <<"mod_last">>), + PageH1 = ?H1GL(translate:translate(Lang, ?T("Users Last Activity")), <<"modules/#mod_last">>, <<"mod_last">>), make_xhtml(PageH1 ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], @@ -508,7 +508,7 @@ process_admin(Host, #request{path = [<<"last-activity">>], Host, Lang, AJID, 3); process_admin(Host, #request{path = [<<"stats">>], lang = Lang}, AJID) -> Res = get_stats(Host, Lang), - PageH1 = ?H1GL(translate:translate(Lang, ?T("Statistics")), <<"modules/#mod-stats">>, <<"mod_stats">>), + PageH1 = ?H1GL(translate:translate(Lang, ?T("Statistics")), <<"modules/#mod_stats">>, <<"mod_stats">>), Level = case Host of global -> 1; _ -> 3 @@ -1413,8 +1413,9 @@ get_node(global, Node, [<<"stats">>], _Query, Lang) -> system_info, [transaction_restarts]), TransactionsLogged = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_log_writes]), - [?XC(<<"h1">>, - (str:translate_and_format(Lang, ?T("Statistics of ~p"), [Node]))), + ?H1GL(str:translate_and_format(Lang, ?T("Statistics of ~p"), [Node]), + <<"modules/#mod_stats">>, + <<"mod_stats">>) ++ [ ?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index 7bd6f2a71..fcca0c337 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -512,7 +512,7 @@ web_mix_channels(User, Server, Lang) -> [?XE(<<"table">>, [THead, ?XE(<<"tbody">>, Entries)])] end, PageTitle = str:translate_and_format(Lang, ?T("Joined MIX channels of ~ts"), [us_to_list(US)]), - (?H1GL(PageTitle, <<"modules/#mod-mix-pam">>, <<"mod_mix_pam">>)) + (?H1GL(PageTitle, <<"modules/#mod_mix_pam">>, <<"mod_mix_pam">>)) ++ FItems. us_to_list({User, Server}) -> diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 59ccb3804..f44b02494 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -608,7 +608,7 @@ web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) -> Acc + mod_muc:count_online_rooms(Host) end, 0, find_hosts(global)), PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), - Res = ?H1GL(PageTitle, <<"modules/#mod-muc">>, <<"mod_muc">>) ++ + Res = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++ [?XCT(<<"h3">>, ?T("Statistics")), ?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?TDTD(?T("Total rooms"), OnlineRoomsNumber) @@ -683,7 +683,7 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) -> 1, Titles), PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), - ?H1GL(PageTitle, <<"modules/#mod-muc">>, <<"mod_muc">>) ++ + ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++ [?XCT(<<"h2">>, ?T("Chatrooms")), ?XE(<<"table">>, [?XE(<<"thead">>, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index f626d8d24..f022644ec 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -983,7 +983,7 @@ user_queue(User, Server, Query, Lang) -> Hdrs = get_messages_subset(User, Server, HdrsAll), FMsgs = format_user_queue(Hdrs), PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]), - (?H1GL(PageTitle, <<"modules/#mod-offline">>, <<"mod_offline">>)) + (?H1GL(PageTitle, <<"modules/#mod_offline">>, <<"mod_offline">>)) ++ [?XREST(?T("Submitted"))] ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 18d69a7ee..498146d63 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -1094,7 +1094,7 @@ user_roster(User, Server, Query, Lang) -> SItems)))])] end, PageTitle = str:translate_and_format(Lang, ?T("Roster of ~ts"), [us_to_list(US)]), - (?H1GL(PageTitle, <<"modules/#mod-roster">>, <<"mod_roster">>)) + (?H1GL(PageTitle, <<"modules/#mod_roster">>, <<"mod_roster">>)) ++ case Res of ok -> [?XREST(?T("Submitted"))]; diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 05c7305d4..447ed4666 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -912,7 +912,7 @@ list_shared_roster_groups(Host, Query, Lang) -> ?INPUTT(<<"submit">>, <<"addnew">>, ?T("Add New"))])])]))])), (?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))), - <<"modules/#mod-shared-roster">>, <<"mod_shared_roster">>)) + <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>)) ++ case Res of ok -> [?XREST(?T("Submitted"))]; @@ -1019,7 +1019,7 @@ shared_roster_group(Host, Group, Query, Lang) -> ?XE(<<"td">>, [?CT(?T("Groups that will be displayed to the members"))]) ])])])), (?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))), - <<"modules/#mod-shared-roster">>, <<"mod_shared_roster">>)) + <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>)) ++ [?XC(<<"h2">>, translate:translate(Lang, ?T("Group")))] ++ case Res of From d2e7b5ffa0096cbd19efeaf74bbe1e78de06afb5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 May 2024 12:50:47 +0200 Subject: [PATCH 0574/1302] hook_deps: Fix to handle FileNo as tuple {FileNumber, CharacterPosition} --- tools/hook_deps.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/hook_deps.sh b/tools/hook_deps.sh index 00aa044a7..e9a8edb81 100755 --- a/tools/hook_deps.sh +++ b/tools/hook_deps.sh @@ -169,7 +169,7 @@ check_iq_handlers_export({HookedFuns, _}, Exports) -> case is_exported(Mod, Fun, 1, Exports) of true -> ok; false -> - err("~s:~B: Error: " + err("~s:~p: Error: " "iq handler is registered on unexported function: " "~s:~s/1~n", [File, FileNo, Mod, Fun]) end @@ -184,7 +184,7 @@ analyze_iq_handlers({Add, Del}) -> case maps:is_key(Handler, Del) of true -> ok; false -> - err("~s:~B: Error: " + err("~s:~p: Error: " "iq handler is added but not removed~n", [File, FileNo]) end @@ -197,7 +197,7 @@ analyze_iq_handlers({Add, Del}) -> case maps:is_key(Handler, Add) of true -> ok; false -> - err("~s:~B: Error: " + err("~s:~p: Error: " "iq handler is removed but not added~n", [File, FileNo]) end @@ -224,7 +224,7 @@ analyze_hooks({Add, Del}) -> case maps:is_key(Key, Del1) of true -> ok; false -> - err("~s:~B: Error: " + err("~s:~p: Error: " "hook ~s->~s->~s is added but was never removed~n", [File, FileNo, Hook, Mod, Fun]) end @@ -234,7 +234,7 @@ analyze_hooks({Add, Del}) -> case maps:is_key(Key, Add1) of true -> ok; false -> - err("~s:~B: Error: " + err("~s:~p: Error: " "hook ~s->~s->~s is removed but was never added~n", [File, FileNo, Hook, Mod, Fun]) end From 2554b51173f43d169c998958deebce3ab76cbd7f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 May 2024 12:55:11 +0200 Subject: [PATCH 0575/1302] hook_deps: Use precise name: hooks are added and later deleted, not removed --- tools/hook_deps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/hook_deps.sh b/tools/hook_deps.sh index e9a8edb81..243c9ac2d 100755 --- a/tools/hook_deps.sh +++ b/tools/hook_deps.sh @@ -225,7 +225,7 @@ analyze_hooks({Add, Del}) -> true -> ok; false -> err("~s:~p: Error: " - "hook ~s->~s->~s is added but was never removed~n", + "hook ~s->~s->~s is added but was never deleted~n", [File, FileNo, Hook, Mod, Fun]) end end, maps:to_list(Add1)), @@ -235,7 +235,7 @@ analyze_hooks({Add, Del}) -> true -> ok; false -> err("~s:~p: Error: " - "hook ~s->~s->~s is removed but was never added~n", + "hook ~s->~s->~s is deleted but was never added~n", [File, FileNo, Hook, Mod, Fun]) end end, maps:to_list(Del1)). From 66a08b6f18c1fe1bbdfc23770f1e868be2d33452 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 10:27:37 +0200 Subject: [PATCH 0576/1302] Run "make doap" --- ejabberd.doap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index 39ab85a4f..26b9794db 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -22,7 +22,7 @@ - + @@ -272,9 +272,9 @@ - 1.1 - - + 1.3.0 + 13.10 + complete mod_configure From cb49fcf459dfc7e23f7887c5020efcba1e4f86ab Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sat, 18 May 2024 05:13:40 +0300 Subject: [PATCH 0577/1302] Fix handling of mqtt_pub table definition from mysql.sql and fix should_update_schema/1 in ejabberd_sql_schema.erl --- src/ejabberd_sql_schema.erl | 16 +++++++++++++++- src/mod_mqtt_sql.erl | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index a4eaea0b2..af9743f82 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -293,6 +293,7 @@ string_to_type(SType) -> <<"integer">> -> integer; <<"int">> -> integer; <<"int(", _/binary>> -> integer; + <<"int ", _/binary>> -> integer; <<"smallint">> -> smallint; <<"smallint(", _/binary>> -> smallint; <<"numeric">> -> numeric; @@ -300,6 +301,7 @@ string_to_type(SType) -> <<"bigserial">> -> bigserial; <<"boolean">> -> boolean; <<"tinyint(1)">> -> boolean; + <<"tinyint", _/binary>> -> smallint; <<"bytea">> -> blob; <<"blob">> -> blob; <<"timestamp", _/binary>> -> timestamp; @@ -340,6 +342,8 @@ check_columns_compatibility(RequiredColumns, Columns) -> {integer, bigint} -> true; {integer, numeric} -> true; {bigint, numeric} -> true; + %% a workaround for MySQL definition of mqtt_pub + {bigint, integer} -> true; {bigserial, integer} -> true; {bigserial, bigint} -> true; {bigserial, numeric} -> true; @@ -768,7 +772,17 @@ should_update_schema(Host) -> true -> case ejabberd_sql:use_new_schema() of true -> - Host == ejabberd_config:get_myname(); + %% TODO: not efficient when there are many hosts + case lists:search( + fun(H) -> + lists:member( + sql, ejabberd_option:auth_method(H)) + end, ejabberd_option:hosts()) of + {value, Host} -> + true; + _ -> + false + end; false -> true end; diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index a57628372..f7bfea717 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -58,7 +58,7 @@ sql_schemas() -> #sql_column{name = <<"content_type">>, type = text}, #sql_column{name = <<"response_topic">>, type = text}, #sql_column{name = <<"correlation_data">>, type = blob}, - #sql_column{name = <<"user_property">>, type = blob}, + #sql_column{name = <<"user_properties">>, type = blob}, #sql_column{name = <<"expiry">>, type = bigint}], indices = [#sql_index{ columns = [<<"topic">>, <<"server_host">>], From f72cb1b5c878e8a900d3316988c6d7d74dc4aca6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 19 May 2024 18:55:38 +0200 Subject: [PATCH 0578/1302] .gitignore: Ignore ctags/etags files Running `make TAGS` generates a `TAGS` file, and "vim-erlang-tags" produces a `tags` file when :ErlangTags is called. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fbb2c2135..0f69a0aa3 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ /.rebar /log/ Mnesia.nonode@nohost/ +/TAGS +/tags # Binaries created with tools/make-{binaries,installers,packages}: /ejabberd_*.deb /ejabberd-*.rpm From 0d1cbc62d4a9b7cfae1e639d8a57fa346cea7866 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 16 May 2024 19:07:30 +0200 Subject: [PATCH 0579/1302] mod_muc_rtbl: Change this module attribute name to the main one: behaviour https://erlang.org/documentation/doc-15.0-rc3/doc/system/modules.html#behaviour-module-attribute --- src/mod_muc_rtbl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 93e13fbb3..e6ff9356b 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -26,7 +26,7 @@ -author("pawel@process-one.net"). -behaviour(gen_mod). --behavior(gen_server). +-behaviour(gen_server). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). From fba915e04ed22751d55527c0ebb3afa2f9e4e30e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 May 2024 16:17:30 +0200 Subject: [PATCH 0580/1302] gen_mod: Add support to specify a hook global, not vhost-specific --- src/gen_mod.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 908755b99..58d855938 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -61,6 +61,7 @@ -type component() :: ejabberd_sm | ejabberd_local. -type registration() :: {hook, atom(), atom(), integer()} | + {hook, atom(), atom(), integer(), binary() | global} | {hook, atom(), module(), atom(), integer()} | {iq_handler, component(), binary(), atom()} | {iq_handler, component(), binary(), module(), atom()}. @@ -341,7 +342,9 @@ add_registrations(Host, Module, Registrations) -> lists:foreach( fun({hook, Hook, Function, Seq}) -> ejabberd_hooks:add(Hook, Host, Module, Function, Seq); - ({hook, Hook, Module1, Function, Seq}) -> + ({hook, Hook, Function, Seq, Host1}) when is_integer(Seq) -> + ejabberd_hooks:add(Hook, Host1, Module, Function, Seq); + ({hook, Hook, Module1, Function, Seq}) when is_integer(Seq) -> ejabberd_hooks:add(Hook, Host, Module1, Function, Seq); ({iq_handler, Component, NS, Function}) -> gen_iq_handler:add_iq_handler( @@ -356,7 +359,9 @@ del_registrations(Host, Module, Registrations) -> lists:foreach( fun({hook, Hook, Function, Seq}) -> ejabberd_hooks:delete(Hook, Host, Module, Function, Seq); - ({hook, Hook, Module1, Function, Seq}) -> + ({hook, Hook, Function, Seq, Host1}) when is_integer(Seq) -> + ejabberd_hooks:delete(Hook, Host1, Module, Function, Seq); + ({hook, Hook, Module1, Function, Seq}) when is_integer(Seq) -> ejabberd_hooks:delete(Hook, Host, Module1, Function, Seq); ({iq_handler, Component, NS, _Function}) -> gen_iq_handler:remove_iq_handler(Component, Host, NS); From 01f2697faf0806b5f456e0d199ad5e1b0a107280 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 May 2024 16:18:16 +0200 Subject: [PATCH 0581/1302] mod_muc_admin: Update module to the new gen_mod API --- src/mod_muc_admin.erl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index f44b02494..484700fc6 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -61,12 +61,12 @@ %% gen_mod %%---------------------------- -start(Host, _Opts) -> +start(_Host, _Opts) -> ejabberd_commands:register_commands(?MODULE, get_commands_spec()), - ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), - ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), - ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), - ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, web_page_host, 50). + {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, + {hook, webadmin_page_main, web_page_main, 50, global}, + {hook, webadmin_menu_host, web_menu_host, 50}, + {hook, webadmin_page_host, web_page_host, 50}]}. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of @@ -74,11 +74,7 @@ stop(Host) -> ejabberd_commands:unregister_commands(get_commands_spec()); true -> ok - end, - ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), - ejabberd_hooks:delete(webadmin_menu_host, Host, ?MODULE, web_menu_host, 50), - ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, web_page_host, 50). + end. reload(_Host, _NewOpts, _OldOpts) -> ok. From a05dbcc2b2fa683348542e6f7fa944f68fa41d69 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 13:00:45 +0200 Subject: [PATCH 0582/1302] Add some XEPs support that was forgotten --- src/ejabberd.erl | 1 + src/ejabberd_auth_anonymous.erl | 2 ++ src/ejabberd_c2s.erl | 1 + src/ejabberd_captcha.erl | 1 + src/mod_offline.erl | 1 + 5 files changed, 6 insertions(+) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index ff62e3b47..abb2d89d6 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -30,6 +30,7 @@ -protocol({rfc, 6122}). -protocol({rfc, 7590}). -protocol({xep, 4, '2.9'}). +-protocol({xep, 59, '1.0'}). -protocol({xep, 86, '1.0'}). -protocol({xep, 106, '1.1'}). -protocol({xep, 170, '1.0'}). diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 9fb67047c..3b2de064c 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -28,6 +28,8 @@ -behaviour(ejabberd_auth). -author('mickael.remond@process-one.net'). +-protocol({xep, 175, '1.2'}). + -export([start/1, stop/1, use_cache/1, diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 2ec5baaf0..6e87389b6 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -27,6 +27,7 @@ -protocol({rfc, 3921}). -protocol({rfc, 6120}). -protocol({rfc, 6121}). +-protocol({xep, 138, '2.1'}). %% ejabberd_listener callbacks -export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]). diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index bf3b3df8d..8514f3cd3 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -26,6 +26,7 @@ -module(ejabberd_captcha). -protocol({xep, 158, '1.0'}). +-protocol({xep, 231, '1.0'}). -behaviour(gen_server). diff --git a/src/mod_offline.erl b/src/mod_offline.erl index f022644ec..f50452876 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -31,6 +31,7 @@ -protocol({xep, 22, '1.4'}). -protocol({xep, 23, '1.3'}). -protocol({xep, 160, '1.0'}). +-protocol({xep, 203, '2.0'}). -protocol({xep, 334, '0.2'}). -behaviour(gen_mod). From 83b1a40f8b6e77dae5d6b2c93ac7132485bddb21 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 13:07:31 +0200 Subject: [PATCH 0583/1302] Run "make doap" --- ejabberd.doap | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ejabberd.doap b/ejabberd.doap index 26b9794db..b0ae77f5c 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -170,6 +170,15 @@ mod_vcard + + + + 1.0 + + + + + @@ -278,6 +287,15 @@ mod_configure + + + + 2.1 + + + ejabberd_c2s + + @@ -341,6 +359,15 @@ + + + + 1.2 + + + ejabberd_auth_anonymous + + @@ -395,6 +422,15 @@ mod_time + + + + 2.0 + + + mod_offline + + @@ -458,6 +494,15 @@ ejabberd_piefxis + + + + 1.0 + + + ejabberd_captcha + + From bedfc2e188691117264398af2c2ecf6709ee48c7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 16:48:34 +0200 Subject: [PATCH 0584/1302] rebar.config: Add temporary workaround to compile Jose with Erlang/OTP 27.0 --- rebar.config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rebar.config b/rebar.config index b1b017843..6119d0298 100644 --- a/rebar.config +++ b/rebar.config @@ -162,6 +162,9 @@ {post, [{compile, {mix, consolidate_protocols}}]} ]}}}. +%% Compiling Jose 1.11.10 with Erlang/OTP 27.0 throws warnings on public_key deprecated functions +{if_rebar3, {overrides, [{del, jose, [{erl_opts, [warnings_as_errors]}]}]}}. + {sub_dirs, ["rel"]}. {keep_build_info, true}. From ad7008a54554772370b9ad9ca190f7912536c374 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 11:04:05 +0200 Subject: [PATCH 0585/1302] CI and Runtime: Update Erlang/OTP 27.0-rc3 to 27 --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c553c95d2..0b9b7b6e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25', '26', '27.0-rc3'] + otp: ['20.0', '25', '26', '27'] runs-on: ubuntu-20.04 services: redis: diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index d538d3473..36c7aaade 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20', '25', '26', '27.0-rc3'] + otp: ['20', '25', '26', '27'] rebar: ['rebar', 'rebar3'] runs-on: ubuntu-22.04 container: From 2cd53410bcf383e876478a7a521de3575a945ae6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 19:00:15 +0200 Subject: [PATCH 0586/1302] Runtime: Avoid testing with OTP 27 + rebar2, because Jose has warnings_as_errors and we can't disable that option in rebar2 using overrides --- .github/workflows/runtime.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 36c7aaade..4c5a6bbfb 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -33,6 +33,9 @@ jobs: matrix: otp: ['20', '25', '26', '27'] rebar: ['rebar', 'rebar3'] + exclude: + - otp: '27' + rebar: 'rebar' runs-on: ubuntu-22.04 container: image: erlang:${{ matrix.otp }} From 750bf07e138895e002505b7c6cc77004435dadcf Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 May 2024 12:48:19 +0200 Subject: [PATCH 0587/1302] Runtime: Define Elixir major versions, and let it pick the latest minor one --- .github/workflows/runtime.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 4c5a6bbfb..a3495b717 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -152,16 +152,16 @@ jobs: fail-fast: false matrix: otp: ['23.0', '25', '26', '27.0-rc3'] - elixir: ['1.13.4', '1.15.7', '1.16'] + elixir: ['1.13', '1.15', '1.16'] exclude: - otp: '23.0' - elixir: '1.15.7' + elixir: '1.15' - otp: '23.0' elixir: '1.16' - otp: '26' - elixir: '1.13.4' + elixir: '1.13' - otp: '27.0-rc3' - elixir: '1.13.4' + elixir: '1.13' runs-on: ubuntu-20.04 steps: @@ -286,16 +286,16 @@ jobs: fail-fast: false matrix: otp: ['23.0', '25', '26', '27.0-rc3'] - elixir: ['1.13.4', '1.15.7', '1.16'] + elixir: ['1.13', '1.15', '1.16'] exclude: - otp: '23.0' - elixir: '1.15.7' + elixir: '1.15' - otp: '23.0' elixir: '1.16' - otp: '26' - elixir: '1.13.4' + elixir: '1.13' - otp: '27.0-rc3' - elixir: '1.13.4' + elixir: '1.13' runs-on: ubuntu-20.04 steps: From 677d66d818f9c1637aebca093fb486f7922684a9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 11:02:42 +0200 Subject: [PATCH 0588/1302] Revert "mix.exs: Dirty workaround to get port_compiler with OTP 27 support" This reverts commit c7c3cc10c521aa8dc44242b65cc72fa79ffef805. --- mix.exs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index 26e40b1db..c58496ce5 100644 --- a/mix.exs +++ b/mix.exs @@ -13,9 +13,7 @@ defmodule Ejabberd.MixProject do erlc_options: erlc_options(), erlc_paths: ["asn1", "src"], # Elixir tests are starting the part of ejabberd they need - aliases: [ - test: "test --no-start", - "deps.get": ["deps.get", "pc_branch"]], + aliases: [test: "test --no-start"], start_permanent: Mix.env() == :prod, language: :erlang, dialyzer: dialyzer(), @@ -413,14 +411,6 @@ defmodule Ejabberd.MixProject do end end -defmodule Mix.Tasks.PcBranch do - use Mix.Task - def run(_) do - command = "find deps -name rebar.config.script -exec sed -i 's/AppendList..pc/AppendList\(\[{pc, {git, \"https:\\/\\/github.com\\/blt\\/port_compiler.git\", {branch, \"otp-27\"}}}/g' {} ';' " - :os.cmd(to_charlist(command)) - end -end - defmodule Mix.Tasks.Compile.Asn1 do use Mix.Task alias Mix.Compilers.Erlang From ca9ca7f5af93ce5edc80660b7623819279419e2f Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 11:02:36 +0200 Subject: [PATCH 0589/1302] Use the new port_compiler 1.15.0 that supports Erlang/OTP 27.0 Revert "rebar.config.script: Use port_compiler branch that supports OTP 27.0-rc2" This partially reverts commit e9b9159d23af4cdccf1954ada632fb693f47b512. --- rebar.config.script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config.script b/rebar.config.script index 4b2e8e702..61131ce1c 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -418,7 +418,7 @@ end, Rules = [ {[plugins], IsRebar3, - AppendList([{pc, ".*", {git, "https://github.com/blt/port_compiler.git", {branch, "otp-27"}}}]), []}, + AppendList([{pc, "1.15.0"}]), []}, {[provider_hooks], IsRebar3, AppendList([{pre, [ {compile, {asn, compile}}, From 5d2a111b9ca65ab9290a4046ffac639f071db813 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 17:13:46 +0200 Subject: [PATCH 0590/1302] Update lock files --- mix.lock | 12 ++++++------ rebar.lock | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mix.lock b/mix.lock index cb1c64290..9ec34c639 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, - "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, + "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, @@ -19,21 +19,21 @@ "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, - "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "167397c07cd3d64d5245d6a7e513baa8ff6bd95d", [branch: "master"]}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "176b4a8c67627c3229fbea3c05d82a5d567e6e49", [branch: "master"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, - "pkix": {:git, "https://github.com/processone/pkix", "15d9ea9b2d5b2b92743cbd32be85327dd87ec868", []}, + "pkix": {:git, "https://github.com/processone/pkix", "03be27c7168449bdfeb8c5b6cc0f800f71f8cfe9", []}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "ddaaf513366be7f96816046c6e269e2f27684762", []}, + "xmpp": {:git, "https://github.com/processone/xmpp", "281b42c544d6dc2028fd4f3d62e553638ab8bdd9", []}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.lock b/rebar.lock index 06f9310fc..5ad19d9f0 100644 --- a/rebar.lock +++ b/rebar.lock @@ -16,7 +16,7 @@ {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.16">>},0}, {<<"p1_acme">>, {git,"https://github.com/processone/p1_acme", - {ref,"167397c07cd3d64d5245d6a7e513baa8ff6bd95d"}}, + {ref,"176b4a8c67627c3229fbea3c05d82a5d567e6e49"}}, 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.23">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, @@ -24,7 +24,7 @@ {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, {<<"pkix">>, {git,"https://github.com/processone/pkix", - {ref,"15d9ea9b2d5b2b92743cbd32be85327dd87ec868"}}, + {ref,"03be27c7168449bdfeb8c5b6cc0f800f71f8cfe9"}}, 0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.14">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"ddaaf513366be7f96816046c6e269e2f27684762"}}, + {ref,"281b42c544d6dc2028fd4f3d62e553638ab8bdd9"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. [ From 034af6aefd81caf35ac45c9572e6f60e2109267c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 May 2024 19:34:21 +0200 Subject: [PATCH 0591/1302] Set process flags when Erlang/OTP 27 to help debugging https://www.erlang.org/blog/highlights-otp-27/#process-labels --- src/ejabberd_c2s.erl | 2 ++ src/ejabberd_listener.erl | 2 ++ src/misc.erl | 9 +++++++++ src/mod_muc_room.erl | 2 ++ 4 files changed, 15 insertions(+) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 6e87389b6..8f7c04c41 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -290,6 +290,7 @@ reject_unauthenticated_packet(State, _Pkt) -> process_auth_result(#{sasl_mech := Mech, auth_module := AuthModule, socket := Socket, ip := IP, lserver := LServer} = State, true, User) -> + misc:set_proc_label({?MODULE, User, LServer}), ?INFO_MSG("(~ts) Accepted c2s ~ts authentication for ~ts@~ts by ~ts backend from ~ts", [xmpp_socket:pp(Socket), Mech, User, LServer, ejabberd_auth:backend_type(AuthModule), @@ -617,6 +618,7 @@ init([State, Opts]) -> access => Access, shaper => Shaper}, State2 = xmpp_stream_in:set_timeout(State1, Timeout), + misc:set_proc_label({?MODULE, init_state}), ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]). handle_call(get_presence, From, #{jid := JID} = State) -> diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 809e4a239..a5c589f99 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -121,6 +121,7 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> ExtraOpts2]) of {ok, Socket} -> set_definitive_udsocket(Port, Opts), + misc:set_proc_label({?MODULE, udp, Port}), case inet:sockname(Socket) of {ok, {Addr, Port1}} -> proc_lib:init_ack({ok, self()}), @@ -155,6 +156,7 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> case inet:sockname(ListenSocket) of {ok, {Addr, Port1}} -> proc_lib:init_ack({ok, self()}), + misc:set_proc_label({?MODULE, tcp, Port}), case application:ensure_started(ejabberd) of ok -> Sup = start_module_sup(Module, Opts), diff --git a/src/misc.erl b/src/misc.erl index a1cd340a2..987a48c6c 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -43,6 +43,7 @@ get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, json_encode/1, json_decode/1, + set_proc_label/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). @@ -780,3 +781,11 @@ to_string(B) when is_binary(B) -> binary_to_list(B); to_string(S) -> S. + +-ifdef(OTP_BELOW_27). +set_proc_label(_Label) -> + ok. +-else. +set_proc_label(Label) -> + proc_lib:set_label(Label). +-endif. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 72ec867be..1c262b26d 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -292,6 +292,7 @@ get_disco_item(Pid, Filter, JID, Lang) -> init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Creator, _Nick, DefRoomOpts, QueueType]) -> process_flag(trap_exit, true), + misc:set_proc_label({?MODULE, Room, Host}), Shaper = ejabberd_shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), State = set_opts(DefRoomOpts, @@ -314,6 +315,7 @@ init([Host, ServerHost, Access, Room, HistorySize, {ok, normal_state, reset_hibernate_timer(State1)}; init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) -> process_flag(trap_exit, true), + misc:set_proc_label({?MODULE, Room, Host}), Shaper = ejabberd_shaper:new(RoomShaper), RoomQueue = room_queue_new(ServerHost, Shaper, QueueType), Jid = jid:make(Room, Host), From 599cddfb6724c6f4c3719c47dcc92af66c2c5f59 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Jan 2024 13:27:28 +0100 Subject: [PATCH 0592/1302] ejabberd_sql_schema: Rewrite recent commit cb49fcf lists:search is available since OTP 21.0, and ejabberd is said to support since OTP 20.0. Also, that code wouldn't work if ejabberd uses different database types (MySQL, PgSQL, ...), as this would only update the first database. --- src/ejabberd_sql_schema.erl | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index af9743f82..0960e1c29 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -773,14 +773,10 @@ should_update_schema(Host) -> case ejabberd_sql:use_new_schema() of true -> %% TODO: not efficient when there are many hosts - case lists:search( - fun(H) -> - lists:member( - sql, ejabberd_option:auth_method(H)) - end, ejabberd_option:hosts()) of - {value, Host} -> + case lists:member(sql, ejabberd_option:auth_method(Host)) of + true -> true; - _ -> + false -> false end; false -> From 1076f8bfc21dba27b06d178fbce60abf35afe8b9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 15 Apr 2024 10:42:46 +0200 Subject: [PATCH 0593/1302] Test: Clear tables when ending a group, instead of when starting Because a group may be started but the tables are not yet created, as they are created automatically when update_sql_schema is enabled. This modifies commit e2b79ea --- test/ejabberd_SUITE.erl | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index d2394083c..7fe6c4542 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -99,7 +99,6 @@ do_init_per_group(mysql, Config) -> case catch ejabberd_sql:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?MYSQL_VHOST), - clear_sql_tables(mysql, Config), update_sql(?MYSQL_VHOST, Config), set_opt(server, ?MYSQL_VHOST, Config); Err -> @@ -109,7 +108,6 @@ do_init_per_group(mssql, Config) -> case catch ejabberd_sql:sql_query(?MSSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?MSSQL_VHOST), - clear_sql_tables(mssql, Config), update_sql(?MSSQL_VHOST, Config), set_opt(server, ?MSSQL_VHOST, Config); Err -> @@ -119,7 +117,6 @@ do_init_per_group(pgsql, Config) -> case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> mod_muc:shutdown_rooms(?PGSQL_VHOST), - clear_sql_tables(pgsql, Config), update_sql(?PGSQL_VHOST, Config), set_opt(server, ?PGSQL_VHOST, Config); Err -> @@ -168,11 +165,34 @@ end_per_group(mnesia, _Config) -> ok; end_per_group(redis, _Config) -> ok; -end_per_group(mysql, _Config) -> +end_per_group(mysql, Config) -> + Query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'mqtt_pub';", + case catch ejabberd_sql:sql_query(?MYSQL_VHOST, [Query]) of + {selected, _, [[<<"0">>]]} -> + ok; + {selected, _, [[<<"1">>]]} -> + clear_sql_tables(mysql, Config); + Other -> + ct:fail({failed_to_check_table_existence, mysql, Other}) + end, ok; -end_per_group(mssql, _Config) -> +end_per_group(mssql, Config) -> + Query = "SELECT * FROM sys.tables WHERE name = 'mqtt_pub'", + case catch ejabberd_sql:sql_query(?MSSQL_VHOST, [Query]) of + {selected, [t]} -> + clear_sql_tables(mssql, Config); + Other -> + ct:fail({failed_to_check_table_existence, mssql, Other}) + end, ok; -end_per_group(pgsql, _Config) -> +end_per_group(pgsql, Config) -> + Query = "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'mqtt_pub');", + case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [Query]) of + {selected, [t]} -> + clear_sql_tables(pgsql, Config); + Other -> + ct:fail({failed_to_check_table_existence, pgsql, Other}) + end, ok; end_per_group(sqlite, _Config) -> ok; From ec13350b240ce3c43084418bf92726124def9b90 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 4 Oct 2023 21:15:07 +0200 Subject: [PATCH 0594/1302] Test: Enable update_sql_schema - Test: Enable update_sql_schema by default - Update test/README about new option - enable new_schema in tests --- test/README | 6 ++++-- test/ejabberd_SUITE_data/ejabberd.yml | 2 ++ test/ejabberd_SUITE_data/macros.yml | 1 + test/suite.erl | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/README b/test/README index 68ff183dc..8254230d2 100644 --- a/test/README +++ b/test/README @@ -17,7 +17,8 @@ $ psql template1 template1=# CREATE USER ejabberd_test WITH PASSWORD 'ejabberd_test'; template1=# CREATE DATABASE ejabberd_test; template1=# GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test; -$ psql ejabberd_test -f sql/pg.sql +# If you disabled the update_sql_schema option, create the schema manually: +# $ psql ejabberd_test -f sql/pg.sql ------------------- MySQL @@ -26,7 +27,8 @@ $ mysql mysql> CREATE USER 'ejabberd_test'@'localhost' IDENTIFIED BY 'ejabberd_test'; mysql> CREATE DATABASE ejabberd_test; mysql> GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost'; -$ mysql ejabberd_test < sql/mysql.sql +# If you disabled the update_sql_schema option, create the schema manually: +# $ mysql ejabberd_test < sql/mysql.sql ------------------- MS SQL Server diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index ebbcfa596..d3a24315f 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -161,6 +161,8 @@ certfiles: new_sql_schema: NEW_SCHEMA +update_sql_schema: UPDATE_SQL_SCHEMA + api_permissions: "public commands": who: all diff --git a/test/ejabberd_SUITE_data/macros.yml b/test/ejabberd_SUITE_data/macros.yml index e4270d4c1..5391562ba 100644 --- a/test/ejabberd_SUITE_data/macros.yml +++ b/test/ejabberd_SUITE_data/macros.yml @@ -14,6 +14,7 @@ define_macro: PUT_URL: "http://upload.@HOST@:@@web_port@@/upload" GET_URL: "http://upload.@HOST@:@@web_port@@/upload" NEW_SCHEMA: @@new_schema@@ + UPDATE_SQL_SCHEMA: @@update_sql_schema@@ MYSQL_USER: "@@mysql_user@@" MYSQL_SERVER: "@@mysql_server@@" MYSQL_PORT: @@mysql_port@@ diff --git a/test/suite.erl b/test/suite.erl index 38803c517..198a4b596 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -59,6 +59,7 @@ init_config(Config) -> [{c2s_port, 5222}, {loglevel, 4}, {new_schema, false}, + {update_sql_schema, true}, {s2s_port, 5269}, {stun_port, 3478}, {component_port, 5270}, From a8e36e7b1b287f7c90d42d9f8bf7f7a3aa8267b5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 May 2024 11:51:44 +0200 Subject: [PATCH 0595/1302] CI: Don't load database schema files for mysql and pgsql --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b9b7b6e7..e3f9198c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,14 +77,12 @@ jobs: IDENTIFIED BY 'ejabberd_test';" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" - mysql -u ejabberd_test -pejabberd_test ejabberd_test < sql/mysql.sql pg_isready sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" sudo -u postgres psql -c "CREATE USER ejabberd_test WITH PASSWORD 'ejabberd_test';" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" - PGPASSWORD="ejabberd_test" psql -h localhost -U ejabberd_test ejabberd_test -f sql/pg.sql sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" @@ -266,11 +264,9 @@ jobs: mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" - mysql -u ejabberd_test -pejabberd_test ejabberd_test < sql/mysql.new.sql sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" - PGPASSWORD="ejabberd_test" psql -h localhost -U ejabberd_test ejabberd_test -f sql/pg.new.sql sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" From 80dc95fc03c1c986afdc2c258f8fed09c3137044 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 25 May 2024 20:33:20 +0200 Subject: [PATCH 0596/1302] Remove unnecessary case from recent commit 599cddf --- src/ejabberd_sql_schema.erl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 0960e1c29..91b8aab72 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -772,13 +772,7 @@ should_update_schema(Host) -> true -> case ejabberd_sql:use_new_schema() of true -> - %% TODO: not efficient when there are many hosts - case lists:member(sql, ejabberd_option:auth_method(Host)) of - true -> - true; - false -> - false - end; + lists:member(sql, ejabberd_option:auth_method(Host)); false -> true end; From d04f92d44e532e6ef60f896ca7ae9a80d3ccd709 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 7 May 2024 16:09:40 +0200 Subject: [PATCH 0597/1302] mod_muc_room: Use ejabberd hooks instead of function calls to mod_muc_log (#4191) --- src/mod_muc_log.erl | 23 +++++++++++++++++------ src/mod_muc_room.erl | 36 ++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index b678961b5..04dc2f3f8 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -34,8 +34,8 @@ -behaviour(gen_mod). %% API --export([start/2, stop/1, reload/3, get_url/1, - check_access_log/2, add_to_log/5]). +-export([start/2, stop/1, reload/3, get_url/2, + check_access_log/3, add_to_log/5]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, @@ -82,7 +82,9 @@ add_to_log(Host, Type, Data, Room, Opts) -> gen_server:cast(get_proc_name(Host), {add_to_log, Type, Data, Room, Opts}). -check_access_log(Host, From) -> +check_access_log(allow, _Host, _From) -> + allow; +check_access_log(_Acc, Host, From) -> case catch gen_server:call(get_proc_name(Host), {check_access_log, Host, From}) of @@ -90,8 +92,10 @@ check_access_log(Host, From) -> Res -> Res end. --spec get_url(#state{}) -> {ok, binary()} | error. -get_url(#state{room = Room, host = Host, server_host = ServerHost}) -> +-spec get_url(any(), #state{}) -> {ok, binary()} | error. +get_url({ok, _} = Acc, _State) -> + Acc; +get_url(_Acc, #state{room = Room, host = Host, server_host = ServerHost}) -> try mod_muc_log_opt:url(ServerHost) of undefined -> error; URL -> @@ -115,6 +119,9 @@ depends(_Host, _Opts) -> init([Host|_]) -> process_flag(trap_exit, true), Opts = gen_mod:get_module_opts(Host, ?MODULE), + ejabberd_hooks:add(muc_log_add, Host, ?MODULE, add_to_log, 100), + ejabberd_hooks:add(muc_log_check_access_log, Host, ?MODULE, check_access_log, 100), + ejabberd_hooks:add(muc_log_get_url, Host, ?MODULE, get_url, 100), {ok, init_state(Host, Opts)}. handle_call({check_access_log, ServerHost, FromJID}, _From, State) -> @@ -137,7 +144,11 @@ handle_cast(Msg, State) -> handle_info(_Info, State) -> {noreply, State}. -terminate(_Reason, _State) -> ok. +terminate(_Reason, #state{host = Host}) -> + ejabberd_hooks:delete(muc_log_add, Host, ?MODULE, add_to_log, 100), + ejabberd_hooks:delete(muc_log_check_access_log, Host, ?MODULE, check_access_log, 100), + ejabberd_hooks:delete(muc_log_get_url, Host, ?MODULE, get_url, 100), + ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 1c262b26d..3d3215f94 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3716,8 +3716,10 @@ is_allowed_log_change(Options, StateData, From) -> false -> true; true -> allow == - mod_muc_log:check_access_log(StateData#state.server_host, - From) + ejabberd_hooks:run_fold(muc_log_check_access_log, + StateData#state.server_host, + deny, + [StateData#state.server_host, From]) end. -spec is_allowed_persistent_change(muc_roomconfig:result(), state(), jid()) -> boolean(). @@ -3856,7 +3858,10 @@ get_config(Lang, StateData, From) -> [] end ++ - case mod_muc_log:check_access_log(StateData#state.server_host, From) of + case ejabberd_hooks:run_fold(muc_log_check_access_log, + StateData#state.server_host, + deny, + [StateData#state.server_host, From]) of allow -> [{enablelogging, Config#config.logging}]; deny -> [] end, @@ -4555,7 +4560,10 @@ iq_disco_info_extras(Lang, StateData, Static) -> end, Fs4 = case Config#config.logging of true -> - case mod_muc_log:get_url(StateData) of + case ejabberd_hooks:run_fold(muc_log_get_url, + StateData#state.server_host, + error, + [StateData]) of {ok, URL} -> [{logs, URL}|Fs3]; error -> @@ -5334,15 +5342,23 @@ handle_roommessage_from_nonparticipant(Packet, StateData, From) -> add_to_log(Type, Data, StateData) when Type == roomconfig_change_disabledlogging -> - mod_muc_log:add_to_log(StateData#state.server_host, - roomconfig_change, Data, StateData#state.jid, - make_opts(StateData, false)); + ejabberd_hooks:run(muc_log_add, + StateData#state.server_host, + [StateData#state.server_host, + roomconfig_change, + Data, + StateData#state.jid, + make_opts(StateData, false)]); add_to_log(Type, Data, StateData) -> case (StateData#state.config)#config.logging of true -> - mod_muc_log:add_to_log(StateData#state.server_host, - Type, Data, StateData#state.jid, - make_opts(StateData, false)); + ejabberd_hooks:run(muc_log_add, + StateData#state.server_host, + [StateData#state.server_host, + Type, + Data, + StateData#state.jid, + make_opts(StateData, false)]); false -> ok end. From 1b81af00b29a35fd37546cb09c20b3c24eaf9b20 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 May 2024 12:38:19 +0200 Subject: [PATCH 0598/1302] Update p1_pgsql and mysql to support Unix Domain Socket (#3716) --- mix.exs | 4 ++-- rebar.config | 4 ++-- src/ejabberd_options_doc.erl | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index c58496ce5..c592422f2 100644 --- a/mix.exs +++ b/mix.exs @@ -174,8 +174,8 @@ defmodule Ejabberd.MixProject do {if_version_below(~c"27", true), {:jiffy, "~> 1.1.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.2.0"}}, - {config(:mysql), {:p1_mysql, ">= 1.0.23" }}, - {config(:pgsql), {:p1_pgsql, "~> 1.1"}}, + {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql" }}, + {config(:pgsql), {:p1_pgsql, git: "https://github.com/processone/p1_pgsql"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: dep diff --git a/rebar.config b/rebar.config index 6119d0298..5d63df68a 100644 --- a/rebar.config +++ b/rebar.config @@ -58,10 +58,10 @@ {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {branch, "master"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.23"}}}}, + {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {branch, "master"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.15", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.25"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {branch, "master"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {branch, "master"}}}, {if_var_true, sqlite, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 5bb1b3b9b..bd80461c0 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1378,11 +1378,13 @@ doc() -> "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {sql_server, - #{value => ?T("Host"), - note => "improved in 23.04", + #{value => "Host | IP Address | ODBC Connection String | Unix Socket Path", + note => "improved in 24.xx", desc => ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " "'mssql' or 'odbc' this can also be an ODBC connection string. " + "When _`sql_type`_ is 'mysql' or 'pgsql', this can be the path to " + "a unix domain socket expressed like: \"unix:/path/to/socket\"." "The default value is 'localhost'.")}}, {sql_ssl, #{value => "true | false", From b288d5c76370e44fef3a9caa6fbb888435057a2a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 May 2024 10:36:24 +0200 Subject: [PATCH 0599/1302] make-binaries: Revert temporarily an OTP commit that breaks MSSQL (#4178) --- tools/make-binaries | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/make-binaries b/tools/make-binaries index 4e5339105..90ddae8e8 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -684,6 +684,9 @@ build_deps() export erl_xcomp_sysroot="$prefix" fi cd "$target_src_dir/$otp_dir" + # Revert https://github.com/erlang/otp/commit/53ef5df40c733ce3d8215c5c98805f99f378f656 + # because it breaks MSSQL, see https://github.com/processone/ejabberd/issues/4178 + sed -i 's|if(size == 0 && (sql_type == SQL_LONGVARCHAR|if((sql_type == SQL_LONGVARCHAR|g' lib/odbc/c_src/odbcserver.c # The additional CFLAGS/LIBS below are required by --enable-static-nifs. # The "-ldl" flag specifically is only needed for ODBC, though. $configure \ From d81a844e1c67d4a7cd853562c8faf4946d97116e Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 25 May 2024 21:07:19 +0200 Subject: [PATCH 0600/1302] make-binaries: Bump versions to Erlang/OTP 26.2.5 and Elixir 1.16.3 --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 90ddae8e8..9fb165083 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,8 +71,8 @@ expat_vsn='2.6.2' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.3.0' -otp_vsn='26.2.3' -elixir_vsn='1.16.2' +otp_vsn='26.2.5' +elixir_vsn='1.16.3' pam_vsn='1.6.1' png_vsn='1.6.42' jpeg_vsn='9f' From ab274147d88fd12fb4b4aa0a001b21fc150a308b Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 25 May 2024 21:04:15 +0200 Subject: [PATCH 0601/1302] Runtime: Add final OTP 27 and preliminary Elixir 1.17 to the matrix --- .github/workflows/runtime.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index a3495b717..87f9328e6 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -151,17 +151,23 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27.0-rc3'] - elixir: ['1.13', '1.15', '1.16'] + otp: ['23.0', '25', '26', '27'] + elixir: ['1.13', '1.15', '1.16', '1.17'] exclude: - otp: '23.0' elixir: '1.15' - otp: '23.0' elixir: '1.16' + - otp: '23.0' + elixir: '1.17' - otp: '26' elixir: '1.13' - - otp: '27.0-rc3' + - otp: '27' elixir: '1.13' + - otp: '27' + elixir: '1.15' + - otp: '27' + elixir: '1.16' runs-on: ubuntu-20.04 steps: @@ -285,17 +291,23 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27.0-rc3'] - elixir: ['1.13', '1.15', '1.16'] + otp: ['23.0', '25', '26', '27'] + elixir: ['1.13', '1.15', '1.16', '1.17'] exclude: - otp: '23.0' elixir: '1.15' - otp: '23.0' elixir: '1.16' + - otp: '23.0' + elixir: '1.17' - otp: '26' elixir: '1.13' - - otp: '27.0-rc3' + - otp: '27' elixir: '1.13' + - otp: '27' + elixir: '1.15' + - otp: '27' + elixir: '1.16' runs-on: ubuntu-20.04 steps: From a179d5e34b5bb7f91c884cdee667ab73c807c1e8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 May 2024 10:35:04 +0200 Subject: [PATCH 0602/1302] mix:exs: Move xmpp from included_applications to applications because now p1_pgsql has it in applications to satisfy dialyzer OTP 26, and we cannot have xmpp both in applications and included_applications. --- mix.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index c592422f2..d2afd331b 100644 --- a/mix.exs +++ b/mix.exs @@ -45,11 +45,11 @@ defmodule Ejabberd.MixProject do [mod: {:ejabberd_app, []}, applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :fast_tls, :fast_xml, :fast_yaml, :jose, - :p1_utils, :stringprep, :syntax_tools, :yconf] + :p1_utils, :stringprep, :syntax_tools, :yconf, :xmpp] ++ cond_apps(), included_applications: [:mnesia, :os_mon, :cache_tab, :eimp, :mqtree, :p1_acme, - :p1_oauth2, :pkix, :xmpp] + :p1_oauth2, :pkix] ++ cond_included_apps()] end From b977eb71939974fbd0e42aef5715af91c19b572e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 May 2024 10:51:14 +0200 Subject: [PATCH 0603/1302] Add support for XEP-0425 version 0.3.0, keep supporting 0.2.1 (#4193) --- src/mod_mam.erl | 2 +- src/mod_muc_room.erl | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index fae766558..57a6d94c9 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -28,7 +28,7 @@ -protocol({xep, 313, '0.6.1', '15.06', "", ""}). -protocol({xep, 334, '0.2'}). -protocol({xep, 359, '0.5.0'}). --protocol({xep, 425, '0.2.1', '23.04', "", ""}). +-protocol({xep, 425, '0.3.0', '24.xx', "", ""}). -protocol({xep, 441, '0.2.0'}). -behaviour(gen_mod). diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 3d3215f94..e97d73f81 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -505,10 +505,12 @@ normal_state({route, <<"">>, process_iq_adhoc(From, IQ, StateData); #register{} -> mod_muc:process_iq_register(IQ); - #fasten_apply_to{} = ApplyTo -> + #message_moderate{} = Moderate -> % moderate:1 + process_iq_moderate(From, IQ, Moderate, StateData); + #fasten_apply_to{id = ModerateId} = ApplyTo -> case xmpp:get_subtag(ApplyTo, #message_moderate{}) of - #message_moderate{} = Moderate -> - process_iq_moderate(From, IQ, ApplyTo, Moderate, StateData); + #message_moderate{} = Moderate -> % moderate:0 + process_iq_moderate(From, IQ, Moderate#message_moderate{id = ModerateId}, StateData); _ -> Txt = ?T("The feature requested is not " "supported by the conference"), @@ -4399,7 +4401,9 @@ make_disco_info(From, StateData) -> ServerHost = StateData#state.server_host, AccessRegister = mod_muc_opt:access_register(ServerHost), Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, - ?NS_COMMANDS, ?NS_MESSAGE_MODERATE, ?NS_MESSAGE_RETRACT, + ?NS_COMMANDS, + ?NS_MESSAGE_MODERATE_0, ?NS_MESSAGE_MODERATE_1, + ?NS_MESSAGE_RETRACT, ?CONFIG_OPT_TO_FEATURE((Config#config.public), <<"muc_public">>, <<"muc_hidden">>), ?CONFIG_OPT_TO_FEATURE((Config#config.persistent), @@ -5158,14 +5162,13 @@ add_presence_hats(JID, Pres, StateData) -> Pres end. --spec process_iq_moderate(jid(), iq(), fasten_apply_to(), message_moderate(), state()) -> +-spec process_iq_moderate(jid(), iq(), message_moderate(), state()) -> {result, undefined, state()} | {error, stanza_error()}. -process_iq_moderate(_From, #iq{type = get}, _ApplyTo, _Moderate, _StateData) -> +process_iq_moderate(_From, #iq{type = get}, _Moderate, _StateData) -> {error, xmpp:err_bad_request()}; process_iq_moderate(From, #iq{type = set, lang = Lang}, - #fasten_apply_to{id = Id}, - #message_moderate{reason = Reason}, + #message_moderate{id = Id, reason = Reason, xmlns = Xmlns}, #state{config = Config, room = Room, host = Host, jid = JID, server_host = Server} = StateData) -> FAffiliation = get_affiliation(From, StateData), @@ -5186,13 +5189,25 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, ok end, By = jid:replace_resource(JID, find_nick_by_jid(From, StateData)), + SubEl = case Xmlns of + ?NS_MESSAGE_MODERATE_0 -> + SubEls = [#xmlel{name = <<"reason">>, + attrs = [], + children = [{xmlcdata, Reason}]}, + #message_retract{id = Id}], + ModeratedEl = #message_moderated{by = By, + sub_els = SubEls}, + #fasten_apply_to{id = Id, + sub_els = [ModeratedEl]}; + ?NS_MESSAGE_MODERATE_1 -> + ModeratedEl = #message_moderated{by = By}, + #message_retract{id = Id, + reason = Reason, + moderated = ModeratedEl} + end, Packet0 = #message{type = groupchat, from = From, - sub_els = [ - #fasten_apply_to{id = Id, sub_els = [ - #message_moderated{by = By, reason = Reason, - retract = #message_retract{id = Id}} - ]}]}, + sub_els = [SubEl]}, {FromNick, _Role} = get_participant_data(From, StateData), Packet = ejabberd_hooks:run_fold(muc_filter_message, StateData#state.server_host, From b840ab99072454dcc32647f4ad88aaf10b6e20cd Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 May 2024 15:22:35 +0200 Subject: [PATCH 0604/1302] Revert "Disable update_sql_schema by default" The update_sql_schema feature was published half a year ago, included in two releases, it is used by the CI tests, and consequently it's stable enough to be enabled by default. This reverts commit ec20691188e77b73bf38b990c5b44c3fb82c553c. --- src/ejabberd_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 9551ba37b..6aca9c80f 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -612,7 +612,7 @@ options() -> {negotiation_timeout, timer:seconds(120)}, {net_ticktime, timer:seconds(60)}, {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, - {update_sql_schema, false}, + {update_sql_schema, true}, {oauth_access, none}, {oauth_cache_life_time, fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, From 52abe8016b8919fffe22d9be8106ca54b0af4bf2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 May 2024 15:21:56 +0200 Subject: [PATCH 0605/1302] Update lock files --- mix.lock | 6 +++--- rebar.lock | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/mix.lock b/mix.lock index 9ec34c639..72b96da22 100644 --- a/mix.lock +++ b/mix.lock @@ -25,15 +25,15 @@ "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "p1_acme": {:git, "https://github.com/processone/p1_acme", "176b4a8c67627c3229fbea3c05d82a5d567e6e49", [branch: "master"]}, - "p1_mysql": {:hex, :p1_mysql, "1.0.23", "9b4e98f1f01e2cc4a759f611f0f015365e65c5f61ea453aa4bdfe950d20a5f11", [:rebar3], [], "hexpm", "9cef98265fb287374e7b64bf4022c4df5ec66196ca31cf4bed73c67c45bf824e"}, + "p1_mysql": {:git, "https://github.com/processone/p1_mysql", "0dccb755a60cda61d41e7b092b344d38d1ebc802", []}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.25", "f59b7faa457fadf0c2713ac335202f41ca1b06b7c4926925b3cb0bc6f0578601", [:rebar3], [{:xmpp, "1.8.1", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b654c1e07d3e775b626b407e7696cacfd88d17be83e7168b9d89c3832d913de7"}, + "p1_pgsql": {:git, "https://github.com/processone/p1_pgsql", "fe0bb2a2a2ae21cd9fc48b81520cfce508520a56", []}, "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, "pkix": {:git, "https://github.com/processone/pkix", "03be27c7168449bdfeb8c5b6cc0f800f71f8cfe9", []}, "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "281b42c544d6dc2028fd4f3d62e553638ab8bdd9", []}, + "xmpp": {:git, "https://github.com/processone/xmpp", "a1fb778bd385d832f913e564558152ea507dac37", []}, "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, } diff --git a/rebar.lock b/rebar.lock index 5ad19d9f0..ca05ecd90 100644 --- a/rebar.lock +++ b/rebar.lock @@ -18,9 +18,15 @@ {git,"https://github.com/processone/p1_acme", {ref,"176b4a8c67627c3229fbea3c05d82a5d567e6e49"}}, 0}, - {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.23">>},0}, + {<<"p1_mysql">>, + {git,"https://github.com/processone/p1_mysql", + {ref,"0dccb755a60cda61d41e7b092b344d38d1ebc802"}}, + 0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.25">>},0}, + {<<"p1_pgsql">>, + {git,"https://github.com/processone/p1_pgsql", + {ref,"fe0bb2a2a2ae21cd9fc48b81520cfce508520a56"}}, + 0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, {<<"pkix">>, {git,"https://github.com/processone/pkix", @@ -32,7 +38,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"281b42c544d6dc2028fd4f3d62e553638ab8bdd9"}}, + {ref,"a1fb778bd385d832f913e564558152ea507dac37"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. [ @@ -52,9 +58,7 @@ {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, {<<"mqtree">>, <<"F8F8B4971E4CA94313BA9BCAAA1AA1077DAABA5E3FD3468FFB491420A4CC3593">>}, - {<<"p1_mysql">>, <<"9B4E98F1F01E2CC4A759F611F0F015365E65C5F61EA453AA4BDFE950D20A5F11">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"F59B7FAA457FADF0C2713AC335202F41CA1B06B7C4926925B3CB0BC6F0578601">>}, {<<"p1_utils">>, <<"2D39B5015A567BBD2CC7033EEB93A7C60D8C84EFE1EF69A3473FAA07FA268187">>}, {<<"sqlite3">>, <<"F9EA0CFF8540865FDFDB7E24EEF34DC46677364B1C070896E99B5BF08C8A7FD7">>}, {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, @@ -77,9 +81,7 @@ {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, {<<"mqtree">>, <<"C87D1C95575DB65AF29B795C9DAA3BED43F5C1BF84072A74469659BCF53594EB">>}, - {<<"p1_mysql">>, <<"9CEF98265FB287374E7B64BF4022C4DF5EC66196CA31CF4BED73C67C45BF824E">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"B654C1E07D3E775B626B407E7696CACFD88D17BE83E7168B9D89C3832D913DE7">>}, {<<"p1_utils">>, <<"9219214428F2C6E5D3187FF8EB9A8783695C2427420BE9A259840E07ADA32847">>}, {<<"sqlite3">>, <<"85054B6CA297343C159ED6794A473FF2C8EEABD854B6FE02F711C0BFD373CE86">>}, {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, From 7ba9f2e4e8c56cf109395b6cfa15466d02053ec0 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 28 May 2024 21:07:04 +0200 Subject: [PATCH 0606/1302] mod_private: Cope with bookmark decoding errors Handle invalid XEP-0048 bookmarks gracefully while attempting to publish them to an XEP-0402 node. --- src/mod_private.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 82c6653a6..9e25dbf7e 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -328,9 +328,13 @@ publish_pep_native_bookmarks(JID, Data) -> true -> case lists:keyfind(?NS_STORAGE_BOOKMARKS, 1, Data) of {_, Bookmarks0} -> - Bookmarks = case xmpp:decode(Bookmarks0) of + Bookmarks = try xmpp:decode(Bookmarks0) of #bookmark_storage{conference = C} -> C; _ -> [] + catch _:{xmpp_codec, Why} -> + ?WARNING_MSG("Failed to decode bookmarks of ~ts: ~ts", + [jid:encode(JID), xmpp:format_error(Why)]), + [] end, PubOpts = [{persist_items, true}, {access_model, whitelist}, {max_items, max}, {notify_retract,true}, {notify_delete,true}, {send_last_published_item, never}], case mod_pubsub:get_items(LBJID, ?NS_PEP_BOOKMARKS) of From 882cd8faf5602b98cfe6df5c1d4c717d4a541b27 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 29 May 2024 10:22:53 +0200 Subject: [PATCH 0607/1302] ejabberd_access_permissions: Fix debug lines indentation that were hard to read --- src/ejabberd_access_permissions.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index ed2365ff7..a844d2e21 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -82,7 +82,7 @@ can_access(Cmd, CallerInfo) -> case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of true -> ?DEBUG("Command '~p' execution allowed by rule " - "'~ts' (CallerInfo=~p)", [Cmd, Name, CallerInfo]), + "'~ts'~n (CallerInfo=~p)", [Cmd, Name, CallerInfo]), allow; _ -> none @@ -93,7 +93,7 @@ can_access(Cmd, CallerInfo) -> case Res of allow -> allow; _ -> - ?DEBUG("Command '~p' execution denied " + ?DEBUG("Command '~p' execution denied~n " "(CallerInfo=~p)", [Cmd, CallerInfo]), deny end. From e4c8dbc2afcc993ceb67d0d9fb005bbb80aa9526 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jun 2024 22:40:55 +0200 Subject: [PATCH 0608/1302] make-binaries: Bump OpenSSL version to 3.3.1 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 9fb165083..f2850b530 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,7 +70,7 @@ termcap_vsn='1.3.1' expat_vsn='2.6.2' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.3.0' +ssl_vsn='3.3.1' otp_vsn='26.2.5' elixir_vsn='1.16.3' pam_vsn='1.6.1' From 103a30df2c25edbc99e4df6c5f11a2e99ed51a15 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 7 Jun 2024 17:21:52 +0200 Subject: [PATCH 0609/1302] Document that XEP-0485 is available in mod_pubsub_serverinfo --- src/ejabberd.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index abb2d89d6..69f02522c 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -45,6 +45,7 @@ -protocol({xep, 424, '0.4.0', '24.02', "", ""}). -protocol({xep, 440, '0.4.0', '24.02', "", ""}). -protocol({xep, 474, '0.3.0', '24.02', "", ""}). +-protocol({xep, 485, '0.2.0', '24.02', "", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). From 2c5a8f0860d874e71f7588842d3844b1b76db663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 13 Jun 2024 18:02:13 +0200 Subject: [PATCH 0610/1302] Make native dynamic node names work when using fully qualified domain names This should fix issue reported in 4184 --- ejabberdctl.template | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 4eea168fb..83ec7e1bd 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -273,7 +273,11 @@ uid() { else # Erlang/OTP 23 or higher: use native dynamic node code # https://www.erlang.org/patches/otp-23.0#OTP-13812 - echo undefined + if [ "$ERLANG_NODE" != "${ERLANG_NODE%.*}" ]; then + echo "undefined@${ERLANG_NODE#*@}" + else + echo "undefined" + fi fi } From fff0b5209c77248f024ef3feb21af34640cf1627 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Jun 2024 10:23:28 +0200 Subject: [PATCH 0611/1302] Apply previous commit to the container ejabberdctl script --- .github/container/ejabberdctl.template | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index fdfbedf10..af2874fff 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -286,7 +286,11 @@ uid() { else # Erlang/OTP 23 or higher: use native dynamic node code # https://www.erlang.org/patches/otp-23.0#OTP-13812 - echo undefined + if [ "$ERLANG_NODE" != "${ERLANG_NODE%.*}" ]; then + echo "undefined@${ERLANG_NODE#*@}" + else + echo "undefined" + fi fi } From be847a7e917046182caeac9e9aebac17181108dd Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Jun 2024 13:46:26 +0200 Subject: [PATCH 0612/1302] rebar.config.script: Require port_compiler 1.15.0 and accept any 1.15.x --- rebar.config.script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config.script b/rebar.config.script index 61131ce1c..0b5c75d15 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -418,7 +418,7 @@ end, Rules = [ {[plugins], IsRebar3, - AppendList([{pc, "1.15.0"}]), []}, + AppendList([{pc, "~> 1.15.0"}]), []}, {[provider_hooks], IsRebar3, AppendList([{pre, [ {compile, {asn, compile}}, From 701d2ce0f6f5913a2d282b43c5c40954380b7889 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 20:58:44 +0200 Subject: [PATCH 0613/1302] Define kick_user command version 2, useful for the WebAdmin command usage --- src/ejabberd_sm.erl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index b986d5400..93973dba1 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -62,6 +62,7 @@ user_resources/2, kick_user/2, kick_user/3, + kick_user_restuple/2, get_session_pid/3, get_session_sid/3, get_session_sids/2, @@ -1047,7 +1048,19 @@ get_commands_spec() -> args_example = [<<"user1">>, <<"example.com">>], result_desc = "Number of resources that were kicked", result_example = 3, - result = {num_resources, integer}}]. + result = {num_resources, integer}}, + + #ejabberd_commands{name = kick_user, tags = [session], + desc = "Disconnect user's active sessions", + module = ?MODULE, function = kick_user_restuple, + version = 2, + note = "modified in 24.xx", + args = [{user, binary}, {host, binary}], + args_desc = ["User name", "Server name"], + args_example = [<<"user1">>, <<"example.com">>], + result_desc = "The result text indicates the number of sessions that were kicked", + result_example = {ok, <<"Kicked sessions: 2">>}, + result = {res, restuple}}]. -spec connected_users() -> [binary()]. @@ -1082,5 +1095,9 @@ kick_user(User, Server, Resource) -> Pid -> ejabberd_c2s:route(Pid, kick) end. +kick_user_restuple(User, Server) -> + NumberBin = integer_to_binary(kick_user(User, Server)), + {ok, <<"Kicked sessions: ", NumberBin/binary>>}. + make_sid() -> {misc:unique_timestamp(), self()}. From 3bda8582252c7a07fc848003e8facb0fad566431 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 2 Jun 2024 13:36:37 +0200 Subject: [PATCH 0614/1302] Add Makefile targets to format and indent source code --- Makefile.in | 15 ++++++++++++++- rebar.config | 4 +++- tools/emacs-indent.sh | 27 +++++++++++++++++++++++++++ tools/rebar3-format.sh | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100755 tools/emacs-indent.sh create mode 100755 tools/rebar3-format.sh diff --git a/Makefile.in b/Makefile.in index ec6f911eb..c89b31249 100644 --- a/Makefile.in +++ b/Makefile.in @@ -261,6 +261,16 @@ _build/edoc/docs.md: edoc_compile _build/edoc/logo.png: edoc_compile wget https://docs.ejabberd.im/assets/img/footer_logo_e.png -O _build/edoc/logo.png +#. +#' format / indent +# + +format: + tools/rebar3-format.sh $(REBAR) + +indent: + tools/emacs-indent.sh + #. #' copy-files # @@ -650,7 +660,7 @@ test: .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ install uninstall uninstall-binary uninstall-all translations deps test \ all dev doap help install-rel relive scripts uninstall-rel update \ - erlang_plt deps_plt ejabberd_plt xref hooks options + erlang_plt deps_plt ejabberd_plt xref hooks options format indent #. #' help @@ -682,6 +692,9 @@ help: @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" @echo "" + @echo " format Format source code using rebar3_format [rebar3]" + @echo " indent Indent source code using erlang-mode [emacs]" + @echo "" @echo " dialyzer Run Dialyzer static analyzer" @echo " hooks Run hooks validator" @echo " test Run Common Tests suite [rebar3]" diff --git a/rebar.config b/rebar.config index 5d63df68a..d989b4dd7 100644 --- a/rebar.config +++ b/rebar.config @@ -152,7 +152,9 @@ {git, "https://github.com/bsanyi/rebar_mix.git", {branch, "consolidation_fix"}}} }]}}. -{if_rebar3, {project_plugins, [configure_deps]}}. +{if_rebar3, {project_plugins, [configure_deps, + {if_var_true, tools, rebar3_format} + ]}}. {if_not_rebar3, {plugins, [ deps_erl_opts, override_deps_versions2, override_opts, configure_deps ]}}. diff --git a/tools/emacs-indent.sh b/tools/emacs-indent.sh new file mode 100755 index 000000000..f3caecf4c --- /dev/null +++ b/tools/emacs-indent.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# To indent and remove tabs, surround the piece of code with: +# %% @indent-begin +# %% @indent-end +# +# Then run: +# make indent +# +# Please note this script only indents the first occurrence. + +FILES=$(git grep --name-only @indent-begin src/) + +for FILENAME in $FILES; do + echo "==> Indenting $FILENAME..." + emacs -batch $FILENAME \ + -f "erlang-mode" \ + --eval "(goto-char (point-min))" \ + --eval "(re-search-forward \"@indent-begin\" nil t)" \ + --eval "(setq begin (line-beginning-position))" \ + --eval "(re-search-forward \"@indent-end\" nil t)" \ + --eval "(setq end (line-beginning-position))" \ + --eval "(erlang-indent-region begin end)" \ + --eval "(untabify begin end)" \ + -f "delete-trailing-whitespace" \ + -f "save-buffer" +done diff --git a/tools/rebar3-format.sh b/tools/rebar3-format.sh new file mode 100755 index 000000000..bb0f521d0 --- /dev/null +++ b/tools/rebar3-format.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# To start formatting a file, add a line that contains: +# @format-begin +# Formatting in that file can later be disabled adding another line with: +# @format-end +# +# It can be reenabled again later in the file. +# +# Finally, call: make format + +REBAR=$1 + +ERLS=$(git grep --name-only @format-begin src/) + +for ERL in $ERLS; do + csplit --quiet --prefix=$ERL-format- $ERL /@format-/ "{*}" +done + +EFMTS=$(find src/*-format-* -type f -exec grep --files-with-matches "@format-begin" '{}' ';') +EFMTS2="" +for EFMT in $EFMTS; do + EFMTS2="$EFMTS2 --files $EFMT" +done +$REBAR format $EFMTS2 + +for ERL in $ERLS; do + SPLITS=$(find $ERL-format-* -type f) + rm $ERL + for SPLIT in $SPLITS; do + cat $SPLIT >> $ERL + rm $SPLIT + done +done From a16e5a7055bd17979cfd171474d2b595dab3392d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 12 Jun 2024 23:38:58 +0200 Subject: [PATCH 0615/1302] WebAdmin: Remove webadmin_view for now, as commands allow more fine-grained permissions --- src/ejabberd_web_admin.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 742ac262c..ea43a63fd 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -73,13 +73,13 @@ get_acl_rule([<<"vhosts">>], _) -> %% The pages of a vhost are only accessible if the user is admin of that vhost: get_acl_rule([<<"server">>, VHost | _RPath], Method) when Method =:= 'GET' orelse Method =:= 'HEAD' -> - {VHost, [configure, webadmin_view]}; + {VHost, [configure]}; get_acl_rule([<<"server">>, VHost | _RPath], 'POST') -> {VHost, [configure]}; %% Default rule: only global admins can access any other random page get_acl_rule(_RPath, Method) when Method =:= 'GET' orelse Method =:= 'HEAD' -> - {global, [configure, webadmin_view]}; + {global, [configure]}; get_acl_rule(_RPath, 'POST') -> {global, [configure]}. @@ -581,7 +581,7 @@ list_vhosts_allowed(JID) -> Hosts = ejabberd_option:hosts(), lists:filter(fun (Host) -> any_rules_allowed(Host, - [configure, webadmin_view], + [configure], JID) end, Hosts). From 724f304e486b165f065f62d3f9caddf8332f92c6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 15:33:48 +0200 Subject: [PATCH 0616/1302] WebAdmin: Unauthorized response: include some text to direct to the logs --- src/ejabberd_web_admin.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index ea43a63fd..ac0d9f3c9 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -174,8 +174,7 @@ process2([<<"server">>, SHost | RPath] = Path, {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"ejabberd\"">>}], - ejabberd_web:make_xhtml([?XCT(<<"h1">>, - ?T("Unauthorized"))])}; + ejabberd_web:make_xhtml(make_unauthorized(Lang))}; {unauthorized, Error} -> {BadUser, _BadPass} = Auth, {IPT, _Port} = Request#request.ip, @@ -186,8 +185,7 @@ process2([<<"server">>, SHost | RPath] = Path, [{<<"WWW-Authenticate">>, <<"basic realm=\"auth error, retry login " "to ejabberd\"">>}], - ejabberd_web:make_xhtml([?XCT(<<"h1">>, - ?T("Unauthorized"))])} + ejabberd_web:make_xhtml(make_unauthorized(Lang))} end; false -> ejabberd_web:error(not_found) end; @@ -206,8 +204,7 @@ process2(RPath, {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"ejabberd\"">>}], - ejabberd_web:make_xhtml([?XCT(<<"h1">>, - ?T("Unauthorized"))])}; + ejabberd_web:make_xhtml(make_unauthorized(Lang))}; {unauthorized, Error} -> {BadUser, _BadPass} = Auth, {IPT, _Port} = Request#request.ip, @@ -218,10 +215,14 @@ process2(RPath, [{<<"WWW-Authenticate">>, <<"basic realm=\"auth error, retry login " "to ejabberd\"">>}], - ejabberd_web:make_xhtml([?XCT(<<"h1">>, - ?T("Unauthorized"))])} + ejabberd_web:make_xhtml(make_unauthorized(Lang))} end. +make_unauthorized(Lang) -> + [?XCT(<<"h1">>, ?T("Unauthorized")), + ?XE(<<"p">>, [?C(<<"There was some problem authenticating, or the account doesn't have privilege.">>)]), + ?XE(<<"p">>, [?C(<<"Please check the log file for a more precise error message.">>)])]. + get_auth_admin(Auth, HostHTTP, RPath, Method) -> case Auth of {SJID, Pass} -> From 9ee23d36490d71535efc1e3363639fde86303c05 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 May 2024 16:16:21 +0200 Subject: [PATCH 0617/1302] WebAdmin: Improve home page - Update ejabberd WebAdmin logo - Add hyperlink in disclaimer text - Add welcome paragraph --- priv/img/admin-logo.png | Bin 26648 -> 9908 bytes src/ejabberd_web_admin.erl | 54 ++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/priv/img/admin-logo.png b/priv/img/admin-logo.png index 0088eddc8a5b6f53e0570dbb454a6e1d66738866..041b37c69bb0e500b6e35cd9773a280690afbd9b 100644 GIT binary patch literal 9908 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 diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index ac0d9f3c9..4203e5d6a 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -397,16 +397,44 @@ logo_fill() -> %%%================================== %%%% process_admin -process_admin(global, #request{path = [], lang = Lang}, AJID) -> +process_admin(global, #request{path = [], lang = Lang} = Request, AJID) -> + Title = ?H1GLraw(<<"">>, <<"">>, <<"home">>), MenuItems = get_menu_items(global, cluster, Lang, AJID, 0), Disclaimer = maybe_disclaimer_not_admin(MenuItems, AJID, Lang), - make_xhtml((?H1GL((translate:translate(Lang, ?T("Administration"))), <<"">>, - <<"Configuration">>)) - ++ Disclaimer ++ - [?XE(<<"ul">>, - [?LI([?ACT(MIU, MIN)]) - || {MIU, MIN} - <- MenuItems])], + WelcomeText = + [?BR, + ?XAE(<<"p">>, [{<<"align">>, <<"center">>}], + [?XA(<<"img">>, [{<<"src">>, <<"logo.png">>}, + {<<"style">>, <<"border-radius:10px; background:#49cbc1; padding: 1.1em;">>}]) + ]), + ?BR, + ?X(<<"hr">>)] ++ Title ++ + [?XE(<<"blockquote">>, + [ + ?XC(<<"p">>, <<"Welcome to ejabberd's WebAdmin!">>), + ?XC(<<"p">>, <<"Browse the menu to navigate your XMPP virtual hosts, " + "Erlang nodes, and other global server pages...">>), + ?XC(<<"p">>, <<"Some pages have a link in the top right corner " + "to relevant documentation in ejabberd Docs.">>), + ?X(<<"hr">>), + ?XE(<<"p">>, + [?C(<<"Many pages use ejabberd's API commands to show information " + "and to allow you perform administrative tasks. " + "Click on a command name to view its details. " + "You can also execute those same API commands " + "using other interfaces, see: ">>), + ?AC(<<"https://docs.ejabberd.im/developer/ejabberd-api/">>, + <<"ejabberd Docs: API">>) + ]), + ?XC(<<"p">>, <<"For example, this is the 'stats' command, " + "it accepts an argument and returns an integer:">>), + make_command(stats, Request)]), + ?X(<<"hr">>), ?BR], + make_xhtml(Disclaimer ++ WelcomeText ++ + [?XE(<<"ul">>, + [?LI([?ACT(MIU, MIN)]) + || {MIU, MIN} + <- MenuItems])], global, Lang, AJID, 0); process_admin(Host, #request{path = [], lang = Lang}, AJID) -> make_xhtml([?XCT(<<"h1">>, ?T("Administration")), @@ -621,12 +649,14 @@ list_vhosts2(Lang, Hosts) -> end, SHosts)))])]. -maybe_disclaimer_not_admin(MenuItems, AJID, Lang) -> +maybe_disclaimer_not_admin(MenuItems, AJID, _Lang) -> case {MenuItems, list_vhosts_allowed(AJID)} of {[_], []} -> - [?XREST(?T("Apparently your account has no administration rights in this server. " - "Please check how to grant admin rights in: " - "https://docs.ejabberd.im/admin/install/next-steps/#administration-account")) + [?BR, + ?DIVRES([?C(<<"Apparently your account has no administration rights in " + "this server. Please check how to grant admin rights: ">>), + ?AC(<<"https://docs.ejabberd.im/admin/install/next-steps/#administration-account">>, + <<"ejabberd Docs: Administration Account">>)]) ]; _ -> [] From 5872ccc992f873d8e8b4ea936c1a9ae544d91c3e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 May 2024 10:50:31 +0200 Subject: [PATCH 0618/1302] WebAdmin: Sort alphabetically the menu items, except the most used ones --- src/ejabberd_web_admin.erl | 55 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 4203e5d6a..8e3cb7a3d 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1158,7 +1158,11 @@ search_running_node(SNode, [Node | Nodes]) -> get_node(global, Node, [], Query, Lang) -> Res = node_parse_query(Node, Query), Base = get_base_path(global, Node, 2), - MenuItems2 = make_menu_items(global, Node, Base, Lang), + BaseItems = [{<<"db">>, <<"Database">>}, + {<<"backup">>, <<"Backup">>}, + {<<"stats">>, <<"Statistics">>}, + {<<"update">>, <<"Update">>}], + MenuItems = make_menu_items(global, Node, Base, Lang, BaseItems), [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Node ~p"), [Node])))] ++ @@ -1168,12 +1172,7 @@ get_node(global, Node, [], Query, Lang) -> nothing -> [] end ++ - [?XE(<<"ul">>, - ([?LI([?ACT(<<"db/">>, ?T("Database"))]), - ?LI([?ACT(<<"backup/">>, ?T("Backup"))]), - ?LI([?ACT(<<"stats/">>, ?T("Statistics"))]), - ?LI([?ACT(<<"update/">>, ?T("Update"))])] - ++ MenuItems2)), + [?XE(<<"ul">>, MenuItems), ?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [?INPUTT(<<"submit">>, <<"restart">>, ?T("Restart")), @@ -1181,7 +1180,7 @@ get_node(global, Node, [], Query, Lang) -> ?INPUTTD(<<"submit">>, <<"stop">>, ?T("Stop"))])]; get_node(Host, Node, [], _Query, Lang) -> Base = get_base_path(Host, Node, 4), - MenuItems2 = make_menu_items(Host, Node, Base, Lang), + MenuItems2 = make_menu_items(Host, Node, Base, Lang, []), [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Node ~p"), [Node]))), ?XE(<<"ul">>, MenuItems2)]; get_node(global, Node, [<<"db">>], Query, Lang) -> @@ -1944,19 +1943,16 @@ make_navigation_menu(Host, Node, Lang, JID, Level) -> NodeMenu = make_node_menu(Host, Node, Lang, Level), make_server_menu(HostMenu, NodeMenu, Lang, JID, Level). -make_menu_items(global, cluster, Base, Lang) -> - HookItems = get_menu_items_hook(server, Lang), - make_menu_items(Lang, {Base, <<"">>, HookItems}); -make_menu_items(global, Node, Base, Lang) -> - HookItems = get_menu_items_hook({node, Node}, Lang), - make_menu_items(Lang, {Base, <<"">>, HookItems}); -make_menu_items(Host, cluster, Base, Lang) -> - HookItems = get_menu_items_hook({host, Host}, Lang), - make_menu_items(Lang, {Base, <<"">>, HookItems}); -make_menu_items(Host, Node, Base, Lang) -> - HookItems = get_menu_items_hook({hostnode, Host, Node}, - Lang), - make_menu_items(Lang, {Base, <<"">>, HookItems}). +make_menu_items(Host, Node, Base, Lang, Acc) -> + Place = case {Host, Node} of + {global, cluster} -> server; + {global, Node} -> {node, Node}; + {Host, cluster} -> {host, Host}; + {Host, Node} -> {hostnode, Host, Node} + end, + HookItems = get_menu_items_hook(Place, Lang), + Items = lists:keysort(2, HookItems ++ Acc), + make_menu_items(Lang, {Base, <<"">>, Items}). make_host_node_menu(global, _, _Lang, _JID, _Level) -> {<<"">>, <<"">>, []}; @@ -1976,14 +1972,15 @@ make_host_menu(global, _HostNodeMenu, _Lang, _JID, _Level) -> make_host_menu(Host, HostNodeMenu, Lang, JID, Level) -> HostBase = get_base_path(Host, cluster, Level), HostFixed = [{<<"users">>, ?T("Users")}, - {<<"online-users">>, ?T("Online Users")}] - ++ + {<<"online-users">>, ?T("Online Users")}], + HostFixedAdditional = get_lastactivity_menuitem_list(Host) ++ [{<<"nodes">>, ?T("Nodes"), HostNodeMenu}, {<<"stats">>, ?T("Statistics")}] ++ get_menu_items_hook({host, Host}, Lang), + HostFixedAll = HostFixed ++ lists:keysort(2, HostFixedAdditional), HostFixed2 = [Tuple - || Tuple <- HostFixed, + || Tuple <- HostFixedAll, is_allowed_path(Host, Tuple, JID)], {HostBase, Host, HostFixed2}. @@ -1997,18 +1994,20 @@ make_node_menu(global, Node, Lang, Level) -> {<<"update">>, ?T("Update")}] ++ get_menu_items_hook({node, Node}, Lang), {NodeBase, iolist_to_binary(atom_to_list(Node)), - NodeFixed}; + lists:keysort(2, NodeFixed)}; make_node_menu(_Host, _Node, _Lang, _Level) -> {<<"">>, <<"">>, []}. make_server_menu(HostMenu, NodeMenu, Lang, JID, Level) -> Base = get_base_path(global, cluster, Level), Fixed = [{<<"vhosts">>, ?T("Virtual Hosts"), HostMenu}, - {<<"nodes">>, ?T("Nodes"), NodeMenu}, - {<<"stats">>, ?T("Statistics")}] + {<<"nodes">>, ?T("Nodes"), NodeMenu}], + FixedAdditional = + [{<<"stats">>, ?T("Statistics")}] ++ get_menu_items_hook(server, Lang), + FixedAll = Fixed ++ lists:keysort(2, FixedAdditional), Fixed2 = [Tuple - || Tuple <- Fixed, + || Tuple <- FixedAll, is_allowed_path(global, Tuple, JID)], {Base, <<"">>, Fixed2}. From 74d6f0a68d4bf3c147bced12da60d24e82b6c6c4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 25 May 2024 22:17:38 +0200 Subject: [PATCH 0619/1302] WebAdmin: logo-fill.png is useless since the visual refresh in commit 9eeee67 --- priv/img/admin-logo-fill.png | Bin 177 -> 0 bytes src/ejabberd_web_admin.erl | 13 ------------- 2 files changed, 13 deletions(-) delete mode 100644 priv/img/admin-logo-fill.png diff --git a/priv/img/admin-logo-fill.png b/priv/img/admin-logo-fill.png deleted file mode 100644 index 862163c504494e431ba84cbd8faa3af1b3069e10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Q+z!VDzu9#Ja>QjEnx?oJHr&dIz4a^wPhLR_zv z@%`&!`q#(zZ<5fzd0PM0dHvg!_3u>AziZ3>Jv(-;{?Z?yN=Hu@#}JO|sfWD<4=4z* zI@&d=xa%Y^az`9s61&DER>1w>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"logo.png">>], _) -> {<<"localhost">>, [all]}; -get_acl_rule([<<"logo-fill.png">>], _) -> - {<<"localhost">>, [all]}; get_acl_rule([<<"favicon.ico">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"additions.js">>], _) -> @@ -388,12 +386,6 @@ logo() -> {error, _} -> <<>> end. -logo_fill() -> - case misc:read_img("admin-logo-fill.png") of - {ok, Img} -> Img; - {error, _} -> <<>> - end. - %%%================================== %%%% process_admin @@ -458,11 +450,6 @@ process_admin(_Host, #request{path = [<<"logo.png">>]}, _) -> [{<<"Content-Type">>, <<"image/png">>}, last_modified(), cache_control_public()], logo()}; -process_admin(_Host, #request{path = [<<"logo-fill.png">>]}, _) -> - {200, - [{<<"Content-Type">>, <<"image/png">>}, last_modified(), - cache_control_public()], - logo_fill()}; process_admin(_Host, #request{path = [<<"additions.js">>]}, _) -> {200, [{<<"Content-Type">>, <<"text/javascript">>}, From 30bd8f157097b0101e6a445073d275273edf53f9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 10 May 2024 17:21:43 +0200 Subject: [PATCH 0620/1302] WebAdmin: Add make_command functions to produce HTML command element Support to sort tables using Sortable library from: https://github.com/tofsjonas/sortable --- include/ejabberd_commands.hrl | 12 + include/ejabberd_web_admin.hrl | 22 +- mix.exs | 1 + priv/css/admin.css | 42 +- priv/css/sortable.min.css | 1 + priv/js/sortable.min.js | 3 + rebar.config | 1 + src/ejabberd_access_permissions.erl | 4 +- src/ejabberd_ctl.erl | 22 +- src/ejabberd_web_admin.erl | 964 +++++++++++++++++++++++++++- 10 files changed, 1037 insertions(+), 35 deletions(-) create mode 100644 priv/css/sortable.min.css create mode 100644 priv/js/sortable.min.js diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index 00001bb0a..d939f3c3d 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -26,6 +26,18 @@ {tuple, [rterm()]} | {list, rterm()} | rescode | restuple. +%% The purpose of a command can either be: +%% - informative: its purpose is to obtain information +%% - modifier: its purpose is to produce some change in the server +%% +%% A modifier command should be designed just to produce its desired side-effect, +%% and its result term should just be success or failure: rescode or restuple. +%% +%% ejabberd_web_admin:make_command/2 considers that commands +%% with result type different than rescode or restuple +%% are commands that can be safely executed automatically +%% to get information and build the web page. + -type oauth_scope() :: atom(). %% ejabberd_commands OAuth ReST ACL definition: diff --git a/include/ejabberd_web_admin.hrl b/include/ejabberd_web_admin.hrl index 7e4df96ce..fb2019a05 100644 --- a/include/ejabberd_web_admin.hrl +++ b/include/ejabberd_web_admin.hrl @@ -62,6 +62,11 @@ [{<<"type">>, Type}, {<<"name">>, Name}, {<<"value">>, Value}])). +-define(INPUTPH(Type, Name, Value, PlaceHolder), + ?XA(<<"input">>, + [{<<"type">>, Type}, {<<"name">>, Name}, + {<<"value">>, Value}, {<<"placeholder">>, PlaceHolder}])). + -define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, (translate:translate(Lang, Value)))). @@ -95,16 +100,27 @@ -define(XRES(Text), ?XAC(<<"p">>, [{<<"class">>, <<"result">>}], Text)). +-define(DIVRES(Elements), + ?XAE(<<"div">>, [{<<"class">>, <<"result">>}], Elements)). + %% Guide Link -define(XREST(Text), ?XRES((translate:translate(Lang, Text)))). -define(GL(Ref, Title), ?XAE(<<"div">>, [{<<"class">>, <<"guidelink">>}], [?XAE(<<"a">>, - [{<<"href">>, <<"https://docs.ejabberd.im/admin/configuration/", Ref/binary>>}, + [{<<"href">>, <<"https://docs.ejabberd.im/", Ref/binary>>}, {<<"target">>, <<"_blank">>}], [?C(<<"docs: ", Title/binary>>)])])). %% h1 with a Guide Link --define(H1GL(Name, Ref, Title), - [?XC(<<"h1">>, Name), ?GL(Ref, Title)]). +-define(H1GLraw(Name, Ref, Title), + [?XC(<<"h1">>, Name), ?GL(Ref, Title), ?BR]). +-define(H1GL(Name, RefConf, Title), + ?H1GLraw(Name, <<"admin/configuration/", RefConf/binary>>, Title)). + +-define(ANCHORL(Ref), + ?XAE(<<"div">>, [{<<"class">>, <<"anchorlink">>}], + [?XAE(<<"a">>, + [{<<"href">>, <<"#", Ref/binary>>}], + [?C(<<"<=">>)])])). diff --git a/mix.exs b/mix.exs index d2afd331b..47e49682b 100644 --- a/mix.exs +++ b/mix.exs @@ -112,6 +112,7 @@ defmodule Ejabberd.MixProject do if_version_below(~c"24", [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ if_version_below(~c"24", [{:d, :OTP_BELOW_24}]) ++ if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++ + if_version_below(~c"26", [{:d, :OTP_BELOW_26}]) ++ if_version_below(~c"27", [{:d, :OTP_BELOW_27}]) ++ if_type_exported(:odbc, {:opaque, :connection_reference, 0}, [{:d, :ODBC_HAS_TYPES}]) defines = for {:d, value} <- result, do: {:d, value} diff --git a/priv/css/admin.css b/priv/css/admin.css index 276bff637..9bb34a105 100644 --- a/priv/css/admin.css +++ b/priv/css/admin.css @@ -136,11 +136,7 @@ ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { margin-bottom: -1px; } thead tr td { - background: #3eaffa; - color: #fff; -} -thead tr td a { - color: #fff; + background: #cae7e4; } td.copy { text-align: center; @@ -227,22 +223,29 @@ h3 { padding-top: 25px; width: 70%; } +div.anchorlink { + display: inline-block; + float: right; + margin-top: 1em; + margin-right: 1em; +} +div.anchorlink a { + padding: 3px; + background: #cae7e4; + font-size: 0.75em; + color: black; +} div.guidelink, p[dir=ltr] { display: inline-block; float: right; - - margin: 0; + margin-top: 1em; margin-right: 1em; } div.guidelink a, p[dir=ltr] a { - display: inline-block; - border-radius: 3px; padding: 3px; - background: #3eaffa; - font-size: 0.75em; color: #fff; } @@ -265,7 +268,7 @@ input, select { font-size: 1em; } -p.result { +.result { border: 1px; border-style: dashed; border-color: #FE8A02; @@ -284,3 +287,18 @@ p.result { color: #cb2431; transition: none; } +h3.api { + border-bottom: 1px solid #b6b6b6; +} +details > summary { + background-color: #dbeceb; + border: none; + cursor: pointer; + list-style: none; + padding: 8px; +} +details > pre, details > p { + background-color: #e6f1f0; + margin: 0; + padding: 10px; +} diff --git a/priv/css/sortable.min.css b/priv/css/sortable.min.css new file mode 100644 index 000000000..5296c0f9f --- /dev/null +++ b/priv/css/sortable.min.css @@ -0,0 +1 @@ +.sortable thead th:not(.no-sort){cursor:pointer}.sortable thead th:not(.no-sort)::after,.sortable thead th:not(.no-sort)::before{transition:color .1s ease-in-out;font-size:1.2em;color:rgba(0,0,0,0)}.sortable thead th:not(.no-sort)::after{margin-left:3px;content:"▸"}.sortable thead th:not(.no-sort):hover::after{color:inherit}.sortable thead th:not(.no-sort)[aria-sort=descending]::after{color:inherit;content:"▾"}.sortable thead th:not(.no-sort)[aria-sort=ascending]::after{color:inherit;content:"▴"}.sortable thead th:not(.no-sort).indicator-left::after{content:""}.sortable thead th:not(.no-sort).indicator-left::before{margin-right:3px;content:"▸"}.sortable thead th:not(.no-sort).indicator-left:hover::before{color:inherit}.sortable thead th:not(.no-sort).indicator-left[aria-sort=descending]::before{color:inherit;content:"▾"}.sortable thead th:not(.no-sort).indicator-left[aria-sort=ascending]::before{color:inherit;content:"▴"}/*# sourceMappingURL=sortable-base.min.css.map */ diff --git a/priv/js/sortable.min.js b/priv/js/sortable.min.js new file mode 100644 index 000000000..eb5443135 --- /dev/null +++ b/priv/js/sortable.min.js @@ -0,0 +1,3 @@ +document.addEventListener("click",function(c){try{function h(b,a){return b.nodeName===a?b:h(b.parentNode,a)}var v=c.shiftKey||c.altKey,d=h(c.target,"TH"),m=d.parentNode,n=m.parentNode,g=n.parentNode;function p(b){var a;return v?b.dataset.sortAlt:null!==(a=b.dataset.sort)&&void 0!==a?a:b.textContent}if("THEAD"===n.nodeName&&g.classList.contains("sortable")&&!d.classList.contains("no-sort")){var q,f=m.cells,r=+d.dataset.sortTbr;for(c=0;c fun(L) when is_list(L) -> lists:map( fun({K, V}) -> {(econf:enum([tag]))(K), (econf:binary())(V)}; - (A) -> (econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl]))(A) + (A) -> (econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl, ejabberd_web_admin]))(A) end, lists:flatten(L)); (A) -> - [(econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl]))(A)] + [(econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl, ejabberd_web_admin]))(A)] end; validator(what) -> econf:and_then( diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index d42ac2393..80d942a6d 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -32,7 +32,8 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([get_commands_spec/0]). +-export([get_commands_spec/0, format_arg/2, + get_usage_command/4]). -include("ejabberd_ctl.hrl"). -include("ejabberd_commands.hrl"). @@ -786,7 +787,7 @@ print_usage_help(MaxC, ShCode) -> longdesc = lists:flatten(LongDesc), args = ArgsDef, result = {help, string}}, - print_usage_command2("help", C, MaxC, ShCode). + print(get_usage_command2("help", C, MaxC, ShCode), []). %%----------------------------- @@ -848,11 +849,14 @@ maybe_add_policy_arguments(Args, _) -> -spec print_usage_command(Cmd::string(), MaxC::integer(), ShCode::boolean(), Version::integer()) -> ok. print_usage_command(Cmd, MaxC, ShCode, Version) -> + print(get_usage_command(Cmd, MaxC, ShCode, Version), []). + +get_usage_command(Cmd, MaxC, ShCode, Version) -> Name = list_to_atom(Cmd), C = ejabberd_commands:get_command_definition(Name, Version), - print_usage_command2(Cmd, C, MaxC, ShCode). + get_usage_command2(Cmd, C, MaxC, ShCode). -print_usage_command2(Cmd, C, MaxC, ShCode) -> +get_usage_command2(Cmd, C, MaxC, ShCode) -> #ejabberd_commands{ tags = TagsAtoms, definer = Definer, @@ -926,12 +930,12 @@ print_usage_command2(Cmd, C, MaxC, ShCode) -> false -> "" end, - case Cmd of - "help" -> ok; - _ -> print([NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, - "\n\n", ExampleFmt, TagsFmt, "\n\n", ModuleFmt, NoteFmt, DescFmt, "\n\n"], []) + First = case Cmd of + "help" -> ""; + _ -> [NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, + "\n\n", ExampleFmt, TagsFmt, "\n\n", ModuleFmt, NoteFmt, DescFmt, "\n\n"] end, - print([LongDescFmt, NoteEjabberdctlList, NoteEjabberdctlTuple], []). + [First, LongDescFmt, NoteEjabberdctlList, NoteEjabberdctlTuple]. %%----------------------------- %% Format Arguments Help diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 0fdb396bf..6fd919a1c 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -29,18 +29,16 @@ -author('alexey@process-one.net'). --export([process/2, list_users/4, - list_users_in_diapason/4, pretty_print_xml/1, - term_to_id/1]). - --include("logger.hrl"). +-export([process/2, pretty_print_xml/1, + make_command/2, make_command/4, make_command_raw_value/3, + make_table/2, make_table/4, + term_to_id/1, id_to_term/1]). -include_lib("xmpp/include/xmpp.hrl"). - +-include("ejabberd_commands.hrl"). -include("ejabberd_http.hrl"). - -include("ejabberd_web_admin.hrl"). - +-include("logger.hrl"). -include("translate.hrl"). -define(INPUTATTRS(Type, Name, Value, Attrs), @@ -65,6 +63,10 @@ get_acl_rule([<<"favicon.ico">>], _) -> {<<"localhost">>, [all]}; get_acl_rule([<<"additions.js">>], _) -> {<<"localhost">>, [all]}; +get_acl_rule([<<"sortable.min.css">>], _) -> + {<<"localhost">>, [all]}; +get_acl_rule([<<"sortable.min.js">>], _) -> + {<<"localhost">>, [all]}; %% This page only displays vhosts that the user is admin: get_acl_rule([<<"vhosts">>], _) -> {<<"localhost">>, [all]}; @@ -318,7 +320,20 @@ make_xhtml(Els, Host, Node, Lang, JID, Level) -> <>}, {<<"type">>, <<"text/css">>}, {<<"rel">>, <<"stylesheet">>}], - children = []}]}, + children = []}, + #xmlel{name = <<"link">>, + attrs = + [{<<"href">>, + <>}, + {<<"type">>, <<"text/css">>}, + {<<"rel">>, <<"stylesheet">>}], + children = []}, + #xmlel{name = <<"script">>, + attrs = + [{<<"src">>, + <>}, + {<<"type">>, <<"text/javascript">>}], + children = [?C(<<" ">>)]}]}, ?XE(<<"body">>, [?XAE(<<"div">>, [{<<"id">>, <<"container">>}], [?XAE(<<"div">>, [{<<"id">>, <<"header">>}], @@ -386,6 +401,18 @@ logo() -> {error, _} -> <<>> end. +sortable_css() -> + case misc:read_css("sortable.min.css") of + {ok, CSS} -> CSS; + {error, _} -> <<>> + end. + +sortable_js() -> + case misc:read_js("sortable.min.js") of + {ok, JS} -> JS; + {error, _} -> <<>> + end. + %%%================================== %%%% process_admin @@ -457,6 +484,17 @@ process_admin(_Host, #request{path = [<<"additions.js">>]}, _) -> additions_js()}; process_admin(global, #request{path = [<<"vhosts">>], lang = Lang}, AJID) -> Res = list_vhosts(Lang, AJID), +process_admin(_Host, #request{path = [<<"sortable.min.css">>]}, _) -> + {200, + [{<<"Content-Type">>, <<"text/css">>}, last_modified(), + cache_control_public()], + sortable_css()}; +process_admin(_Host, #request{path = [<<"sortable.min.js">>]}, _) -> + {200, + [{<<"Content-Type">>, <<"text/javascript">>}, + last_modified(), cache_control_public()], + sortable_js()}; + make_xhtml((?H1GL((translate:translate(Lang, ?T("Virtual Hosts"))), <<"basic/#xmpp-domains">>, ?T("XMPP Domains"))) ++ Res, @@ -585,7 +623,22 @@ process_admin(Host, #request{lang = Lang} = Request, AJID) -> _ -> make_xhtml(Res, Host, Lang, AJID, Level) end. +term_to_id([]) -> <<>>; term_to_id(T) -> base64:encode((term_to_binary(T))). +id_to_term(<<>>) -> []; +id_to_term(I) -> binary_to_term(base64:decode(I)). + +can_user_access_host(Host, #request{auth = Auth, + host = HostHTTP, + method = Method}) -> + Path = [<<"server">>, Host], + case get_auth_admin(Auth, HostHTTP, Path, Method) of + {ok, _} -> + true; + {unauthorized, _Error} -> + false + end. + %%%================================== %%%% list_vhosts @@ -2069,4 +2122,897 @@ any_rules_allowed(Host, Access, Entity) -> allow == acl:match_rule(Host, Rule, Entity) end, Access). +%%%================================== + +%%% @format-begin + +%%%% make_command: API + +-spec make_command(Name :: atom(), Request :: http_request()) -> xmlel(). +make_command(Name, Request) -> + make_command2(Name, Request, [], []). + +-spec make_command(Name :: atom(), + Request :: http_request(), + BaseArguments :: [{ArgName :: binary(), ArgValue :: binary()}], + [Option]) -> + xmlel() | {raw_and_value, any(), xmlel()} + when Option :: + {only, presentation | without_presentation | button | result | value | raw_and_value} | + {input_name_append, [binary()]} | + {force_execution, boolean()} | + {table_options, {PageSize :: integer(), RemainingPath :: [binary()]}} | + {result_named, boolean()} | + {result_links, + [{ResultName :: atom(), + LinkType :: host | node | user | room | shared_roster | arg_host | paragraph, + Level :: integer(), + Append :: binary()}]} | + {style, normal | danger}. +make_command(Name, Request, BaseArguments, Options) -> + make_command2(Name, Request, BaseArguments, Options). + +-spec make_command_raw_value(Name :: atom(), + Request :: http_request(), + BaseArguments :: [{ArgName :: binary(), ArgValue :: binary()}]) -> + any(). +make_command_raw_value(Name, Request, BaseArguments) -> + make_command2(Name, Request, BaseArguments, [{only, raw_value}]). + +%%%================================== +%%%% make_command: main + +-spec make_command2(Name :: atom(), + Request :: http_request(), + BaseArguments :: [{ArgName :: binary(), ArgValue :: binary()}], + [Option]) -> + xmlel() | any() + when Option :: + {only, + presentation | + without_presentation | + button | + result | + value | + raw_value | + raw_and_value} | + {input_name_append, [binary()]} | + {force_execution, boolean()} | + {table_options, {PageSize :: integer(), RemainingPath :: [binary()]}} | + {result_named, boolean()} | + {result_links, + [{ResultName :: atom(), + LinkType :: host | node | user | room | shared_roster | arg_host | paragraph, + Level :: integer(), + Append :: binary()}]} | + {style, normal | danger}. +make_command2(Name, Request, BaseArguments, Options) -> + Only = proplists:get_value(only, Options, all), + ForceExecution = proplists:get_value(force_execution, Options, false), + InputNameAppend = proplists:get_value(input_name_append, Options, []), + Resultnamed = proplists:get_value(result_named, Options, false), + ResultLinks = proplists:get_value(result_links, Options, []), + TO = proplists:get_value(table_options, Options, {999999, []}), + Style = proplists:get_value(style, Options, normal), + #request{us = {RUser, RServer}, + ip = RIp, + host = RHost} = + Request, + CallerInfo = + #{usr => {RUser, RServer, <<"">>}, + ip => RIp, + caller_host => RHost, + caller_module => ?MODULE}, + try {ejabberd_commands:get_command_definition(Name), + ejabberd_access_permissions:can_access(Name, CallerInfo)} + of + {C, allow} -> + make_command2(Name, + Request, + CallerInfo, + BaseArguments, + C, + Only, + ForceExecution, + InputNameAppend, + Resultnamed, + ResultLinks, + Style, + TO); + {_C, deny} -> + ?DEBUG("Blocked access to command ~p for~n CallerInfo: ~p", [Name, CallerInfo]), + ?C(<<"">>) + catch + A:B -> + ?INFO_MSG("Problem preparing command ~p: ~p", [Name, {A, B}]), + ?C(<<"">>) + end. + +make_command2(Name, + Request, + CallerInfo, + BaseArguments, + C, + Only, + ForceExecution, + InputNameAppend, + Resultnamed, + ResultLinks, + Style, + TO) -> + {ArgumentsFormat, _Rename, ResultFormatApi} = ejabberd_commands:get_command_format(Name), + Method = + case {ForceExecution, ResultFormatApi} of + {true, _} -> + auto; + {_, {_, rescode}} -> + manual; + {_, {_, restuple}} -> + manual; + _ -> + auto + end, + PresentationEls = make_command_presentation(Name, C#ejabberd_commands.tags), + Query = Request#request.q, + {ArgumentsUsed1, ExecRes} = + execute_command(Name, + Query, + BaseArguments, + Method, + ArgumentsFormat, + CallerInfo, + InputNameAppend), + ArgumentsFormatDetailed = + add_arguments_details(ArgumentsFormat, + C#ejabberd_commands.args_desc, + C#ejabberd_commands.args_example), + ArgumentsEls = + make_command_arguments(Name, + Query, + Only, + Method, + Style, + ArgumentsFormatDetailed, + BaseArguments, + InputNameAppend), + Automated = + case ArgumentsEls of + [] -> + true; + _ -> + false + end, + ArgumentsUsed = + (catch lists:zip( + lists:map(fun({A, _}) -> A end, ArgumentsFormat), ArgumentsUsed1)), + ResultEls = + make_command_result(ExecRes, + ArgumentsUsed, + ResultFormatApi, + Automated, + Resultnamed, + ResultLinks, + TO), + make_command3(Only, ExecRes, PresentationEls, ArgumentsEls, ResultEls). + +make_command3(presentation, _ExecRes, PresentationEls, _ArgumentsEls, _ResultEls) -> + ?XAE(<<"p">>, [{<<"class">>, <<"api">>}], PresentationEls); +make_command3(button, _ExecRes, _PresentationEls, [Button], _ResultEls) -> + Button; +make_command3(result, + _ExecRes, + _PresentationEls, + _ArgumentsEls, + [{xmlcdata, _}, Xmlel]) -> + ?XAE(<<"p">>, [{<<"class">>, <<"api">>}], [Xmlel]); +make_command3(value, _ExecRes, _PresentationEls, _ArgumentsEls, [{xmlcdata, _}, Xmlel]) -> + Xmlel; +make_command3(value, + _ExecRes, + _PresentationEls, + _ArgumentsEls, + [{xmlel, _, _, _} = Xmlel]) -> + Xmlel; +make_command3(raw_and_value, + ExecRes, + _PresentationEls, + _ArgumentsEls, + [{xmlel, _, _, _} = Xmlel]) -> + {raw_and_value, ExecRes, Xmlel}; +make_command3(raw_value, ExecRes, _PresentationEls, _ArgumentsEls, _ResultEls) -> + ExecRes; +make_command3(without_presentation, + _ExecRes, + _PresentationEls, + ArgumentsEls, + ResultEls) -> + ?XAE(<<"p">>, + [{<<"class">>, <<"api">>}], + [?XE(<<"blockquote">>, ArgumentsEls ++ ResultEls)]); +make_command3(all, _ExecRes, PresentationEls, ArgumentsEls, ResultEls) -> + ?XAE(<<"p">>, + [{<<"class">>, <<"api">>}], + PresentationEls ++ [?XE(<<"blockquote">>, ArgumentsEls ++ ResultEls)]). + +add_arguments_details(ArgumentsFormat, Descriptions, none) -> + add_arguments_details(ArgumentsFormat, Descriptions, []); +add_arguments_details(ArgumentsFormat, none, Examples) -> + add_arguments_details(ArgumentsFormat, [], Examples); +add_arguments_details(ArgumentsFormat, Descriptions, Examples) -> + lists_zipwith3(fun({A, B}, C, D) -> {A, B, C, D} end, + ArgumentsFormat, + Descriptions, + Examples, + {pad, {none, "", ""}}). + +-ifdef(OTP_BELOW_26). + +lists_zipwith3(Combine, List1, List2, List3, {pad, {DefaultX, DefaultY, DefaultZ}}) -> + lists_zipwith3(Combine, List1, List2, List3, DefaultX, DefaultY, DefaultZ, []). + +lists_zipwith3(_Combine, [], [], [], _DefaultX, _DefaultY, _DefaultZ, Res) -> + lists:reverse(Res); +lists_zipwith3(Combine, + [E1 | List1], + [E2 | List2], + [E3 | List3], + DefX, + DefY, + DefZ, + Res) -> + E123 = Combine(E1, E2, E3), + lists_zipwith3(Combine, List1, List2, List3, DefX, DefY, DefZ, [E123 | Res]); +lists_zipwith3(Combine, [E1 | List1], [], [], DefX, DefY, DefZ, Res) -> + E123 = Combine(E1, DefY, DefZ), + lists_zipwith3(Combine, List1, [], [], DefX, DefY, DefZ, [E123 | Res]); +lists_zipwith3(Combine, [E1 | List1], [], [E3 | List3], DefX, DefY, DefZ, Res) -> + E123 = Combine(E1, DefY, E3), + lists_zipwith3(Combine, List1, [], List3, DefX, DefY, DefZ, [E123 | Res]); +lists_zipwith3(Combine, [E1 | List1], [E2 | List2], [], DefX, DefY, DefZ, Res) -> + E123 = Combine(E1, E2, DefZ), + lists_zipwith3(Combine, List1, List2, [], DefX, DefY, DefZ, [E123 | Res]). + +-else. + +lists_zipwith3(Combine, List1, List2, List3, How) -> + lists:zipwith3(Combine, List1, List2, List3, How). + +-endif. + +%%%================================== +%%%% make_command: presentation + +make_command_presentation(Name, Tags) -> + NameBin = misc:atom_to_binary(Name), + NiceNameBin = nice_this(Name), + Text = ejabberd_ctl:get_usage_command(atom_to_list(Name), 100, false, 1000000), + AnchorLink = [?ANCHORL(NameBin)], + MaybeDocsLink = + case lists:member(internal, Tags) of + true -> + []; + false -> + [?GL(<<"developer/ejabberd-api/admin-api/#", NameBin/binary>>, NameBin)] + end, + [?XE(<<"details">>, + [?XAE(<<"summary">>, [{<<"id">>, NameBin}], [?XC(<<"strong">>, NiceNameBin)])] + ++ MaybeDocsLink + ++ AnchorLink + ++ [?XC(<<"pre">>, list_to_binary(Text))])]. + +nice_this(This, integer) -> + {nice_this(This), right}; +nice_this(This, _Format) -> + nice_this(This). + +-spec nice_this(This :: atom() | string() | [byte()]) -> NiceThis :: binary(). +nice_this(This) when is_atom(This) -> + nice_this(atom_to_list(This)); +nice_this(This) when is_binary(This) -> + nice_this(binary_to_list(This)); +nice_this(This) when is_list(This) -> + list_to_binary(lists:flatten([string:titlecase(Word) + || Word <- string:replace(This, "_", " ", all)])). + +-spec long_this(These :: [This :: atom()]) -> Long :: binary(). +long_this(These) -> + list_to_binary(lists:join($/, [atom_to_list(This) || This <- These])). + +%%%================================== +%%%% make_command: arguments + +make_command_arguments(Name, + Query, + Only, + Method, + Style, + ArgumentsFormat, + BaseArguments, + InputNameAppend) -> + ArgumentsFormat2 = remove_base_arguments(ArgumentsFormat, BaseArguments), + ArgumentsFields = make_arguments_fields(Name, Query, ArgumentsFormat2), + Button = make_button_element(Name, Method, Style, InputNameAppend), + ButtonElement = + ?XE(<<"tr">>, + [?X(<<"td">>), ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], [Button])]), + case {(ArgumentsFields /= []) or (Method == manual), Only} of + {false, _} -> + []; + {true, button} -> + [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], [Button])]; + {true, _} -> + [?XAE(<<"form">>, + [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], + [?XE(<<"table">>, ArgumentsFields ++ [ButtonElement])])] + end. + +remove_base_arguments(ArgumentsFormat, BaseArguments) -> + lists:filter(fun({ArgName, _ArgFormat, _ArgDesc, _ArgExample}) -> + not + lists:keymember( + misc:atom_to_binary(ArgName), 1, BaseArguments) + end, + ArgumentsFormat). + +make_button_element(Name, _, Style, InputNameAppend) -> + Id = term_to_id(InputNameAppend), + NameBin = <<(misc:atom_to_binary(Name))/binary, Id/binary>>, + NiceNameBin = nice_this(Name), + case Style of + danger -> + ?INPUTD(<<"submit">>, NameBin, NiceNameBin); + _ -> + ?INPUT(<<"submit">>, NameBin, NiceNameBin) + end. + +make_arguments_fields(Name, Query, ArgumentsFormat) -> + lists:map(fun({ArgName, ArgFormat, _ArgDescription, ArgExample}) -> + ArgExampleBin = format_result(ArgExample, {ArgName, ArgFormat}), + ArgNiceNameBin = nice_this(ArgName), + ArgLongNameBin = long_this([Name, ArgName]), + ArgValue = + case lists:keysearch(ArgLongNameBin, 1, Query) of + {value, {ArgLongNameBin, V}} -> + V; + _ -> + <<"">> + end, + ?XE(<<"tr">>, + [?XC(<<"td">>, <>), + ?XE(<<"td">>, + [?INPUTPH(<<"text">>, ArgLongNameBin, ArgValue, ArgExampleBin)])]) + end, + ArgumentsFormat). + +%%%================================== +%%%% make_command: execute + +execute_command(Name, + Query, + BaseArguments, + Method, + ArgumentsFormat, + CallerInfo, + InputNameAppend) -> + try Args = prepare_arguments(Name, BaseArguments ++ Query, ArgumentsFormat), + {Args, + execute_command2(Name, Query, Args, Method, ArgumentsFormat, CallerInfo, InputNameAppend)} + of + R -> + R + catch + A:E -> + {error, {A, E}} + end. + +execute_command2(Name, + Query, + Arguments, + Method, + ArgumentsFormat, + CallerInfo, + InputNameAppend) -> + AllArgumentsProvided = length(Arguments) == length(ArgumentsFormat), + PressedExecuteButton = is_this_to_execute(Name, Query, Arguments, InputNameAppend), + LetsExecute = + case {Method, PressedExecuteButton, AllArgumentsProvided} of + {auto, _, true} -> + true; + {manual, true, true} -> + true; + _ -> + false + end, + case LetsExecute of + true -> + catch ejabberd_commands:execute_command2(Name, Arguments, CallerInfo); + false -> + not_executed + end. + +is_this_to_execute(Name, Query, Arguments, InputNameAppend) -> + NiceNameBin = nice_this(Name), + NameBin = misc:atom_to_binary(Name), + AppendBin = term_to_id(lists:sublist(Arguments, length(InputNameAppend))), + ArgumentsId = <>, + {value, {ArgumentsId, NiceNameBin}} == lists:keysearch(ArgumentsId, 1, Query). + +prepare_arguments(ComName, Args, ArgsFormat) -> + lists:foldl(fun({ArgName, ArgFormat}, FinalArguments) -> + %% Give priority to the value enforced in our code + %% Otherwise use the value provided by the user + case {lists:keyfind( + misc:atom_to_binary(ArgName), 1, Args), + lists:keyfind(long_this([ComName, ArgName]), 1, Args)} + of + %% Value enforced in our code + {{_, Value}, _} -> + [format_arg(Value, ArgFormat) | FinalArguments]; + %% User didn't provide value in the field + {_, {_, <<>>}} -> + FinalArguments; + %% Value provided by the user in the form field + {_, {_, Value}} -> + [format_arg(Value, ArgFormat) | FinalArguments]; + {false, false} -> + FinalArguments + end + end, + [], + lists:reverse(ArgsFormat)). + +format_arg(Value, any) -> + Value; +format_arg(Value, atom) when is_atom(Value) -> + Value; +format_arg(Value, binary) when is_binary(Value) -> + Value; +format_arg(Value, ArgFormat) -> + ejabberd_ctl:format_arg(binary_to_list(Value), ArgFormat). + +%%%================================== +%%%% make_command: result + +make_command_result(not_executed, _, _, _, _, _, _) -> + []; +make_command_result({error, ErrorElement}, _, _, _, _, _, _) -> + [?DIVRES([?C(<<"Error: ">>), + ?XC(<<"code">>, list_to_binary(io_lib:format("~p", [ErrorElement])))])]; +make_command_result(Value, + ArgumentsUsed, + {ResName, _ResFormat} = ResultFormatApi, + Automated, + Resultnamed, + ResultLinks, + TO) -> + ResNameBin = nice_this(ResName), + ResultValueEl = + make_command_result_element(ArgumentsUsed, Value, ResultFormatApi, ResultLinks, TO), + ResultEls = + case Resultnamed of + true -> + [?C(<>), ResultValueEl]; + false -> + [ResultValueEl] + end, + case Automated of + true -> + ResultEls; + false -> + [?DIVRES(ResultEls)] + end. + +make_command_result_element(ArgumentsUsed, + ListOfTuples, + {_ArgName, {list, {_ListElementsName, {tuple, TupleElements}}}}, + ResultLinks, + {PageSize, RPath}) -> + HeadElements = + [nice_this(ElementName, ElementFormat) || {ElementName, ElementFormat} <- TupleElements], + ContentElements = + [list_to_tuple([make_result(format_result(V, {ElementName, ElementFormat}), + ElementName, + ArgumentsUsed, + ResultLinks) + || {V, {ElementName, ElementFormat}} + <- lists:zip(tuple_to_list(Tuple), TupleElements)]) + || Tuple <- ListOfTuples], + make_table(PageSize, RPath, HeadElements, ContentElements); +make_command_result_element(_ArgumentsUsed, + Values, + {_ArgName, {tuple, TupleElements}}, + _ResultLinks, + _TO) -> + ?XE(<<"table">>, + [?XE(<<"thead">>, + [?XE(<<"tr">>, + [?XC(<<"td">>, nice_this(ElementName)) + || {ElementName, _ElementFormat} <- TupleElements])]), + ?XE(<<"tbody">>, + [?XE(<<"tr">>, + [?XC(<<"td">>, format_result(V, {ElementName, ElementFormat})) + || {V, {ElementName, ElementFormat}} + <- lists:zip(tuple_to_list(Values), TupleElements)])])]); +make_command_result_element(ArgumentsUsed, + Value, + {_ArgName, {list, {ElementsName, ElementsFormat}}}, + ResultLinks, + {PageSize, RPath}) -> + HeadElements = [nice_this(ElementsName)], + ContentElements = + [{make_result(format_result(V, {ElementsName, ElementsFormat}), + ElementsName, + ArgumentsUsed, + ResultLinks)} + || V <- Value], + make_table(PageSize, RPath, HeadElements, ContentElements); +make_command_result_element(ArgumentsUsed, Value, ResultFormatApi, ResultLinks, _TO) -> + Res = make_result(format_result(Value, ResultFormatApi), + unknown_element_name, + ArgumentsUsed, + ResultLinks), + Res2 = + case Res of + [{xmlel, _, _, _} | _] = X -> + X; + Z -> + [Z] + end, + ?XE(<<"code">>, Res2). + +make_result(Binary, ElementName, ArgumentsUsed, [{ResultName, arg_host, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + {_, Host} = lists:keyfind(host, 1, ArgumentsUsed), + UrlBinary = + replace_url_elements([<<"server/">>, host, <<"/">>, Append], [{host, Host}], Level), + ?AC(UrlBinary, Binary); +make_result(Binary, ElementName, _ArgumentsUsed, [{ResultName, host, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + UrlBinary = + replace_url_elements([<<"server/">>, host, <<"/">>, Append], [{host, Binary}], Level), + ?AC(UrlBinary, Binary); +make_result(Binary, + ElementName, + _ArgumentsUsed, + [{ResultName, mnesia_table, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + Node = misc:atom_to_binary(node()), + UrlBinary = + replace_url_elements([<<"node/">>, node, <<"/db/table/">>, tablename, <<"/">>, Append], + [{node, Node}, {tablename, Binary}], + Level), + ?AC(UrlBinary, Binary); +make_result(Binary, ElementName, _ArgumentsUsed, [{ResultName, node, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + UrlBinary = + replace_url_elements([<<"node/">>, node, <<"/">>, Append], [{node, Binary}], Level), + ?AC(UrlBinary, Binary); +make_result(Binary, ElementName, _ArgumentsUsed, [{ResultName, user, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + Jid = try jid:decode(Binary) of + #jid{} = J -> + J + catch + _:{bad_jid, _} -> + %% TODO: Find a method to be able to link to this user to delete it + ?INFO_MSG("Error parsing Binary that is not a valid JID:~n ~p", [Binary]), + jid:decode(<<"unknown-username@localhost">>) + end, + {User, Host, _R} = jid:split(Jid), + case lists:member(Host, ejabberd_config:get_option(hosts)) of + true -> + UrlBinary = + replace_url_elements([<<"server/">>, host, <<"/user/">>, user, <<"/">>, Append], + [{user, misc:url_encode(User)}, {host, Host}], + Level), + ?AC(UrlBinary, Binary); + false -> + ?C(Binary) + end; +make_result(Binary, ElementName, _ArgumentsUsed, [{ResultName, room, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + Jid = jid:decode(Binary), + {Roomname, Service, _} = jid:split(Jid), + Host = ejabberd_router:host_of_route(Service), + case lists:member(Host, ejabberd_config:get_option(hosts)) of + true -> + UrlBinary = + replace_url_elements([<<"server/">>, + host, + <<"/muc/rooms/room/">>, + room, + <<"/">>, + Append], + [{room, misc:url_encode(Roomname)}, {host, Host}], + Level), + ?AC(UrlBinary, Binary); + false -> + ?C(Binary) + end; +make_result(Binary, + ElementName, + ArgumentsUsed, + [{ResultName, shared_roster, Level, Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + First = proplists:get_value(first, ArgumentsUsed), + Second = proplists:get_value(second, ArgumentsUsed), + {GroupId, Host} = + case jid:decode(First) of + #jid{luser = <<"">>, lserver = G} -> + {G, Second}; + #jid{luser = G, lserver = H} -> + {G, H} + end, + UrlBinary = + replace_url_elements([<<"server/">>, + host, + <<"/shared-roster/group/">>, + srg, + <<"/">>, + Append], + [{host, Host}, {srg, GroupId}], + Level), + ?AC(UrlBinary, Binary); +make_result([{xmlcdata, _, _, _} | _] = Any, + _ElementName, + _ArgumentsUsed, + _ResultLinks) -> + Any; +make_result([{xmlel, _, _, _} | _] = Any, _ElementName, _ArgumentsUsed, _ResultLinks) -> + Any; +make_result(Binary, + ElementName, + _ArgumentsUsed, + [{ResultName, paragraph, _Level, _Append}]) + when (ElementName == ResultName) or (ElementName == unknown_element_name) -> + ?XC(<<"pre">>, Binary); +make_result(Binary, _ElementName, _ArgumentsUsed, _ResultLinks) -> + ?C(Binary). + +replace_url_elements(UrlComponents, Replacements, Level) -> + Base = get_base_path_sum(0, 0, Level), + Binary2 = + lists:foldl(fun (El, Acc) when is_binary(El) -> + [El | Acc]; + (El, Acc) when is_atom(El) -> + {El, Value} = lists:keyfind(El, 1, Replacements), + [Value | Acc] + end, + [], + UrlComponents), + Binary3 = + binary:list_to_bin( + lists:reverse(Binary2)), + <>. + +format_result(Value, {_ResultName, integer}) when is_integer(Value) -> + integer_to_binary(Value); +format_result(Value, {_ResultName, string}) when is_list(Value) -> + Value; +format_result(Value, {_ResultName, string}) when is_binary(Value) -> + Value; +format_result(Value, {_ResultName, atom}) when is_atom(Value) -> + misc:atom_to_binary(Value); +format_result(Value, {_ResultName, any}) -> + Value; +format_result({ok, String}, {_ResultName, restuple}) when is_list(String) -> + list_to_binary(String); +format_result({error, Type, Code, Desc}, {_ResultName, restuple}) -> + <<"Error: ", + (misc:atom_to_binary(Type))/binary, + " ", + (integer_to_binary(Code))/binary, + ": ", + (list_to_binary(Desc))/binary>>; +format_result([], {_Name, {list, _ElementsDef}}) -> + ""; +format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> + Separator = ",", + [format_result(FirstElement, ElementsDef) | lists:map(fun(Element) -> + [Separator | format_result(Element, + ElementsDef)] + end, + Elements)]; +format_result(Value, _ResultFormat) when is_atom(Value) -> + misc:atom_to_binary(Value); +format_result(Value, _ResultFormat) when is_list(Value) -> + list_to_binary(Value); +format_result(Value, _ResultFormat) when is_binary(Value) -> + Value; +format_result(Value, _ResultFormat) -> + io_lib:format("~p", [Value]). + +%%%================================== +%%%% make_table + +-spec make_table(PageSize :: integer(), + RemainingPath :: [binary()], + NameOptionList :: [Name :: binary() | {Name :: binary(), left | right}], + Values :: [tuple()]) -> + xmlel(). +make_table(PageSize, RPath, NameOptionList, Values1) -> + Values = + case lists:member(<<"sort">>, RPath) of + true -> + Values1; + false -> + GetXmlValue = + fun ({xmlcdata, _} = X) -> + X; + ({xmlel, _, _, _} = X) -> + X; + ({raw_and_value, _V, X}) -> + X + end, + ConvertTupleToTuple = + fun(Row1) -> list_to_tuple(lists:map(GetXmlValue, tuple_to_list(Row1))) end, + lists:map(ConvertTupleToTuple, Values1) + end, + make_table1(PageSize, RPath, <<"">>, <<"">>, 1, NameOptionList, Values). + +make_table1(PageSize, + [<<"page">>, PageNumber | RPath], + PageUrlBase, + SortUrlBase, + _Start, + NameOptionList, + Values1) -> + make_table1(PageSize, + RPath, + <>, + <>, + 1 + PageSize * binary_to_integer(PageNumber), + NameOptionList, + Values1); +make_table1(PageSize, + [<<"sort">>, SortType | RPath], + PageUrlBase, + SortUrlBase, + Start, + NameOptionList, + Rows1) -> + ColumnToSort = + length(lists:takewhile(fun (A) when A == SortType -> + false; + ({A, _}) when A == SortType -> + false; + (_) -> + true + end, + NameOptionList)) + + 1, + Direction = + case lists:nth(ColumnToSort, NameOptionList) of + {_, right} -> + descending; + {_, left} -> + ascending; + _ -> + ascending + end, + ColumnToSort = ColumnToSort, + GetRawValue = + fun ({xmlcdata, _} = X) -> + X; + ({xmlel, _, _, _} = X) -> + X; + ({raw_and_value, R, _X}) -> + R + end, + GetXmlValue = + fun ({xmlcdata, _} = X) -> + X; + ({xmlel, _, _, _} = X) -> + X; + ({raw_and_value, _R, X}) -> + X + end, + SortTwo = + fun(A1, B1) -> + A2 = GetRawValue(element(ColumnToSort, A1)), + B2 = GetRawValue(element(ColumnToSort, B1)), + case Direction of + ascending -> + A2 < B2; + descending -> + A2 > B2 + end + end, + Rows1Sorted = lists:sort(SortTwo, Rows1), + ConvertTupleToTuple = + fun(Row1) -> list_to_tuple(lists:map(GetXmlValue, tuple_to_list(Row1))) end, + Rows = lists:map(ConvertTupleToTuple, Rows1Sorted), + make_table1(PageSize, + RPath, + PageUrlBase, + <>, + Start, + NameOptionList, + Rows); +make_table1(PageSize, [], PageUrlBase, SortUrlBase, Start, NameOptionList, Values1) -> + Values = lists:sublist(Values1, Start, PageSize), + Table = make_table(NameOptionList, Values), + Size = length(Values1), + Remaining = + case Size rem PageSize of + 0 -> + 0; + _ -> + 1 + end, + NumPages = max(0, Size div PageSize + Remaining - 1), + PLinks1 = + lists:foldl(fun(N, Acc) -> + NBin = integer_to_binary(N), + Acc + ++ [?C(<<", ">>), + ?AC(<>, NBin)] + end, + [], + lists:seq(1, NumPages)), + PLinks = + case PLinks1 of + [] -> + []; + _ -> + [?XE(<<"p">>, [?C(<<"Page: ">>), ?AC(<>, <<"0">>) | PLinks1])] + end, + + Names = + lists:map(fun ({Name, _}) -> + Name; + (Name) -> + Name + end, + NameOptionList), + [_ | SLinks1] = + lists:foldl(fun(N, Acc) -> + [?C(<<", ">>), ?AC(<>, N) | Acc] + end, + [], + lists:reverse(Names)), + SLinks = + case {PLinks, SLinks1} of + {_, []} -> + []; + {[], _} -> + []; + {_, [_]} -> + []; + {_, SLinks2} -> + [?XE(<<"p">>, [?C(<<"Sort all pages by: ">>) | SLinks2])] + end, + + ?XE(<<"div">>, [Table | PLinks ++ SLinks]). + +-spec make_table(NameOptionList :: [Name :: binary() | {Name :: binary(), left | right}], + Values :: [tuple()]) -> + xmlel(). +make_table(NameOptionList, Values) -> + NamesAndAttributes = [make_column_attributes(NameOption) || NameOption <- NameOptionList], + {Names, ColumnsAttributes} = lists:unzip(NamesAndAttributes), + make_table(Names, ColumnsAttributes, Values). + +make_table(Names, ColumnsAttributes, Values) -> + ?XAE(<<"table">>, + [{<<"class">>, <<"sortable">>}], + [?XE(<<"thead">>, + [?XE(<<"tr">>, [?XC(<<"th">>, nice_this(HeadElement)) || HeadElement <- Names])]), + ?XE(<<"tbody">>, + [?XE(<<"tr">>, + [?XAE(<<"td">>, CAs, [V]) + || {CAs, V} <- lists:zip(ColumnsAttributes, tuple_to_list(ValueTuple))]) + || ValueTuple <- Values])]). + +make_column_attributes({Name, Option}) -> + {Name, [make_column_attribute(Option)]}; +make_column_attributes(Name) -> + {Name, []}. + +make_column_attribute(left) -> + {<<"class">>, <<"alignleft">>}; +make_column_attribute(right) -> + {<<"class">>, <<"alignright">>}. + +%%%================================== %%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: From ed4843b0ed2496c8ca725739fb7b08c9cbd97fe1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 28 May 2024 18:36:01 +0200 Subject: [PATCH 0621/1302] Document 'any' argument and result type, useful for internal commands Also 'atom' may be used as argument type by some internal commands, for example the ones that refer to erlang node names. --- include/ejabberd_commands.hrl | 9 +++++++-- src/ejabberd_ctl.erl | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index d939f3c3d..a6a960d8c 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -19,13 +19,18 @@ %%%---------------------------------------------------------------------- -type aterm() :: {atom(), atype()}. --type atype() :: integer | string | binary | +-type atype() :: integer | string | binary | any | atom | {tuple, [aterm()]} | {list, aterm()}. -type rterm() :: {atom(), rtype()}. --type rtype() :: integer | string | atom | +-type rtype() :: integer | string | atom | any | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple. +%% The 'any' and 'atom' argument types and 'any' result type +%% should only be used %% by commands with tag 'internal', +%% which are meant to be used only internally in ejabberd, +%% and not called using external frontends. + %% The purpose of a command can either be: %% - informative: its purpose is to obtain information %% - modifier: its purpose is to produce some change in the server diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 80d942a6d..89adcdcd6 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -976,11 +976,14 @@ format_usage_ctype1({Name, Type, Description}, Indentation, ShCode) -> format_usage_ctype(Type, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> + when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) + or (Type==rescode) or (Type==restuple) -> io_lib:format("~p", [Type]); format_usage_ctype({Name, Type}, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) or (Type==rescode) or (Type==restuple)-> + when (Type==atom) or (Type==integer) or (Type==string) or (Type==binary) + or (Type==rescode) or (Type==restuple) + or (Type==any) -> io_lib:format("~p::~p", [Name, Type]); format_usage_ctype({Name, {list, ElementDef}}, Indentation) -> From 2b1d4ff98dcf3730388620d7ab4456fd659a95e4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 29 May 2024 11:00:42 +0200 Subject: [PATCH 0622/1302] Commands with 'internal' tag: don't list and block execution by frontends --- src/ejabberd_commands.erl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 9323ad656..509f3622d 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -188,7 +188,9 @@ list_commands(Version) -> Commands = get_commands_definition(Version), [{Name, Args, Desc} || #ejabberd_commands{name = Name, args = Args, - desc = Desc} <- Commands]. + tags = Tags, + desc = Desc} <- Commands, + not lists:member(internal, Tags)]. -spec get_command_format(atom()) -> {[aterm()], [{atom(),atom()}], rterm()}. @@ -264,10 +266,16 @@ execute_command2(Name, Arguments, CallerInfo) -> execute_command2(Name, Arguments, CallerInfo, Version) -> Command = get_command_definition(Name, Version), - case ejabberd_access_permissions:can_access(Name, CallerInfo) of - allow -> + FrontedCalledInternal = + maps:get(caller_module, CallerInfo, none) /= ejabberd_web_admin + andalso lists:member(internal, Command#ejabberd_commands.tags), + case {ejabberd_access_permissions:can_access(Name, CallerInfo), + FrontedCalledInternal} of + {allow, false} -> do_execute_command(Command, Arguments); - _ -> + {_, true} -> + throw({error, frontend_cannot_call_an_internal_command}); + {deny, false} -> throw({error, access_rules_unauthorized}) end. @@ -289,7 +297,8 @@ get_tags_commands() -> get_tags_commands(Version) -> CommandTags = [{Name, Tags} || #ejabberd_commands{name = Name, tags = Tags} - <- get_commands_definition(Version)], + <- get_commands_definition(Version), + not lists:member(internal, Tags)], Dict = lists:foldl( fun({CommandNameAtom, CTags}, D) -> CommandName = atom_to_list(CommandNameAtom), From 5a34020d23f455f80a144bcb0d8ee94770c0dbb1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Jun 2024 00:15:42 +0200 Subject: [PATCH 0623/1302] WebAdmin: Move content to commands; new pages; hook changes; new commands Also: - Added support to view user subpages in the menu - Webadmin hooks now get the full request - New commands added to be used in webadmin pages --- src/ejabberd_acme.erl | 24 + src/ejabberd_admin.erl | 451 ++++++++- src/ejabberd_oauth.erl | 33 + src/ejabberd_web_admin.erl | 1769 +++++++++++++----------------------- src/ext_mod.erl | 163 +++- src/mod_admin_extra.erl | 478 +++++++++- src/mod_mix_pam.erl | 19 +- src/mod_muc_admin.erl | 554 +++++++++-- src/mod_offline.erl | 163 +--- src/mod_roster.erl | 279 ++---- src/mod_shared_roster.erl | 665 ++++++++------ 11 files changed, 2709 insertions(+), 1889 deletions(-) diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 4e997da9b..dcd2bb428 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -32,11 +32,16 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% WebAdmin +-export([webadmin_menu_node/3, webadmin_page_node/3]). -include("logger.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -include_lib("public_key/include/public_key.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). -define(CALL_TIMEOUT, timer:minutes(10)). @@ -108,6 +113,8 @@ init([]) -> ejabberd_hooks:add(config_reloaded, ?MODULE, register_certfiles, 40), ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 110), ejabberd_hooks:add(config_reloaded, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:add(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110), + ejabberd_hooks:add(webadmin_page_node, ?MODULE, webadmin_page_node, 110), ejabberd_commands:register_commands(get_commands_spec()), register_certfiles(), {ok, #state{}}. @@ -153,6 +160,8 @@ terminate(_Reason, _State) -> ejabberd_hooks:delete(config_reloaded, ?MODULE, register_certfiles, 40), ejabberd_hooks:delete(ejabberd_started, ?MODULE, ejabberd_started, 110), ejabberd_hooks:delete(config_reloaded, ?MODULE, ejabberd_started, 110), + ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, webadmin_menu_node, 110), + ejabberd_hooks:delete(webadmin_page_node, ?MODULE, webadmin_page_node, 110), ejabberd_commands:unregister_commands(get_commands_spec()). code_change(_OldVsn, State, _Extra) -> @@ -547,6 +556,21 @@ list_certificates() -> {Domain, Path, sets:is_element(E, Used)} end, Known)). +%%%=================================================================== +%%% WebAdmin +%%%=================================================================== + +webadmin_menu_node(Acc, _Node, _Lang) -> + Acc ++ [{<<"acme">>, <<"ACME">>}]. + +webadmin_page_node(_, Node, #request{path = [<<"acme">>]} = R) -> + Head = ?H1GLraw(<<"ACME Certificates">>, <<"admin/configuration/basic/#acme">>, <<"ACME">>), + Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [request_certificate, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [revoke_certificate, R])], + Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [list_certificates, R])], + {stop, Head ++ Get ++ Set}; +webadmin_page_node(Acc, _, _) -> Acc. + %%%=================================================================== %%% Other stuff %%%=================================================================== diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 28215491c..86fe2a9be 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -39,7 +39,9 @@ dump_config/1, convert_to_yaml/2, %% Cluster - join_cluster/1, leave_cluster/1, list_cluster/0, + join_cluster/1, leave_cluster/1, + list_cluster/0, list_cluster_detailed/0, + get_cluster_node_details3/0, %% Erlang update_list/0, update/1, update/0, %% Accounts @@ -50,7 +52,7 @@ %% Purge DB delete_expired_messages/0, delete_old_messages/1, %% Mnesia - set_master/1, + get_master/0, set_master/1, backup_mnesia/1, restore_mnesia/1, dump_mnesia/1, dump_table/2, load_mnesia/1, mnesia_info/0, mnesia_table_info/1, @@ -61,13 +63,27 @@ clear_cache/0, gc/0, get_commands_spec/0, - delete_old_messages_batch/4, delete_old_messages_status/1, delete_old_messages_abort/1]). + delete_old_messages_batch/4, delete_old_messages_status/1, delete_old_messages_abort/1, + %% Internal + mnesia_list_tables/0, + mnesia_table_details/1, + mnesia_table_change_storage/2, + mnesia_table_clear/1, + mnesia_table_delete/1, + echo/1, echo3/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --include("logger.hrl"). +-export([web_menu_main/2, web_page_main/2, + web_menu_node/3, web_page_node/3]). + +-include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). %+++ TODO -record(state, {}). @@ -77,6 +93,10 @@ start_link() -> init([]) -> process_flag(trap_exit, true), ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:add(webadmin_menu_node, ?MODULE, web_menu_node, 50), + ejabberd_hooks:add(webadmin_page_node, ?MODULE, web_page_node, 50), {ok, #state{}}. handle_call(Request, From, State) -> @@ -92,6 +112,10 @@ handle_info(Info, State) -> {noreply, State}. terminate(_Reason, _State) -> + ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), + ejabberd_hooks:delete(webadmin_menu_node, ?MODULE, web_menu_node, 50), + ejabberd_hooks:delete(webadmin_page_node, ?MODULE, web_page_node, 50), ejabberd_commands:unregister_commands(get_commands_spec()). code_change(_OldVsn, State, _Extra) -> @@ -179,8 +203,9 @@ get_commands_spec() -> desc = "Update the given module", longdesc = "To update all the possible modules, use `all`.", module = ?MODULE, function = update, - args_example = ["mod_vcard"], + args_example = ["all"], args = [{module, string}], + result_example = {ok, <<"Updated modules: mod_configure, mod_vcard">>}, result = {res, restuple}}, #ejabberd_commands{name = register, tags = [accounts], @@ -225,14 +250,12 @@ get_commands_spec() -> #ejabberd_commands{name = join_cluster, tags = [cluster], desc = "Join this node into the cluster handled by Node", - longdesc = "This command works only with ejabberdctl, " - "not mod_http_api or other code that runs inside the " - "same ejabberd node that will be joined.", + note = "improved in 24.xx", module = ?MODULE, function = join_cluster, args_desc = ["Nodename of the node to join"], args_example = [<<"ejabberd1@machine7">>], args = [{node, binary}], - result = {res, rescode}}, + result = {res, restuple}}, #ejabberd_commands{name = leave_cluster, tags = [cluster], desc = "Remove and shutdown Node from the running cluster", longdesc = "This command can be run from any running " @@ -247,11 +270,27 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = list_cluster, tags = [cluster], - desc = "List nodes that are part of the cluster handled by Node", + desc = "List running nodes that are part of this cluster", module = ?MODULE, function = list_cluster, result_example = [ejabberd1@machine7, ejabberd1@machine8], args = [], result = {nodes, {list, {node, atom}}}}, + #ejabberd_commands{name = list_cluster_detailed, tags = [cluster], + desc = "List nodes (both running and known) and some stats", + note = "added in 24.xx", + module = ?MODULE, function = list_cluster_detailed, + args = [], + result_example = [{'ejabberd@localhost', "true", + "The node ejabberd is started. Status...", + 7, 348, 60, none}], + result = {nodes, {list, {node, {tuple, [{name, atom}, + {running, string}, + {status, string}, + {online_users, integer}, + {processes, integer}, + {uptime_seconds, integer}, + {master_node, atom} + ]}}}}}, #ejabberd_commands{name = import_file, tags = [mnesia], desc = "Import user data from jabberd14 spool file", @@ -377,6 +416,12 @@ get_commands_spec() -> args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"], args = [{host, string}, {file, string}], result = {res, rescode}}, + #ejabberd_commands{name = get_master, tags = [cluster], + desc = "Get master node of the clustered Mnesia tables", + note = "added in 24.xx", + longdesc = "If there is no master, returns `none`.", + module = ?MODULE, function = get_master, + result = {nodename, atom}}, #ejabberd_commands{name = set_master, tags = [cluster], desc = "Set master node of the clustered Mnesia tables", longdesc = "If `nodename` is set to `self`, then this " @@ -472,9 +517,100 @@ get_commands_spec() -> desc = "Generate Unix manpage for current ejabberd version", note = "added in 20.01", module = ejabberd_doc, function = man, - args = [], result = {res, restuple}} - ]. + args = [], result = {res, restuple}}, + #ejabberd_commands{name = webadmin_host_user_queue, tags = [internal], + desc = "Generate WebAdmin offline queue HTML", + module = mod_offline, function = webadmin_host_user_queue, + args = [{user, binary}, {host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + + #ejabberd_commands{name = webadmin_host_last_activity, tags = [internal], + desc = "Generate WebAdmin Last Activity HTML", + module = ejabberd_web_admin, function = webadmin_host_last_activity, + args = [{host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_host_srg, tags = [internal], + desc = "Generate WebAdmin Shared Roster Group HTML", + module = mod_shared_roster, function = webadmin_host_srg, + args = [{host, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_host_srg_group, tags = [internal], + desc = "Generate WebAdmin Shared Roster Group HTML for a group", + module = mod_shared_roster, function = webadmin_host_srg_group, + args = [{host, binary}, {group, binary}, {query, any}, {lang, binary}], + result = {res, any}}, + + #ejabberd_commands{name = webadmin_node_contrib, tags = [internal], + desc = "Generate WebAdmin ejabberd-contrib HTML", + module = ext_mod, function = webadmin_node_contrib, + args = [{node, atom}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML", + module = ejabberd_web_admin, function = webadmin_node_db, + args = [{node, atom}, {query, any}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db_table, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML for a table", + module = ejabberd_web_admin, function = webadmin_node_db_table, + args = [{node, atom}, {table, binary}, {lang, binary}], + result = {res, any}}, + #ejabberd_commands{name = webadmin_node_db_table_page, tags = [internal], + desc = "Generate WebAdmin Mnesia database HTML for a table content", + module = ejabberd_web_admin, function = webadmin_node_db_table_page, + args = [{node, atom}, {table, binary}, {page, integer}], + result = {res, any}}, + + #ejabberd_commands{name = mnesia_list_tables, tags = [internal, mnesia], + desc = "List of Mnesia tables", + module = ?MODULE, function = mnesia_list_tables, + result = {tables, {list, {table, {tuple, [{name, atom}, + {storage_type, binary}, + {elements, integer}, + {memory_kb, integer}, + {memory_mb, integer} + ]}}}}}, + #ejabberd_commands{name = mnesia_table_details, tags = [internal, mnesia], + desc = "Get details of a Mnesia table", + module = ?MODULE, function = mnesia_table_details, + args = [{table, binary}], + result = {details, {list, {detail, {tuple, [{name, atom}, + {value, binary} + ]}}}}}, + + #ejabberd_commands{name = mnesia_table_change_storage, tags = [internal, mnesia], + desc = "Change storage type of a Mnesia table to: ram_copies, disc_copies, or disc_only_copies.", + module = ?MODULE, function = mnesia_table_change_storage, + args = [{table, binary}, {storage_type, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = mnesia_table_clear, tags = [internal, mnesia], + desc = "Delete all content in a Mnesia table", + module = ?MODULE, function = mnesia_table_clear, + args = [{table, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = mnesia_table_destroy, tags = [internal, mnesia], + desc = "Destroy a Mnesia table", + module = ?MODULE, function = mnesia_table_destroy, + args = [{table, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = echo, tags = [internal], + desc = "Return the same sentence that was provided", + module = ?MODULE, function = echo, + args_desc = ["Sentence to echoe"], + args_example = [<<"Test Sentence">>], + args = [{sentence, binary}], + result = {sentence, string}, + result_example = "Test Sentence"}, + #ejabberd_commands{name = echo3, tags = [internal], + desc = "Return the same sentence that was provided", + module = ?MODULE, function = echo3, + args_desc = ["First argument", "Second argument", "Sentence to echoe"], + args_example = [<<"example.com">>, <<"Group1">>, <<"Test Sentence">>], + args = [{first, binary}, {second, binary}, {sentence, binary}], + result = {sentence, string}, + result_example = "Test Sentence"} + ]. %%% %%% Server management @@ -491,7 +627,7 @@ status() -> {value, {_, _, Version}} -> {ok, io_lib:format("ejabberd ~s is running in that node", [Version])} end, - {Is_running, String1 ++ String2}. + {Is_running, String1 ++ " " ++String2}. stop() -> _ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm), @@ -583,8 +719,14 @@ update_list() -> [atom_to_list(Beam) || Beam <- UpdatedBeams]. update("all") -> - [update_module(ModStr) || ModStr <- update_list()], - {ok, []}; + ResList = [{ModStr, update_module(ModStr)} || ModStr <- update_list()], + String = case string:join([Mod || {Mod, {ok, _}} <- ResList], ", ") of + [] -> + "No modules updated"; + ModulesString -> + "Updated modules: " ++ ModulesString + end, + {ok, String}; update(ModStr) -> update_module(ModStr). @@ -593,7 +735,10 @@ update_module(ModuleNameBin) when is_binary(ModuleNameBin) -> update_module(ModuleNameString) -> ModuleName = list_to_atom(ModuleNameString), case ejabberd_update:update([ModuleName]) of - {ok, _Res} -> {ok, []}; + {ok, []} -> + {ok, "Not updated: "++ModuleNameString}; + {ok, [{ModuleName, _}]} -> + {ok, "Updated: "++ModuleNameString}; {error, Reason} -> {error, Reason} end. @@ -681,7 +826,25 @@ convert_to_yaml(In, Out) -> %%% join_cluster(NodeBin) -> - ejabberd_cluster:join(list_to_atom(binary_to_list(NodeBin))). + Node = list_to_atom(binary_to_list(NodeBin)), + IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()), + IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()), + Ping = net_adm:ping(Node), + join_cluster(Node, IsNodes, IsKnownNodes, Ping). + +join_cluster(_Node, true, _IsKnownNodes, _Ping) -> + {error, "This node already joined that running node."}; +join_cluster(_Node, _IsNodes, true, _Ping) -> + {error, "This node already joined that known node."}; +join_cluster(_Node, _IsNodes, _IsKnownNodes, pang) -> + {error, "This node cannot reach that node."}; +join_cluster(Node, false, false, pong) -> + case timer:apply_after(1000, ejabberd_cluster, join, [Node]) of + {ok, _} -> + {ok, "Trying to join the cluster, wait a few seconds and check the list of nodes."}; + Error -> + {error, io_lib:format("Can't join cluster: ~p", [Error])} + end. leave_cluster(NodeBin) -> ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))). @@ -689,6 +852,33 @@ leave_cluster(NodeBin) -> list_cluster() -> ejabberd_cluster:get_nodes(). +list_cluster_detailed() -> + KnownNodes = ejabberd_cluster:get_known_nodes(), + RunningNodes = ejabberd_cluster:get_nodes(), + [get_cluster_node_details(Node, RunningNodes) || Node <- KnownNodes]. + +get_cluster_node_details(Node, RunningNodes) -> + get_cluster_node_details2(Node, lists:member(Node, RunningNodes)). + +get_cluster_node_details2(Node, false) -> + {Node, "false", "", -1, -1, -1, "unknown"}; +get_cluster_node_details2(Node, true) -> + try ejabberd_cluster:call(Node, ejabberd_admin, get_cluster_node_details3, []) of + Result -> Result + catch + E:R -> + Status = io_lib:format("~p: ~p", [E, R]), + {Node, "true", Status, -1, -1, -1, "unknown"} + end. + +get_cluster_node_details3() -> + {ok, StatusString} = status(), + UptimeSeconds = mod_admin_extra:stats(<<"uptimeseconds">>), + Processes = mod_admin_extra:stats(<<"processes">>), + OnlineUsers = mod_admin_extra:stats(<<"onlineusersnode">>), + GetMaster = get_master(), + {node(), "true", StatusString, OnlineUsers, Processes, UptimeSeconds, GetMaster}. + %%% %%% Migration management %%% @@ -791,6 +981,12 @@ delete_old_messages_abort(Server) -> %%% Mnesia management %%% +get_master() -> + case mnesia:table_info(session, master_nodes) of + [] -> none; + [Node] -> Node + end. + set_master("self") -> set_master(node()); set_master(NodeString) when is_list(NodeString) -> @@ -798,7 +994,7 @@ set_master(NodeString) when is_list(NodeString) -> set_master(Node) when is_atom(Node) -> case mnesia:set_master_nodes([Node]) of ok -> - {ok, ""}; + {ok, "ok"}; {error, Reason} -> String = io_lib:format("Can't set master node ~p at node ~p:~n~p", [Node, node(), Reason]), @@ -1008,3 +1204,222 @@ is_my_host(Host) -> try ejabberd_router:is_my_host(Host) catch _:{invalid_domain, _} -> false end. + +%%% +%%% Internal +%%% + +%% @format-begin + +%% mnesia:del_table_copy(Table, Node); +%% mnesia:change_table_copy_type(Table, Node, Type); + +mnesia_table_change_storage(STable, SType) -> + Table = binary_to_existing_atom(STable, latin1), + Type = + case SType of + <<"ram_copies">> -> + ram_copies; + <<"disc_copies">> -> + disc_copies; + <<"disc_only_copies">> -> + disc_only_copies; + _ -> + false + end, + mnesia:add_table_copy(Table, node(), Type). + +mnesia_table_clear(STable) -> + Table = binary_to_existing_atom(STable, latin1), + mnesia:clear_table(Table). + +mnesia_table_delete(STable) -> + Table = binary_to_existing_atom(STable, latin1), + mnesia:delete_table(Table). + +mnesia_table_details(STable) -> + Table = binary_to_existing_atom(STable, latin1), + [{Name, iolist_to_binary(str:format("~p", [Value]))} + || {Name, Value} <- mnesia:table_info(Table, all)]. + +mnesia_list_tables() -> + STables = + lists:sort( + mnesia:system_info(tables)), + lists:map(fun(Table) -> + TInfo = mnesia:table_info(Table, all), + {value, {storage_type, Type}} = lists:keysearch(storage_type, 1, TInfo), + {value, {size, Size}} = lists:keysearch(size, 1, TInfo), + {value, {memory, Memory}} = lists:keysearch(memory, 1, TInfo), + MemoryB = Memory * erlang:system_info(wordsize), + MemoryKB = MemoryB div 1024, + MemoryMB = MemoryKB div 1024, + {Table, storage_type_bin(Type), Size, MemoryKB, MemoryMB} + end, + STables). + +storage_type_bin(ram_copies) -> + <<"RAM copy">>; +storage_type_bin(disc_copies) -> + <<"RAM and disc copy">>; +storage_type_bin(disc_only_copies) -> + <<"Disc only copy">>; +storage_type_bin(unknown) -> + <<"Remote copy">>. + +echo(Sentence) -> + Sentence. + +echo3(_, _, Sentence) -> + Sentence. + +%%% +%%% Web Admin: Main +%%% + +web_menu_main(Acc, _Lang) -> + Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stanza">>, <<"Stanza">>}]. + +web_page_main(_, #request{path = [<<"purge">>]} = R) -> + Types = + [{<<"#erlang">>, <<"Erlang">>}, + {<<"#users">>, <<"Users">>}, + {<<"#offline">>, <<"Offline">>}, + {<<"#mam">>, <<"MAM">>}, + {<<"#pubsub">>, <<"PubSub">>}, + {<<"#push">>, <<"Push">>}], + Head = [?XC(<<"h1">>, <<"Purge">>)], + Set = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- Types]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"erlang">>}], <<"Erlang">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(clear_cache, R), + ejabberd_web_admin:make_command(gc, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"users">>}], <<"Users">>), + ?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_users, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"offline">>}], <<"Offline">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_expired_messages, R), + ejabberd_web_admin:make_command(delete_old_messages, R), + ejabberd_web_admin:make_command(delete_old_messages_batch, R), + ejabberd_web_admin:make_command(delete_old_messages_status, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"mam">>}], <<"MAM">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_old_mam_messages, R), + ejabberd_web_admin:make_command(delete_old_mam_messages_batch, R), + ejabberd_web_admin:make_command(delete_old_mam_messages_status, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"pubsub">>}], <<"PubSub">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(delete_expired_pubsub_items, R), + ejabberd_web_admin:make_command(delete_old_pubsub_items, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"push">>}], <<"Push">>), + ?XE(<<"blockquote">>, [ejabberd_web_admin:make_command(delete_old_push_sessions, R)])], + {stop, Head ++ Set}; +web_page_main(_, #request{path = [<<"stanza">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Stanza">>)], + Set = [ejabberd_web_admin:make_command(send_message, R), + ejabberd_web_admin:make_command(send_stanza, R), + ejabberd_web_admin:make_command(send_stanza_c2s, R)], + {stop, Head ++ Set}; +web_page_main(Acc, _) -> + Acc. + +%%% +%%% Web Admin: Node +%%% + +web_menu_node(Acc, _Node, _Lang) -> + Acc + ++ [{<<"cluster">>, <<"Clustering">>}, + {<<"update">>, <<"Code Update">>}, + {<<"config-file">>, <<"Configuration File">>}, + {<<"logs">>, <<"Logs">>}, + {<<"stop">>, <<"Stop Node">>}]. + +web_page_node(_, Node, #request{path = [<<"cluster">>]} = R) -> + {ok, Names} = net_adm:names(), + NodeNames = lists:join(", ", [Name || {Name, _Port} <- Names]), + Hint = + list_to_binary(io_lib:format("Hint: Erlang nodes found in this machine that may be running ejabberd: ~s", + [NodeNames])), + Head = ?H1GLraw(<<"Clustering">>, <<"admin/guide/clustering/">>, <<"Clustering">>), + Set1 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [join_cluster, R, [], [{style, danger}]]), + ?XE(<<"blockquote">>, [?C(Hint)]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [leave_cluster, R, [], [{style, danger}]])], + Set2 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [set_master, R, [], [{style, danger}]])], + timer:sleep(100), % leaving a cluster takes a while, let's delay the get commands + Get1 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [list_cluster_detailed, + R, + [], + [{result_links, [{name, node, 3, <<"">>}]}]])], + Get2 = + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [get_master, + R, + [], + [{result_named, true}, + {result_links, [{nodename, node, 3, <<"">>}]}]])], + {stop, Head ++ Get1 ++ Set1 ++ Get2 ++ Set2}; +web_page_node(_, Node, #request{path = [<<"update">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Code Update">>)], + Set = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update, R])], + Get = [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [update_list, R])], + {stop, Head ++ Get ++ Set}; +web_page_node(_, Node, #request{path = [<<"config-file">>]} = R) -> + Res = ?H1GLraw(<<"Configuration File">>, + <<"admin/configuration/file-format/">>, + <<"File Format">>) + ++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [convert_to_yaml, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [dump_config, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reload_config, R])], + {stop, Res}; +web_page_node(_, Node, #request{path = [<<"stop">>]} = R) -> + Res = [?XC(<<"h1">>, <<"Stop This Node">>), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [restart, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stop_kindly, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stop, R, [], [{style, danger}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [halt, R, [], [{style, danger}]])], + {stop, Res}; +web_page_node(_, Node, #request{path = [<<"logs">>]} = R) -> + Res = ?H1GLraw(<<"Logs">>, <<"admin/configuration/basic/#logging">>, <<"Logging">>) + ++ [ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [set_loglevel, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [get_loglevel, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [reopen_log, R]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [rotate_log, R])], + {stop, Res}; +web_page_node(Acc, _, _) -> + Acc. diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 96c664acf..8c42dbe77 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -54,6 +54,8 @@ oauth_add_client_implicit/3, oauth_remove_client/1]). +-export([web_menu_main/2, web_page_main/2]). + -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). @@ -230,6 +232,8 @@ init([]) -> application:set_env(oauth2, expiry_time, Expire div 1000), application:start(oauth2), ejabberd_commands:register_commands(get_commands_spec()), + ejabberd_hooks:add(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:add(webadmin_page_main, ?MODULE, web_page_main, 50), ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), erlang:send_after(expire(), self(), clean), {ok, ok}. @@ -255,6 +259,8 @@ handle_info(Info, State) -> {noreply, State}. terminate(_Reason, _State) -> + ejabberd_hooks:delete(webadmin_menu_main, ?MODULE, web_menu_main, 50), + ejabberd_hooks:delete(webadmin_page_main, ?MODULE, web_page_main, 50), ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50). code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -794,3 +800,30 @@ logo() -> {error, _} -> <<>> end. + +%%% +%%% WebAdmin +%%% + +%% @format-begin + +web_menu_main(Acc, _Lang) -> + Acc ++ [{<<"oauth">>, <<"OAuth">>}]. + +web_page_main(_, #request{path = [<<"oauth">>]} = R) -> + Head = ?H1GLraw(<<"OAuth">>, <<"developer/ejabberd-api/oauth/">>, <<"OAuth">>), + Set = [?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"token">>}], <<"Token">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(oauth_list_tokens, R), + ejabberd_web_admin:make_command(oauth_issue_token, R), + ejabberd_web_admin:make_command(oauth_revoke_token, R)]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"client">>}], <<"Client">>), + ?XE(<<"blockquote">>, + [ejabberd_web_admin:make_command(oauth_add_client_implicit, R), + ejabberd_web_admin:make_command(oauth_add_client_password, R), + ejabberd_web_admin:make_command(oauth_remove_client, R)])], + {stop, Head ++ Set}; +web_page_main(Acc, _) -> + Acc. diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 6fd919a1c..37150abef 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -34,6 +34,10 @@ make_table/2, make_table/4, term_to_id/1, id_to_term/1]). +%% Internal commands +-export([webadmin_host_last_activity/3, + webadmin_node_db_table_page/3]). + -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). -include("ejabberd_http.hrl"). @@ -104,7 +108,7 @@ get_menu_items(global, cluster, Lang, JID, Level) -> end, Items); get_menu_items(Host, cluster, Lang, JID, Level) -> - {_Base, _, Items} = make_host_menu(Host, [], Lang, JID, Level), + {_Base, _, Items} = make_host_menu(Host, [], [], Lang, JID, Level), lists:map(fun ({URI, Name}) -> {<>, Name}; ({URI, Name, _SubMenu}) -> @@ -274,18 +278,26 @@ get_auth_account2(HostOfRule, AccessRule, User, Server, %%%% make_xhtml make_xhtml(Els, Host, Lang, JID, Level) -> - make_xhtml(Els, Host, cluster, Lang, JID, Level). + make_xhtml(Els, Host, cluster, unspecified, Lang, JID, Level). + +make_xhtml(Els, Host, Username, Lang, JID, Level) when + (Username == unspecified) or (is_binary(Username)) -> + make_xhtml(Els, Host, cluster, Username, Lang, JID, Level); + +make_xhtml(Els, Host, Node, Lang, JID, Level) -> + make_xhtml(Els, Host, Node, unspecified, Lang, JID, Level). -spec make_xhtml([xmlel()], Host::global | binary(), Node::cluster | atom(), + Username::unspecified | binary(), Lang::binary(), jid(), Level::integer()) -> {200, [html], xmlel()}. -make_xhtml(Els, Host, Node, Lang, JID, Level) -> +make_xhtml(Els, Host, Node, Username, Lang, JID, Level) -> Base = get_base_path_sum(0, 0, Level), - MenuItems = make_navigation(Host, Node, Lang, JID, Level), + MenuItems = make_navigation(Host, Node, Username, Lang, JID, Level), {200, [html], #xmlel{name = <<"html">>, attrs = @@ -462,6 +474,7 @@ process_admin(Host, #request{path = [], lang = Lang}, AJID) -> || {MIU, MIN} <- get_menu_items(Host, cluster, Lang, AJID, 2)])], Host, Lang, AJID, 2); + process_admin(Host, #request{path = [<<"style.css">>]}, _) -> {200, [{<<"Content-Type">>, <<"text/css">>}, last_modified(), @@ -482,8 +495,6 @@ process_admin(_Host, #request{path = [<<"additions.js">>]}, _) -> [{<<"Content-Type">>, <<"text/javascript">>}, last_modified(), cache_control_public()], additions_js()}; -process_admin(global, #request{path = [<<"vhosts">>], lang = Lang}, AJID) -> - Res = list_vhosts(Lang, AJID), process_admin(_Host, #request{path = [<<"sortable.min.css">>]}, _) -> {200, [{<<"Content-Type">>, <<"text/css">>}, last_modified(), @@ -495,133 +506,174 @@ process_admin(_Host, #request{path = [<<"sortable.min.js">>]}, _) -> last_modified(), cache_control_public()], sortable_js()}; - make_xhtml((?H1GL((translate:translate(Lang, ?T("Virtual Hosts"))), - <<"basic/#xmpp-domains">>, ?T("XMPP Domains"))) - ++ Res, - global, Lang, AJID, 1); -process_admin(Host, #request{path = [<<"users">>], q = Query, - lang = Lang}, AJID) +%% @format-begin + +process_admin(global, #request{path = [<<"vhosts">> | RPath], lang = Lang} = R, AJID) -> + Hosts = + case make_command_raw_value(registered_vhosts, R, []) of + Hs when is_list(Hs) -> + Hs; + _ -> + {User, Server} = R#request.us, + ?INFO_MSG("Access to WebAdmin page vhosts/ for account ~s@~s was denied", + [User, Server]), + [] + end, + Level = 1 + length(RPath), + HostsAllowed = [Host || Host <- Hosts, can_user_access_host(Host, R)], + Table = + make_table(20, + RPath, + [<<"host">>, {<<"registered users">>, right}, {<<"online users">>, right}], + [{make_command(echo, + R, + [{<<"sentence">>, Host}], + [{only, value}, + {result_links, [{sentence, host, Level, <<"">>}]}]), + make_command(stats_host, + R, + [{<<"name">>, <<"registeredusers">>}, {<<"host">>, Host}], + [{only, value}, + {result_links, [{stat, arg_host, Level, <<"users/">>}]}]), + make_command(stats_host, + R, + [{<<"name">>, <<"onlineusers">>}, {<<"host">>, Host}], + [{only, value}, + {result_links, [{stat, arg_host, Level, <<"online-users/">>}]}])} + || Host <- HostsAllowed]), + VhostsElements = + [make_command(registered_vhosts, R, [], [{only, presentation}]), + make_command(stats_host, R, [], [{only, presentation}]), + ?XE(<<"blockquote">>, [Table])], + make_xhtml(?H1GL(translate:translate(Lang, ?T("Virtual Hosts")), + <<"basic/#xmpp-domains">>, + ?T("XMPP Domains")) + ++ VhostsElements, + global, + Lang, + AJID, + Level); +process_admin(Host, + #request{path = [<<"users">>, <<"diapason">>, Diap | RPath], lang = Lang} = R, + AJID) when is_binary(Host) -> - Res = list_users(Host, Query, Lang, fun url_func/1), - make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, - Lang, AJID, 3); -process_admin(Host, #request{path = [<<"users">>, Diap], - lang = Lang}, AJID) + Level = 5 + length(RPath), + RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), + Res = list_users_in_diapason(Host, Level, 30, RPath, R, Diap, RegisterEl), + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); +process_admin(Host, + #request{path = [<<"users">>, <<"top">>, Attribute | RPath], lang = Lang} = R, + AJID) when is_binary(Host) -> - Res = list_users_in_diapason(Host, Diap, Lang, - fun url_func/1), - make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, - Lang, AJID, 4); -process_admin(Host, #request{path = [<<"online-users">>], - lang = Lang}, AJID) + Level = 5 + length(RPath), + RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), + Res = list_users_top(Host, Level, 30, RPath, R, Attribute, RegisterEl), + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); +process_admin(Host, #request{path = [<<"users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> - Res = list_online_users(Host, Lang), - make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, - Host, Lang, AJID, 3); -process_admin(Host, #request{path = [<<"last-activity">>], - q = Query, lang = Lang}, AJID) + Level = 3 + length(RPath), + RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), + Res = list_users(Host, Level, 30, RPath, R, RegisterEl), + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); +process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> - ?DEBUG("Query: ~p", [Query]), - Month = case lists:keysearch(<<"period">>, 1, Query) of - {value, {_, Val}} -> Val; - _ -> <<"month">> - end, - Res = case lists:keysearch(<<"ordinary">>, 1, Query) of - {value, {_, _}} -> - list_last_activity(Host, Lang, false, Month); - _ -> list_last_activity(Host, Lang, true, Month) - end, - PageH1 = ?H1GL(translate:translate(Lang, ?T("Users Last Activity")), <<"modules/#mod_last">>, <<"mod_last">>), - make_xhtml(PageH1 ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?CT(?T("Period: ")), - ?XAE(<<"select">>, [{<<"name">>, <<"period">>}], - (lists:map(fun ({O, V}) -> - Sel = if O == Month -> - [{<<"selected">>, - <<"selected">>}]; - true -> [] - end, - ?XAC(<<"option">>, - (Sel ++ - [{<<"value">>, O}]), - V) - end, - [{<<"month">>, translate:translate(Lang, ?T("Last month"))}, - {<<"year">>, translate:translate(Lang, ?T("Last year"))}, - {<<"all">>, - translate:translate(Lang, ?T("All activity"))}]))), - ?C(<<" ">>), - ?INPUTT(<<"submit">>, <<"ordinary">>, - ?T("Show Ordinary Table")), - ?C(<<" ">>), - ?INPUTT(<<"submit">>, <<"integral">>, - ?T("Show Integral Table"))])] - ++ Res, - Host, Lang, AJID, 3); -process_admin(Host, #request{path = [<<"stats">>], lang = Lang}, AJID) -> - Res = get_stats(Host, Lang), - PageH1 = ?H1GL(translate:translate(Lang, ?T("Statistics")), <<"modules/#mod_stats">>, <<"mod_stats">>), - Level = case Host of - global -> 1; - _ -> 3 - end, - make_xhtml(PageH1 ++ Res, Host, Lang, AJID, Level); -process_admin(Host, #request{path = [<<"user">>, U], - q = Query, lang = Lang}, AJID) -> + Level = 3 + length(RPath), + Res = [make_command(connected_users_vhost, + R, + [{<<"host">>, Host}], + [{table_options, {2, RPath}}, + {result_links, [{sessions, user, Level, <<"">>}]}])], + make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, Host, Lang, AJID, Level); +process_admin(Host, + #request{path = [<<"last-activity">>], + q = Query, + lang = Lang} = + R, + AJID) + when is_binary(Host) -> + PageH1 = + ?H1GL(translate:translate(Lang, ?T("Users Last Activity")), + <<"modules/#mod_last">>, + <<"mod_last">>), + Res = make_command(webadmin_host_last_activity, + R, + [{<<"host">>, Host}, {<<"query">>, Query}, {<<"lang">>, Lang}], + []), + make_xhtml(PageH1 ++ [Res], Host, Lang, AJID, 3); +process_admin(Host, #request{path = [<<"user">>, U], lang = Lang} = R, AJID) -> case ejabberd_auth:user_exists(U, Host) of - true -> - Res = user_info(U, Host, Query, Lang), - make_xhtml(Res, Host, Lang, AJID, 4); - false -> - make_xhtml([?XCT(<<"h1">>, ?T("Not Found"))], Host, - Lang, AJID, 4) + true -> + Res = user_info(U, Host, R), + make_xhtml(Res, Host, U, Lang, AJID, 4); + false -> + make_xhtml([?XCT(<<"h1">>, ?T("Not Found"))], Host, Lang, AJID, 4) end; -process_admin(Host, #request{path = [<<"nodes">>], lang = Lang}, AJID) -> - Res = get_nodes(Lang), - Level = case Host of - global -> 1; - _ -> 3 - end, +process_admin(Host, #request{path = [<<"nodes">>], lang = Lang} = R, AJID) -> + Level = + case Host of + global -> + 1; + _ -> + 3 + end, + Res = ?H1GLraw(<<"Nodes">>, <<"admin/guide/clustering/">>, <<"Clustering">>) + ++ [make_command(list_cluster, R, [], [{result_links, [{node, node, 1, <<"">>}]}])], make_xhtml(Res, Host, Lang, AJID, Level); -process_admin(Host, #request{path = [<<"node">>, SNode | NPath], - q = Query, lang = Lang}, AJID) -> +process_admin(Host, + #request{path = [<<"node">>, SNode | NPath], lang = Lang} = Request, + AJID) -> case search_running_node(SNode) of - false -> - make_xhtml([?XCT(<<"h1">>, ?T("Node not found"))], Host, - Lang, AJID, 2); - Node -> - Res = get_node(Host, Node, NPath, Query, Lang), - Level = case Host of - global -> 2 + length(NPath); - _ -> 4 + length(NPath) - end, - make_xhtml(Res, Host, Node, Lang, AJID, Level) + false -> + make_xhtml([?XCT(<<"h1">>, ?T("Node not found"))], Host, Lang, AJID, 2); + Node -> + Res = get_node(Host, Node, NPath, Request#request{path = NPath}), + Level = + case Host of + global -> + 2 + length(NPath); + _ -> + 4 + length(NPath) + end, + make_xhtml(Res, Host, Node, Lang, AJID, Level) end; %%%================================== %%%% process_admin default case -process_admin(Host, #request{lang = Lang} = Request, AJID) -> - Res = case Host of - global -> - ejabberd_hooks:run_fold( - webadmin_page_main, Host, [], [Request]); - _ -> - ejabberd_hooks:run_fold( - webadmin_page_host, Host, [], [Host, Request]) - end, - Level = case Host of - global -> length(Request#request.path); - _ -> 2 + length(Request#request.path) - end, +process_admin(Host, #request{path = Path, lang = Lang} = Request, AJID) -> + {Username, RPath} = + case Path of + [<<"user">>, U | UPath] -> + {U, UPath}; + _ -> + {unspecified, Path} + end, + Request2 = Request#request{path = RPath}, + Res = case {Host, Username} of + {global, _} -> + ejabberd_hooks:run_fold(webadmin_page_main, Host, [], [Request2]); + {_, unspecified} -> + ejabberd_hooks:run_fold(webadmin_page_host, Host, [], [Host, Request2]); + {_Host, Username} -> + ejabberd_hooks:run_fold(webadmin_page_hostuser, + Host, + [], + [Host, Username, Request2]) + end, + Level = + case Host of + global -> + length(Request#request.path); + _ -> + 2 + length(Request#request.path) + end, case Res of - [] -> - setelement(1, - make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Lang, - AJID, Level), - 404); - _ -> make_xhtml(Res, Host, Lang, AJID, Level) + [] -> + setelement(1, + make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Lang, AJID, Level), + 404); + _ -> + make_xhtml(Res, Host, Username, Lang, AJID, Level) end. +%% @format-end term_to_id([]) -> <<>>; term_to_id(T) -> base64:encode((term_to_binary(T))). @@ -643,9 +695,6 @@ can_user_access_host(Host, #request{auth = Auth, %%%================================== %%%% list_vhosts -list_vhosts(Lang, JID) -> - list_vhosts2(Lang, list_vhosts_allowed(JID)). - list_vhosts_allowed(JID) -> Hosts = ejabberd_option:hosts(), lists:filter(fun (Host) -> @@ -655,40 +704,6 @@ list_vhosts_allowed(JID) -> end, Hosts). -list_vhosts2(Lang, Hosts) -> - SHosts = lists:sort(Hosts), - [?XE(<<"table">>, - [?XE(<<"thead">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Host")), - ?XACT(<<"td">>, - [{<<"class">>, <<"alignright">>}], - ?T("Registered Users")), - ?XACT(<<"td">>, - [{<<"class">>, <<"alignright">>}], - ?T("Online Users"))])]), - ?XE(<<"tbody">>, - (lists:map(fun (Host) -> - OnlineUsers = - length(ejabberd_sm:get_vh_session_list(Host)), - RegisteredUsers = - ejabberd_auth:count_users(Host), - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?AC(<<"../server/", Host/binary, - "/">>, - Host)]), - ?XAE(<<"td">>, - [{<<"class">>, <<"alignright">>}], - [?AC(<<"../server/", Host/binary, "/users/">>, - pretty_string_int(RegisteredUsers))]), - ?XAE(<<"td">>, - [{<<"class">>, <<"alignright">>}], - [?AC(<<"../server/", Host/binary, "/online-users/">>, - pretty_string_int(OnlineUsers))])]) - end, - SHosts)))])]. - maybe_disclaimer_not_admin(MenuItems, AJID, _Lang) -> case {MenuItems, list_vhosts_allowed(AJID)} of {[_], []} -> @@ -705,186 +720,173 @@ maybe_disclaimer_not_admin(MenuItems, AJID, _Lang) -> %%%================================== %%%% list_users -list_users(Host, Query, Lang, URLFunc) -> - Res = list_users_parse_query(Query, Host), - Users = ejabberd_auth:get_users(Host), - SUsers = lists:sort([{S, U} || {U, S} <- Users]), - FUsers = case length(SUsers) of - N when N =< 100 -> - [list_given_users(Host, SUsers, <<"../">>, Lang, - URLFunc)]; - N -> - NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) - + 1, - M = trunc(N / NParts) + 1, - lists:flatmap(fun (K) -> - L = K + M - 1, - Last = if L < N -> - su_to_list(lists:nth(L, - SUsers)); - true -> - su_to_list(lists:last(SUsers)) - end, - Name = <<(su_to_list(lists:nth(K, - SUsers)))/binary, - $\s, 226, 128, 148, $\s, - Last/binary>>, - [?AC((URLFunc({user_diapason, K, L})), - Name), - ?BR] - end, - lists:seq(1, N, M)) - end, - case Res of -%% Parse user creation query and try register: - ok -> [?XREST(?T("Submitted"))]; - error -> [?XREST(?T("Bad format"))]; - nothing -> [] - end - ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - ([?XE(<<"table">>, - [?XE(<<"tr">>, - [?XC(<<"td">>, <<(translate:translate(Lang, ?T("User")))/binary, ":">>), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"newusername">>, <<"">>)]), - ?XE(<<"td">>, [?C(<<" @ ", Host/binary>>)])]), - ?XE(<<"tr">>, - [?XC(<<"td">>, <<(translate:translate(Lang, ?T("Password")))/binary, ":">>), - ?XE(<<"td">>, - [?INPUT(<<"password">>, <<"newuserpassword">>, - <<"">>)]), - ?X(<<"td">>)]), - ?XE(<<"tr">>, - [?X(<<"td">>), - ?XAE(<<"td">>, [{<<"class">>, <<"alignright">>}], - [?INPUTT(<<"submit">>, <<"addnewuser">>, - ?T("Add User"))]), - ?X(<<"td">>)])]), - ?P] - ++ FUsers))]. +%% @format-begin -list_users_parse_query(Query, Host) -> - case lists:keysearch(<<"addnewuser">>, 1, Query) of - {value, _} -> - {value, {_, Username}} = - lists:keysearch(<<"newusername">>, 1, Query), - {value, {_, Password}} = - lists:keysearch(<<"newuserpassword">>, 1, Query), - try jid:decode(<>) - of - #jid{user = User, server = Server} -> - case ejabberd_auth:try_register(User, Server, Password) - of - {error, _Reason} -> error; - _ -> ok - end - catch _:{bad_jid, _} -> - error - end; - false -> nothing +list_users(Host, Level, PageSize, RPath, R, RegisterEl) -> + Usernames = + case make_command_raw_value(registered_users, R, [{<<"host">>, Host}]) of + As when is_list(As) -> + As; + _ -> + {Aser, Aerver} = R#request.us, + ?INFO_MSG("Access to WebAdmin page users/ for account ~s@~s was denied", + [Aser, Aerver]), + [] + end, + case length(Usernames) of + N when N =< 10 -> + list_users(Host, Level, PageSize, RPath, R, Usernames, RegisterEl); + N when N > 10 -> + list_users_diapason(Host, R, Usernames, N, RegisterEl) end. -list_users_in_diapason(Host, Diap, Lang, URLFunc) -> - Users = ejabberd_auth:get_users(Host), - SUsers = lists:sort([{S, U} || {U, S} <- Users]), +list_users(Host, Level, PageSize, RPath, R, Usernames, RegisterEl) -> + Columns = + [<<"user">>, + {<<"offline">>, right}, + {<<"roster">>, right}, + {<<"timestamp">>, left}, + {<<"status">>, left}], + Rows = + [{make_command(echo, + R, + [{<<"sentence">>, + jid:encode( + jid:make(Username, Host))}], + [{only, raw_and_value}, {result_links, [{sentence, user, Level, <<"">>}]}]), + make_command(get_offline_count, + R, + [{<<"user">>, Username}, {<<"host">>, Host}], + [{only, raw_and_value}, + {result_links, + [{value, arg_host, Level, <<"user/", Username/binary, "/queue/">>}]}]), + make_command(get_roster_count, + R, + [{<<"user">>, Username}, {<<"host">>, Host}], + [{only, raw_and_value}, + {result_links, + [{value, arg_host, Level, <<"user/", Username/binary, "/roster/">>}]}]), + ?C(element(1, + make_command_raw_value(get_last, + R, + [{<<"user">>, Username}, {<<"host">>, Host}]))), + ?C(element(2, + make_command_raw_value(get_last, + R, + [{<<"user">>, Username}, {<<"host">>, Host}])))} + || Username <- Usernames], + [RegisterEl, + make_command(registered_users, R, [], [{only, presentation}]), + make_command(get_offline_count, R, [], [{only, presentation}]), + make_command(get_roster_count, R, [], [{only, presentation}]), + make_command(get_last, R, [], [{only, presentation}]), + make_table(PageSize, RPath, Columns, Rows)]. + +list_users_diapason(Host, R, Usernames, N, RegisterEl) -> + URLFunc = fun url_func/1, + SUsers = [{Host, U} || U <- Usernames], + NParts = trunc(math:sqrt(N * 6.17999999999999993783e-1)) + 1, + M = trunc(N / NParts) + 1, + FUsers = + lists:flatmap(fun(K) -> + L = K + M - 1, + Last = + if L < N -> + su_to_list(lists:nth(L, SUsers)); + true -> + su_to_list(lists:last(SUsers)) + end, + Name = + <<(su_to_list(lists:nth(K, SUsers)))/binary, + $\s, + 226, + 128, + 148, + $\s, + Last/binary>>, + [?AC(URLFunc({user_diapason, K, L}), Name), ?BR] + end, + lists:seq(1, N, M)), + [RegisterEl, + make_command(get_offline_count, R, [], [{only, presentation}]), + ?AC(<<"top/offline/">>, <<"View Top Offline Queues">>), + make_command(get_roster_count, R, [], [{only, presentation}]), + ?AC(<<"top/roster/">>, <<"View Top Rosters">>), + make_command(get_last, R, [], [{only, presentation}]), + ?AC(<<"top/last/">>, <<"View Top-Oldest Last Activity">>), + make_command(registered_users, R, [], [{only, presentation}])] + ++ FUsers. + +list_users_in_diapason(Host, Level, PageSize, RPath, R, Diap, RegisterEl) -> + Usernames = + case make_command_raw_value(registered_users, R, [{<<"host">>, Host}]) of + As when is_list(As) -> + As; + _ -> + {Aser, Aerver} = R#request.us, + ?INFO_MSG("Access to WebAdmin page users/ for account ~s@~s was denied", + [Aser, Aerver]), + [] + end, + SUsers = lists:sort([{Host, U} || U <- Usernames]), [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), N1 = binary_to_integer(S1), N2 = binary_to_integer(S2), Sub = lists:sublist(SUsers, N1, N2 - N1 + 1), - [list_given_users(Host, Sub, <<"../../">>, Lang, - URLFunc)]. + Usernames2 = [U || {_, U} <- Sub], + list_users(Host, Level, PageSize, RPath, R, Usernames2, RegisterEl). -list_given_users(Host, Users, Prefix, Lang, URLFunc) -> - ModOffline = get_offlinemsg_module(Host), - ?XE(<<"table">>, - [?XE(<<"thead">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("User")), - ?XACT(<<"td">>, - [{<<"class">>, <<"alignright">>}], - ?T("Offline Messages")), - ?XCT(<<"td">>, ?T("Last Activity"))])]), - ?XE(<<"tbody">>, - (lists:map(fun (_SU = {Server, User}) -> - US = {User, Server}, - QueueLenStr = get_offlinemsg_length(ModOffline, - User, - Server), - FQueueLen = [?AC((URLFunc({users_queue, Prefix, - User, Server})), - QueueLenStr)], - FLast = case - ejabberd_sm:get_user_resources(User, - Server) - of - [] -> - case get_last_info(User, Server) of - not_found -> translate:translate(Lang, ?T("Never")); - {ok, Shift, _Status} -> - TimeStamp = {Shift div - 1000000, - Shift rem - 1000000, - 0}, - {{Year, Month, Day}, - {Hour, Minute, Second}} = - calendar:now_to_local_time(TimeStamp), - (str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", - [Year, - Month, - Day, - Hour, - Minute, - Second])) - end; - _ -> translate:translate(Lang, ?T("Online")) - end, - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?AC((URLFunc({user, Prefix, - misc:url_encode(User), - Server})), - (us_to_list(US)))]), - ?XAE(<<"td">>, - [{<<"class">>, <<"alignright">>}], - FQueueLen), - ?XC(<<"td">>, FLast)]) - end, - Users)))]). - -get_offlinemsg_length(ModOffline, User, Server) -> - case ModOffline of - none -> <<"disabled">>; - _ -> - pretty_string_int(ModOffline:count_offline_messages(User,Server)) - end. - -get_offlinemsg_module(Server) -> - case gen_mod:is_loaded(Server, mod_offline) of - true -> mod_offline; - false -> none - end. +list_users_top(Host, Level, PageSize, RPath, R, Operation, RegisterEl) -> + Usernames = + case make_command_raw_value(registered_users, R, [{<<"host">>, Host}]) of + As when is_list(As) -> + As; + _ -> + {Aser, Aerver} = R#request.us, + ?INFO_MSG("Access to WebAdmin page users/ for account ~s@~s was denied", + [Aser, Aerver]), + [] + end, + {Command, Reverse} = + case Operation of + <<"roster">> -> + {get_roster_count, true}; + <<"offline">> -> + {get_offline_count, true}; + <<"last">> -> + {get_last, false} + end, + UsernamesCounts = + [{U, + make_command(Command, + R, + [{<<"user">>, U}, {<<"host">>, Host}], + [{only, raw_value}, + {result_links, + [{value, arg_host, Level, <<"user/", U/binary, "/roster/">>}]}])} + || U <- Usernames], + USorted = lists:keysort(2, UsernamesCounts), + UReversed = + case Reverse of + true -> + lists:reverse(USorted); + false -> + USorted + end, + Usernames2 = [U || {U, _} <- lists:sublist(UReversed, 100)], + list_users(Host, Level, PageSize, RPath, R, Usernames2, RegisterEl). get_lastactivity_menuitem_list(Server) -> case gen_mod:is_loaded(Server, mod_last) of - true -> - case mod_last_opt:db_type(Server) of - mnesia -> [{<<"last-activity">>, ?T("Last Activity")}]; - _ -> [] - end; - false -> - [] - end. - -get_last_info(User, Server) -> - case gen_mod:is_loaded(Server, mod_last) of - true -> - mod_last:get_last_info(User, Server); - false -> - not_found + true -> + case mod_last_opt:db_type(Server) of + mnesia -> + [{<<"last-activity">>, ?T("Last Activity")}]; + _ -> + [] + end; + false -> + [] end. us_to_list({User, Server}) -> @@ -892,157 +894,58 @@ us_to_list({User, Server}) -> su_to_list({Server, User}) -> jid:encode({User, Server, <<"">>}). +%% @format-end + +%%%================================== +%%%% last-activity + +webadmin_host_last_activity(Host, Query, Lang) -> + ?DEBUG("Query: ~p", [Query]), + Month = case lists:keysearch(<<"period">>, 1, Query) of + {value, {_, Val}} -> Val; + _ -> <<"month">> + end, + Res = case lists:keysearch(<<"ordinary">>, 1, Query) of + {value, {_, _}} -> + list_last_activity(Host, Lang, false, Month); + _ -> list_last_activity(Host, Lang, true, Month) + end, + [?XAE(<<"form">>, + [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], + [?CT(?T("Period: ")), + ?XAE(<<"select">>, [{<<"name">>, <<"period">>}], + (lists:map(fun ({O, V}) -> + Sel = if O == Month -> + [{<<"selected">>, + <<"selected">>}]; + true -> [] + end, + ?XAC(<<"option">>, + (Sel ++ + [{<<"value">>, O}]), + V) + end, + [{<<"month">>, translate:translate(Lang, ?T("Last month"))}, + {<<"year">>, translate:translate(Lang, ?T("Last year"))}, + {<<"all">>, + translate:translate(Lang, ?T("All activity"))}]))), + ?C(<<" ">>), + ?INPUTT(<<"submit">>, <<"ordinary">>, + ?T("Show Ordinary Table")), + ?C(<<" ">>), + ?INPUTT(<<"submit">>, <<"integral">>, + ?T("Show Integral Table"))])] + ++ Res. %%%================================== %%%% get_stats -get_stats(global, Lang) -> - OnlineUsers = ejabberd_sm:connected_users_number(), - RegisteredUsers = lists:foldl(fun (Host, Total) -> - ejabberd_auth:count_users(Host) - + Total - end, - 0, ejabberd_option:hosts()), - OutS2SNumber = ejabberd_s2s:outgoing_s2s_number(), - InS2SNumber = ejabberd_s2s:incoming_s2s_number(), - [?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Registered Users:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(RegisteredUsers)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Online Users:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(OnlineUsers)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Outgoing s2s Connections:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(OutS2SNumber)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Incoming s2s Connections:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(InS2SNumber)))])])])]; -get_stats(Host, Lang) -> - OnlineUsers = - length(ejabberd_sm:get_vh_session_list(Host)), - RegisteredUsers = - ejabberd_auth:count_users(Host), - [?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Registered Users:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(RegisteredUsers)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Online Users:")), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(OnlineUsers)))])])])]. - -list_online_users(Host, _Lang) -> - Users = [{S, U} - || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Host)], - SUsers = lists:usort(Users), - lists:flatmap(fun ({_S, U} = SU) -> - [?AC(<<"../user/", - (misc:url_encode(U))/binary, "/">>, - (su_to_list(SU))), - ?BR] - end, - SUsers). - -user_info(User, Server, Query, Lang) -> +user_info(User, Server, #request{q = Query, lang = Lang} = R) -> LServer = jid:nameprep(Server), US = {jid:nodeprep(User), LServer}, Res = user_parse_query(User, Server, Query), - Resources = ejabberd_sm:get_user_resources(User, - Server), - FResources = - case Resources of - [] -> [?CT(?T("None"))]; - _ -> - [?XE(<<"ul">>, - (lists:map( - fun (R) -> - FIP = case - ejabberd_sm:get_user_info(User, - Server, - R) - of - offline -> <<"">>; - Info - when - is_list(Info) -> - Node = - proplists:get_value(node, - Info), - Conn = - proplists:get_value(conn, - Info), - {IP, Port} = - proplists:get_value(ip, - Info), - ConnS = case Conn of - c2s -> - <<"plain">>; - c2s_tls -> - <<"tls">>; - c2s_compressed -> - <<"zlib">>; - c2s_compressed_tls -> - <<"tls+zlib">>; - http_bind -> - <<"http-bind">>; - websocket -> - <<"websocket">>; - _ -> - <<"unknown">> - end, - <> - end, - case direction(Lang) of - [{_, <<"rtl">>}] -> ?LI([?C((<>))]); - _ -> ?LI([?C((<>))]) - end - end, - lists:sort(Resources))))] - end, - FPassword = [?INPUT(<<"text">>, <<"password">>, <<"">>), - ?C(<<" ">>), - ?INPUTT(<<"submit">>, <<"chpassword">>, - ?T("Change Password"))], UserItems = ejabberd_hooks:run_fold(webadmin_user, - LServer, [], [User, Server, Lang]), - LastActivity = case ejabberd_sm:get_user_resources(User, - Server) - of - [] -> - case get_last_info(User, Server) of - not_found -> translate:translate(Lang, ?T("Never")); - {ok, Shift, _Status} -> - TimeStamp = {Shift div 1000000, - Shift rem 1000000, 0}, - {{Year, Month, Day}, {Hour, Minute, Second}} = - calendar:now_to_local_time(TimeStamp), - (str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", - [Year, Month, Day, - Hour, Minute, - Second])) - end; - _ -> translate:translate(Lang, ?T("Online")) - end, + LServer, [], [User, Server, R]), [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("User ~ts"), [us_to_list(US)])))] ++ @@ -1054,16 +957,24 @@ user_info(User, Server, Query, Lang) -> ++ [?XAE(<<"form">>, [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - ([?XCT(<<"h3">>, ?T("Connected Resources:"))] ++ - FResources ++ - [?XCT(<<"h3">>, ?T("Password:"))] ++ - FPassword ++ - [?XCT(<<"h3">>, ?T("Last Activity"))] ++ - [?C(LastActivity)] ++ - UserItems ++ - [?P, - ?INPUTTD(<<"submit">>, <<"removeuser">>, - ?T("Remove User"))]))]. + ([make_command(user_sessions_info, R, + [{<<"user">>, User}, {<<"host">>, Server}], + [{result_links, [{node, node, 4, <<>>}]}]), + make_command(change_password, R, + [{<<"user">>, User}, {<<"host">>, Server}], + [{style, danger}]), + make_command(get_last, R, + [{<<"user">>, User}, {<<"host">>, Server}], + []), + make_command(set_last, R, + [{<<"user">>, User}, {<<"host">>, Server}], + [])] ++ + UserItems ++ + [?P, + make_command(unregister, R, + [{<<"user">>, User}, {<<"host">>, Server}], + [{style, danger}]) + ]))]. user_parse_query(User, Server, Query) -> lists:foldl(fun ({Action, _Value}, Acc) @@ -1073,19 +984,6 @@ user_parse_query(User, Server, Query) -> end, nothing, Query). -user_parse_query1(<<"password">>, _User, _Server, - _Query) -> - nothing; -user_parse_query1(<<"chpassword">>, User, Server, - Query) -> - case lists:keysearch(<<"password">>, 1, Query) of - {value, {_, Password}} -> - ejabberd_auth:set_password(User, Server, Password), ok; - _ -> error - end; -user_parse_query1(<<"removeuser">>, User, Server, - _Query) -> - ejabberd_auth:remove_user(User, Server), ok; user_parse_query1(Action, User, Server, Query) -> case ejabberd_hooks:run_fold(webadmin_user_parse_query, Server, [], [Action, User, Server, Query]) @@ -1157,33 +1055,6 @@ histogram([], _Integral, _Current, Count, Hist) -> %%%================================== %%%% get_nodes -get_nodes(Lang) -> - RunningNodes = ejabberd_cluster:get_nodes(), - StoppedNodes = ejabberd_cluster:get_known_nodes() - -- RunningNodes, - FRN = if RunningNodes == [] -> ?CT(?T("None")); - true -> - ?XE(<<"ul">>, - (lists:map(fun (N) -> - S = iolist_to_binary(atom_to_list(N)), - ?LI([?AC(<<"../node/", S/binary, "/">>, - S)]) - end, - lists:sort(RunningNodes)))) - end, - FSN = if StoppedNodes == [] -> ?CT(?T("None")); - true -> - ?XE(<<"ul">>, - (lists:map(fun (N) -> - S = iolist_to_binary(atom_to_list(N)), - ?LI([?C(S)]) - end, - lists:sort(StoppedNodes)))) - end, - [?XCT(<<"h1">>, ?T("Nodes")), - ?XCT(<<"h3">>, ?T("Running Nodes")), FRN, - ?XCT(<<"h3">>, ?T("Stopped Nodes")), FSN]. - search_running_node(SNode) -> RunningNodes = ejabberd_cluster:get_nodes(), search_running_node(SNode, RunningNodes). @@ -1195,601 +1066,104 @@ search_running_node(SNode, [Node | Nodes]) -> _ -> search_running_node(SNode, Nodes) end. -get_node(global, Node, [], Query, Lang) -> - Res = node_parse_query(Node, Query), +get_node(global, Node, [], #request{lang = Lang}) -> Base = get_base_path(global, Node, 2), - BaseItems = [{<<"db">>, <<"Database">>}, - {<<"backup">>, <<"Backup">>}, - {<<"stats">>, <<"Statistics">>}, - {<<"update">>, <<"Update">>}], + BaseItems = [{<<"db">>, <<"Mnesia Tables">>}, + {<<"backup">>, <<"Mnesia Backup">>}], MenuItems = make_menu_items(global, Node, Base, Lang, BaseItems), [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Node ~p"), [Node])))] ++ - case Res of - ok -> [?XREST(?T("Submitted"))]; - error -> [?XREST(?T("Bad format"))]; - nothing -> [] - end - ++ - [?XE(<<"ul">>, MenuItems), - ?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?INPUTT(<<"submit">>, <<"restart">>, ?T("Restart")), - ?C(<<" ">>), - ?INPUTTD(<<"submit">>, <<"stop">>, ?T("Stop"))])]; -get_node(Host, Node, [], _Query, Lang) -> + [?XE(<<"ul">>, MenuItems)]; +get_node(Host, Node, [], #request{lang = Lang}) -> Base = get_base_path(Host, Node, 4), MenuItems2 = make_menu_items(Host, Node, Base, Lang, []), [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Node ~p"), [Node]))), ?XE(<<"ul">>, MenuItems2)]; -get_node(global, Node, [<<"db">>], Query, Lang) -> - case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of - {badrpc, _Reason} -> - [?XCT(<<"h1">>, ?T("RPC Call Error"))]; - Tables -> - ResS = case node_db_parse_query(Node, Tables, Query) of - nothing -> []; - ok -> [?XREST(?T("Submitted"))] - end, - STables = lists:sort(Tables), - Rows = lists:map(fun (Table) -> - STable = - iolist_to_binary(atom_to_list(Table)), - TInfo = case ejabberd_cluster:call(Node, mnesia, - table_info, - [Table, all]) - of - {badrpc, _} -> []; - I -> I - end, - {Type, Size, Memory} = case - {lists:keysearch(storage_type, - 1, - TInfo), - lists:keysearch(size, - 1, - TInfo), - lists:keysearch(memory, - 1, - TInfo)} - of - {{value, - {storage_type, - T}}, - {value, {size, S}}, - {value, - {memory, M}}} -> - {T, S, M}; - _ -> {unknown, 0, 0} - end, - MemoryB = Memory*erlang:system_info(wordsize), - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?AC(<<"./", STable/binary, - "/">>, - STable)]), - ?XE(<<"td">>, - [db_storage_select(STable, Type, - Lang)]), - ?XAE(<<"td">>, - [{<<"class">>, <<"alignright">>}], - [?AC(<<"./", STable/binary, - "/1/">>, - (pretty_string_int(Size)))]), - ?XAC(<<"td">>, - [{<<"class">>, <<"alignright">>}], - (pretty_string_int(MemoryB)))]) - end, - STables), - [?XC(<<"h1">>, - (str:translate_and_format(Lang, ?T("Database Tables at ~p"), - [Node])) - )] - ++ - ResS ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?XAE(<<"table">>, [], - [?XE(<<"thead">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Name")), - ?XCT(<<"td">>, ?T("Storage Type")), - ?XACT(<<"td">>, - [{<<"class">>, <<"alignright">>}], - ?T("Elements")), - ?XACT(<<"td">>, - [{<<"class">>, <<"alignright">>}], - ?T("Memory"))])]), - ?XE(<<"tbody">>, - (Rows ++ - [?XE(<<"tr">>, - [?XAE(<<"td">>, - [{<<"colspan">>, <<"4">>}, - {<<"class">>, <<"alignright">>}], - [?INPUTT(<<"submit">>, - <<"submit">>, - ?T("Submit"))])])]))])])] - end; -get_node(global, Node, [<<"db">>, TableName], _Query, Lang) -> - make_table_view(Node, TableName, Lang); -get_node(global, Node, [<<"db">>, TableName, PageNumber], _Query, Lang) -> - make_table_elements_view(Node, TableName, Lang, binary_to_integer(PageNumber)); -get_node(global, Node, [<<"backup">>], Query, Lang) -> - HomeDirRaw = case {os:getenv("HOME"), os:type()} of - {EnvHome, _} when is_list(EnvHome) -> list_to_binary(EnvHome); - {false, {win32, _Osname}} -> <<"C:/">>; - {false, _} -> <<"/tmp/">> - end, - HomeDir = filename:nativename(HomeDirRaw), - ResS = case node_backup_parse_query(Node, Query) of - nothing -> []; - ok -> [?XREST(?T("Submitted"))]; - {error, Error} -> - [?XRES(<<(translate:translate(Lang, ?T("Error")))/binary, ": ", - ((str:format("~p", [Error])))/binary>>)] - end, - [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Backup of ~p"), [Node])))] - ++ - ResS ++ - [?XCT(<<"p">>, - ?T("Please note that these options will " - "only backup the builtin Mnesia database. " - "If you are using the ODBC module, you " - "also need to backup your SQL database " - "separately.")), - ?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Store binary backup:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"storepath">>, - (filename:join(HomeDir, - "ejabberd.backup")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"store">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Restore binary backup immediately:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"restorepath">>, - (filename:join(HomeDir, - "ejabberd.backup")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"restore">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Restore binary backup after next ejabberd " - "restart (requires less memory):")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"fallbackpath">>, - (filename:join(HomeDir, - "ejabberd.backup")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"fallback">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Store plain text backup:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"dumppath">>, - (filename:join(HomeDir, - "ejabberd.dump")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"dump">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Restore plain text backup immediately:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"loadpath">>, - (filename:join(HomeDir, - "ejabberd.dump")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"load">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Import users data from a PIEFXIS file " - "(XEP-0227):")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, - <<"import_piefxis_filepath">>, - (filename:join(HomeDir, - "users.xml")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, - <<"import_piefxis_file">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Export data of all users in the server " - "to PIEFXIS files (XEP-0227):")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, - <<"export_piefxis_dirpath">>, - HomeDir)]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, - <<"export_piefxis_dir">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?CT(?T("Export data of users in a host to PIEFXIS " - "files (XEP-0227):")), - ?C(<<" ">>), - make_select_host(Lang, <<"export_piefxis_host_dirhost">>)]), - ?XE(<<"td">>, - [?INPUT(<<"text">>, - <<"export_piefxis_host_dirpath">>, - HomeDir)]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, - <<"export_piefxis_host_dir">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?CT(?T("Export all tables as SQL queries " - "to a file:")), - ?C(<<" ">>), - make_select_host(Lang, <<"export_sql_filehost">>)]), - ?XE(<<"td">>, - [?INPUT(<<"text">>, - <<"export_sql_filepath">>, - (filename:join(HomeDir, - "db.sql")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"export_sql_file">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Import user data from jabberd14 spool " - "file:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"import_filepath">>, - (filename:join(HomeDir, - "user1.xml")))]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"import_file">>, - ?T("OK"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, - ?T("Import users data from jabberd14 spool " - "directory:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"import_dirpath">>, - <<"/var/spool/jabber/">>)]), - ?XE(<<"td">>, - [?INPUTT(<<"submit">>, <<"import_dir">>, - ?T("OK"))])])])])])]; -get_node(global, Node, [<<"stats">>], _Query, Lang) -> - UpTime = ejabberd_cluster:call(Node, erlang, statistics, - [wall_clock]), - UpTimeS = (str:format("~.3f", - [element(1, UpTime) / 1000])), - UpTimeDate = uptime_date(Node), - CPUTime = ejabberd_cluster:call(Node, erlang, statistics, [runtime]), - CPUTimeS = (str:format("~.3f", - [element(1, CPUTime) / 1000])), - OnlineUsers = ejabberd_sm:connected_users_number(), - TransactionsCommitted = ejabberd_cluster:call(Node, mnesia, - system_info, [transaction_commits]), - TransactionsAborted = ejabberd_cluster:call(Node, mnesia, - system_info, [transaction_failures]), - TransactionsRestarted = ejabberd_cluster:call(Node, mnesia, - system_info, [transaction_restarts]), - TransactionsLogged = ejabberd_cluster:call(Node, mnesia, system_info, - [transaction_log_writes]), - ?H1GL(str:translate_and_format(Lang, ?T("Statistics of ~p"), [Node]), - <<"modules/#mod_stats">>, - <<"mod_stats">>) ++ [ - ?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Uptime:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - UpTimeS)]), - ?XE(<<"tr">>, - [?X(<<"td">>), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - UpTimeDate)]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("CPU Time:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - CPUTimeS)]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Online Users:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(OnlineUsers)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Transactions Committed:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(TransactionsCommitted)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Transactions Aborted:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(TransactionsAborted)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Transactions Restarted:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(TransactionsRestarted)))]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Transactions Logged:")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(TransactionsLogged)))])])])]; -get_node(global, Node, [<<"update">>], Query, Lang) -> - ejabberd_cluster:call(Node, code, purge, [ejabberd_update]), - Res = node_update_parse_query(Node, Query), - ejabberd_cluster:call(Node, code, load_file, [ejabberd_update]), - {ok, _Dir, UpdatedBeams, Script, LowLevelScript, - Check} = - ejabberd_cluster:call(Node, ejabberd_update, update_info, []), - Mods = case UpdatedBeams of - [] -> ?CT(?T("None")); - _ -> - BeamsLis = lists:map(fun (Beam) -> - BeamString = - iolist_to_binary(atom_to_list(Beam)), - ?LI([?INPUT(<<"checkbox">>, - <<"selected">>, - BeamString), - ?C(BeamString)]) - end, - UpdatedBeams), - SelectButtons = [?BR, - ?INPUTATTRS(<<"button">>, <<"selectall">>, - ?T("Select All"), - [{<<"onClick">>, - <<"selectAll()">>}]), - ?C(<<" ">>), - ?INPUTATTRS(<<"button">>, <<"unselectall">>, - ?T("Unselect All"), - [{<<"onClick">>, - <<"unSelectAll()">>}])], - ?XAE(<<"ul">>, [{<<"class">>, <<"nolistyle">>}], - (BeamsLis ++ SelectButtons)) - end, - FmtScript = (?XC(<<"pre">>, - (str:format("~p", [Script])))), - FmtLowLevelScript = (?XC(<<"pre">>, - (str:format("~p", [LowLevelScript])))), - [?XC(<<"h1">>, - (str:translate_and_format(Lang, ?T("Update ~p"), [Node])))] - ++ - case Res of - ok -> [?XREST(?T("Submitted"))]; - {error, ErrorText} -> - [?XREST(<<"Error: ", ErrorText/binary>>)]; - nothing -> [] - end - ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?XCT(<<"h2">>, ?T("Update plan")), - ?XCT(<<"h3">>, ?T("Modified modules")), Mods, - ?XCT(<<"h3">>, ?T("Update script")), FmtScript, - ?XCT(<<"h3">>, ?T("Low level update script")), - FmtLowLevelScript, ?XCT(<<"h3">>, ?T("Script check")), - ?XC(<<"pre">>, (misc:atom_to_binary(Check))), - ?BR, - ?INPUTT(<<"submit">>, <<"update">>, ?T("Update"))])]; -get_node(Host, Node, NPath, Query, Lang) -> + +get_node(global, Node, [<<"db">> | RPath], R) -> + PageTitle = <<"Mnesia Tables">>, + Title = ?XC(<<"h1">>, PageTitle), + Level = length(RPath), + [Title, ?BR | webadmin_db(Node, RPath, R, Level)]; + +get_node(global, Node, [<<"backup">>], #request{lang = Lang} = R) -> + Types = [{<<"#binary">>, <<"Binary">>}, + {<<"#plaintext">>, <<"Plain Text">>}, + {<<"#piefxis">>, <<"PIEXFIS (XEP-0227)">>}, + {<<"#sql">>, <<"SQL">>}, + {<<"#prosody">>, <<"Prosody">>}, + {<<"#jabberd14">>, <<"jabberd 1.4">>}], + + [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Backup of ~p"), [Node]))), + ?XCT(<<"p">>, + ?T("Please note that these options will " + "only backup the builtin Mnesia database. " + "If you are using the ODBC module, you " + "also need to backup your SQL database " + "separately.")), + ?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- Types]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"binary">>}], <<"Binary">>), + ?XCT(<<"p">>, ?T("Store binary backup:")), + ?XE(<<"blockquote">>, [make_command(backup, R)]), + ?XCT(<<"p">>, ?T("Restore binary backup immediately:")), + ?XE(<<"blockquote">>, [make_command(restore, R, [], [{style, danger}])]), + ?XCT(<<"p">>, ?T("Restore binary backup after next ejabberd " + "restart (requires less memory):")), + ?XE(<<"blockquote">>, [make_command(install_fallback, R, [], [{style, danger}])]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"plaintext">>}], <<"Plain Text">>), + ?XCT(<<"p">>, ?T("Store plain text backup:")), + ?XE(<<"blockquote">>, [make_command(dump, R)]), + ?XCT(<<"p">>, ?T("Restore plain text backup immediately:")), + ?XE(<<"blockquote">>, [make_command(load, R, [], [{style, danger}])]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"piefxis">>}], <<"PIEFXIS (XEP-0227)">>), + ?XCT(<<"p">>, ?T("Import users data from a PIEFXIS file (XEP-0227):")), + ?XE(<<"blockquote">>, [make_command(import_piefxis, R)]), + ?XCT(<<"p">>, ?T("Export data of all users in the server to PIEFXIS files (XEP-0227):")), + ?XE(<<"blockquote">>, [make_command(export_piefxis, R)]), + ?XCT(<<"p">>, ?T("Export data of users in a host to PIEFXIS files (XEP-0227):")), + ?XE(<<"blockquote">>, [make_command(export_piefxis_host, R)]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"sql">>}], <<"SQL">>), + ?XCT(<<"p">>, ?T("Export all tables as SQL queries to a file:")), + ?XE(<<"blockquote">>, [make_command(export2sql, R)]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"prosody">>}], <<"Prosody">>), + ?XCT(<<"p">>, <<"Import data from Prosody:">>), + ?XE(<<"blockquote">>, [make_command(import_prosody, R)]), + + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"jabberd14">>}], <<"jabberd 1.4">>), + ?XCT(<<"p">>, ?T("Import user data from jabberd14 spool file:")), + ?XE(<<"blockquote">>, [make_command(import_file, R)]), + ?XCT(<<"p">>, ?T("Import users data from jabberd14 spool directory:")), + ?XE(<<"blockquote">>, [make_command(import_dir, R)]) + ]; +get_node(Host, Node, _NPath, Request) -> Res = case Host of global -> ejabberd_hooks:run_fold(webadmin_page_node, Host, [], - [Node, NPath, Query, Lang]); + [Node, Request]); _ -> ejabberd_hooks:run_fold(webadmin_page_hostnode, Host, [], - [Host, Node, NPath, Query, Lang]) + [Host, Node, Request]) end, case Res of [] -> [?XC(<<"h1">>, <<"Not Found">>)]; _ -> Res end. -uptime_date(Node) -> - Localtime = ejabberd_cluster:call(Node, erlang, localtime, []), - Now = calendar:datetime_to_gregorian_seconds(Localtime), - {Wall, _} = ejabberd_cluster:call(Node, erlang, statistics, [wall_clock]), - LastRestart = Now - (Wall div 1000), - {{Year, Month, Day}, {Hour, Minute, Second}} = - calendar:gregorian_seconds_to_datetime(LastRestart), - str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", - [Year, Month, Day, Hour, Minute, Second]). - %%%================================== %%%% node parse -node_parse_query(Node, Query) -> - case lists:keysearch(<<"restart">>, 1, Query) of - {value, _} -> - case ejabberd_cluster:call(Node, init, restart, []) of - {badrpc, _Reason} -> error; - _ -> ok - end; - _ -> - case lists:keysearch(<<"stop">>, 1, Query) of - {value, _} -> - case ejabberd_cluster:call(Node, init, stop, []) of - {badrpc, _Reason} -> error; - _ -> ok - end; - _ -> nothing - end - end. - -make_select_host(Lang, Name) -> - ?XAE(<<"select">>, - [{<<"name">>, Name}], - (lists:map(fun (Host) -> - ?XACT(<<"option">>, - ([{<<"value">>, Host}]), Host) - end, - ejabberd_config:get_option(hosts)))). - -db_storage_select(ID, Opt, Lang) -> - ?XAE(<<"select">>, - [{<<"name">>, <<"table", ID/binary>>}], - (lists:map(fun ({O, Desc}) -> - Sel = if O == Opt -> - [{<<"selected">>, <<"selected">>}]; - true -> [] - end, - ?XACT(<<"option">>, - (Sel ++ - [{<<"value">>, - iolist_to_binary(atom_to_list(O))}]), - Desc) - end, - [{ram_copies, ?T("RAM copy")}, - {disc_copies, ?T("RAM and disc copy")}, - {disc_only_copies, ?T("Disc only copy")}, - {unknown, ?T("Remote copy")}, - {delete_content, ?T("Delete content")}, - {delete_table, ?T("Delete table")}]))). - -node_db_parse_query(_Node, _Tables, [{nokey, <<>>}]) -> - nothing; -node_db_parse_query(Node, Tables, Query) -> - lists:foreach(fun (Table) -> - STable = iolist_to_binary(atom_to_list(Table)), - case lists:keysearch(<<"table", STable/binary>>, 1, - Query) - of - {value, {_, SType}} -> - Type = case SType of - <<"unknown">> -> unknown; - <<"ram_copies">> -> ram_copies; - <<"disc_copies">> -> disc_copies; - <<"disc_only_copies">> -> - disc_only_copies; - <<"delete_content">> -> delete_content; - <<"delete_table">> -> delete_table; - _ -> false - end, - if Type == false -> ok; - Type == delete_content -> - mnesia:clear_table(Table); - Type == delete_table -> - mnesia:delete_table(Table); - Type == unknown -> - mnesia:del_table_copy(Table, Node); - true -> - case mnesia:add_table_copy(Table, Node, - Type) - of - {aborted, _} -> - mnesia:change_table_copy_type(Table, - Node, - Type); - _ -> ok - end - end; - _ -> ok - end - end, - Tables), - ok. - -node_backup_parse_query(_Node, [{nokey, <<>>}]) -> - nothing; -node_backup_parse_query(Node, Query) -> - lists:foldl(fun (Action, nothing) -> - case lists:keysearch(Action, 1, Query) of - {value, _} -> - case lists:keysearch(<>, 1, - Query) - of - {value, {_, Path}} -> - Res = case Action of - <<"store">> -> - ejabberd_cluster:call(Node, mnesia, backup, - [binary_to_list(Path)]); - <<"restore">> -> - ejabberd_cluster:call(Node, ejabberd_admin, - restore, [Path]); - <<"fallback">> -> - ejabberd_cluster:call(Node, mnesia, - install_fallback, - [binary_to_list(Path)]); - <<"dump">> -> - ejabberd_cluster:call(Node, ejabberd_admin, - dump_to_textfile, - [Path]); - <<"load">> -> - ejabberd_cluster:call(Node, mnesia, - load_textfile, - [binary_to_list(Path)]); - <<"import_piefxis_file">> -> - ejabberd_cluster:call(Node, ejabberd_piefxis, - import_file, [Path]); - <<"export_piefxis_dir">> -> - ejabberd_cluster:call(Node, ejabberd_piefxis, - export_server, [Path]); - <<"export_piefxis_host_dir">> -> - {value, {_, Host}} = - lists:keysearch(<>, - 1, Query), - ejabberd_cluster:call(Node, ejabberd_piefxis, - export_host, - [Path, Host]); - <<"export_sql_file">> -> - {value, {_, Host}} = - lists:keysearch(<>, - 1, Query), - ejabberd_cluster:call(Node, ejd2sql, - export, [Host, Path]); - <<"import_file">> -> - ejabberd_cluster:call(Node, ejabberd_admin, - import_file, [Path]); - <<"import_dir">> -> - ejabberd_cluster:call(Node, ejabberd_admin, - import_dir, [Path]) - end, - case Res of - {error, Reason} -> {error, Reason}; - {badrpc, Reason} -> {badrpc, Reason}; - _ -> ok - end; - OtherError -> {error, OtherError} - end; - _ -> nothing - end; - (_Action, Res) -> Res - end, - nothing, - [<<"store">>, <<"restore">>, <<"fallback">>, <<"dump">>, - <<"load">>, <<"import_file">>, <<"import_dir">>, - <<"import_piefxis_file">>, <<"export_piefxis_dir">>, - <<"export_piefxis_host_dir">>, <<"export_sql_file">>]). - -node_update_parse_query(Node, Query) -> - case lists:keysearch(<<"update">>, 1, Query) of - {value, _} -> - ModulesToUpdateStrings = - proplists:get_all_values(<<"selected">>, Query), - ModulesToUpdate = [misc:binary_to_atom(M) - || M <- ModulesToUpdateStrings], - case ejabberd_cluster:call(Node, ejabberd_update, update, - [ModulesToUpdate]) - of - {ok, _} -> ok; - {error, Error} -> - ?ERROR_MSG("~p~n", [Error]), - {error, (str:format("~p", [Error]))}; - {badrpc, Error} -> - ?ERROR_MSG("Bad RPC: ~p~n", [Error]), - {error, - <<"Bad RPC: ", ((str:format("~p", [Error])))/binary>>} - end; - _ -> nothing - end. - pretty_print_xml(El) -> list_to_binary(pretty_print_xml(El, <<"">>)). @@ -1848,12 +1222,8 @@ pretty_print_xml(#xmlel{name = Name, attrs = Attrs, end]. url_func({user_diapason, From, To}) -> - <<(integer_to_binary(From))/binary, "-", - (integer_to_binary(To))/binary, "/">>; -url_func({users_queue, Prefix, User, _Server}) -> - <>; -url_func({user, Prefix, User, _Server}) -> - <>. + <<"diapason/", (integer_to_binary(From))/binary, "-", + (integer_to_binary(To))/binary, "/">>. last_modified() -> {<<"Last-Modified">>, @@ -1877,49 +1247,7 @@ pretty_string_int(String) when is_binary(String) -> %%%================================== %%%% mnesia table view -make_table_view(Node, STable, Lang) -> - Table = misc:binary_to_atom(STable), - TInfo = ejabberd_cluster:call(Node, mnesia, table_info, [Table, all]), - {value, {storage_type, Type}} = lists:keysearch(storage_type, 1, TInfo), - {value, {size, Size}} = lists:keysearch(size, 1, TInfo), - {value, {memory, Memory}} = lists:keysearch(memory, 1, TInfo), - MemoryB = Memory*erlang:system_info(wordsize), - TableInfo = str:format("~p", [TInfo]), - [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Database Tables at ~p"), - [Node]))), - ?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Name")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - STable - )]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Node")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - misc:atom_to_binary(Node) - )]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Storage Type")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - misc:atom_to_binary(Type) - )]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Elements")), - ?XAE(<<"td">>, - [{<<"class">>, <<"alignright">>}], - [?AC(<<"1/">>, - (pretty_string_int(Size)))]) - ]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Memory")), - ?XAC(<<"td">>, [{<<"class">>, <<"alignright">>}], - (pretty_string_int(MemoryB)) - )]) - ])]), - ?XC(<<"pre">>, TableInfo)]. - -make_table_elements_view(Node, STable, Lang, PageNumber) -> +webadmin_node_db_table_page(Node, STable, PageNumber) -> Table = misc:binary_to_atom(STable), TInfo = ejabberd_cluster:call(Node, mnesia, table_info, [Table, all]), {value, {storage_type, Type}} = lists:keysearch(storage_type, 1, TInfo), @@ -1928,10 +1256,7 @@ make_table_elements_view(Node, STable, Lang, PageNumber) -> TableContentErl = get_table_content(Node, Table, Type, PageNumber, PageSize), TableContent = str:format("~p", [TableContentErl]), PagesLinks = build_elements_pages_list(Size, PageNumber, PageSize), - [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Database Tables at ~p"), - [Node]))), - ?P, ?AC(<<"../">>, STable), ?P - ] ++ PagesLinks ++ [?XC(<<"pre">>, TableContent)]. + [?P] ++ PagesLinks ++ [?XC(<<"pre">>, TableContent)]. build_elements_pages_list(Size, PageNumber, PageSize) -> PagesNumber = calculate_pages_number(Size, PageSize), @@ -1963,22 +1288,133 @@ get_table_content(Node, Table, _Type, PageNumber, PageSize) -> || Key <- Keys], lists:flatten(Res). +%% @format-begin + +webadmin_db(Node, [<<"table">>, TableName, <<"details">> | RPath], R, Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = + make_breadcrumb({table_section, Level, Service, TableName, <<"Details">>, RPath}), + Get = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [mnesia_table_details, R, [{<<"table">>, TableName}], []])], + Breadcrumb ++ Get; +webadmin_db(Node, + [<<"table">>, TableName, <<"elements">>, PageNumber | RPath], + R, + Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = + make_breadcrumb({table_section, Level, Service, TableName, <<"Elements">>, RPath}), + Get = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [webadmin_node_db_table_page, + R, + [{<<"node">>, Node}, + {<<"table">>, TableName}, + {<<"page">>, PageNumber}], + []])], + Breadcrumb ++ Get; +webadmin_db(Node, [<<"table">>, TableName, <<"change-storage">> | RPath], R, Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = + make_breadcrumb({table_section, Level, Service, TableName, <<"Change Storage">>, RPath}), + Set = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [mnesia_table_change_storage, R, [{<<"table">>, TableName}], []])], + Breadcrumb ++ Set; +webadmin_db(Node, [<<"table">>, TableName, <<"clear">> | RPath], R, Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = + make_breadcrumb({table_section, Level, Service, TableName, <<"Clear Content">>, RPath}), + Set = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [mnesia_table_clear, + R, + [{<<"table">>, TableName}], + [{style, danger}]])], + Breadcrumb ++ Set; +webadmin_db(Node, [<<"table">>, TableName, <<"destroy">> | RPath], R, Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = + make_breadcrumb({table_section, Level, Service, TableName, <<"Destroy Table">>, RPath}), + Set = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [mnesia_table_destroy, + R, + [{<<"table">>, TableName}], + [{style, danger}]])], + Breadcrumb ++ Set; +webadmin_db(_Node, [<<"table">>, TableName | _RPath], _R, Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = make_breadcrumb({table, Level, Service, TableName}), + MenuItems = + [{<<"details/">>, <<"Details">>}, + {<<"elements/1/">>, <<"Elements">>}, + {<<"change-storage/">>, <<"Change Storage">>}, + {<<"clear/">>, <<"Clear Content">>}, + {<<"destroy/">>, <<"Destroy Table">>}], + Get = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- MenuItems])], + Breadcrumb ++ Get; +webadmin_db(Node, _RPath, R, _Level) -> + Service = <<"Mnesia Tables">>, + Breadcrumb = make_breadcrumb({service, Service}), + Get = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [mnesia_list_tables, + R, + [], + [{result_links, [{name, mnesia_table, 3, <<"">>}]}]])], + Breadcrumb ++ Get. + +make_breadcrumb({service, Service}) -> + make_breadcrumb([Service]); +make_breadcrumb({table, Level, Service, Name}) -> + make_breadcrumb([{Level, Service}, separator, Name]); +make_breadcrumb({table_section, Level, Service, Name, Section, RPath}) -> + make_breadcrumb([{Level, Service}, separator, {Level - 2, Name}, separator, Section + | RPath]); +make_breadcrumb(Elements) -> + lists:map(fun ({xmlel, _, _, _} = Xmlel) -> + Xmlel; + (<<"sort">>) -> + ?C(<<" +">>); + (<<"page">>) -> + ?C(<<" #">>); + (separator) -> + ?C(<<" > ">>); + (Bin) when is_binary(Bin) -> + ?C(Bin); + ({Level, Bin}) when is_integer(Level) and is_binary(Bin) -> + ?AC(binary:copy(<<"../">>, Level), Bin) + end, + Elements). +%% @format-end + %%%================================== %%%% navigation menu -make_navigation(Host, Node, Lang, JID, Level) -> - Menu = make_navigation_menu(Host, Node, Lang, JID, Level), +make_navigation(Host, Node, Username, Lang, JID, Level) -> + Menu = make_navigation_menu(Host, Node, Username, Lang, JID, Level), make_menu_items(Lang, Menu). -spec make_navigation_menu(Host::global | binary(), Node::cluster | atom(), + Username::unspecified | binary(), Lang::binary(), JID::jid(), Level::integer()) -> Menu::{URL::binary(), Title::binary()} | {URL::binary(), Title::binary(), [Menu::any()]}. -make_navigation_menu(Host, Node, Lang, JID, Level) -> +make_navigation_menu(Host, Node, Username, Lang, JID, Level) -> HostNodeMenu = make_host_node_menu(Host, Node, Lang, JID, Level), - HostMenu = make_host_menu(Host, HostNodeMenu, Lang, + HostUserMenu = make_host_user_menu(Host, Username, Lang, + JID, Level), + HostMenu = make_host_menu(Host, HostNodeMenu, HostUserMenu, Lang, JID, Level), NodeMenu = make_node_menu(Host, Node, Lang, Level), make_server_menu(HostMenu, NodeMenu, Lang, JID, Level). @@ -2005,18 +1441,30 @@ make_host_node_menu(Host, Node, Lang, JID, Level) -> || Tuple <- HostNodeFixed, is_allowed_path(Host, Tuple, JID)], {HostNodeBase, iolist_to_binary(atom_to_list(Node)), - HostNodeFixed2}. + lists:keysort(2, HostNodeFixed2)}. -make_host_menu(global, _HostNodeMenu, _Lang, _JID, _Level) -> +make_host_user_menu(global, _, _Lang, _JID, _Level) -> {<<"">>, <<"">>, []}; -make_host_menu(Host, HostNodeMenu, Lang, JID, Level) -> +make_host_user_menu(_, unspecified, _Lang, _JID, _Level) -> + {<<"">>, <<"">>, []}; +make_host_user_menu(Host, Username, Lang, JID, Level) -> + HostNodeBase = get_base_path(Host, Username, Level), + HostNodeFixed = get_menu_items_hook({hostuser, Host, Username}, Lang), + HostNodeFixed2 = [Tuple + || Tuple <- HostNodeFixed, + is_allowed_path(Host, Tuple, JID)], + {HostNodeBase, Username, + lists:keysort(2, HostNodeFixed2)}. + +make_host_menu(global, _HostNodeMenu, _HostUserMenu, _Lang, _JID, _Level) -> + {<<"">>, <<"">>, []}; +make_host_menu(Host, HostNodeMenu, HostUserMenu, Lang, JID, Level) -> HostBase = get_base_path(Host, cluster, Level), - HostFixed = [{<<"users">>, ?T("Users")}, + HostFixed = [{<<"users">>, ?T("Users"), HostUserMenu}, {<<"online-users">>, ?T("Online Users")}], HostFixedAdditional = get_lastactivity_menuitem_list(Host) ++ - [{<<"nodes">>, ?T("Nodes"), HostNodeMenu}, - {<<"stats">>, ?T("Statistics")}] + [{<<"nodes">>, ?T("Nodes"), HostNodeMenu}] ++ get_menu_items_hook({host, Host}, Lang), HostFixedAll = HostFixed ++ lists:keysort(2, HostFixedAdditional), HostFixed2 = [Tuple @@ -2028,10 +1476,8 @@ make_node_menu(_Host, cluster, _Lang, _Level) -> {<<"">>, <<"">>, []}; make_node_menu(global, Node, Lang, Level) -> NodeBase = get_base_path(global, Node, Level), - NodeFixed = [{<<"db">>, ?T("Database")}, - {<<"backup">>, ?T("Backup")}, - {<<"stats">>, ?T("Statistics")}, - {<<"update">>, ?T("Update")}] + NodeFixed = [{<<"db">>, <<"Mnesia Tables">>}, + {<<"backup">>, <<"Mnesia Backup">>}] ++ get_menu_items_hook({node, Node}, Lang), {NodeBase, iolist_to_binary(atom_to_list(Node)), lists:keysort(2, NodeFixed)}; @@ -2042,9 +1488,7 @@ make_server_menu(HostMenu, NodeMenu, Lang, JID, Level) -> Base = get_base_path(global, cluster, Level), Fixed = [{<<"vhosts">>, ?T("Virtual Hosts"), HostMenu}, {<<"nodes">>, ?T("Nodes"), NodeMenu}], - FixedAdditional = - [{<<"stats">>, ?T("Statistics")}] - ++ get_menu_items_hook(server, Lang), + FixedAdditional = get_menu_items_hook(server, Lang), FixedAll = Fixed ++ lists:keysort(2, FixedAdditional), Fixed2 = [Tuple || Tuple <- FixedAll, @@ -2054,6 +1498,9 @@ make_server_menu(HostMenu, NodeMenu, Lang, JID, Level) -> get_menu_items_hook({hostnode, Host, Node}, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_hostnode, Host, [], [Host, Node, Lang]); +get_menu_items_hook({hostuser, Host, Username}, Lang) -> + ejabberd_hooks:run_fold(webadmin_menu_hostuser, Host, + [], [Host, Username, Lang]); get_menu_items_hook({host, Host}, Lang) -> ejabberd_hooks:run_fold(webadmin_menu_host, Host, [], [Host, Lang]); diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 3c3182415..fb7d5d438 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -37,13 +37,14 @@ config_dir/0, get_commands_spec/0]). -export([modules_configs/0, module_ebin_dir/1]). -export([compile_erlang_file/2, compile_elixir_file/2]). --export([web_menu_node/3, web_page_node/5, get_page/3]). +-export([web_menu_node/3, web_page_node/3, webadmin_node_contrib/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). -include("logger.hrl"). -include("translate.hrl"). @@ -924,26 +925,156 @@ parse_details(Body) -> ) ). -web_menu_node(Acc, _Node, Lang) -> - Acc ++ [{<<"contrib">>, translate:translate(Lang, ?T("Contrib Modules"))}]. +%% @format-begin -web_page_node(_, Node, [<<"contrib">>], Query, Lang) -> - Res = rpc:call(Node, ?MODULE, get_page, [Node, Query, Lang]), - {stop, Res}; -web_page_node(Acc, _, _, _, _) -> +web_menu_node(Acc, _Node, _Lang) -> + Acc + ++ [{<<"contrib">>, <<"Contrib Modules (Detailed)">>}, + {<<"contrib-api">>, <<"Contrib Modules (API)">>}]. + +web_page_node(_, + Node, + #request{path = [<<"contrib">>], + q = Query, + lang = Lang} = + R) -> + Title = + ?H1GL(<<"Contrib Modules (Detailed)">>, + <<"../../developer/extending-ejabberd/modules/#ejabberd-contrib">>, + <<"ejabberd-contrib">>), + Res = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [webadmin_node_contrib, + R, + [{<<"node">>, Node}, {<<"query">>, Query}, {<<"lang">>, Lang}], + []])], + {stop, Title ++ Res}; +web_page_node(_, Node, #request{path = [<<"contrib-api">> | RPath]} = R) -> + Title = + ?H1GL(<<"Contrib Modules (API)">>, + <<"../../developer/extending-ejabberd/modules/#ejabberd-contrib">>, + <<"ejabberd-contrib">>), + _TableInstalled = make_table_installed(Node, R, RPath), + _TableAvailable = make_table_available(Node, R, RPath), + TableInstalled = make_table_installed(Node, R, RPath), + TableAvailable = make_table_available(Node, R, RPath), + Res = [?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"specs">>}], <<"Specs">>), + ?XE(<<"blockquote">>, + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [modules_update_specs, R])]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"installed">>}], <<"Installed">>), + ?XE(<<"blockquote">>, + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [modules_installed, R, [], [{only, presentation}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_uninstall, R, [], [{only, presentation}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_upgrade, R, [], [{only, presentation}]]), + TableInstalled]), + ?X(<<"hr">>), + ?XAC(<<"h2">>, [{<<"id">>, <<"available">>}], <<"Available">>), + ?XE(<<"blockquote">>, + [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [modules_available, R, [], [{only, presentation}]]), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_install, R, [], [{only, presentation}]]), + TableAvailable, + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [module_check, R])])], + {stop, Title ++ Res}; +web_page_node(Acc, _, _) -> Acc. -get_page(Node, Query, Lang) -> +make_table_installed(Node, R, RPath) -> + Columns = [<<"Name">>, <<"Summary">>, <<"">>, <<"">>], + ModulesInstalled = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command_raw_value, + [modules_installed, R, []]), + Rows = + lists:map(fun({Name, Summary}) -> + NameBin = misc:atom_to_binary(Name), + Upgrade = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_upgrade, + R, + [{<<"module">>, NameBin}], + [{only, button}, {input_name_append, [NameBin]}]]), + Uninstall = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_uninstall, + R, + [{<<"module">>, NameBin}], + [{only, button}, + {style, danger}, + {input_name_append, [NameBin]}]]), + {?C(NameBin), ?C(list_to_binary(Summary)), Upgrade, Uninstall} + end, + ModulesInstalled), + ejabberd_web_admin:make_table(200, RPath, Columns, Rows). + +make_table_available(Node, R, RPath) -> + Columns = [<<"Name">>, <<"Summary">>, <<"">>], + ModulesAll = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command_raw_value, + [modules_available, R, []]), + ModulesInstalled = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command_raw_value, + [modules_installed, R, []]), + ModulesNotInstalled = + lists:filter(fun({Mod, _}) -> not lists:keymember(Mod, 1, ModulesInstalled) end, + ModulesAll), + Rows = + lists:map(fun({Name, Summary}) -> + NameBin = misc:atom_to_binary(Name), + Install = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [module_install, + R, + [{<<"module">>, NameBin}], + [{only, button}, {input_name_append, [NameBin]}]]), + {?C(NameBin), ?C(list_to_binary(Summary)), Install} + end, + ModulesNotInstalled), + ejabberd_web_admin:make_table(200, RPath, Columns, Rows). + +webadmin_node_contrib(Node, Query, Lang) -> QueryRes = list_modules_parse_query(Query), - Title = ?H1GL(translate:translate(Lang, ?T("Contrib Modules")), - <<"../../developer/extending-ejabberd/modules/#ejabberd-contrib">>, - <<"ejabberd-contrib">>), Contents = get_content(Node, Query, Lang), - Result = case QueryRes of - ok -> [?XREST(?T("Submitted"))]; - nothing -> [] - end, - Title ++ Result ++ Contents. + Result = + case QueryRes of + ok -> + [?XREST(?T("Submitted"))]; + nothing -> + [] + end, + Result ++ Contents. +%% @format-end get_module_home(Module, Attrs) -> case get_module_information(home, Attrs) of diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 8616bdd24..1bfbeb4d1 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -59,7 +59,7 @@ % Roster add_rosteritem/7, delete_rosteritem/4, - get_roster/2, push_roster/3, + get_roster/2, get_roster_count/2, push_roster/3, push_roster_all/1, push_alltoall/2, push_roster_item/5, build_roster_item/3, @@ -67,8 +67,10 @@ private_get/4, private_set/3, % Shared roster - srg_create/5, + srg_create/5, srg_add/2, srg_delete/2, srg_list/1, srg_get_info/2, + srg_set_info/4, + srg_get_displayed/2, srg_add_displayed/3, srg_del_displayed/3, srg_get_members/2, srg_user_add/4, srg_user_del/4, % Send message @@ -80,9 +82,17 @@ % Stats stats/1, stats/2 ]). +-export([web_menu_main/2, web_page_main/2, + web_menu_host/3, web_page_host/3, + web_menu_hostuser/4, web_page_hostuser/4, + web_menu_hostnode/4, web_page_hostnode/4, + web_menu_node/3, web_page_node/3]). +-import(ejabberd_web_admin, [make_command/4, make_table/2]). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -include("mod_roster.hrl"). -include("mod_privacy.hrl"). -include("ejabberd_sm.hrl"). @@ -94,7 +104,17 @@ %%% start(_Host, _Opts) -> - ejabberd_commands:register_commands(?MODULE, get_commands_spec()). + ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, + {hook, webadmin_page_main, web_page_main, 50, global}, + {hook, webadmin_menu_host, web_menu_host, 50}, + {hook, webadmin_page_host, web_page_host, 50}, + {hook, webadmin_menu_hostuser, web_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, web_page_hostuser, 50}, + {hook, webadmin_menu_hostnode, web_menu_hostnode, 50}, + {hook, webadmin_page_hostnode, web_page_hostnode, 50}, + {hook, webadmin_menu_node, web_menu_node, 50, global}, + {hook, webadmin_page_node, web_page_node, 50, global}]}. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of @@ -670,6 +690,16 @@ get_commands_spec() -> {pending, string}, {groups, {list, {group, string}}} ]}}}}}, + #ejabberd_commands{name = get_roster_count, tags = [roster], + desc = "Get number of contacts in a local user roster", + note = "added in 24.xx", + policy = user, + module = ?MODULE, function = get_roster_count, + args = [], + args_rename = [{server, host}], + result_example = 5, + result_desc = "Number", + result = {value, integer}}, #ejabberd_commands{name = push_roster, tags = [roster], desc = "Push template roster from file to a user", longdesc = "The text file must contain an erlang term: a list " @@ -777,6 +807,14 @@ get_commands_spec() -> args_desc = ["Group identifier", "Group server name", "Group name", "Group description", "List of groups to display"], result = {res, rescode}}, + #ejabberd_commands{name = srg_add, tags = [shared_roster_group], + desc = "Add/Create a Shared Roster Group (without details)", + module = ?MODULE, function = srg_add, + note = "added in 24.xx", + args = [{group, binary}, {host, binary}], + args_example = [<<"group3">>, <<"myserver.com">>], + args_desc = ["Group identifier", "Group server name"], + result = {res, rescode}}, #ejabberd_commands{name = srg_delete, tags = [shared_roster_group], desc = "Delete a Shared Roster Group", module = ?MODULE, function = srg_delete, @@ -802,6 +840,48 @@ get_commands_spec() -> result_example = [{<<"name">>, "Group 3"}, {<<"displayed_groups">>, "group1"}], result_desc = "List of group information, as key and value", result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}}, + #ejabberd_commands{name = srg_set_info, tags = [shared_roster_group], + desc = "Set info of a Shared Roster Group", + module = ?MODULE, function = srg_set_info, + note = "added in 24.xx", + args = [{group, binary}, {host, binary}, {key, binary}, {value, binary}], + args_example = [<<"group3">>, <<"myserver.com">>, <<"label">>, <<"Family">>], + args_desc = ["Group identifier", "Group server name", + "Information key: label, description", + "Information value"], + result = {res, rescode}}, + + #ejabberd_commands{name = srg_get_displayed, tags = [shared_roster_group], + desc = "Get displayed groups of a Shared Roster Group", + module = ?MODULE, function = srg_get_displayed, + note = "added in 24.xx", + args = [{group, binary}, {host, binary}], + args_example = [<<"group3">>, <<"myserver.com">>], + args_desc = ["Group identifier", "Group server name"], + result_example = [<<"group1">>, <<"group2">>], + result_desc = "List of groups to display", + result = {display, {list, {group, binary}}}}, + #ejabberd_commands{name = srg_add_displayed, tags = [shared_roster_group], + desc = "Add a group to displayed_groups of a Shared Roster Group", + module = ?MODULE, function = srg_add_displayed, + note = "added in 24.xx", + args = [{group, binary}, {host, binary}, + {add, binary}], + args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>], + args_desc = ["Group identifier", "Group server name", + "Group to add to displayed_groups"], + result = {res, rescode}}, + #ejabberd_commands{name = srg_del_displayed, tags = [shared_roster_group], + desc = "Delete a group from displayed_groups of a Shared Roster Group", + module = ?MODULE, function = srg_del_displayed, + note = "added in 24.xx", + args = [{group, binary}, {host, binary}, + {del, binary}], + args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>], + args_desc = ["Group identifier", "Group server name", + "Group to delete from displayed_groups"], + result = {res, rescode}}, + #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group], desc = "Get members of a Shared Roster Group", module = ?MODULE, function = srg_get_members, @@ -836,6 +916,18 @@ get_commands_spec() -> result_example = 5, result_desc = "Number", result = {value, integer}}, + #ejabberd_commands{name = get_offline_messages, + tags = [internal, offline], + desc = "Get the offline messages", + policy = user, + module = mod_offline, function = get_offline_messages, + args = [], + result = {queue, {list, {messages, {tuple, [{time, string}, + {from, string}, + {to, string}, + {packet, string} + ]}}}}}, + #ejabberd_commands{name = send_message, tags = [stanza], desc = "Send a message to a local or remote bare of full JID", longdesc = "When sending a groupchat message to a MUC room, " @@ -1586,6 +1678,15 @@ make_roster_xmlrpc(Roster) -> end, Roster). +get_roster_count(User, Server) -> + case jid:make(User, Server) of + error -> + throw({error, "Invalid 'user'/'server'"}); + #jid{luser = U, lserver = S} -> + Items = ejabberd_hooks:run_fold(roster_get, S, [], [{U, S}]), + length(Items) + end. + %%----------------------------- %% Push Roster from file %%----------------------------- @@ -1748,12 +1849,29 @@ srg_create(Group, Host, Label, Description, Display) when is_binary(Display) -> srg_create(Group, Host, Label, Description, DisplayList); srg_create(Group, Host, Label, Description, DisplayList) -> + {_DispGroups, WrongDispGroups} = filter_groups_existence(Host, DisplayList), + case (WrongDispGroups -- [Group]) /= [] of + true -> + {wrong_displayed_groups, WrongDispGroups}; + false -> + srg_create2(Group, Host, Label, Description, DisplayList) + end. + +srg_create2(Group, Host, Label, Description, DisplayList) -> Opts = [{label, Label}, {displayed_groups, DisplayList}, {description, Description}], {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts), ok. +srg_add(Group, Host) -> + Opts = [{label, <<"">>}, + {description, <<"">>}, + {displayed_groups, []} + ], + {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts), + ok. + srg_delete(Group, Host) -> {atomic, _} = mod_shared_roster:delete_group(Host, Group), ok. @@ -1769,9 +1887,109 @@ srg_get_info(Group, Host) -> [{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts]. to_list([]) -> []; -to_list([H|T]) -> [to_list(H)|to_list(T)]; +to_list([H|_]=List) when is_binary(H) -> lists:join(", ", [to_list(E) || E <- List]); to_list(E) when is_atom(E) -> atom_to_list(E); -to_list(E) -> binary_to_list(E). +to_list(E) when is_binary(E) -> binary_to_list(E). + +%% @format-begin + +srg_set_info(Group, Host, Key, Value) -> + Opts = + case mod_shared_roster:get_group_opts(Host, Group) of + Os when is_list(Os) -> + Os; + error -> + [] + end, + Opts2 = srg_set_info(Key, Value, Opts), + case mod_shared_roster:set_group_opts(Host, Group, Opts2) of + {atomic, ok} -> + ok; + Problem -> + ?INFO_MSG("Problem: ~n ~p", [Problem]), %+++ + error + end. + +srg_set_info(<<"description">>, Value, Opts) -> + [{description, Value} | proplists:delete(description, Opts)]; +srg_set_info(<<"label">>, Value, Opts) -> + [{label, Value} | proplists:delete(label, Opts)]; +srg_set_info(<<"all_users">>, <<"true">>, Opts) -> + [{all_users, true} | proplists:delete(all_users, Opts)]; +srg_set_info(<<"online_users">>, <<"true">>, Opts) -> + [{online_users, true} | proplists:delete(online_users, Opts)]; +srg_set_info(<<"all_users">>, _, Opts) -> + proplists:delete(all_users, Opts); +srg_set_info(<<"online_users">>, _, Opts) -> + proplists:delete(online_users, Opts); +srg_set_info(Key, _Value, Opts) -> + ?ERROR_MSG("Unknown Key in srg_set_info: ~p", [Key]), + Opts. + +srg_get_displayed(Group, Host) -> + Opts = + case mod_shared_roster:get_group_opts(Host, Group) of + Os when is_list(Os) -> + Os; + error -> + [] + end, + proplists:get_value(displayed_groups, Opts). + +srg_add_displayed(Group, Host, NewGroup) -> + Opts = + case mod_shared_roster:get_group_opts(Host, Group) of + Os when is_list(Os) -> + Os; + error -> + [] + end, + {DispGroups, WrongDispGroups} = filter_groups_existence(Host, [NewGroup]), + case WrongDispGroups /= [] of + true -> + {wrong_displayed_groups, WrongDispGroups}; + false -> + DisplayedOld = proplists:get_value(displayed_groups, Opts, []), + Opts2 = + [{displayed_groups, lists:flatten(DisplayedOld, DispGroups)} + | proplists:delete(displayed_groups, Opts)], + case mod_shared_roster:set_group_opts(Host, Group, Opts2) of + {atomic, ok} -> + ok; + Problem -> + ?INFO_MSG("Problem: ~n ~p", [Problem]), %+++ + error + end + end. + +srg_del_displayed(Group, Host, OldGroup) -> + Opts = + case mod_shared_roster:get_group_opts(Host, Group) of + Os when is_list(Os) -> + Os; + error -> + [] + end, + DisplayedOld = proplists:get_value(displayed_groups, Opts, []), + {DispGroups, OldDispGroups} = lists:partition(fun(G) -> G /= OldGroup end, DisplayedOld), + case OldDispGroups == [] of + true -> + {inexistent_displayed_groups, OldGroup}; + false -> + Opts2 = [{displayed_groups, DispGroups} | proplists:delete(displayed_groups, Opts)], + case mod_shared_roster:set_group_opts(Host, Group, Opts2) of + {atomic, ok} -> + ok; + Problem -> + ?INFO_MSG("Problem: ~n ~p", [Problem]), %+++ + error + end + end. + +filter_groups_existence(Host, Groups) -> + lists:partition(fun(Group) -> error /= mod_shared_roster:get_group_opts(Host, Group) end, + Groups). +%% @format-end srg_get_members(Group, Host) -> Members = mod_shared_roster:get_group_explicit_users(Host,Group), @@ -1915,6 +2133,256 @@ num_prio(Priority) when is_integer(Priority) -> num_prio(_) -> -1. +%%% +%%% Web Admin +%%% + +%% @format-begin + +%%% Main + +web_menu_main(Acc, _Lang) -> + Acc ++ [{<<"stats">>, <<"Statistics">>}]. + +web_page_main(_, #request{path = [<<"stats">>]} = R) -> + Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>) + ++ [make_command(stats_host, R, [], [{only, presentation}]), + make_command(incoming_s2s_number, R, [], [{only, presentation}]), + make_command(outgoing_s2s_number, R, [], [{only, presentation}]), + make_table([<<"stat name">>, {<<"stat value">>, right}], + [{?C(<<"Registered Users:">>), + make_command(stats, + R, + [{<<"name">>, <<"registeredusers">>}], + [{only, value}])}, + {?C(<<"Online Users:">>), + make_command(stats, + R, + [{<<"name">>, <<"onlineusers">>}], + [{only, value}])}, + {?C(<<"S2S Connections Incoming:">>), + make_command(incoming_s2s_number, R, [], [{only, value}])}, + {?C(<<"S2S Connections Outgoing:">>), + make_command(outgoing_s2s_number, R, [], [{only, value}])}])], + {stop, Res}; +web_page_main(Acc, _) -> + Acc. + +%%% Host + +web_menu_host(Acc, _Host, _Lang) -> + Acc ++ [{<<"purge">>, <<"Purge">>}, {<<"stats">>, <<"Statistics">>}]. + +web_page_host(_, Host, #request{path = [<<"purge">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Purge">>)], + Set = [ejabberd_web_admin:make_command(delete_old_users_vhost, + R, + [{<<"host">>, Host}], + [])], + {stop, Head ++ Set}; +web_page_host(_, Host, #request{path = [<<"stats">>]} = R) -> + Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>) + ++ [make_command(stats_host, R, [], [{only, presentation}]), + make_table([<<"stat name">>, {<<"stat value">>, right}], + [{?C(<<"Registered Users:">>), + make_command(stats_host, + R, + [{<<"host">>, Host}, {<<"name">>, <<"registeredusers">>}], + [{only, value}, + {result_links, [{stat, arg_host, 3, <<"users">>}]}])}, + {?C(<<"Online Users:">>), + make_command(stats_host, + R, + [{<<"host">>, Host}, {<<"name">>, <<"onlineusers">>}], + [{only, value}, + {result_links, + [{stat, arg_host, 3, <<"online-users">>}]}])}])], + {stop, Res}; +web_page_host(Acc, _, _) -> + Acc. + +%%% HostUser + +web_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc + ++ [{<<"auth">>, <<"Authentication">>}, + {<<"mam">>, <<"MAM">>}, + {<<"privacy">>, <<"Privacy Lists">>}, + {<<"private">>, <<"Private XML Storage">>}, + {<<"session">>, <<"Sessions">>}, + {<<"vcard">>, <<"vCard">>}]. + +web_page_hostuser(_, Host, User, #request{path = [<<"auth">>]} = R) -> + Ban = make_command(ban_account, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}]), + Unban = make_command(unban_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + Res = ?H1GLraw(<<"Authentication">>, + <<"admin/configuration/authentication/">>, + <<"Authentication">>) + ++ [make_command(register, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(check_account, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + ?X(<<"hr">>), + make_command(check_password, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(check_password_hash, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(change_password, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}]), + ?X(<<"hr">>), + make_command(get_ban_details, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + Ban, + Unban, + ?X(<<"hr">>), + make_command(unregister, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"mam">>]} = R) -> + Res = ?H1GL(<<"MAM">>, <<"modules/#mod_mam">>, <<"mod_mam">>) + ++ [make_command(remove_mam_for_user, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}]), + make_command(remove_mam_for_user_with_peer, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"privacy">>]} = R) -> + Res = ?H1GL(<<"Privacy Lists">>, <<"modules/#mod_privacy">>, <<"mod_privacy">>) + ++ [make_command(privacy_set, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"private">>]} = R) -> + Res = ?H1GL(<<"Private XML Storage">>, <<"modules/#mod_private">>, <<"mod_private">>) + ++ [make_command(private_set, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(private_get, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) -> + Head = [?XC(<<"h1">>, <<"Sessions">>), ?BR], + Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(kick_user, R, [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}]), + make_command(kick_session, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{style, danger}])], + timer:sleep(100), % kicking sessions takes a while, let's delay the get commands + Get = [make_command(user_sessions_info, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{result_links, [{node, node, 5, <<>>}]}]), + make_command(user_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(get_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(num_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Head ++ Get ++ Set}; +web_page_hostuser(_, Host, User, #request{path = [<<"vcard">>]} = R) -> + Head = ?H1GL(<<"vCard">>, <<"modules/#mod_vcard">>, <<"mod_vcard">>), + Set = [make_command(set_nickname, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + timer:sleep(100), % setting vcard takes a while, let's delay the get commands + FieldNames = [<<"VERSION">>, <<"FN">>, <<"NICKNAME">>, <<"BDAY">>], + FieldNames2 = + [{<<"N">>, <<"FAMILY">>}, + {<<"N">>, <<"GIVEN">>}, + {<<"N">>, <<"MIDDLE">>}, + {<<"ADR">>, <<"CTRY">>}, + {<<"ADR">>, <<"LOCALITY">>}, + {<<"EMAIL">>, <<"USERID">>}], + Get = [make_command(get_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + ?XE(<<"blockquote">>, + [make_table([<<"name">>, <<"value">>], + [{?C(FieldName), + make_command(get_vcard, + R, + [{<<"user">>, User}, + {<<"host">>, Host}, + {<<"name">>, FieldName}], + [{only, value}])} + || FieldName <- FieldNames])]), + make_command(get_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + ?XE(<<"blockquote">>, + [make_table([<<"name">>, <<"subname">>, <<"value">>], + [{?C(FieldName), + ?C(FieldSubName), + make_command(get_vcard2, + R, + [{<<"user">>, User}, + {<<"host">>, Host}, + {<<"name">>, FieldName}, + {<<"subname">>, FieldSubName}], + [{only, value}])} + || {FieldName, FieldSubName} <- FieldNames2])]), + make_command(get_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Head ++ Get ++ Set}; +web_page_hostuser(Acc, _, _, _) -> + Acc. + +%%% HostNode + +web_menu_hostnode(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"modules">>, <<"Modules">>}]. + +web_page_hostnode(_, Host, Node, #request{path = [<<"modules">>]} = R) -> + Res = ?H1GLraw(<<"Modules">>, <<"admin/configuration/modules/">>, <<"Modules Options">>) + ++ [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [restart_module, R, [{<<"host">>, Host}], []])], + {stop, Res}; +web_page_hostnode(Acc, _Host, _Node, _Request) -> + Acc. + +%%% Node + +web_menu_node(Acc, _Node, _Lang) -> + Acc ++ [{<<"stats">>, <<"Statistics">>}]. + +web_page_node(_, Node, #request{path = [<<"stats">>]} = R) -> + UpSecs = + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]), + UpDaysBin = integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs)) div 24000), + UpDays = + #xmlel{name = <<"code">>, + attrs = [], + children = [{xmlcdata, UpDaysBin}]}, + Res = ?H1GL(<<"Statistics">>, <<"modules/#mod_stats">>, <<"mod_stats">>) + ++ [make_command(stats, R, [], [{only, presentation}]), + make_table([<<"stat name">>, {<<"stat value">>, right}], + [{?C(<<"Online Users in this node:">>), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stats, + R, + [{<<"name">>, <<"onlineusersnode">>}], + [{only, value}]])}, + {?C(<<"Uptime Seconds:">>), UpSecs}, + {?C(<<"Uptime Seconds (rounded to days):">>), UpDays}, + {?C(<<"Processes:">>), + ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [stats, + R, + [{<<"name">>, <<"processes">>}], + [{only, value}]])}])], + {stop, Res}; +web_page_node(Acc, _, _) -> + Acc. +%% @format-end + +%%% +%%% Document +%%% + mod_options(_) -> []. mod_doc() -> diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index fcca0c337..eb94877d2 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -34,7 +34,7 @@ process_iq/1, get_mix_roster_items/2, webadmin_user/4, - webadmin_page/3]). + webadmin_menu_hostuser/4, webadmin_page_hostuser/4]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -69,7 +69,8 @@ start(Host, Opts) -> ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(roster_get, Host, ?MODULE, get_mix_roster_items, 50), ejabberd_hooks:add(webadmin_user, Host, ?MODULE, webadmin_user, 50), - ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), + ejabberd_hooks:add(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), + ejabberd_hooks:add(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0, ?MODULE, process_iq), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2, ?MODULE, process_iq); Err -> @@ -82,7 +83,8 @@ stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_mix_roster_items, 50), ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, webadmin_user, 50), - ejabberd_hooks:delete(webadmin_page_host, Host, ?MODULE, webadmin_page, 50), + ejabberd_hooks:delete(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), + ejabberd_hooks:delete(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2). @@ -469,7 +471,7 @@ delete_cache(Mod, JID, Channel) -> %%%=================================================================== %%% Webadmin interface %%%=================================================================== -webadmin_user(Acc, User, Server, Lang) -> +webadmin_user(Acc, User, Server, #request{lang = Lang}) -> QueueLen = case get_channels({jid:nodeprep(User), jid:nameprep(Server), <<>>}) of {ok, Channels} -> length(Channels); error -> -1 @@ -482,12 +484,13 @@ webadmin_user(Acc, User, Server, Lang) -> ?C(<<" | ">>), FQueueView]. -webadmin_page(_, Host, - #request{us = _US, path = [<<"user">>, U, <<"mix_channels">>], - lang = Lang} = _Request) -> +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"mix_channels">>, <<"MIX Channels">>}]. + +webadmin_page_hostuser(_, Host, U, #request{path = [<<"mix_channels">>], lang = Lang}) -> Res = web_mix_channels(U, Host, Lang), {stop, Res}; -webadmin_page(Acc, _, _) -> Acc. +webadmin_page_hostuser(Acc, _, _, _) -> Acc. web_mix_channels(User, Server, Lang) -> LUser = jid:nodeprep(User), diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 484700fc6..b9e840022 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -34,20 +34,24 @@ create_room_with_opts/4, create_room/3, destroy_room/2, create_rooms_file/1, destroy_rooms_file/1, rooms_unused_list/2, rooms_unused_destroy/2, - rooms_empty_list/1, rooms_empty_destroy/1, + rooms_empty_list/1, rooms_empty_destroy/1, rooms_empty_destroy_restuple/1, get_user_rooms/2, get_user_subscriptions/2, get_room_occupants/2, get_room_occupants_number/2, send_direct_invitation/5, change_room_option/4, get_room_options/2, set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3, - web_menu_main/2, web_page_main/2, web_menu_host/3, subscribe_room/4, subscribe_room_many/3, unsubscribe_room/2, get_subscribers/2, get_room_serverhost/1, - web_page_host/3, + web_menu_main/2, web_page_main/2, + web_menu_host/3, web_page_host/3, + web_menu_hostuser/4, web_page_hostuser/4, + webadmin_muc/2, mod_opt_type/1, mod_options/1, get_commands_spec/0, find_hosts/1, room_diagnostics/2, get_room_pid/2, get_room_history/2]). +-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/4]). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_muc.hrl"). @@ -66,7 +70,10 @@ start(_Host, _Opts) -> {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, {hook, webadmin_page_main, web_page_main, 50, global}, {hook, webadmin_menu_host, web_menu_host, 50}, - {hook, webadmin_page_host, web_page_host, 50}]}. + {hook, webadmin_page_host, web_page_host, 50}, + {hook, webadmin_menu_hostuser, web_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, web_page_hostuser, 50} + ]}. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of @@ -235,6 +242,19 @@ get_commands_spec() -> args = [{service, binary}], args_rename = [{host, service}], result = {rooms, {list, {room, string}}}}, + #ejabberd_commands{name = rooms_empty_destroy, tags = [muc], + desc = "Destroy the rooms that have no messages in archive", + longdesc = "The MUC service argument can be `global` to get all hosts.", + module = ?MODULE, function = rooms_empty_destroy_restuple, + version = 2, + note = "modified in 24.xx", + args_desc = ["MUC service, or `global` for all"], + args_example = ["conference.example.com"], + result_desc = "List of empty rooms that have been destroyed", + result_example = {ok, <<"Destroyed rooms: 2">>}, + args = [{service, binary}], + args_rename = [{host, service}], + result = {res, restuple}}, #ejabberd_commands{name = get_user_rooms, tags = [muc], desc = "Get the list of rooms where this user is occupant", @@ -478,7 +498,13 @@ get_commands_spec() -> result = {history, {list, {entry, {tuple, [{timestamp, string}, - {message, string}]}}}}} + {message, string}]}}}}}, + + #ejabberd_commands{name = webadmin_muc, tags = [internal], + desc = "Generate WebAdmin MUC Rooms HTML", + module = ?MODULE, function = webadmin_muc, + args = [{request, any}, {lang, binary}], + result = {res, any}} ]. @@ -580,6 +606,8 @@ get_user_subscriptions(User, Server) -> %% Web Admin %%---------------------------- +%% @format-begin + %%--------------- %% Web Admin Menu @@ -589,112 +617,404 @@ web_menu_main(Acc, Lang) -> web_menu_host(Acc, _Host, Lang) -> Acc ++ [{<<"muc">>, translate:translate(Lang, ?T("Multi-User Chat"))}]. - %%--------------- %% Web Admin Page -define(TDTD(L, N), - ?XE(<<"tr">>, [?XCT(<<"td">>, L), - ?XC(<<"td">>, integer_to_binary(N)) - ])). + ?XE(<<"tr">>, [?XCT(<<"td">>, L), ?XC(<<"td">>, integer_to_binary(N))])). -web_page_main(_, #request{path=[<<"muc">>], lang = Lang} = _Request) -> - OnlineRoomsNumber = lists:foldl( - fun(Host, Acc) -> - Acc + mod_muc:count_online_rooms(Host) - end, 0, find_hosts(global)), +web_page_main(_, #request{path = [<<"muc">>], lang = Lang} = R) -> PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), - Res = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++ - [?XCT(<<"h3">>, ?T("Statistics")), - ?XAE(<<"table">>, [], - [?XE(<<"tbody">>, [?TDTD(?T("Total rooms"), OnlineRoomsNumber) - ]) - ]), - ?XE(<<"ul">>, [?LI([?ACT(<<"rooms/">>, ?T("List of rooms"))])]) - ], + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Res = [make_command(webadmin_muc, R, [{<<"request">>, R}, {<<"lang">>, Lang}], [])], + {stop, Title ++ Res}; +web_page_main(Acc, _) -> + Acc. + +web_page_host(_, Host, #request{path = [<<"muc">> | RPath], lang = Lang} = R) -> + PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), + Service = find_service(Host), + Level = length(RPath), + Res = webadmin_muc_host(Host, Service, RPath, R, Lang, Level, PageTitle), {stop, Res}; +web_page_host(Acc, _, _) -> + Acc. -web_page_main(_, #request{path=[<<"muc">>, <<"rooms">>], q = Q, lang = Lang} = _Request) -> - Sort_query = get_sort_query(Q), - Res = make_rooms_page(global, Lang, Sort_query), - {stop, Res}; +%%--------------- +%% WebAdmin MUC Host Page -web_page_main(Acc, _) -> Acc. +webadmin_muc_host(Host, + Service, + [<<"create-room">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Create Room">>, RPath}), + Set = [make_command(create_room, R, [{<<"service">>, Service}, {<<"host">>, Host}], []), + make_command(create_room_with_opts, + R, + [{<<"service">>, Service}, {<<"host">>, Host}], + [])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"nick-register">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({service_section, Level, Service, <<"Nick Register">>, RPath}), + Set = [make_command(muc_register_nick, R, [{<<"service">>, Service}], []), + make_command(muc_unregister_nick, R, [{<<"service">>, Service}], [])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms-empty">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Rooms Empty">>, RPath}), + Set = [make_command(rooms_empty_list, + R, + [{<<"service">>, Service}], + [{table_options, {2, RPath}}, + {result_links, [{room, room, 3 + Level, <<"">>}]}]), + make_command(rooms_empty_destroy, R, [{<<"service">>, Service}], [])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms-unused">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({service_section, Level, Service, <<"Rooms Unused">>, RPath}), + Set = [make_command(rooms_unused_list, + R, + [{<<"service">>, Service}], + [{result_links, [{room, room, 3 + Level, <<"">>}]}]), + make_command(rooms_unused_destroy, R, [{<<"service">>, Service}], [])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms-regex">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({service_section, Level, Service, <<"Rooms by Regex">>, RPath}), + Set = [make_command(muc_online_rooms_by_regex, + R, + [{<<"service">>, Service}], + [{result_links, [{jid, room, 3 + Level, <<"">>}]}])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"affiliations">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Affiliations">>, Name, R, RPath}), + Set = [make_command(set_room_affiliation, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [])], + Get = [make_command(get_room_affiliations, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{table_options, {20, RPath}}])], + Title ++ Breadcrumb ++ Get ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"history">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"History">>, Name, R, RPath}), + Get = [make_command(get_room_history, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{table_options, {10, RPath}}, + {result_links, [{message, paragraph, 1, <<"">>}]}])], + Title ++ Breadcrumb ++ Get; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"invite">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Invite">>, Name, R, RPath}), + Set = [make_command(send_direct_invitation, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"occupants">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Occupants">>, Name, R, RPath}), + Get = [make_command(get_room_occupants, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{table_options, {20, RPath}}, + {result_links, [{jid, user, 3 + Level, <<"">>}]}])], + Title ++ Breadcrumb ++ Get; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"options">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Options">>, Name, R, RPath}), + Set = [make_command(change_room_option, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [])], + Get = [make_command(get_room_options, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [])], + Title ++ Breadcrumb ++ Get ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"subscribers">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = + ?H1GLraw(PageTitle, + <<"developer/xmpp-clients-bots/extensions/muc-sub/">>, + <<"MUC/Sub Extension">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Subscribers">>, Name, R, RPath}), + Set = [make_command(subscribe_room, + R, + [{<<"room">>, jid:encode({Name, Service, <<"">>})}], + []), + make_command(unsubscribe_room, + R, + [{<<"room">>, jid:encode({Name, Service, <<"">>})}], + [{style, danger}])], + Get = [make_command(get_subscribers, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{table_options, {20, RPath}}, + {result_links, [{jid, user, 3 + Level, <<"">>}]}])], + Title ++ Breadcrumb ++ Get ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name, <<"destroy">> | RPath], + R, + _Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = + make_breadcrumb({room_section, Level, Service, <<"Destroy">>, Name, R, RPath}), + Set = [make_command(destroy_room, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{style, danger}])], + Title ++ Breadcrumb ++ Set; +webadmin_muc_host(_Host, + Service, + [<<"rooms">>, <<"room">>, Name | _RPath], + _R, + Lang, + Level, + PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = make_breadcrumb({room, Level, Service, Name}), + MenuItems = + [{<<"affiliations/">>, <<"Affiliations">>}, + {<<"history/">>, <<"History">>}, + {<<"invite/">>, <<"Invite">>}, + {<<"occupants/">>, <<"Occupants">>}, + {<<"options/">>, <<"Options">>}, + {<<"subscribers/">>, <<"Subscribers">>}, + {<<"destroy/">>, <<"Destroy">>}], + Get = [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- MenuItems])], + Title ++ Breadcrumb ++ Get; +webadmin_muc_host(_Host, Service, [<<"rooms">> | RPath], R, _Lang, Level, PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = make_breadcrumb({service_section, Level, Service, <<"Rooms">>, RPath}), + Columns = [<<"jid">>, <<"occupants">>], + Rows = + lists:map(fun(NameService) -> + #jid{user = Name} = jid:decode(NameService), + {make_command(echo, + R, + [{<<"sentence">>, jid:encode({Name, Service, <<"">>})}], + [{only, raw_and_value}, + {result_links, [{sentence, room, 3 + Level, <<"">>}]}]), + make_command(get_room_occupants_number, + R, + [{<<"name">>, Name}, {<<"service">>, Service}], + [{only, raw_and_value}])} + end, + make_command_raw_value(muc_online_rooms, R, [{<<"service">>, Service}])), + Get = [make_command(muc_online_rooms, R, [], [{only, presentation}]), + make_command(get_room_occupants_number, R, [], [{only, presentation}]), + make_table(20, RPath, Columns, Rows)], + Title ++ Breadcrumb ++ Get; +webadmin_muc_host(_Host, Service, [], _R, Lang, _Level, PageTitle) -> + Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), + Breadcrumb = make_breadcrumb({service, Service}), + MenuItems = + [{<<"create-room/">>, <<"Create Room">>}, + {<<"rooms/">>, <<"Rooms">>}, + {<<"rooms-regex/">>, <<"Rooms by Regex">>}, + {<<"rooms-empty/">>, <<"Rooms Empty">>}, + {<<"rooms-unused/">>, <<"Rooms Unused">>}, + {<<"nick-register/">>, <<"Nick Register">>}], + Get = [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- MenuItems])], + Title ++ Breadcrumb ++ Get; +webadmin_muc_host(_Host, _Service, _RPath, _R, _Lang, _Level, _PageTitle) -> + []. -web_page_host(_, Host, - #request{path = [<<"muc">>], - q = Q, - lang = Lang} = _Request) -> - Sort_query = get_sort_query(Q), - Res = make_rooms_page(Host, Lang, Sort_query), - {stop, Res}; -web_page_host(Acc, _, _) -> Acc. +make_breadcrumb({service, Service}) -> + make_breadcrumb([Service]); +make_breadcrumb({service_section, Level, Service, Section, RPath}) -> + make_breadcrumb([{Level, Service}, separator, Section | RPath]); +make_breadcrumb({room, Level, Service, Name}) -> + make_breadcrumb([{Level, Service}, + separator, + {Level - 1, <<"Rooms">>}, + separator, + jid:encode({Name, Service, <<"">>})]); +make_breadcrumb({room_section, Level, Service, Section, Name, R, RPath}) -> + make_breadcrumb([{Level, Service}, + separator, + {Level - 1, <<"Rooms">>}, + separator, + make_command(echo, + R, + [{<<"sentence">>, jid:encode({Name, Service, <<"">>})}], + [{only, value}, + {result_links, [{sentence, room, 3 + Level, <<"">>}]}]), + separator, + Section + | RPath]); +make_breadcrumb(Elements) -> + lists:map(fun ({xmlel, _, _, _} = Xmlel) -> + Xmlel; + (<<"sort">>) -> + ?C(<<" +">>); + (<<"page">>) -> + ?C(<<" #">>); + (separator) -> + ?C(<<" > ">>); + (Bin) when is_binary(Bin) -> + ?C(Bin); + ({Level, Bin}) when is_integer(Level) and is_binary(Bin) -> + ?AC(binary:copy(<<"../">>, Level), Bin) + end, + Elements). +%%--------------- +%% %% Returns: {normal | reverse, Integer} get_sort_query(Q) -> case catch get_sort_query2(Q) of - {ok, Res} -> Res; - _ -> {normal, 1} + {ok, Res} -> + Res; + _ -> + {normal, 1} end. get_sort_query2(Q) -> {value, {_, Binary}} = lists:keysearch(<<"sort">>, 1, Q), Integer = list_to_integer(string:strip(binary_to_list(Binary), right, $/)), case Integer >= 0 of - true -> {ok, {normal, Integer}}; - false -> {ok, {reverse, abs(Integer)}} + true -> + {ok, {normal, Integer}}; + false -> + {ok, {reverse, abs(Integer)}} end. -make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) -> +webadmin_muc(#request{q = Q} = R, Lang) -> + {Sort_direction, Sort_column} = get_sort_query(Q), + Host = global, Service = find_service(Host), Rooms_names = get_online_rooms(Service), Rooms_infos = build_info_rooms(Rooms_names), Rooms_sorted = sort_rooms(Sort_direction, Sort_column, Rooms_infos), Rooms_prepared = prepare_rooms_infos(Rooms_sorted), - TList = lists:map( - fun(Room) -> - ?XE(<<"tr">>, [?XC(<<"td">>, E) || E <- Room]) - end, Rooms_prepared), - Titles = [?T("Jabber ID"), - ?T("# participants"), - ?T("Last message"), - ?T("Public"), - ?T("Persistent"), - ?T("Logging"), - ?T("Just created"), - ?T("Room title"), - ?T("Node")], + TList = + lists:map(fun([RoomJid | Room]) -> + JidLink = + make_command(echo, + R, + [{<<"sentence">>, RoomJid}], + [{only, value}, + {result_links, [{sentence, room, 1, <<"">>}]}]), + ?XE(<<"tr">>, [?XE(<<"td">>, [JidLink]) | [?XC(<<"td">>, E) || E <- Room]]) + end, + Rooms_prepared), + Titles = + [?T("Jabber ID"), + ?T("# participants"), + ?T("Last message"), + ?T("Public"), + ?T("Persistent"), + ?T("Logging"), + ?T("Just created"), + ?T("Room title"), + ?T("Node")], {Titles_TR, _} = - lists:mapfoldl( - fun(Title, Num_column) -> - NCS = integer_to_binary(Num_column), - TD = ?XE(<<"td">>, [?CT(Title), - ?C(<<" ">>), - ?AC(<<"?sort=", NCS/binary>>, <<"<">>), - ?C(<<" ">>), - ?AC(<<"?sort=-", NCS/binary>>, <<">">>)]), - {TD, Num_column+1} - end, - 1, - Titles), - PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), - ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>) ++ + lists:mapfoldl(fun(Title, Num_column) -> + NCS = integer_to_binary(Num_column), + TD = ?XE(<<"td">>, + [?CT(Title), + ?C(<<" ">>), + ?AC(<<"?sort=", NCS/binary>>, <<"<">>), + ?C(<<" ">>), + ?AC(<<"?sort=-", NCS/binary>>, <<">">>)]), + {TD, Num_column + 1} + end, + 1, + Titles), [?XCT(<<"h2">>, ?T("Chatrooms")), ?XE(<<"table">>, - [?XE(<<"thead">>, - [?XE(<<"tr">>, Titles_TR)] - ), - ?XE(<<"tbody">>, TList) - ] - ) - ]. + [?XE(<<"thead">>, [?XE(<<"tr">>, Titles_TR)]), ?XE(<<"tbody">>, TList)])]. sort_rooms(Direction, Column, Rooms) -> Rooms2 = lists:keysort(Column, Rooms), case Direction of - normal -> Rooms2; - reverse -> lists:reverse(Rooms2) + normal -> + Rooms2; + reverse -> + lists:reverse(Rooms2) end. build_info_rooms(Rooms) -> @@ -712,16 +1032,16 @@ build_info_room({Name, Host, _ServerHost, Pid}) -> Num_participants = maps:size(S#state.users), Node = node(Pid), - History = (S#state.history)#lqueue.queue, + History = S#state.history#lqueue.queue, Ts_last_message = - case p1_queue:is_empty(History) of - true -> - <<"A long time ago">>; - false -> - Last_message1 = get_queue_last(History), - {_, _, _, Ts_last, _} = Last_message1, - xmpp_util:encode_timestamp(Ts_last) - end, + case p1_queue:is_empty(History) of + true -> + <<"A long time ago">>; + false -> + Last_message1 = get_queue_last(History), + {_, _, _, Ts_last, _} = Last_message1, + xmpp_util:encode_timestamp(Ts_last) + end, {<>, Num_participants, @@ -739,6 +1059,7 @@ get_queue_last(Queue) -> prepare_rooms_infos(Rooms) -> [prepare_room_info(Room) || Room <- Rooms]. + prepare_room_info(Room_info) -> {NameHost, Num_participants, @@ -748,7 +1069,8 @@ prepare_room_info(Room_info) -> Logging, Just_created, Title, - Node} = Room_info, + Node} = + Room_info, [NameHost, integer_to_binary(Num_participants), Ts_last_message, @@ -763,10 +1085,61 @@ justcreated_to_binary(J) when is_integer(J) -> JNow = misc:usec_to_now(J), {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(JNow), str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", - [Year, Month, Day, Hour, Minute, Second]); + [Year, Month, Day, Hour, Minute, Second]); justcreated_to_binary(J) when is_atom(J) -> misc:atom_to_binary(J). +%%-------------------- +%% Web Admin Host User + +web_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc + ++ [{<<"muc-rooms">>, <<"MUC Rooms Online">>}, + {<<"muc-affiliations">>, <<"MUC Rooms Affiliations">>}, + {<<"muc-sub">>, <<"MUC Rooms Subscriptions">>}, + {<<"muc-register">>, <<"MUC Service Registration">>}]. + +web_page_hostuser(_, Host, User, #request{path = [<<"muc-rooms">> | RPath]} = R) -> + Level = 5 + length(RPath), + Res = ?H1GL(<<"MUC Rooms Online">>, <<"modules/#mod_muc">>, <<"mod_muc">>) + ++ [make_command(get_user_rooms, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{table_options, {2, RPath}}, + {result_links, [{room, room, Level, <<"">>}]}])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"muc-affiliations">>]} = R) -> + Jid = jid:encode( + jid:make(User, Host)), + Res = ?H1GL(<<"MUC Rooms Affiliations">>, <<"modules/#mod_muc">>, <<"mod_muc">>) + ++ [make_command(set_room_affiliation, R, [{<<"jid">>, Jid}], []), + make_command(get_room_affiliation, R, [{<<"jid">>, Jid}], [])], + {stop, Res}; +web_page_hostuser(_, Host, User, #request{path = [<<"muc-sub">> | RPath]} = R) -> + Title = + ?H1GLraw(<<"MUC Rooms Subscriptions">>, + <<"developer/xmpp-clients-bots/extensions/muc-sub/">>, + <<"MUC/Sub">>), + Level = 5 + length(RPath), + Set = [make_command(subscribe_room, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(unsubscribe_room, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + Get = [make_command(get_user_subscriptions, + R, + [{<<"user">>, User}, {<<"host">>, Host}], + [{table_options, {20, RPath}}, + {result_links, [{roomjid, room, Level, <<"">>}]}])], + {stop, Title ++ Get ++ Set}; +web_page_hostuser(_, Host, User, #request{path = [<<"muc-register">>]} = R) -> + Jid = jid:encode( + jid:make(User, Host)), + Res = ?H1GL(<<"MUC Service Registration">>, <<"modules/#mod_muc">>, <<"mod_muc">>) + ++ [make_command(muc_register_nick, R, [{<<"jid">>, Jid}], []), + make_command(muc_unregister_nick, R, [{<<"jid">>, Jid}], [])], + {stop, Res}; +web_page_hostuser(Acc, _, _, _) -> + Acc. +%% @format-end + %%---------------------------- %% Create/Delete Room %%---------------------------- @@ -898,6 +1271,10 @@ rooms_empty_list(Service) -> rooms_empty_destroy(Service) -> rooms_report(empty, destroy, Service, 0). +rooms_empty_destroy_restuple(Service) -> + DestroyedRooms = rooms_report(empty, destroy, Service, 0), + NumberBin = integer_to_binary(length(DestroyedRooms)), + {ok, <<"Destroyed rooms: ", NumberBin/binary>>}. rooms_report(Method, Action, Service, Days) -> {NA, NP, RP} = muc_unused(Method, Action, Service, Days), @@ -1413,7 +1790,8 @@ get_room_history(Name, Service) -> History = p1_queue:to_list((StateData#state.history)#lqueue.queue), lists:map( fun({_Nick, Packet, _HaveSubject, TimeStamp, _Size}) -> - {xmpp_util:encode_timestamp(TimeStamp), fxml:element_to_binary(xmpp:encode(Packet))} + {xmpp_util:encode_timestamp(TimeStamp), + ejabberd_web_admin:pretty_print_xml(xmpp:encode(Packet))} end, History); _ -> throw({error, "Unable to fetch room state."}) diff --git a/src/mod_offline.erl b/src/mod_offline.erl index f50452876..e8c8c52bf 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -60,13 +60,17 @@ find_x_expire/2, c2s_handle_info/2, c2s_copy_session/2, - webadmin_page/3, + get_offline_messages/2, + webadmin_menu_hostuser/4, + webadmin_page_hostuser/4, webadmin_user/4, webadmin_user_parse_query/5, c2s_handle_bind2_inline/1]). -export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). +-import(ejabberd_web_admin, [make_command/4, make_command/2]). + -deprecated({get_queue_length,2}). -include("logger.hrl"). @@ -133,7 +137,8 @@ start(Host, Opts) -> {hook, c2s_handle_info, c2s_handle_info, 50}, {hook, c2s_copy_session, c2s_copy_session, 50}, {hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50}, - {hook, webadmin_page_host, webadmin_page, 50}, + {hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, {hook, webadmin_user, webadmin_user, 50}, {hook, webadmin_user_parse_query, webadmin_user_parse_query, 50}, {iq_handler, ejabberd_sm, ?NS_FLEX_OFFLINE, handle_offline_query}]}. @@ -730,12 +735,39 @@ discard_warn_sender(Packet, Reason) -> ok end. -webadmin_page(_, Host, - #request{us = _US, path = [<<"user">>, U, <<"queue">>], - q = Query, lang = Lang} = - _Request) -> - Res = user_queue(U, Host, Query, Lang), {stop, Res}; -webadmin_page(Acc, _, _) -> Acc. +%%% +%%% Commands +%%% + +get_offline_messages(User, Server) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, ?MODULE), + HdrsAll = case Mod:read_message_headers(LUser, LServer) of + error -> []; + L -> L + end, + format_user_queue(HdrsAll). + +%%% +%%% WebAdmin +%%% + +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"queue">>, <<"Offline Queue">>}]. + +webadmin_page_hostuser(_, Host, U, + #request{us = _US, path = [<<"queue">> | RPath], + lang = Lang} = R) -> + US = {U, Host}, + PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]), + Head = ?H1GL(PageTitle, <<"modules/#mod_offline">>, <<"mod_offline">>), + Res = make_command(get_offline_messages, R, [{<<"user">>, U}, + {<<"host">>, Host}], + [{table_options, {10, RPath}}, + {result_links, [{packet, paragraph, 1, <<"">>}]}]), + {stop, Head ++ [Res]}; +webadmin_page_hostuser(Acc, _, _, _) -> Acc. get_offline_els(LUser, LServer) -> [Packet || {_Seq, Packet} <- read_messages(LUser, LServer)]. @@ -939,8 +971,7 @@ count_mam_messages(LUser, LServer, ReadMsgs) -> format_user_queue(Hdrs) -> lists:map( - fun({Seq, From, To, TS, El}) -> - ID = integer_to_binary(Seq), + fun({_Seq, From, To, TS, El}) -> FPacket = ejabberd_web_admin:pretty_print_xml(El), SFrom = jid:encode(From), STo = jid:encode(To), @@ -956,14 +987,7 @@ format_user_queue(Hdrs) -> {_, _, _} = Now -> format_time(Now) end, - ?XE(<<"tr">>, - [?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], - [?INPUT(<<"checkbox">>, <<"selected">>, ID)]), - ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], Time), - ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], SFrom), - ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], STo), - ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], - [?XC(<<"pre">>, FPacket)])]) + {Time, SFrom, STo, FPacket} end, Hdrs). format_time(Now) -> @@ -971,111 +995,18 @@ format_time(Now) -> str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second]). -user_queue(User, Server, Query, Lang) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - Mod = gen_mod:db_mod(LServer, ?MODULE), - user_queue_parse_query(LUser, LServer, Query), - HdrsAll = case Mod:read_message_headers(LUser, LServer) of - error -> []; - L -> L - end, - Hdrs = get_messages_subset(User, Server, HdrsAll), - FMsgs = format_user_queue(Hdrs), - PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]), - (?H1GL(PageTitle, <<"modules/#mod_offline">>, <<"mod_offline">>)) - ++ [?XREST(?T("Submitted"))] ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [?XE(<<"table">>, - [?XE(<<"thead">>, - [?XE(<<"tr">>, - [?X(<<"td">>), ?XCT(<<"td">>, ?T("Time")), - ?XCT(<<"td">>, ?T("From")), - ?XCT(<<"td">>, ?T("To")), - ?XCT(<<"td">>, ?T("Packet"))])]), - ?XE(<<"tbody">>, - if FMsgs == [] -> - [?XE(<<"tr">>, - [?XAC(<<"td">>, [{<<"colspan">>, <<"4">>}], - <<" ">>)])]; - true -> FMsgs - end)]), - ?BR, - ?INPUTTD(<<"submit">>, <<"delete">>, - ?T("Delete Selected"))])]. - -user_queue_parse_query(LUser, LServer, Query) -> - Mod = gen_mod:db_mod(LServer, ?MODULE), - case lists:keysearch(<<"delete">>, 1, Query) of - {value, _} -> - case user_queue_parse_query(LUser, LServer, Query, Mod, false) of - true -> - flush_cache(Mod, LUser, LServer); - false -> - ok - end; - _ -> - ok - end. - -user_queue_parse_query(LUser, LServer, Query, Mod, Acc) -> - case lists:keytake(<<"selected">>, 1, Query) of - {value, {_, Seq}, Query2} -> - NewAcc = case catch binary_to_integer(Seq) of - I when is_integer(I), I>=0 -> - Mod:remove_message(LUser, LServer, I), - true; - _ -> - Acc - end, - user_queue_parse_query(LUser, LServer, Query2, Mod, NewAcc); - false -> - Acc - end. - us_to_list({User, Server}) -> jid:encode({User, Server, <<"">>}). get_queue_length(LUser, LServer) -> count_offline_messages(LUser, LServer). -get_messages_subset(User, Host, MsgsAll) -> - MaxOfflineMsgs = case get_max_user_messages(User, Host) of - Number when is_integer(Number) -> Number; - _ -> 100 - end, - Length = length(MsgsAll), - get_messages_subset2(MaxOfflineMsgs, Length, MsgsAll). +webadmin_user(Acc, User, Server, R) -> + Acc ++ [make_command(get_offline_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])]. -get_messages_subset2(Max, Length, MsgsAll) when Length =< Max * 2 -> - MsgsAll; -get_messages_subset2(Max, Length, MsgsAll) -> - FirstN = Max, - {MsgsFirstN, Msgs2} = lists:split(FirstN, MsgsAll), - MsgsLastN = lists:nthtail(Length - FirstN - FirstN, - Msgs2), - NoJID = jid:make(<<"...">>, <<"...">>), - Seq = <<"0">>, - IntermediateMsg = #xmlel{name = <<"...">>, attrs = [], - children = []}, - MsgsFirstN ++ [{Seq, NoJID, NoJID, IntermediateMsg}] ++ MsgsLastN. - -webadmin_user(Acc, User, Server, Lang) -> - QueueLen = count_offline_messages(jid:nodeprep(User), - jid:nameprep(Server)), - FQueueLen = ?C(integer_to_binary(QueueLen)), - FQueueView = ?AC(<<"queue/">>, - ?T("View Queue")), - Acc ++ - [?XCT(<<"h3">>, ?T("Offline Messages:")), - FQueueLen, - ?C(<<" | ">>), - FQueueView, - ?C(<<" | ">>), - ?INPUTTD(<<"submit">>, <<"removealloffline">>, - ?T("Remove All Offline Messages"))]. +%%% +%%% +%%% -spec delete_all_msgs(binary(), binary()) -> {atomic, any()}. delete_all_msgs(User, Server) -> diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 498146d63..902986943 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -46,13 +46,16 @@ import_start/2, import_stop/2, is_subscribed/2, c2s_self_presence/1, in_subscription/2, out_subscription/1, set_items/3, remove_user/2, - get_jid_info/4, encode_item/1, webadmin_page/3, - webadmin_user/4, get_versioning_feature/2, + get_jid_info/4, encode_item/1, get_versioning_feature/2, roster_version/2, mod_doc/0, mod_opt_type/1, mod_options/1, set_roster/1, del_roster/3, process_rosteritems/5, depends/2, set_item_and_notify_clients/3]). +-export([webadmin_page_hostuser/4, webadmin_menu_hostuser/4, webadmin_user/4]). + +-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/4]). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_roster.hrl"). @@ -98,7 +101,8 @@ start(Host, Opts) -> {hook, remove_user, remove_user, 50}, {hook, c2s_self_presence, c2s_self_presence, 50}, {hook, c2s_post_auth_features, get_versioning_feature, 50}, - {hook, webadmin_page_host, webadmin_page, 50}, + {hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, {hook, webadmin_user, webadmin_user, 50}, {iq_handler, ejabberd_sm, ?NS_ROSTER, process_iq}]}. @@ -1016,205 +1020,84 @@ process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -webadmin_page(_, Host, - #request{us = _US, path = [<<"user">>, U, <<"roster">>], - q = Query, lang = Lang} = - _Request) -> - Res = user_roster(U, Host, Query, Lang), {stop, Res}; -webadmin_page(Acc, _, _) -> Acc. +%%% @format-begin -user_roster(User, Server, Query, Lang) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, - Items1 = get_roster(LUser, LServer), - Res = user_roster_parse_query(User, Server, Items1, - Query), - Items = get_roster(LUser, LServer), - SItems = lists:sort(Items), - FItems = case SItems of - [] -> [?CT(?T("None"))]; - _ -> - [?XE(<<"table">>, - [?XE(<<"thead">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Jabber ID")), - ?XCT(<<"td">>, ?T("Nickname")), - ?XCT(<<"td">>, ?T("Subscription")), - ?XCT(<<"td">>, ?T("Pending")), - ?XCT(<<"td">>, ?T("Groups"))])]), - ?XE(<<"tbody">>, - (lists:map(fun (R) -> - Groups = lists:flatmap(fun - (Group) -> - [?C(Group), - ?BR] - end, - R#roster.groups), - Pending = - ask_to_pending(R#roster.ask), - TDJID = - build_contact_jid_td(R#roster.jid), - ?XE(<<"tr">>, - [TDJID, - ?XAC(<<"td">>, - [{<<"class">>, - <<"valign">>}], - (R#roster.name)), - ?XAC(<<"td">>, - [{<<"class">>, - <<"valign">>}], - (iolist_to_binary(atom_to_list(R#roster.subscription)))), - ?XAC(<<"td">>, - [{<<"class">>, - <<"valign">>}], - (iolist_to_binary(atom_to_list(Pending)))), - ?XAE(<<"td">>, - [{<<"class">>, - <<"valign">>}], - Groups), - if Pending == in -> - ?XAE(<<"td">>, - [{<<"class">>, - <<"valign">>}], - [?INPUTT(<<"submit">>, - <<"validate", - (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, - ?T("Validate"))]); - true -> ?X(<<"td">>) - end, - ?XAE(<<"td">>, - [{<<"class">>, - <<"valign">>}], - [?INPUTTD(<<"submit">>, - <<"remove", - (ejabberd_web_admin:term_to_id(R#roster.jid))/binary>>, - ?T("Remove"))])]) - end, - SItems)))])] - end, - PageTitle = str:translate_and_format(Lang, ?T("Roster of ~ts"), [us_to_list(US)]), - (?H1GL(PageTitle, <<"modules/#mod_roster">>, <<"mod_roster">>)) - ++ - case Res of - ok -> [?XREST(?T("Submitted"))]; - error -> [?XREST(?T("Bad format"))]; - nothing -> [] - end - ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - ( [?P, ?INPUT(<<"text">>, <<"newjid">>, <<"">>), - ?C(<<" ">>), - ?INPUTT(<<"submit">>, <<"addjid">>, - ?T("Add Jabber ID"))] - ++ FItems))]. +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"roster">>, <<"Roster">>}]. -build_contact_jid_td(RosterJID) -> - ContactJID = jid:make(RosterJID), - JIDURI = case {ContactJID#jid.luser, - ContactJID#jid.lserver} - of - {<<"">>, _} -> <<"">>; - {CUser, CServer} -> - case lists:member(CServer, ejabberd_option:hosts()) of - false -> <<"">>; - true -> - <<"../../../../../server/", CServer/binary, "/user/", - CUser/binary, "/">> - end - end, - case JIDURI of - <<>> -> - ?XAC(<<"td">>, [{<<"class">>, <<"valign">>}], - (jid:encode(RosterJID))); - URI when is_binary(URI) -> - ?XAE(<<"td">>, [{<<"class">>, <<"valign">>}], - [?AC(JIDURI, (jid:encode(RosterJID)))]) - end. +webadmin_page_hostuser(_, Host, Username, #request{path = [<<"roster">> | RPath]} = R) -> + Head = ?H1GL(<<"Roster">>, <<"modules/#mod_roster">>, <<"mod_roster">>), + %% Execute twice: first to perform the action, the second to get new roster + _ = make_webadmin_roster_table(Host, Username, R, RPath), + RV2 = make_webadmin_roster_table(Host, Username, R, RPath), + Set = [make_command(add_rosteritem, + R, + [{<<"localuser">>, Username}, {<<"localhost">>, Host}], + []), + make_command(push_roster, R, [{<<"user">>, Username}, {<<"host">>, Host}], [])], + Get = [make_command(get_roster, R, [], [{only, presentation}]), + make_command(delete_rosteritem, R, [], [{only, presentation}]), + RV2], + {stop, Head ++ Get ++ Set}; +webadmin_page_hostuser(Acc, _, _, _) -> + Acc. -user_roster_parse_query(User, Server, Items, Query) -> - case lists:keysearch(<<"addjid">>, 1, Query) of - {value, _} -> - case lists:keysearch(<<"newjid">>, 1, Query) of - {value, {_, SJID}} -> - try jid:decode(SJID) of - JID -> - user_roster_subscribe_jid(User, Server, JID), ok - catch _:{bad_jid, _} -> - error - end; - false -> error - end; - false -> - case catch user_roster_item_parse_query(User, Server, - Items, Query) - of - submitted -> ok; - {'EXIT', _Reason} -> error; - _ -> nothing - end - end. +make_webadmin_roster_table(Host, Username, R, RPath) -> + Contacts = + case make_command_raw_value(get_roster, R, [{<<"user">>, Username}, {<<"host">>, Host}]) + of + Cs when is_list(Cs) -> + Cs; + _ -> + [] + end, + Level = 5 + length(RPath), + Columns = + [<<"jid">>, <<"nick">>, <<"subscription">>, <<"pending">>, <<"groups">>, <<"">>], + Rows = + lists:map(fun({Jid, Nick, Subscriptions, Pending, Groups}) -> + {JidSplit, ProblematicBin} = + try jid:decode(Jid) of + #jid{} = J -> + {jid:split(J), <<"">>} + catch + _:{bad_jid, _} -> + ?INFO_MSG("Error parsing contact of ~s@~s that is invalid JID: ~s", + [Username, Host, Jid]), + {{<<"000--error-parsing-jid">>, <<"localhost">>, <<"">>}, + <<", Error parsing JID: ", Jid/binary>>} + end, + {make_command(echo, + R, + [{<<"sentence">>, jid:encode(JidSplit)}], + [{only, raw_and_value}, + {result_links, [{sentence, user, Level, <<"">>}]}]), + ?C(<>), + ?C(Subscriptions), + ?C(Pending), + ?C(Groups), + make_command(delete_rosteritem, + R, + [{<<"localuser">>, Username}, + {<<"localhost">>, Host}, + {<<"user">>, element(1, JidSplit)}, + {<<"host">>, element(2, JidSplit)}], + [{only, button}, + {style, danger}, + {input_name_append, + [Username, + Host, + element(1, JidSplit), + element(2, JidSplit)]}])} + end, + lists:keysort(1, Contacts)), + Table = make_table(20, RPath, Columns, Rows), + ?XE(<<"blockquote">>, [Table]). -user_roster_subscribe_jid(User, Server, JID) -> - UJID = jid:make(User, Server), - Presence = #presence{from = UJID, to = JID, type = subscribe}, - out_subscription(Presence), - ejabberd_router:route(Presence). - -user_roster_item_parse_query(User, Server, Items, - Query) -> - lists:foreach(fun (R) -> - JID = R#roster.jid, - case lists:keysearch(<<"validate", - (ejabberd_web_admin:term_to_id(JID))/binary>>, - 1, Query) - of - {value, _} -> - JID1 = jid:make(JID), - UJID = jid:make(User, Server), - Pres = #presence{from = UJID, to = JID1, - type = subscribed}, - out_subscription(Pres), - ejabberd_router:route(Pres), - throw(submitted); - false -> - case lists:keysearch(<<"remove", - (ejabberd_web_admin:term_to_id(JID))/binary>>, - 1, Query) - of - {value, _} -> - UJID = jid:make(User, Server), - RosterItem = #roster_item{ - jid = jid:make(JID), - subscription = remove}, - process_iq_set( - #iq{type = set, - from = UJID, - to = UJID, - id = p1_rand:get_string(), - sub_els = [#roster_query{ - items = [RosterItem]}]}), - throw(submitted); - false -> ok - end - end - end, - Items), - nothing. - -us_to_list({User, Server}) -> - jid:encode({User, Server, <<"">>}). - -webadmin_user(Acc, User, Server, Lang) -> - QueueLen = length(get_roster(jid:nodeprep(User), jid:nameprep(Server))), - FQueueLen = ?C(integer_to_binary(QueueLen)), - FQueueView = ?AC(<<"roster/">>, ?T("View Roster")), - Acc ++ - [?XCT(<<"h3">>, ?T("Roster:")), - FQueueLen, - ?C(<<" | ">>), - FQueueView]. +webadmin_user(Acc, User, Server, R) -> + Acc + ++ [make_command(get_roster_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])]. +%%% @format-end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec has_duplicated_groups([binary()]) -> boolean(). diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 447ed4666..cf1e0f536 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -41,6 +41,8 @@ is_user_in_group/3, add_user_to_group/3, opts_to_binary/1, remove_user_from_group/3, mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]). +-import(ejabberd_web_admin, [make_command/4, make_command_raw_value/3, make_table/2, make_table/4]). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -861,286 +863,402 @@ unset_presence(User, Server, Resource, Status) -> end. %%--------------------- -%% Web Admin +%% Web Admin: Page Frontend %%--------------------- +%% @format-begin + webadmin_menu(Acc, _Host, Lang) -> - [{<<"shared-roster">>, translate:translate(Lang, ?T("Shared Roster Groups"))} - | Acc]. + [{<<"shared-roster">>, translate:translate(Lang, ?T("Shared Roster Groups"))} | Acc]. -webadmin_page(_, Host, - #request{us = _US, path = [<<"shared-roster">>], - q = Query, lang = Lang} = - _Request) -> - Res = list_shared_roster_groups(Host, Query, Lang), - {stop, Res}; -webadmin_page(_, Host, - #request{us = _US, path = [<<"shared-roster">>, Group], - q = Query, lang = Lang} = - _Request) -> - Res = shared_roster_group(Host, Group, Query, Lang), - {stop, Res}; -webadmin_page(Acc, _, _) -> Acc. +webadmin_page(_, + Host, + #request{us = _US, + path = [<<"shared-roster">> | RPath], + lang = Lang} = + R) -> + PageTitle = translate:translate(Lang, ?T("Shared Roster Groups")), + Head = ?H1GL(PageTitle, <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>), + Level = length(RPath), + Res = case check_group_exists(Host, RPath) of + true -> + webadmin_page_backend(Host, RPath, R, Lang, Level); + false -> + [?XREST(<<"Group does not exist.">>)] + end, + {stop, Head ++ Res}; +webadmin_page(Acc, _, _) -> + Acc. -list_shared_roster_groups(Host, Query, Lang) -> - Res = list_sr_groups_parse_query(Host, Query), - SRGroups = list_groups(Host), - FGroups = (?XAE(<<"table">>, [], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?X(<<"td">>), - ?XE(<<"td">>, [?CT(?T("Name:"))]) - ])]++ - (lists:map(fun (Group) -> - ?XE(<<"tr">>, - [?XE(<<"td">>, - [?INPUT(<<"checkbox">>, - <<"selected">>, - Group)]), - ?XE(<<"td">>, - [?AC(<>, - Group)])]) - end, - lists:sort(SRGroups)) - ++ - [?XE(<<"tr">>, - [?X(<<"td">>), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"namenew">>, - <<"">>), - ?C(<<" ">>), - ?INPUTT(<<"submit">>, <<"addnew">>, - ?T("Add New"))])])]))])), - (?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))), - <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>)) - ++ - case Res of - ok -> [?XREST(?T("Submitted"))]; - error -> [?XREST(?T("Bad format"))]; - nothing -> [] - end - ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [FGroups, ?BR, - ?INPUTTD(<<"submit">>, <<"delete">>, - ?T("Delete Selected"))])]. +check_group_exists(Host, [<<"group">>, Id | _]) -> + case get_group_opts(Host, Id) of + error -> + false; + _ -> + true + end; +check_group_exists(_, _) -> + true. -list_sr_groups_parse_query(Host, Query) -> - case lists:keysearch(<<"addnew">>, 1, Query) of - {value, _} -> list_sr_groups_parse_addnew(Host, Query); - _ -> - case lists:keysearch(<<"delete">>, 1, Query) of - {value, _} -> list_sr_groups_parse_delete(Host, Query); - _ -> nothing - end - end. +%%--------------------- +%% Web Admin: Page Backend +%%--------------------- -list_sr_groups_parse_addnew(Host, Query) -> - case lists:keysearch(<<"namenew">>, 1, Query) of - {value, {_, Group}} when Group /= <<"">> -> - create_group(Host, Group), - ok; - _ -> - error - end. +webadmin_page_backend(Host, [<<"group">>, Id, <<"info">> | RPath], R, _Lang, Level) -> + Breadcrumb = + make_breadcrumb({group_section, + Level, + <<"Groups of ", Host/binary>>, + Id, + <<"Information">>, + RPath}), + SetLabel = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, {<<"group">>, Id}, {<<"key">>, <<"label">>}], + [{only, without_presentation}, {input_name_append, [Id, Host, <<"label">>]}]), + SetDescription = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, {<<"group">>, Id}, {<<"key">>, <<"description">>}], + [{only, without_presentation}, + {input_name_append, [Id, Host, <<"description">>]}]), + SetAll = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, + {<<"group">>, Id}, + {<<"key">>, <<"all_users">>}, + {<<"value">>, <<"true">>}], + [{only, button}, + {input_name_append, [Id, Host, <<"all_users">>, <<"true">>]}]), + UnsetAll = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, + {<<"group">>, Id}, + {<<"key">>, <<"all_users">>}, + {<<"value">>, <<"false">>}], + [{only, button}, + {input_name_append, [Id, Host, <<"all_users">>, <<"false">>]}]), + SetOnline = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, + {<<"group">>, Id}, + {<<"key">>, <<"online_users">>}, + {<<"value">>, <<"true">>}], + [{only, button}, + {input_name_append, [Id, Host, <<"online_users">>, <<"true">>]}]), + UnsetOnline = + make_command(srg_set_info, + R, + [{<<"host">>, Host}, + {<<"group">>, Id}, + {<<"key">>, <<"online_users">>}, + {<<"value">>, <<"false">>}], + [{only, button}, + {input_name_append, [Id, Host, <<"online_users">>, <<"false">>]}]), + GetInfo = + make_command_raw_value(srg_get_info, R, [{<<"group">>, Id}, {<<"host">>, Host}]), + AllElement = + case proplists:get_value(<<"all_users">>, GetInfo, not_found) of + "true" -> + {?C("Unset @all@: "), UnsetAll}; + _ -> + {?C("Set @all@: "), SetAll} + end, + OnlineElement = + case proplists:get_value(<<"online_users">>, GetInfo, not_found) of + "true" -> + {?C("Unset @online@: "), UnsetOnline}; + _ -> + {?C("Set @online@: "), SetOnline} + end, + Types = + [{?C("Set label: "), SetLabel}, + {?C("Set description: "), SetDescription}, + AllElement, + OnlineElement], + Get = [?BR, + make_command(srg_get_info, R, [{<<"host">>, Host}, {<<"group">>, Id}], []), + make_command(srg_set_info, R, [], [{only, presentation}]), + make_table(20, [], [{<<"">>, right}, <<"">>], Types)], + Breadcrumb ++ Get; +webadmin_page_backend(Host, + [<<"group">>, Id, <<"displayed">> | RPath], + R, + _Lang, + Level) -> + Breadcrumb = + make_breadcrumb({group_section, + Level, + <<"Groups of ", Host/binary>>, + Id, + <<"Displayed Groups">>, + RPath}), + AddDisplayed = + make_command(srg_add_displayed, R, [{<<"host">>, Host}, {<<"group">>, Id}], []), + _ = make_webadmin_displayed_table(Host, Id, R), + DisplayedTable = make_webadmin_displayed_table(Host, Id, R), + Get = [?BR, + make_command(srg_get_displayed, R, [], [{only, presentation}]), + make_command(srg_del_displayed, R, [], [{only, presentation}]), + ?XE(<<"blockquote">>, [DisplayedTable]), + AddDisplayed], + Breadcrumb ++ Get; +webadmin_page_backend(Host, [<<"group">>, Id, <<"members">> | RPath], R, _Lang, Level) -> + Breadcrumb = + make_breadcrumb({group_section, + Level, + <<"Groups of ", Host/binary>>, + Id, + <<"Members">>, + RPath}), + UserAdd = make_command(srg_user_add, R, [{<<"grouphost">>, Host}, {<<"group">>, Id}], []), + _ = make_webadmin_members_table(Host, Id, R), + MembersTable = make_webadmin_members_table(Host, Id, R), + Get = [make_command(srg_get_members, R, [], [{only, presentation}]), + make_command(srg_user_del, R, [], [{only, presentation}]), + ?XE(<<"blockquote">>, [MembersTable]), + UserAdd], + Breadcrumb ++ Get; +webadmin_page_backend(Host, [<<"group">>, Id, <<"delete">> | RPath], R, _Lang, Level) -> + Breadcrumb = + make_breadcrumb({group_section, + Level, + <<"Groups of ", Host/binary>>, + Id, + <<"Delete">>, + RPath}), + Get = [make_command(srg_delete, + R, + [{<<"host">>, Host}, {<<"group">>, Id}], + [{style, danger}])], + Breadcrumb ++ Get; +webadmin_page_backend(Host, [<<"group">>, Id | _RPath], _R, _Lang, Level) -> + Breadcrumb = make_breadcrumb({group, Level, <<"Groups of ", Host/binary>>, Id}), + MenuItems = + [{<<"info/">>, <<"Information">>}, + {<<"members/">>, <<"Members">>}, + {<<"displayed/">>, <<"Displayed Groups">>}, + {<<"delete/">>, <<"Delete">>}], + Get = [?XE(<<"ul">>, [?LI([?AC(MIU, MIN)]) || {MIU, MIN} <- MenuItems])], + Breadcrumb ++ Get; +webadmin_page_backend(Host, RPath, R, _Lang, Level) -> + Breadcrumb = make_breadcrumb({groups, <<"Groups of ", Host/binary>>}), + _ = make_webadmin_srg_table(Host, R, 3 + Level, RPath), + Set = [make_command(srg_add, R, [{<<"host">>, Host}], []), + make_command(srg_create, R, [{<<"host">>, Host}], [])], + RV2 = make_webadmin_srg_table(Host, R, 3 + Level, RPath), + Get = [make_command(srg_list, R, [{<<"host">>, Host}], [{only, presentation}]), + make_command(srg_get_info, R, [{<<"host">>, Host}], [{only, presentation}]), + make_command(srg_delete, R, [{<<"host">>, Host}], [{only, presentation}]), + ?XE(<<"blockquote">>, [RV2])], + Breadcrumb ++ Get ++ Set. -list_sr_groups_parse_delete(Host, Query) -> - SRGroups = list_groups(Host), - lists:foreach(fun (Group) -> - case lists:member({<<"selected">>, Group}, Query) of - true -> delete_group(Host, Group); - _ -> ok - end - end, - SRGroups), - ok. +%%--------------------- +%% Web Admin: Table Generation +%%--------------------- -shared_roster_group(Host, Group, Query, Lang) -> - Res = shared_roster_group_parse_query(Host, Group, - Query), - GroupOpts = get_group_opts(Host, Group), - Label = get_opt(GroupOpts, label, <<"">>), %%++ - Description = get_opt(GroupOpts, description, <<"">>), - AllUsers = get_opt(GroupOpts, all_users, false), - OnlineUsers = get_opt(GroupOpts, online_users, false), - DisplayedGroups = get_opt(GroupOpts, displayed_groups, - []), - Members = get_group_explicit_users(Host, - Group), - FMembers = iolist_to_binary( - [if AllUsers -> <<"@all@\n">>; - true -> <<"">> - end, - if OnlineUsers -> <<"@online@\n">>; - true -> <<"">> - end, - [[us_to_list(Member), $\n] || Member <- Members]]), - FDisplayedGroups = [<> || DG <- DisplayedGroups], - DescNL = length(ejabberd_regexp:split(Description, - <<"\n">>)), - FGroup = (?XAE(<<"table">>, - [{<<"class">>, <<"withtextareas">>}], - [?XE(<<"tbody">>, - [?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Name:")), - ?XE(<<"td">>, [?C(Group)]), - ?XE(<<"td">>, [?C(<<"">>)])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Label:")), - ?XE(<<"td">>, - [?INPUT(<<"text">>, <<"label">>, Label)]), - ?XE(<<"td">>, [?CT(?T("Name in the rosters where this group will be displayed"))])]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Description:")), - ?XE(<<"td">>, - [?TEXTAREA(<<"description">>, - integer_to_binary(lists:max([3, - DescNL])), - <<"20">>, Description)]), - ?XE(<<"td">>, [?CT(?T("Only admins can see this"))]) -]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Members:")), - ?XE(<<"td">>, - [?TEXTAREA(<<"members">>, - integer_to_binary(lists:max([3, - length(Members)+3])), - <<"20">>, FMembers)]), - ?XE(<<"td">>, [?C(<<"JIDs, @all@, @online@">>)]) -]), - ?XE(<<"tr">>, - [?XCT(<<"td">>, ?T("Displayed:")), - ?XE(<<"td">>, - [?TEXTAREA(<<"dispgroups">>, - integer_to_binary(lists:max([3, length(FDisplayedGroups)])), - <<"20">>, - list_to_binary(FDisplayedGroups))]), - ?XE(<<"td">>, [?CT(?T("Groups that will be displayed to the members"))]) -])])])), - (?H1GL((translate:translate(Lang, ?T("Shared Roster Groups"))), - <<"modules/#mod_shared_roster">>, <<"mod_shared_roster">>)) - ++ - [?XC(<<"h2">>, translate:translate(Lang, ?T("Group")))] ++ - case Res of - ok -> [?XREST(?T("Submitted"))]; - {error_elements, NonAddedList1, NG1} -> - make_error_el(Lang, - ?T("Members not added (inexistent vhost!): "), - [jid:encode({U,S,<<>>}) || {U,S} <- NonAddedList1]) - ++ make_error_el(Lang, ?T("'Displayed groups' not added (they do not exist!): "), NG1); - error -> [?XREST(?T("Bad format"))]; - nothing -> [] - end - ++ - [?XAE(<<"form">>, - [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}], - [FGroup, ?BR, - ?INPUTT(<<"submit">>, <<"submit">>, ?T("Submit"))])]. +make_webadmin_srg_table(Host, R, Level, RPath) -> + Groups = + case make_command_raw_value(srg_list, R, [{<<"host">>, Host}]) of + Gs when is_list(Gs) -> + Gs; + _ -> + [] + end, + Columns = + [<<"id">>, + <<"label">>, + <<"description">>, + <<"all">>, + <<"online">>, + {<<"members">>, right}, + {<<"displayed">>, right}, + <<"">>], + Rows = + [{make_command(echo3, + R, + [{<<"first">>, Id}, {<<"second">>, Host}, {<<"sentence">>, Id}], + [{only, value}, {result_links, [{sentence, shared_roster, Level, <<"">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + iolist_to_binary(proplists:get_value(<<"label">>, + make_command_raw_value(srg_get_info, + R, + [{<<"group">>, + Id}, + {<<"host">>, + Host}]), + ""))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"info">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + iolist_to_binary(proplists:get_value(<<"description">>, + make_command_raw_value(srg_get_info, + R, + [{<<"group">>, + Id}, + {<<"host">>, + Host}]), + ""))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"info">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + iolist_to_binary(proplists:get_value(<<"all_users">>, + make_command_raw_value(srg_get_info, + R, + [{<<"group">>, + Id}, + {<<"host">>, + Host}]), + ""))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"info">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + iolist_to_binary(proplists:get_value(<<"online_users">>, + make_command_raw_value(srg_get_info, + R, + [{<<"group">>, + Id}, + {<<"host">>, + Host}]), + ""))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"info">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + integer_to_binary(length(make_command_raw_value(srg_get_members, + R, + [{<<"group">>, Id}, + {<<"host">>, Host}])))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"members">>}]}]), + make_command(echo3, + R, + [{<<"first">>, Id}, + {<<"second">>, Host}, + {<<"sentence">>, + integer_to_binary(length(make_command_raw_value(srg_get_displayed, + R, + [{<<"group">>, Id}, + {<<"host">>, Host}])))}], + [{only, value}, + {result_links, [{sentence, shared_roster, Level, <<"displayed">>}]}]), + make_command(srg_delete, + R, + [{<<"group">>, Id}, {<<"host">>, Host}], + [{only, button}, {style, danger}, {input_name_append, [Id, Host]}])} + || Id <- Groups], + make_table(20, RPath, Columns, Rows). -make_error_el(_, _, []) -> - []; -make_error_el(Lang, Message, BinList) -> - NG2 = str:join(BinList, <<", ">>), - NG3 = translate:translate(Lang, Message), - NG4 = str:concat(NG3, NG2), - [?XRES(NG4)]. +make_webadmin_members_table(Host, Id, R) -> + Members = + case make_command_raw_value(srg_get_members, R, [{<<"host">>, Host}, {<<"group">>, Id}]) + of + Ms when is_list(Ms) -> + Ms; + _ -> + [] + end, + make_table([<<"member">>, <<"">>], + [{make_command(echo, + R, + [{<<"sentence">>, Jid}], + [{only, value}, {result_links, [{sentence, user, 6, <<"">>}]}]), + make_command(srg_user_del, + R, + [{<<"user">>, + element(1, + jid:split( + jid:decode(Jid)))}, + {<<"host">>, + element(2, + jid:split( + jid:decode(Jid)))}, + {<<"group">>, Id}, + {<<"grouphost">>, Host}], + [{only, button}, + {style, danger}, + {input_name_append, + [element(1, + jid:split( + jid:decode(Jid))), + element(2, + jid:split( + jid:decode(Jid))), + Id, + Host]}])} + || Jid <- Members]). -shared_roster_group_parse_query(Host, Group, Query) -> - case lists:keysearch(<<"submit">>, 1, Query) of - {value, _} -> - {value, {_, Label}} = lists:keysearch(<<"label">>, 1, - Query), %++ - {value, {_, Description}} = - lists:keysearch(<<"description">>, 1, Query), - {value, {_, SMembers}} = lists:keysearch(<<"members">>, - 1, Query), - {value, {_, SDispGroups}} = - lists:keysearch(<<"dispgroups">>, 1, Query), - LabelOpt = if Label == <<"">> -> []; - true -> [{label, Label}] %++ - end, - DescriptionOpt = if Description == <<"">> -> []; - true -> [{description, Description}] - end, - DispGroups1 = str:tokens(SDispGroups, <<"\r\n">>), - {DispGroups, WrongDispGroups} = filter_groups_existence(Host, DispGroups1), - DispGroupsOpt = if DispGroups == [] -> []; - true -> [{displayed_groups, DispGroups}] - end, - OldMembers = get_group_explicit_users(Host, - Group), - SJIDs = str:tokens(SMembers, <<", \r\n">>), - NewMembers = lists:foldl(fun (_SJID, error) -> error; - (SJID, USs) -> - case SJID of - <<"@all@">> -> USs; - <<"@online@">> -> USs; - _ -> - try jid:decode(SJID) of - JID -> - [{JID#jid.luser, - JID#jid.lserver} - | USs] - catch _:{bad_jid, _} -> - error - end - end - end, - [], SJIDs), - AllUsersOpt = case lists:member(<<"@all@">>, SJIDs) of - true -> [{all_users, true}]; - false -> [] - end, - OnlineUsersOpt = case lists:member(<<"@online@">>, - SJIDs) - of - true -> [{online_users, true}]; - false -> [] - end, - CurrentDisplayedGroups = get_displayed_groups(Group, Host), - AddedDisplayedGroups = DispGroups -- CurrentDisplayedGroups, - RemovedDisplayedGroups = CurrentDisplayedGroups -- DispGroups, - displayed_groups_update(OldMembers, RemovedDisplayedGroups, remove), - displayed_groups_update(OldMembers, AddedDisplayedGroups, both), - set_group_opts(Host, Group, - LabelOpt ++ - DispGroupsOpt ++ - DescriptionOpt ++ - AllUsersOpt ++ OnlineUsersOpt), - if NewMembers == error -> error; - true -> - AddedMembers = NewMembers -- OldMembers, - RemovedMembers = OldMembers -- NewMembers, - lists:foreach( - fun(US) -> - remove_user_from_group(Host, - US, - Group) - end, - RemovedMembers), - NonAddedMembers = lists:filter( - fun(US) -> - error == add_user_to_group(Host, US, - Group) - end, - AddedMembers), - case (NonAddedMembers /= []) or (WrongDispGroups /= []) of - true -> {error_elements, NonAddedMembers, WrongDispGroups}; - false -> ok - end - end; - _ -> nothing - end. +make_webadmin_displayed_table(Host, Id, R) -> + Displayed = + case make_command_raw_value(srg_get_displayed, R, [{<<"host">>, Host}, {<<"group">>, Id}]) + of + Ms when is_list(Ms) -> + Ms; + _ -> + [] + end, + make_table([<<"group">>, <<"">>], + [{make_command(echo3, + R, + [{<<"first">>, ThisId}, + {<<"second">>, Host}, + {<<"sentence">>, ThisId}], + [{only, value}, + {result_links, [{sentence, shared_roster, 6, <<"">>}]}]), + make_command(srg_del_displayed, + R, + [{<<"group">>, Id}, {<<"host">>, Host}, {<<"del">>, ThisId}], + [{only, button}, + {style, danger}, + {input_name_append, [Id, Host, ThisId]}])} + || ThisId <- Displayed]). -get_opt(Opts, Opt, Default) -> - case lists:keysearch(Opt, 1, Opts) of - {value, {_, Val}} -> Val; - false -> Default - end. - -us_to_list({User, Server}) -> - jid:encode({User, Server, <<"">>}). +make_breadcrumb({groups, Service}) -> + make_breadcrumb([Service]); +make_breadcrumb({group, Level, Service, Name}) -> + make_breadcrumb([{Level, Service}, separator, Name]); +make_breadcrumb({group_section, Level, Service, Name, Section, RPath}) -> + make_breadcrumb([{Level, Service}, separator, {Level - 2, Name}, separator, Section + | RPath]); +make_breadcrumb(Elements) -> + lists:map(fun ({xmlel, _, _, _} = Xmlel) -> + Xmlel; + (<<"sort">>) -> + ?C(<<" +">>); + (<<"page">>) -> + ?C(<<" #">>); + (separator) -> + ?C(<<" > ">>); + (Bin) when is_binary(Bin) -> + ?C(Bin); + ({Level, Bin}) when is_integer(Level) and is_binary(Bin) -> + ?AC(binary:copy(<<"../">>, Level), Bin) + end, + Elements). +%% @format-end split_grouphost(Host, Group) -> case str:tokens(Group, <<"@">>) of @@ -1148,17 +1266,6 @@ split_grouphost(Host, Group) -> [_] -> {Host, Group} end. -filter_groups_existence(Host, Groups) -> - lists:partition( - fun(Group) -> error /= get_group_opts(Host, Group) end, - Groups). - -displayed_groups_update(Members, DisplayedGroups, Subscription) -> - lists:foreach( - fun({U, S}) -> - push_displayed_to_user(U, S, S, Subscription, DisplayedGroups) - end, Members). - opts_to_binary(Opts) -> lists:map( fun({label, Label}) -> From 4819baaa078bbf2907844d25cb6b45f8f17366e8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jun 2024 21:37:15 +0200 Subject: [PATCH 0624/1302] Test: Update to the new webadmin pages --- test/ejabberd_SUITE_data/ejabberd.yml | 1 + test/webadmin_tests.erl | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index d3a24315f..0155e8f90 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -108,6 +108,7 @@ max_fsm_queue: 1000 queue_type: file modules: mod_adhoc: [] + mod_admin_extra: [] mod_admin_update_sql: [] mod_announce: [] mod_configure: [] diff --git a/test/webadmin_tests.erl b/test/webadmin_tests.erl index a8251dca7..753f87930 100644 --- a/test/webadmin_tests.erl +++ b/test/webadmin_tests.erl @@ -76,10 +76,11 @@ adduser(Config) -> Body = make_query( Config, "server/" ++ binary_to_list(Server) ++ "/users/", - <<"newusername=", (mue(User))/binary, "&newuserpassword=", - (mue(Password))/binary, "&addnewuser=Add+User">>), + <<"register/user=", (mue(User))/binary, "®ister/password=", + (mue(Password))/binary, "®ister=Register">>), Password = ejabberd_auth:get_password(User, Server), - ?match({_, _}, binary:match(Body, <<"Submitted

">>)). + ?match({_, _}, binary:match(Body, <<"
ok
">>)). removeuser(Config) -> User = <<"userwebadmin-", (?config(user, Config))/binary>>, @@ -101,7 +102,7 @@ removeuser(Config) -> Config, "server/" ++ binary_to_list(Server) ++ "/user/" ++ binary_to_list(mue(User)) ++ "/", - <<"password=&removeuser=Remove+User">>), + <<"&unregister=Unregister">>), false = ejabberd_auth:user_exists(User, Server), ?match(nomatch, binary:match(Body, <<"

Last Activity

20">>)). From 899b77cdf4844aad28e862cac254121229373ee2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 12 Jun 2024 23:37:15 +0200 Subject: [PATCH 0625/1302] ejabberd_ctl: Improve parsing of commas in arguments --- src/ejabberd_ctl.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 89adcdcd6..aad3e0eb3 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -367,9 +367,9 @@ format_arg(Arg, string) -> Parse = "~" ++ NumChars ++ "c", format_arg2(Arg, Parse); format_arg(Arg, {list, {_ArgName, ArgFormat}}) -> - [format_arg(Element, ArgFormat) || Element <- string:tokens(Arg, ",")]; + [format_arg(string:trim(Element), ArgFormat) || Element <- string:tokens(Arg, ",")]; format_arg(Arg, {list, ArgFormat}) -> - [format_arg(Element, ArgFormat) || Element <- string:tokens(Arg, ",")]; + [format_arg(string:trim(Element), ArgFormat) || Element <- string:tokens(Arg, ",")]; format_arg(Arg, {tuple, Elements}) -> Args = string:tokens(Arg, ":"), list_to_tuple(format_args(Args, Elements)); From 40c27b54b0e570e7df28da1ab96ff00bfb6b731c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 19 Jun 2024 19:51:50 +0200 Subject: [PATCH 0626/1302] ejabberd_ctl: Fix output of UTF-8-encoded binaries Converting binary results to char() lists using binary_to_list/1 yields incorrect results for characters outside the ISO Latin-1 range. --- src/ejabberd_ctl.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index aad3e0eb3..87e2cb624 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -410,13 +410,13 @@ format_result([A|_]=String, {_Name, string}, _Version) when is_list(String) and io_lib:format("~ts", [String]); format_result(Binary, {_Name, binary}, _Version) when is_binary(Binary) -> - io_lib:format("~ts", [binary_to_list(Binary)]); + io_lib:format("~ts", [Binary]); format_result(String, {_Name, binary}, _Version) when is_list(String) -> io_lib:format("~ts", [String]); format_result(Binary, {_Name, string}, _Version) when is_binary(Binary) -> - io_lib:format("~ts", [binary_to_list(Binary)]); + io_lib:format("~ts", [Binary]); format_result(Atom, {_Name, string}, _Version) when is_atom(Atom) -> io_lib:format("~ts", [atom_to_list(Atom)]); From 0d3148264799f38227b97791999a2f337b4aff0e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Jun 2024 13:35:02 +0200 Subject: [PATCH 0627/1302] Use newer rebar3_hex with new Erlang, but Erlang 20 requires 7.0.7 --- rebar.config | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index d3c32650a..7f7b77622 100644 --- a/rebar.config +++ b/rebar.config @@ -144,7 +144,9 @@ {if_rebar3, sql}, {if_var_true, tools, tools}]}]}. -{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}, +{if_rebar3, {plugins, [{if_version_below, "21", {rebar3_hex, "7.0.7"}}, + {if_version_above, "20", {rebar3_hex, "~> 7.0.8"}}, + {provider_asn1, "0.2.0"}, %% Protocol consolidation doesn't work correctly in upstream rebar_mix, see %% https://github.com/Supersonido/rebar_mix/issues/27#issuecomment-894873335 %% Let's use this fixed rebar_mix fork, see its PR: From 9a6bd83aa1945396d91f6cdbbb8d19ed5fa0b999 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Jun 2024 13:58:56 +0200 Subject: [PATCH 0628/1302] Remove obsolete code for Erlang/OTP older than 20.0 --- src/ejabberd_ctl.erl | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 87e2cb624..14780278a 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -598,17 +598,9 @@ get_shell_info() -> _:_ -> {78, false} end. -%% Erlang/OTP 20.0 introduced string:find/2, but we must support old 19.3 -string_find([], _SearchPattern) -> - nomatch; -string_find([A | String], [A]) -> - String; -string_find([_ | String], SearchPattern) -> - string_find(String, SearchPattern). - %% Split this command description in several lines of proper length prepare_description(DescInit, MaxC, Desc) -> - case string_find(Desc, "\n") of + case string:find(Desc, "\n") of nomatch -> prepare_description2(DescInit, MaxC, Desc); _ -> From d170885d8dabc03ab61a9668c006f356ffcb71ab Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 09:42:59 +0200 Subject: [PATCH 0629/1302] Run "make doap" (#4237) --- ejabberd.doap | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index b0ae77f5c..60d047342 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -758,8 +758,8 @@ - 0.2.1 - 23.04 + 0.3.0 + /home/bernar/e/git/ejabberd/src/mod_mam.erl:-protocol({xep, 425, '0.3.0', '24.xx', "", ""}). mod_mam @@ -791,5 +791,14 @@
+ + + + 0.2.0 + 24.02 + + , mod_pubsub_serverinfo in ejabberd-contrib.git + + From b85cd9a48789f299000ea197d6e8fc04318aa868 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 00:06:51 +0200 Subject: [PATCH 0630/1302] join_cluster_here: New command to join a remote node into our local cluster --- src/ejabberd_admin.erl | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 86fe2a9be..6de2f7b2f 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -40,6 +40,7 @@ convert_to_yaml/2, %% Cluster join_cluster/1, leave_cluster/1, + join_cluster_here/1, list_cluster/0, list_cluster_detailed/0, get_cluster_node_details3/0, %% Erlang @@ -249,13 +250,21 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = join_cluster, tags = [cluster], - desc = "Join this node into the cluster handled by Node", + desc = "Join our local node into the cluster handled by Node", note = "improved in 24.xx", module = ?MODULE, function = join_cluster, args_desc = ["Nodename of the node to join"], args_example = [<<"ejabberd1@machine7">>], args = [{node, binary}], result = {res, restuple}}, + #ejabberd_commands{name = join_cluster_here, tags = [cluster], + desc = "Join a remote Node here, into our cluster", + note = "added in 24.xx", + module = ?MODULE, function = join_cluster_here, + args_desc = ["Nodename of the node to join here"], + args_example = [<<"ejabberd1@machine7">>], + args = [{node, binary}], + result = {res, restuple}}, #ejabberd_commands{name = leave_cluster, tags = [cluster], desc = "Remove and shutdown Node from the running cluster", longdesc = "This command can be run from any running " @@ -841,9 +850,30 @@ join_cluster(_Node, _IsNodes, _IsKnownNodes, pang) -> join_cluster(Node, false, false, pong) -> case timer:apply_after(1000, ejabberd_cluster, join, [Node]) of {ok, _} -> - {ok, "Trying to join the cluster, wait a few seconds and check the list of nodes."}; + {ok, "Trying to join that cluster, wait a few seconds and check the list of nodes."}; Error -> - {error, io_lib:format("Can't join cluster: ~p", [Error])} + {error, io_lib:format("Can't join that cluster: ~p", [Error])} + end. + +join_cluster_here(NodeBin) -> + Node = list_to_atom(binary_to_list(NodeBin)), + IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()), + IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()), + Ping = net_adm:ping(Node), + join_cluster_here(Node, IsNodes, IsKnownNodes, Ping). + +join_cluster_here(_Node, true, _IsKnownNodes, _Ping) -> + {error, "This node already joined that running node."}; +join_cluster_here(_Node, _IsNodes, true, _Ping) -> + {error, "This node already joined that known node."}; +join_cluster_here(_Node, _IsNodes, _IsKnownNodes, pang) -> + {error, "This node cannot reach that node."}; +join_cluster_here(Node, false, false, pong) -> + case ejabberd_cluster:call(Node, ejabberd_admin, join_cluster, [atom_to_binary(node())]) of + {ok, _} -> + {ok, "Trying to join node to this cluster, wait a few seconds and check the list of nodes."}; + Error -> + {error, io_lib:format("Can't join node to this cluster: ~p", [Error])} end. leave_cluster(NodeBin) -> @@ -1350,6 +1380,11 @@ web_page_node(_, Node, #request{path = [<<"cluster">>]} = R) -> Head = ?H1GLraw(<<"Clustering">>, <<"admin/guide/clustering/">>, <<"Clustering">>), Set1 = [ejabberd_cluster:call(Node, + ejabberd_web_admin, + make_command, + [join_cluster_here, R, [], []]), + ?XE(<<"blockquote">>, [?C(Hint)]), + ejabberd_cluster:call(Node, ejabberd_web_admin, make_command, [join_cluster, R, [], [{style, danger}]]), From ce348596b77804df7ee3fe9e0675707698510910 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Jun 2024 18:36:07 +0200 Subject: [PATCH 0631/1302] WebAdmin: Improve CSS of welcome page, docs links, anchor element --- include/ejabberd_web_admin.hrl | 4 ++-- priv/css/admin.css | 11 ++++++++++- src/ejabberd_web_admin.erl | 13 ++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/ejabberd_web_admin.hrl b/include/ejabberd_web_admin.hrl index fb2019a05..f2e5e582e 100644 --- a/include/ejabberd_web_admin.hrl +++ b/include/ejabberd_web_admin.hrl @@ -115,7 +115,7 @@ %% h1 with a Guide Link -define(H1GLraw(Name, Ref, Title), - [?XC(<<"h1">>, Name), ?GL(Ref, Title), ?BR]). + [?XC(<<"h1">>, Name), ?GL(Ref, Title), ?BR, ?BR]). -define(H1GL(Name, RefConf, Title), ?H1GLraw(Name, <<"admin/configuration/", RefConf/binary>>, Title)). @@ -123,4 +123,4 @@ ?XAE(<<"div">>, [{<<"class">>, <<"anchorlink">>}], [?XAE(<<"a">>, [{<<"href">>, <<"#", Ref/binary>>}], - [?C(<<"<=">>)])])). + [?C(unicode:characters_to_binary("¶"))])])). diff --git a/priv/css/admin.css b/priv/css/admin.css index 9bb34a105..0d3d0c4c6 100644 --- a/priv/css/admin.css +++ b/priv/css/admin.css @@ -131,6 +131,12 @@ ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { background: #424a55; color: #fff; } +#welcome { + padding: 2em; + border-top: 0.2em solid #cae7e4; + border-bottom: 0.2em solid #cae7e4; + background-color: #f4f9f9; +} #lastactivity li { padding: 2px; margin-bottom: -1px; @@ -248,6 +254,7 @@ p[dir=ltr] a { background: #3eaffa; font-size: 0.75em; color: #fff; + border-radius: 2px; } table { margin-top: 1em; @@ -296,9 +303,11 @@ details > summary { cursor: pointer; list-style: none; padding: 8px; + border-radius: 4px; } details > pre, details > p { - background-color: #e6f1f0; + background-color: #f2f8f7; + border-bottom: 0.2em solid #dbeceb; margin: 0; padding: 10px; } diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 37150abef..a5492be31 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -437,12 +437,11 @@ process_admin(global, #request{path = [], lang = Lang} = Request, AJID) -> ?XAE(<<"p">>, [{<<"align">>, <<"center">>}], [?XA(<<"img">>, [{<<"src">>, <<"logo.png">>}, {<<"style">>, <<"border-radius:10px; background:#49cbc1; padding: 1.1em;">>}]) - ]), - ?BR, - ?X(<<"hr">>)] ++ Title ++ - [?XE(<<"blockquote">>, - [ - ?XC(<<"p">>, <<"Welcome to ejabberd's WebAdmin!">>), + ]) + ] ++ Title ++ [ + ?XAE(<<"blockquote">>, + [{<<"id">>, <<"welcome">>}], + [?XC(<<"p">>, <<"Welcome to ejabberd's WebAdmin!">>), ?XC(<<"p">>, <<"Browse the menu to navigate your XMPP virtual hosts, " "Erlang nodes, and other global server pages...">>), ?XC(<<"p">>, <<"Some pages have a link in the top right corner " @@ -460,7 +459,7 @@ process_admin(global, #request{path = [], lang = Lang} = Request, AJID) -> ?XC(<<"p">>, <<"For example, this is the 'stats' command, " "it accepts an argument and returns an integer:">>), make_command(stats, Request)]), - ?X(<<"hr">>), ?BR], + ?BR], make_xhtml(Disclaimer ++ WelcomeText ++ [?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) From eceb9b729fefb7d22e1a2ba8b78a4a095c6d5fa4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 10:47:39 +0200 Subject: [PATCH 0632/1302] WebAdmin: New login box in the left menu bar --- priv/css/admin.css | 6 +++ src/ejabberd_web_admin.erl | 100 ++++++++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/priv/css/admin.css b/priv/css/admin.css index 0d3d0c4c6..9e76cccda 100644 --- a/priv/css/admin.css +++ b/priv/css/admin.css @@ -131,6 +131,12 @@ ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { background: #424a55; color: #fff; } +#navitemlogin { + padding: 0.5em; + border-top: 0.2em solid #cae7e4; + border-bottom: 0.2em solid #cae7e4; + padding-left: 0.5em; +} #welcome { padding: 2em; border-top: 0.2em solid #cae7e4; diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index a5492be31..d8fb14d60 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -160,6 +160,12 @@ process(Path, #request{raw_path = RawPath} = Request) -> {301, [{<<"Location">>, <>}], <<>>} end. +process2([<<"logout">> | _], #request{lang = Lang}) -> + Text = [?XCT(<<"h1">>, ?T("Logged Out")), + ?XE(<<"p">>, [?C(<<"Your web browser has logout from WebAdmin. Close this window, or login again in: ">>), + ?AC(<<"../">>, <<"ejabberd WebAdmin">>)])], + {401, [{<<"WWW-Authenticate">>, <<"basic realm=\"ejabberd\"">>}], + ejabberd_web:make_xhtml(Text)}; process2([<<"server">>, SHost | RPath] = Path, #request{auth = Auth, lang = Lang, host = HostHTTP, method = Method} = @@ -277,27 +283,28 @@ get_auth_account2(HostOfRule, AccessRule, User, Server, %%%================================== %%%% make_xhtml -make_xhtml(Els, Host, Lang, JID, Level) -> - make_xhtml(Els, Host, cluster, unspecified, Lang, JID, Level). +make_xhtml(Els, Host, Request, JID, Level) -> + make_xhtml(Els, Host, cluster, unspecified, Request, JID, Level). -make_xhtml(Els, Host, Username, Lang, JID, Level) when +make_xhtml(Els, Host, Username, Request, JID, Level) when (Username == unspecified) or (is_binary(Username)) -> - make_xhtml(Els, Host, cluster, Username, Lang, JID, Level); + make_xhtml(Els, Host, cluster, Username, Request, JID, Level); -make_xhtml(Els, Host, Node, Lang, JID, Level) -> - make_xhtml(Els, Host, Node, unspecified, Lang, JID, Level). +make_xhtml(Els, Host, Node, Request, JID, Level) -> + make_xhtml(Els, Host, Node, unspecified, Request, JID, Level). -spec make_xhtml([xmlel()], Host::global | binary(), Node::cluster | atom(), Username::unspecified | binary(), - Lang::binary(), + Request::http_request(), jid(), Level::integer()) -> {200, [html], xmlel()}. -make_xhtml(Els, Host, Node, Username, Lang, JID, Level) -> +make_xhtml(Els, Host, Node, Username, #request{lang = Lang} = R, JID, Level) -> Base = get_base_path_sum(0, 0, Level), - MenuItems = make_navigation(Host, Node, Username, Lang, JID, Level), + MenuItems = make_navigation(Host, Node, Username, Lang, JID, Level) + ++ make_login_items(R, Level), {200, [html], #xmlel{name = <<"html">>, attrs = @@ -465,14 +472,14 @@ process_admin(global, #request{path = [], lang = Lang} = Request, AJID) -> [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- MenuItems])], - global, Lang, AJID, 0); -process_admin(Host, #request{path = [], lang = Lang}, AJID) -> + global, Request, AJID, 0); +process_admin(Host, #request{path = [], lang = Lang} = R, AJID) -> make_xhtml([?XCT(<<"h1">>, ?T("Administration")), ?XE(<<"ul">>, [?LI([?ACT(MIU, MIN)]) || {MIU, MIN} <- get_menu_items(Host, cluster, Lang, AJID, 2)])], - Host, Lang, AJID, 2); + Host, R, AJID, 2); process_admin(Host, #request{path = [<<"style.css">>]}, _) -> {200, @@ -549,7 +556,7 @@ process_admin(global, #request{path = [<<"vhosts">> | RPath], lang = Lang} = R, ?T("XMPP Domains")) ++ VhostsElements, global, - Lang, + R, AJID, Level); process_admin(Host, @@ -559,7 +566,7 @@ process_admin(Host, Level = 5 + length(RPath), RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), Res = list_users_in_diapason(Host, Level, 30, RPath, R, Diap, RegisterEl), - make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"users">>, <<"top">>, Attribute | RPath], lang = Lang} = R, AJID) @@ -567,13 +574,13 @@ process_admin(Host, Level = 5 + length(RPath), RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), Res = list_users_top(Host, Level, 30, RPath, R, Attribute, RegisterEl), - make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> Level = 3 + length(RPath), RegisterEl = make_command(register, R, [{<<"host">>, Host}], []), Res = list_users(Host, Level, 30, RPath, R, RegisterEl), - make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, Lang, AJID, Level); + make_xhtml([?XCT(<<"h1">>, ?T("Users"))] ++ Res, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> Level = 3 + length(RPath), @@ -582,7 +589,7 @@ process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = [{<<"host">>, Host}], [{table_options, {2, RPath}}, {result_links, [{sessions, user, Level, <<"">>}]}])], - make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, Host, Lang, AJID, Level); + make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"last-activity">>], q = Query, @@ -598,16 +605,16 @@ process_admin(Host, R, [{<<"host">>, Host}, {<<"query">>, Query}, {<<"lang">>, Lang}], []), - make_xhtml(PageH1 ++ [Res], Host, Lang, AJID, 3); + make_xhtml(PageH1 ++ [Res], Host, R, AJID, 3); process_admin(Host, #request{path = [<<"user">>, U], lang = Lang} = R, AJID) -> case ejabberd_auth:user_exists(U, Host) of true -> Res = user_info(U, Host, R), - make_xhtml(Res, Host, U, Lang, AJID, 4); + make_xhtml(Res, Host, U, R, AJID, 4); false -> - make_xhtml([?XCT(<<"h1">>, ?T("Not Found"))], Host, Lang, AJID, 4) + make_xhtml([?XCT(<<"h1">>, ?T("Not Found"))], Host, R, AJID, 4) end; -process_admin(Host, #request{path = [<<"nodes">>], lang = Lang} = R, AJID) -> +process_admin(Host, #request{path = [<<"nodes">>]} = R, AJID) -> Level = case Host of global -> @@ -617,13 +624,13 @@ process_admin(Host, #request{path = [<<"nodes">>], lang = Lang} = R, AJID) -> end, Res = ?H1GLraw(<<"Nodes">>, <<"admin/guide/clustering/">>, <<"Clustering">>) ++ [make_command(list_cluster, R, [], [{result_links, [{node, node, 1, <<"">>}]}])], - make_xhtml(Res, Host, Lang, AJID, Level); + make_xhtml(Res, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"node">>, SNode | NPath], lang = Lang} = Request, AJID) -> case search_running_node(SNode) of false -> - make_xhtml([?XCT(<<"h1">>, ?T("Node not found"))], Host, Lang, AJID, 2); + make_xhtml([?XCT(<<"h1">>, ?T("Node not found"))], Host, Request, AJID, 2); Node -> Res = get_node(Host, Node, NPath, Request#request{path = NPath}), Level = @@ -633,11 +640,11 @@ process_admin(Host, _ -> 4 + length(NPath) end, - make_xhtml(Res, Host, Node, Lang, AJID, Level) + make_xhtml(Res, Host, Node, Request, AJID, Level) end; %%%================================== %%%% process_admin default case -process_admin(Host, #request{path = Path, lang = Lang} = Request, AJID) -> +process_admin(Host, #request{path = Path} = Request, AJID) -> {Username, RPath} = case Path of [<<"user">>, U | UPath] -> @@ -667,10 +674,10 @@ process_admin(Host, #request{path = Path, lang = Lang} = Request, AJID) -> case Res of [] -> setelement(1, - make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Lang, AJID, Level), + make_xhtml([?XC(<<"h1">>, <<"Not Found">>)], Host, Request, AJID, Level), 404); _ -> - make_xhtml(Res, Host, Username, Lang, AJID, Level) + make_xhtml(Res, Host, Username, Request, AJID, Level) end. %% @format-end @@ -1569,9 +1576,46 @@ any_rules_allowed(Host, Access, Entity) -> end, Access). %%%================================== +%%%% login box %%% @format-begin +make_login_items(#request{us = {Username, Host}} = R, Level) -> + UserBin = + jid:encode( + jid:make(Username, Host, <<"">>)), + UserEl = + make_command(echo, + R, + [{<<"sentence">>, UserBin}], + [{only, value}, {result_links, [{sentence, user, Level, <<"">>}]}]), + UserEl2 = + case UserEl of + {xmlcdata, <<>>} -> + {xmlel, <<"code">>, [], [{xmlel, <<"a">>, [], [{xmlcdata, UserBin}]}]}; + _ -> + UserEl + end, + [{xmlel, + <<"li">>, + [], + [{xmlel, + <<"div">>, + [{<<"id">>, <<"navitemlogin">>}], + [?XE(<<"ul">>, + [?LI([?C(unicode:characters_to_binary("👤")), UserEl2]), + ?LI([?C(unicode:characters_to_binary("🏭")), + make_command(echo, + R, + [{<<"sentence">>, atom_to_binary(node())}], + [{only, value}, + {result_links, [{sentence, node, Level, <<"">>}]}])]), + ?LI([?C(unicode:characters_to_binary("📤")), + ?AC(<<(binary:copy(<<"../">>, Level))/binary, "logout/">>, + <<"Logout">>)])])]}]}]. + +%%%================================== + %%%% make_command: API -spec make_command(Name :: atom(), Request :: http_request()) -> xmlel(). @@ -1582,7 +1626,7 @@ make_command(Name, Request) -> Request :: http_request(), BaseArguments :: [{ArgName :: binary(), ArgValue :: binary()}], [Option]) -> - xmlel() | {raw_and_value, any(), xmlel()} + xmlel() | {xmlcdata, binary()} | {raw_and_value, any(), xmlel()} when Option :: {only, presentation | without_presentation | button | result | value | raw_and_value} | {input_name_append, [binary()]} | From f28e7b37bc351e289d31ccc0f810e4ad241d52c3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 11:11:17 +0200 Subject: [PATCH 0633/1302] Fix call to atom_to_binary/1 for Erlang/OTP older than 23 --- src/ejabberd_admin.erl | 2 +- src/ejabberd_web_admin.erl | 2 +- src/misc.erl | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 6de2f7b2f..a1e8d52e3 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -869,7 +869,7 @@ join_cluster_here(_Node, _IsNodes, true, _Ping) -> join_cluster_here(_Node, _IsNodes, _IsKnownNodes, pang) -> {error, "This node cannot reach that node."}; join_cluster_here(Node, false, false, pong) -> - case ejabberd_cluster:call(Node, ejabberd_admin, join_cluster, [atom_to_binary(node())]) of + case ejabberd_cluster:call(Node, ejabberd_admin, join_cluster, [misc:atom_to_binary(node())]) of {ok, _} -> {ok, "Trying to join node to this cluster, wait a few seconds and check the list of nodes."}; Error -> diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index d8fb14d60..7db93bf6a 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1607,7 +1607,7 @@ make_login_items(#request{us = {Username, Host}} = R, Level) -> ?LI([?C(unicode:characters_to_binary("🏭")), make_command(echo, R, - [{<<"sentence">>, atom_to_binary(node())}], + [{<<"sentence">>, misc:atom_to_binary(node())}], [{only, value}, {result_links, [{sentence, node, Level, <<"">>}]}])]), ?LI([?C(unicode:characters_to_binary("📤")), diff --git a/src/misc.erl b/src/misc.erl index 987a48c6c..d53dc3c7b 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -316,6 +316,9 @@ binary_to_atom(Bin) -> tuple_to_binary(T) -> iolist_to_binary(tuple_to_list(T)). +%% erlang:atom_to_binary/1 is available since OTP 23 +%% https://www.erlang.org/doc/apps/erts/erlang#atom_to_binary/1 +%% Let's use /2 for backwards compatibility. atom_to_binary(A) -> erlang:atom_to_binary(A, utf8). From 35783d9a2c236f97e15bcc567751061ce676a038 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 May 2024 13:41:23 +0200 Subject: [PATCH 0634/1302] Format also files in test/ --- tools/rebar3-format.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/rebar3-format.sh b/tools/rebar3-format.sh index bb0f521d0..d83d8e803 100755 --- a/tools/rebar3-format.sh +++ b/tools/rebar3-format.sh @@ -11,13 +11,16 @@ REBAR=$1 -ERLS=$(git grep --name-only @format-begin src/) +FORMAT() +{ +FPATH=$1 +ERLS=$(git grep --name-only @format-begin "$FPATH"/) for ERL in $ERLS; do csplit --quiet --prefix=$ERL-format- $ERL /@format-/ "{*}" done -EFMTS=$(find src/*-format-* -type f -exec grep --files-with-matches "@format-begin" '{}' ';') +EFMTS=$(find "$FPATH"/*-format-* -type f -exec grep --files-with-matches "@format-begin" '{}' ';') EFMTS2="" for EFMT in $EFMTS; do EFMTS2="$EFMTS2 --files $EFMT" @@ -32,3 +35,7 @@ for ERL in $ERLS; do rm $SPLIT done done +} + +FORMAT src +FORMAT test From 9b675fe7757dbab3e53de00eb0e2a23c40c7170e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 12:04:31 +0200 Subject: [PATCH 0635/1302] Add support to test also EUnit suite --- .github/workflows/ci.yml | 1 + Makefile.in | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3f9198c2..6ab74d3c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,6 +132,7 @@ jobs: - run: make options - run: make xref - run: make dialyzer + - run: make test-eunit - name: Check Production Release run: | diff --git a/Makefile.in b/Makefile.in index c89b31249..5adf241d5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -653,12 +653,15 @@ test: @cd priv && ln -sf ../sql $(REBAR) $(SKIPDEPS) ct +test-eunit: + $(REBAR) $(SKIPDEPS) eunit --verbose + #. #' phony # .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean prod rel \ - install uninstall uninstall-binary uninstall-all translations deps test \ + install uninstall uninstall-binary uninstall-all translations deps test test-eunit \ all dev doap help install-rel relive scripts uninstall-rel update \ erlang_plt deps_plt ejabberd_plt xref hooks options format indent @@ -698,6 +701,7 @@ help: @echo " dialyzer Run Dialyzer static analyzer" @echo " hooks Run hooks validator" @echo " test Run Common Tests suite [rebar3]" + @echo " test-eunit Run EUnit suite [rebar3]" @echo " xref Run cross reference analysis [rebar3]" #. From 992d84c002e29ce39902e917fb81b9adcc5ae9f2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 12:04:43 +0200 Subject: [PATCH 0636/1302] Add JSON EUnit testing --- test/json_test.erl | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 test/json_test.erl diff --git a/test/json_test.erl b/test/json_test.erl new file mode 100644 index 000000000..49fad0f25 --- /dev/null +++ b/test/json_test.erl @@ -0,0 +1,72 @@ +-module(json_test). + +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +%% @format-begin + +encode_binary_test() -> + Binary = <<"This is an error text.">>, + Encoded = <<"\"This is an error text.\"">>, + ?assertMatch(Encoded, misc:json_encode(Binary)). + +-ifdef(OTP_BELOW_27). +-ifdef(OTP_BELOW_26). + +%% OTP 25 or lower +encode_map_test() -> + Map = #{name => <<"room">>, + service => <<"conference">>, + jid => jid:encode({<<"user">>, <<"server">>, <<"">>}), + affiliation => member}, + Encoded = + <<"{\"service\":\"conference\",\"name\":\"room\",\"jid\":\"user@server\",\"affiliation\":\"member\"}">>, + ?assertMatch(Encoded, misc:json_encode(Map)). + +-else. + +%% OTP 26 +encode_map_test() -> + Map = #{name => <<"room">>, + service => <<"conference">>, + jid => jid:encode({<<"user">>, <<"server">>, <<"">>}), + affiliation => member}, + Encoded = + <<"{\"affiliation\":\"member\",\"jid\":\"user@server\",\"service\":\"conference\",\"name\":\"room\"}">>, + ?assertMatch(Encoded, misc:json_encode(Map)). + +-endif. + +-else. + +%% OTP 27 or higher or higher +encode_map_test() -> + Map = #{name => <<"room">>, + service => <<"conference">>, + jid => jid:encode({<<"user">>, <<"server">>, <<"">>}), + affiliation => member}, + Encoded27 = + <<"{\"name\":\"room\",\"service\":\"conference\",\"jid\":\"user@server\",\"affiliation\":\"member\"}">>, + ?assertMatch(Encoded27, misc:json_encode(Map)). + +-endif. + +decode_test() -> + Encoded = + <<"{\"affiliation\":\"member\",\"jid\":\"user@server\",\"service\":\"conference\",\"name\":\"room\"}">>, + TupleList = + #{<<"affiliation">> => <<"member">>, + <<"jid">> => <<"user@server">>, + <<"name">> => <<"room">>, + <<"service">> => <<"conference">>}, + ?assertMatch(TupleList, misc:json_decode(Encoded)). + +decode_maps_test() -> + Encoded = + <<"{\"affiliation\":\"member\",\"jid\":\"user@server\",\"service\":\"conference\",\"name\":\"room\"}">>, + Map = #{<<"affiliation">> => misc:atom_to_binary(member), + <<"jid">> => jid:encode({<<"user">>, <<"server">>, <<"">>}), + <<"name">> => <<"room">>, + <<"service">> => <<"conference">>}, + ?assertMatch(Map, misc:json_decode(Encoded)). From 0b6d40c9df5dbad3aeadb98160c4eb0fd64ff453 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 6 Dec 2023 18:16:43 +0100 Subject: [PATCH 0637/1302] Log messages not only when node joins the cluster, also when leaves it --- src/ejabberd_s2s.erl | 1 + src/ejabberd_sm_mnesia.erl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 7fb99a7ed..957d68208 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -237,6 +237,7 @@ handle_cast(Msg, State) -> {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> + ?INFO_MSG("Node ~p has left our Mnesia S2S tables", [Node]), clean_table_from_bad_node(Node), {noreply, State}; handle_info({mnesia_system_event, {mnesia_up, Node}}, State) -> diff --git a/src/ejabberd_sm_mnesia.erl b/src/ejabberd_sm_mnesia.erl index 2b223c85b..26b4c6806 100644 --- a/src/ejabberd_sm_mnesia.erl +++ b/src/ejabberd_sm_mnesia.erl @@ -112,6 +112,7 @@ handle_cast(Msg, State) -> {noreply, State}. handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> + ?INFO_MSG("Node ~p has left our Mnesia SM tables", [Node]), Sessions = ets:select( session, From afe32e2c05025c23df7b810b6723a9e1147c9eef Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 17:01:27 +0200 Subject: [PATCH 0638/1302] Fix generate-doap.sh to accept x in the version number --- ejabberd.doap | 2 +- tools/generate-doap.sh | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index 60d047342..da8678d5e 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -759,7 +759,7 @@ 0.3.0 - /home/bernar/e/git/ejabberd/src/mod_mam.erl:-protocol({xep, 425, '0.3.0', '24.xx', "", ""}). + 24.xx mod_mam diff --git a/tools/generate-doap.sh b/tools/generate-doap.sh index 74de66134..57241fef5 100755 --- a/tools/generate-doap.sh +++ b/tools/generate-doap.sh @@ -75,7 +75,7 @@ write_rfcs() out=$2 int=$(echo $1 | sed 's/^0*//') - imp=$(grep "\-protocol({rfc, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1 \2/") + imp=$(grep "\-protocol({rfc, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-x]*\)'.*/\1 \2/") [ "$imp" = "" ] && imp="NA 0.0" echo " " >>$out @@ -87,16 +87,16 @@ write_xeps() out=$2 comments2="" int=$(echo $1 | sed 's/^0*//') - imp=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1 \2/") + imp=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-x]*\)'.*/\1 \2/") [ "$imp" = "" ] && imp="NA 0.0" - sourcefiles=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-]*\)'.*/\1/" | tr '\012' ',' | sed 's|,$||' | sed 's|,|, |g' | sed 's|^ejabberd$||') + sourcefiles=$(grep "\-protocol({xep, $int," $BASE/src/* | sed "s/.*src\/\(.*\).erl.*'\([0-9.-x]*\)'.*/\1/" | tr '\012' ',' | sed 's|,$||' | sed 's|,|, |g' | sed 's|^ejabberd$||') - versionsold=$(grep "\-protocol({xep, $int, .*'})\." $BASE/src/* | sed "s/.*'\([0-9.-]*\)'.*/\1/" | head -1) - versionsnew=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*'\([0-9.-]*\)', '.*/\1/" | head -1) + versionsold=$(grep "\-protocol({xep, $int, .*'})\." $BASE/src/* | sed "s/.*'\([0-9.-x]*\)'.*/\1/" | head -1) + versionsnew=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*'\([0-9.-x]*\)', '.*/\1/" | head -1) versions="$versionsold$versionsnew" - since=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*', '\([0-9.-]*\)',.*/\1/" | head -1) + since=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*', '\([0-9.-x]*\)',.*/\1/" | head -1) status=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*', \"\([a-z]*\)\", \".*/\1/" | head -1) comments=$(grep "\-protocol({xep, $int, .*\"})\." $BASE/src/* | sed "s/.*\", \"\(.*\)\"}.*/\1/" | head -1) From 28e94513db379ad50efc66f8df87177d00e718a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 27 Jun 2024 13:50:03 +0200 Subject: [PATCH 0639/1302] Update deps --- mix.exs | 10 +++++----- mix.lock | 42 +++++++++++++++++++++--------------------- rebar.config | 36 ++++++++++++++++++------------------ 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/mix.exs b/mix.exs index 47e49682b..4c4259f5b 100644 --- a/mix.exs +++ b/mix.exs @@ -140,12 +140,12 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, git: "https://github.com/processone/p1_acme", branch: "master"}, + {:p1_acme, "~> 1.0"}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, - {:pkix, git: "https://github.com/processone/pkix"}, + {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", override: true}, + {:xmpp, ">= 1.8.2"}, {:yconf, "~> 1.0"}] ++ cond_deps() end @@ -175,8 +175,8 @@ defmodule Ejabberd.MixProject do {if_version_below(~c"27", true), {:jiffy, "~> 1.1.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.2.0"}}, - {config(:mysql), {:p1_mysql, git: "https://github.com/processone/p1_mysql" }}, - {config(:pgsql), {:p1_pgsql, git: "https://github.com/processone/p1_pgsql"}}, + {config(:mysql), {:p1_mysql, ">= 1.0.24"}}, + {config(:pgsql), {:p1_pgsql, ">= 1.1.26"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: dep diff --git a/mix.lock b/mix.lock index 72b96da22..4078a6bf3 100644 --- a/mix.lock +++ b/mix.lock @@ -1,39 +1,39 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, - "cache_tab": {:hex, :cache_tab, "1.0.30", "6d35eecfb65fbe5fc85988503a27338d32de01243f3fc8ea3ee7161af08725a4", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6d8a5e00d8f84c42627706a6dbedb02e34d58495f3ed61935c8475ca0531cda0"}, + "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, - "eimp": {:hex, :eimp, "1.0.22", "fa9b376ef0b50e8455db15c7c11dea4522c6902e04412288aab436d26335f6eb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b3b9ffb1d9a5f4a2ba88ac418a819164932d9a9d3a2fc3d32ca338ce855c4392"}, + "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "esip": {:hex, :esip, "1.0.52", "a2840287c493a4280e6fba57a257706843b025c315875e38b03fd07190e22dba", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.12", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "6f00165395900500aa262ce0297162d93931c78c1464d89fd0edc6e3d6bc011f"}, - "ex_doc": {:hex, :ex_doc, "0.33.0", "690562b153153c7e4d455dc21dab86e445f66ceba718defe64b0ef6f0bd83ba0", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "3f69adc28274cb51be37d09b03e4565232862a4b10288a3894587b0131412124"}, + "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, + "esip": {:hex, :esip, "1.0.53", "482786a79d8f5b0aefc60ca68b8c6c7f074f6ede2653705e4206c6779edc5a56", [:rebar3], [{:fast_tls, "1.1.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.13", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a9a1bd5ea52b0e2d1b1d1fec5fb7f0301e90610db1ecfeb205a18cc4e1cb3fc7"}, + "ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, - "ezlib": {:hex, :ezlib, "1.0.12", "ffe906ba10d03aaee7977e1e0e81d9ffc3bb8b47fb9cd8e2e453507a2e56221f", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "30e94355fb42260aab6e12582cb0c56bf233515e655c8aeaf48760e7561e4ebb"}, - "fast_tls": {:hex, :fast_tls, "1.1.19", "f52731a4b35259fa06cf23e2a0732920ad9efce7c3d68377f129a474998747bb", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "db34322c8782d4c5139ccb80709d8ec8c38089b44262edd0c2f660ac495bd389"}, - "fast_xml": {:hex, :fast_xml, "1.1.51", "a7f8c6942591632309099386d5c339c89997ac2bbdd1216f6c196dee6d7828a9", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7fce41b7d1a4ba438a2d7a088dabe74a3ca0739f1af2abcb77e62daf43e0409a"}, - "fast_yaml": {:hex, :fast_yaml, "1.0.36", "65413a34a570fd4e205a460ba602e4ee7a682f35c22d2e1c839025dbf515105c", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "1abe8f758fc2a86b08edff80bbc687cfd41ebc1412cfec0ef4a0acfcd032052f"}, + "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, + "fast_tls": {:hex, :fast_tls, "1.1.20", "d6f12d9ae4fe57e880b144b912e735af89343a8463d39b7eb4be3f6ca6163879", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d09a12472a56a34c5eecaaed33ea283f00fcdf9dc2e8282ecbaae827f13fc21b"}, + "fast_xml": {:hex, :fast_xml, "1.1.52", "0289daafbf1190b0e53b444d4885cccf41e4b05768d4b3acc76dd8d143668e10", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "795192390e06d2b65016a6990bbfa5727f4a26d2914808b1c3c9a32eedcd1bfd"}, + "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jiffy": {:hex, :jiffy, "1.1.1", "aca10f47aa91697bf24ab9582c74e00e8e95474c7ef9f76d4f1a338d0f5de21b", [:rebar3], [], "hexpm", "62e1f0581c3c19c33a725c781dfa88410d8bff1bbafc3885a2552286b4785c4c"}, + "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, - "mqtree": {:hex, :mqtree, "1.0.16", "f8f8b4971e4ca94313ba9bcaaa1aa1077daaba5e3fd3468ffb491420a4cc3593", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c87d1c95575db65af29b795c9daa3bed43f5c1bf84072a74469659bcf53594eb"}, + "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "176b4a8c67627c3229fbea3c05d82a5d567e6e49", [branch: "master"]}, - "p1_mysql": {:git, "https://github.com/processone/p1_mysql", "0dccb755a60cda61d41e7b092b344d38d1ebc802", []}, + "p1_acme": {:hex, :p1_acme, "1.0.23", "791aef0f79dc7f768b228808250c349fa9ce585cd8779da50ca93106eb3394d0", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "8ce196f26e3d22ea10b7809122950465878c127f80767e325207aed7e8d0dd59"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.24", "0ed1e098c5a4525032448c65a2715f30980aae725615a4d255fd25f26bb22507", [:rebar3], [], "hexpm", "f058865f64257f507a2c6a5aff369b1375dbcb30b3d4258dad4f1b3eaffb655f"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:git, "https://github.com/processone/p1_pgsql", "fe0bb2a2a2ae21cd9fc48b81520cfce508520a56", []}, - "p1_utils": {:hex, :p1_utils, "1.0.25", "2d39b5015a567bbd2cc7033eeb93a7c60d8c84efe1ef69a3473faa07fa268187", [:rebar3], [], "hexpm", "9219214428f2c6e5d3187ff8eb9a8783695c2427420be9a259840e07ada32847"}, - "pkix": {:git, "https://github.com/processone/pkix", "03be27c7168449bdfeb8c5b6cc0f800f71f8cfe9", []}, - "sqlite3": {:hex, :sqlite3, "1.1.14", "f9ea0cff8540865fdfdb7e24eef34dc46677364b1c070896e99b5bf08c8a7fd7", [:rebar3], [], "hexpm", "85054b6ca297343c159ed6794a473ff2c8eeabd854b6fe02f711c0bfd373ce86"}, - "stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"}, - "stun": {:hex, :stun, "1.2.12", "a65df67a8aaaecb6a94d687977b2e9f161820819910cb97bbe26a3525356525b", [:rebar3], [{:fast_tls, "1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a2055032b6d338d0454142004bcb12fafb0c64ab1f273f1d0c6923ebbc8ede40"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.26", "d3c3748c3638a1d7db5644e4fc63a6da7614b3009e172ef92a01d0217c3bec65", [:rebar3], [{:xmpp, "~> 1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "85f230db530333106b8a1f9e5d5af032e6c3dd23b432e03d68e9d29013a6dcfc"}, + "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, + "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, + "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, + "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, + "stun": {:hex, :stun, "1.2.13", "c3e855f10f6b0c3ac150bce3d6c96c04a85207a3a5c7a7207876d8b36db2b0a4", [:rebar3], [{:fast_tls, "1.1.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9cf4191491a60573ed6197e636530af1d25c9b064845aabed0c02f780d33ea3f"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "a1fb778bd385d832f913e564558152ea507dac37", []}, - "yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"}, + "xmpp": {:hex, :xmpp, "1.8.2", "7c26fae7ca83b307bab99624595dce706d427a17eed9c6305980550f8120a3a3", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "53a9f85ad44103a358dc173225bc96d08076d4e78506f87d838337b2fa3b381f"}, + "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index 7f7b77622..bbb02af56 100644 --- a/rebar.config +++ b/rebar.config @@ -26,19 +26,19 @@ {if_version_below, "24", {base64url, "~> 1.0", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} }}, - {cache_tab, "~> 1.0.30", {git, "https://github.com/processone/cache_tab", {tag, "1.0.30"}}}, - {eimp, "~> 1.0.22", {git, "https://github.com/processone/eimp", {tag, "1.0.22"}}}, + {cache_tab, "~> 1.0.30", {git, "https://github.com/processone/cache_tab", {tag, "1.0.31"}}}, + {eimp, "~> 1.0.22", {git, "https://github.com/processone/eimp", {tag, "1.0.23"}}}, {if_var_true, pam, {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.52"}}}}, + {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.53"}}}}, {if_var_true, zlib, - {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.12"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.19"}}}, - {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.51"}}}, - {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.36"}}}, + {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.20"}}}, + {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.52"}}}, + {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}} @@ -55,22 +55,22 @@ {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}}, {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, - {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.16"}}}, - {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {branch, "master"}}}, + {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, + {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.23"}}}, {if_var_true, mysql, - {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {branch, "master"}}}}, + {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.24"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {branch, "master"}}}}, - {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.25"}}}, - {pkix, ".*", {git, "https://github.com/processone/pkix", {branch, "master"}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.26"}}}}, + {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, + {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, - {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.14"}}}}, - {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}}, + {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, + {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, - {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.12"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", {branch, "master"}}}, - {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}} + {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.13"}}}}, + {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", {tag, "1.8.2"}}}, + {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. {gitonly_deps, [ejabberd_po]}. From 45f0f983b1064d8aa2372d641f1dfd298574e712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 27 Jun 2024 14:51:14 +0200 Subject: [PATCH 0640/1302] Override jose version for 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, - {if_version_below(~c"24", true), {:jose, "1.11.1"}}, + {if_version_below(~c"24", true), {:jose, "1.11.1", override: true}}, {if_version_below(~c"27", true), {:jiffy, "~> 1.1.1"}}, {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.2.0"}}, From 3ff3bdf81290040ec914d91384579b3fc0032cf4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 11:33:17 +0200 Subject: [PATCH 0641/1302] Update Chinese translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 418 ++++++++++++++++++++--------------------------- 1 file changed, 174 insertions(+), 244 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 64924a826..b6817a1f6 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -3,21 +3,19 @@ %% To improve translations please read: %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ -{" (Add * to the end of field to match substring)"," (在字段末添加*来匹配子串)"}. -{" has set the subject to: "," 已将标题设置为: "}. +{" (Add * to the end of field to match substring)"," (在字段末尾添加 * 以匹配子字符串)"}. +{" has set the subject to: "," 已将话题设置为: "}. {"# participants","# 参与者"}. -{"A description of the node","该节点的描述"}. -{"A friendly name for the node","该节点的友好名称"}. -{"A password is required to enter this room","进入此房间需要密码"}. +{"A description of the node","节点的描述"}. +{"A friendly name for the node","节点的易记名称"}. +{"A password is required to enter this room","进入此群聊需要密码"}. {"A Web Page","网页"}. {"Accept","接受"}. -{"Access denied by service policy","访问被服务策略拒绝"}. +{"Access denied by service policy","服务策略拒绝访问"}. {"Access model","访问模型"}. {"Account doesn't exist","账号不存在"}. -{"Action on user","对用户执行的操作"}. +{"Action on user","对用户的操作"}. {"Add a hat to a user","给用户添加头衔"}. -{"Add Jabber ID","添加 Jabber ID"}. -{"Add New","添加新用户"}. {"Add User","添加用户"}. {"Administration of ","管理 "}. {"Administration","管理"}. @@ -26,21 +24,21 @@ {"All Users","所有用户"}. {"Allow subscription","允许订阅"}. {"Allow this Jabber ID to subscribe to this pubsub node?","允许此 Jabber ID 订阅此 pubsub 节点?"}. -{"Allow this person to register with the room?","允许此用户注册此房间?"}. -{"Allow users to change the subject","允许用户更改主题"}. -{"Allow users to query other users","允许用户查询其它用户"}. +{"Allow this person to register with the room?","允许此用户注册此群聊?"}. +{"Allow users to change the subject","允许用户更改话题"}. +{"Allow users to query other users","允许用户查询其他用户"}. {"Allow users to send invites","允许用户发送邀请"}. {"Allow users to send private messages","允许用户发送私信"}. {"Allow visitors to change nickname","允许访客更改昵称"}. {"Allow visitors to send private messages to","允许访客发送私信至"}. {"Allow visitors to send status text in presence updates","允许访客在在线状态更新中发送状态文本"}. {"Allow visitors to send voice requests","允许访客发送发言请求"}. -{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义房间成员资格相关联的 LDAP 群组;按群组特定于实现或特定于部署的定义,应该是一个 LDAP 专有名称。"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义群聊成员资格相关联的 LDAP 群组;按群组特定于实现或特定于部署的定义,应该是一个 LDAP 专有名称。"}. {"Announcements","公告"}. -{"Answer associated with a picture","与图片关联的回答"}. -{"Answer associated with a video","与视频关联的回答"}. -{"Answer associated with speech","与讲话关联的回答"}. -{"Answer to a question","问题的回答"}. +{"Answer associated with a picture","与图片相关的答案"}. +{"Answer associated with a video","与视频相关的答案"}. +{"Answer associated with speech","与讲话相关的答案"}. +{"Answer to a question","问题的答案"}. {"Anyone in the specified roster group(s) may subscribe and retrieve items","指定花名册群组中的人可以订阅并检索内容项"}. {"Anyone may associate leaf nodes with the collection","任何人都可以将叶子节点与集合关联"}. {"Anyone may publish","任何人都可以发布"}. @@ -48,76 +46,65 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","对全部或来源进行了状态订阅的任何人均可订阅并检索内容项"}. {"Anyone with Voice","任何有发言权的人"}. {"Anyone","任何人"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","显然,您的账号在此服务器上没有管理权限。请检查如何授予管理员权限:https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","四月"}. -{"Attribute 'channel' is required for this request","此请求要求'频道'属性"}. -{"Attribute 'id' is mandatory for MIX messages","对 MIX 消息,'id' 属性为必填项"}. -{"Attribute 'jid' is not allowed here","此处不允许 'jid' 属性"}. -{"Attribute 'node' is not allowed here","此处不允许 'node' 属性"}. -{"Attribute 'to' of stanza that triggered challenge","触发挑战一节的 'to' 属性"}. +{"Attribute 'channel' is required for this request","此请求要求“channel”属性"}. +{"Attribute 'id' is mandatory for MIX messages","对于 MIX 消息,“id”属性是必需的"}. +{"Attribute 'jid' is not allowed here","此处不允许“jid”属性"}. +{"Attribute 'node' is not allowed here","此处不允许“node”属性"}. +{"Attribute 'to' of stanza that triggered challenge","触发挑战节的“to”属性"}. {"August","八月"}. {"Automatic node creation is not enabled","未启用自动节点创建"}. {"Backup Management","备份管理"}. -{"Backup of ~p","~p的备份"}. +{"Backup of ~p","~p 的备份"}. {"Backup to File at ","备份文件位于 "}. {"Backup","备份"}. {"Bad format","格式错误"}. -{"Birthday","出生日期"}. +{"Birthday","生日"}. {"Both the username and the resource are required","用户名和资源均为必填项"}. -{"Bytestream already activated","字节流已经被激活"}. +{"Bytestream already activated","字节流已激活"}. {"Cannot remove active list","无法移除活动列表"}. {"Cannot remove default list","无法移除默认列表"}. {"CAPTCHA web page","验证码网页"}. {"Challenge ID","挑战 ID"}. {"Change Password","更改密码"}. {"Change User Password","更改用户密码"}. -{"Changing password is not allowed","不允许修改密码"}. -{"Changing role/affiliation is not allowed","不允许修改角色/从属关系"}. +{"Changing password is not allowed","不允许更改密码"}. +{"Changing role/affiliation is not allowed","不允许更改角色/从属关系"}. {"Channel already exists","频道已存在"}. {"Channel does not exist","频道不存在"}. {"Channel JID","频道 JID"}. {"Channels","频道"}. {"Characters not allowed:","不允许字符:"}. -{"Chatroom configuration modified","聊天室配置已修改"}. -{"Chatroom is created","聊天室已创建"}. -{"Chatroom is destroyed","聊天室已解散"}. -{"Chatroom is started","聊天室已启动"}. -{"Chatroom is stopped","聊天室已停用"}. -{"Chatrooms","聊天室"}. -{"Choose a username and password to register with this server","选择要在此服务器上注册的用户名和密码"}. -{"Choose storage type of tables","请选择表格的存储类型"}. -{"Choose whether to approve this entity's subscription.","选择是否允许该实体的订阅。"}. +{"Chatroom configuration modified","群聊配置已修改"}. +{"Chatroom is created","群聊已创建"}. +{"Chatroom is destroyed","群聊已解散"}. +{"Chatroom is started","群聊已开始"}. +{"Chatroom is stopped","群聊已停止"}. +{"Chatrooms","群聊"}. +{"Choose a username and password to register with this server","请选择要在此服务器中注册的用户名和密码"}. +{"Choose storage type of tables","请选择表单的存储类型"}. +{"Choose whether to approve this entity's subscription.","选择是否批准此实体的订阅。"}. {"City","城市"}. {"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. {"Commands","命令"}. -{"Conference room does not exist","会议室不存在"}. -{"Configuration of room ~s","房间~s的配置"}. +{"Conference room does not exist","群聊不存在"}. +{"Configuration of room ~s","群聊 ~s 的配置"}. {"Configuration","配置"}. -{"Connected Resources:","已连接资源:"}. -{"Contact Addresses (normally, room owner or owners)","联系人地址(通常为房间所有者)"}. -{"Contrib Modules","Contrib 模块"}. +{"Contact Addresses (normally, room owner or owners)","联系人地址(通常为群聊所有者)"}. {"Country","国家/地区"}. -{"CPU Time:","CPU 时间:"}. {"Current Discussion Topic","当前讨论话题"}. {"Database failure","数据库失败"}. -{"Database Tables at ~p","位于~p的数据库表"}. {"Database Tables Configuration at ","数据库表格配置位于 "}. {"Database","数据库"}. {"December","十二月"}. {"Default users as participants","默认用户为参与者"}. -{"Delete content","删除内容"}. {"Delete message of the day on all hosts","删除所有主机上的每日消息"}. {"Delete message of the day","删除每日消息"}. -{"Delete Selected","删除已选内容"}. -{"Delete table","删除表格"}. {"Delete User","删除用户"}. {"Deliver event notifications","传递事件通知"}. -{"Deliver payloads with event notifications","用事件通告传输有效负载"}. -{"Description:","描述:"}. +{"Deliver payloads with event notifications","用事件通知传递有效负载"}. {"Disc only copy","仅磁盘复制"}. -{"'Displayed groups' not added (they do not exist!): ","'显示的群组' 未被添加(它们不存在!): "}. -{"Displayed:","已显示:"}. -{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人,甚至是 XMPP 服务器的管理员。"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人,甚至是 XMPP 服务的管理员。"}. {"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. {"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许有重复的群组"}. @@ -127,103 +114,94 @@ {"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. {"ejabberd Multicast service","ejabberd 多重映射服务"}. -{"ejabberd Publish-Subscribe module","ejabberd 发行-订阅模块"}. +{"ejabberd Publish-Subscribe module","ejabberd 发布—订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. {"ejabberd vCard module","ejabberd vCard 模块"}. {"ejabberd Web Admin","ejabberd Web 管理员"}. {"ejabberd","ejabberd"}. -{"Elements","元素"}. {"Email Address","电子邮件地址"}. {"Email","电子邮件"}. {"Enable hats","启用头衔"}. -{"Enable logging","启用服务器端聊天记录"}. +{"Enable logging","启用日志记录"}. {"Enable message archiving","启用消息存档"}. -{"Enabling push without 'node' attribute is not supported","不支持未使用'node'属性就开启推送"}. +{"Enabling push without 'node' attribute is not supported","不支持没有“node”属性就启用推送"}. {"End User Session","结束用户会话"}. -{"Enter nickname you want to register","请输入您想要注册的昵称"}. +{"Enter nickname you want to register","请输入要注册的昵称"}. {"Enter path to backup file","请输入备份文件的路径"}. {"Enter path to jabberd14 spool dir","请输入 jabberd14 spool 目录的路径"}. {"Enter path to jabberd14 spool file","请输入 jabberd14 spool 文件的路径"}. {"Enter path to text file","请输入文本文件的路径"}. -{"Enter the text you see","请输入您所看到的文本"}. +{"Enter the text you see","请输入您看到的文本"}. {"Erlang XMPP Server","Erlang XMPP 服务器"}. -{"Error","错误"}. {"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除 Jabber ID"}. {"Export all tables as SQL queries to a file:","将所有表以 SQL 查询语句导出到文件:"}. -{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器上所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. -{"Export data of users in a host to PIEFXIS files (XEP-0227):","将某主机的用户数据导出到 PIEFXIS 文件(XEP-0227):"}. -{"External component failure","外部组件失败"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器中所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","将主机中用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. +{"External component failure","外部组件故障"}. {"External component timeout","外部组件超时"}. {"Failed to activate bytestream","激活字节流失败"}. -{"Failed to extract JID from your voice request approval","从您的发言请求批准提取 JID 失败"}. +{"Failed to extract JID from your voice request approval","无法从您的发言请求批准中提取 JID"}. {"Failed to map delegated namespace to external component","未能将代理命名空间映射到外部组件"}. {"Failed to parse HTTP response","HTTP 响应解析失败"}. -{"Failed to process option '~s'","选项'~s'处理失败"}. +{"Failed to process option '~s'","无法处理选项“~s”"}. {"Family Name","姓氏"}. -{"FAQ Entry","常见问题入口"}. +{"FAQ Entry","常见问题解答"}. {"February","二月"}. {"File larger than ~w bytes","文件大于 ~w 字节"}. -{"Fill in the form to search for any matching XMPP User","填充表单来搜索任何匹配的 XMPP 用户"}. +{"Fill in the form to search for any matching XMPP User","填写表单以搜索任何匹配的 XMPP 用户"}. {"Friday","星期五"}. {"From ~ts","来自 ~ts"}. -{"From","从"}. -{"Full List of Room Admins","房间管理员完整列表"}. -{"Full List of Room Owners","房间所有者完整列表"}. +{"Full List of Room Admins","群聊管理员完整列表"}. +{"Full List of Room Owners","群聊所有者完整列表"}. {"Full Name","全名"}. {"Get List of Online Users","获取在线用户列表"}. {"Get List of Registered Users","获取注册用户列表"}. {"Get Number of Online Users","获取在线用户数"}. {"Get Number of Registered Users","获取注册用户数"}. -{"Get Pending","获取挂起"}. +{"Get Pending","获取待处理"}. {"Get User Last Login Time","获取用户上次登录时间"}. -{"Get User Password","获取用户密码"}. {"Get User Statistics","获取用户统计"}. {"Given Name","中间名"}. -{"Grant voice to this person?","允许此用户发言?"}. -{"Groups that will be displayed to the members","将显示给成员的群组"}. -{"Groups","组"}. -{"Group","组"}. -{"has been banned","已被禁止"}. +{"Grant voice to this person?","授予此用户发言权?"}. +{"has been banned","已被封禁"}. {"has been kicked because of a system shutdown","因系统关机而被踢出"}. -{"has been kicked because of an affiliation change","因从属关系改变而被踢出"}. -{"has been kicked because the room has been changed to members-only","被踢了,因为房间已改为仅成员进入"}. +{"has been kicked because of an affiliation change","由于从属关系的改变而踢出"}. +{"has been kicked because the room has been changed to members-only","被踢出,因为群聊已更改为仅成员进入"}. {"has been kicked","已被踢出"}. {"Hat title","头衔标题"}. {"Hat URI","头衔 URI"}. {"Hats limit exceeded","已超过头衔限制"}. {"Host unknown","主机未知"}. -{"Host","主机"}. {"HTTP File Upload","HTTP 文件上传"}. {"Idle connection","空闲连接"}. -{"If you don't see the CAPTCHA image here, visit the web page.","如果您在此处没有看到 CAPTCHA 图片,请访问网页。"}. +{"If you don't see the CAPTCHA image here, visit the web page.","如果您在此处没有看到验证码图片,请访问网页。"}. {"Import Directory","导入目录"}. {"Import File","导入文件"}. {"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. {"Import User from File at ","从以下位置的文件导入用户 "}. -{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件(XEP-0227) 导入用户数据:"}. +{"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件(XEP-0227)导入用户数据:"}. {"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. {"Import Users from Dir at ","从以下位置目录导入用户 "}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. -{"Improper domain part of 'from' attribute","不恰当的'from'属性域名部分"}. -{"Improper message type","不恰当的消息类型"}. -{"Incoming s2s Connections:","入站 s2s 连接:"}. +{"Improper domain part of 'from' attribute","“from”属性域名部分不正确"}. +{"Improper message type","消息类型不正确"}. {"Incorrect CAPTCHA submit","提交的验证码不正确"}. {"Incorrect data form","数据形式不正确"}. {"Incorrect password","密码不正确"}. -{"Incorrect value of 'action' attribute","'action' 属性的值不正确"}. -{"Incorrect value of 'action' in data form","数据表单中 'action' 的值不正确"}. -{"Incorrect value of 'path' in data form","数据表单中 'path' 的值不正确"}. +{"Incorrect value of 'action' attribute","“action”属性的值不正确"}. +{"Incorrect value of 'action' in data form","数据表单中“action”的值不正确"}. +{"Incorrect value of 'path' in data form","数据表单中“path”的值不正确"}. {"Installed Modules:","已安装的模块:"}. {"Install","安装"}. {"Insufficient privilege","权限不足"}. {"Internal server error","内部服务器错误"}. -{"Invalid 'from' attribute in forwarded message","转发的信息中 'from' 属性的值无效"}. +{"Invalid 'from' attribute in forwarded message","转发消息中的“from”属性无效"}. {"Invalid node name","无效的节点名称"}. -{"Invalid 'previd' value","无效的 'previd' 值"}. +{"Invalid 'previd' value","“previd”值无效"}. {"Invitations are not allowed in this conference","此群聊不允许邀请"}. {"IP addresses","IP 地址"}. -{"is now known as","现在称呼为"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此房间发送错误消息。参与者(~s) 发送了错误消息(~s),被踢出了此房间"}. +{"is now known as","现在昵称为"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此群聊发送错误消息。参与者(~s)发送了错误消息(~s),被踢出了群聊"}. {"It is not allowed to send private messages of type \"groupchat\"","不允许发送“群聊”类型的私信"}. {"It is not allowed to send private messages to the conference","不允许向群聊发送私信"}. {"Jabber ID","Jabber ID"}. @@ -232,80 +210,71 @@ {"JID normalization failed","JID 规范化失败"}. {"Joined MIX channels of ~ts","加入了 ~ts 的 MIX 频道"}. {"Joined MIX channels:","加入了 MIX 频道:"}. -{"joins the room","加入房间"}. +{"joins the room","加入群聊"}. {"July","七月"}. {"June","六月"}. {"Just created","刚刚创建"}. -{"Label:","标签:"}. {"Last Activity","上次活动"}. {"Last login","上次登录"}. {"Last message","最近消息"}. {"Last month","上个月"}. -{"Last year","上一年"}. +{"Last year","去年"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的 SHA-256 哈希的最低有效位应等于十六进制标签"}. -{"leaves the room","离开房间"}. -{"List of rooms","房间列表"}. +{"leaves the room","离开群聊"}. {"List of users with hats","有头衔用户的列表"}. {"List users with hats","有头衔用户列表"}. {"Logging","正在记录"}. -{"Low level update script","低级别更新脚本"}. {"Make participants list public","公开参与者列表"}. -{"Make room CAPTCHA protected","使房间受验证码保护"}. -{"Make room members-only","使房间仅成员进入"}. -{"Make room moderated","对房间进行审核"}. -{"Make room password protected","使房间受密码保护"}. -{"Make room persistent","使房间持久"}. -{"Make room public searchable","使房间可公开搜索"}. +{"Make room CAPTCHA protected","让群聊受验证码保护"}. +{"Make room members-only","让群聊仅成员进入"}. +{"Make room moderated","开启群聊发言审核"}. +{"Make room password protected","让群聊受密码保护"}. +{"Make room persistent","让群聊持续存在"}. +{"Make room public searchable","让群聊可公开搜索"}. {"Malformed username","用户名格式不正确"}. {"MAM preference modification denied by service policy","MAM 偏好被服务策略拒绝"}. {"March","三月"}. -{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要保留的最大项目数 #,`max`表示除了服务器强加的最大值之外没有特定限制"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要持久化的最大项目数 #,或“max”表示除服务器施加的最大值之外没有特定限制"}. {"Max payload size in bytes","最大有效负载字节数"}. {"Maximum file size","最大文件大小"}. -{"Maximum Number of History Messages Returned by Room","房间返回的历史消息最大值"}. -{"Maximum number of items to persist","持久化内容的最大条目数"}. +{"Maximum Number of History Messages Returned by Room","群聊返回的聊天记录消息的最大值"}. +{"Maximum number of items to persist","要持久化的最大项目数"}. {"Maximum Number of Occupants","最大占用人数"}. {"May","五月"}. -{"Members not added (inexistent vhost!): ","成员未添加(不存在的vhost!): "}. -{"Membership is required to enter this room","进入此房间需要成员资格"}. -{"Members:","成员:"}. -{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住您的密码,或将其写在纸上并放在安全的地方。 在 XMPP 中,如果您忘记密码,则没有自动恢复密码的方法。"}. -{"Memory","内存"}. -{"Mere Availability in XMPP (No Show Value)","仅 XMPP 中的可用性(不显示值)"}. +{"Membership is required to enter this room","进入此群聊需要成员资格"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住密码,或将密码写在纸上,放在安全的地方。在 XMPP 中,如果您忘记密码,没有自动恢复密码的方法。"}. +{"Mere Availability in XMPP (No Show Value)","XMPP 中的可用性(无显示值)"}. {"Message body","消息主体"}. {"Message not found in forwarded payload","转发的有效载荷中找不到消息"}. {"Messages from strangers are rejected","拒绝来自陌生人的消息"}. {"Messages of type headline","标题类型的消息"}. {"Messages of type normal","普通类型的消息"}. {"Middle Name","中间名"}. -{"Minimum interval between voice requests (in seconds)","发言请求的最小间隔(秒)"}. +{"Minimum interval between voice requests (in seconds)","发言请求之间的最短间隔时间(秒)"}. {"Moderator privileges required","需要主持人权限"}. -{"Moderators Only","仅限主持人"}. +{"Moderators Only","仅主持人"}. {"Moderator","主持人"}. -{"Modified modules","被修改模块"}. -{"Module failed to handle the query","模块未能处理查询"}. +{"Module failed to handle the query","模块无法处理查询"}. {"Monday","星期一"}. {"Multicast","多重映射"}. {"Multiple elements are not allowed by RFC6121","按照 RFC6121,多个 元素是不允许的"}. {"Multi-User Chat","多用户聊天"}. -{"Name in the rosters where this group will be displayed","花名册中将显示的该分组的名称"}. -{"Name","姓名"}. -{"Name:","姓名:"}. -{"Natural Language for Room Discussions","房间讨论的自然语言"}. -{"Natural-Language Room Name","自然语言房间名称"}. -{"Neither 'jid' nor 'nick' attribute found","属性 'jid' 或 'nick' 均未发现"}. -{"Neither 'role' nor 'affiliation' attribute found","属性 'role' 或 'affiliation' 均未发现"}. -{"Never","从未"}. +{"Name","名称"}. +{"Natural Language for Room Discussions","群聊讨论的自然语言"}. +{"Natural-Language Room Name","自然语言群聊名称"}. +{"Neither 'jid' nor 'nick' attribute found","未找到“jid”和“nick”属性"}. +{"Neither 'role' nor 'affiliation' attribute found","未找到“role”或“affiliation”属性"}. +{"Never","从不"}. {"New Password:","新密码:"}. {"Nickname can't be empty","昵称不能为空"}. {"Nickname Registration at ","昵称注册于 "}. -{"Nickname ~s does not exist in the room","昵称~s不在该房间"}. +{"Nickname ~s does not exist in the room","昵称 ~s 不在此群聊"}. {"Nickname","昵称"}. -{"No address elements found","没有找到地址的各元素"}. -{"No addresses element found","没有找到各地址的元素"}. -{"No 'affiliation' attribute found","未发现 'affiliation' 属性"}. -{"No available resource found","没发现可用资源"}. -{"No body provided for announce message","通知消息无正文内容"}. +{"No address elements found","找不到地址元素"}. +{"No addresses element found","找不到地址元素"}. +{"No 'affiliation' attribute found","未找到“affiliation”属性"}. +{"No available resource found","找不到可用资源"}. +{"No body provided for announce message","没有为公告消息提供正文"}. {"No child elements found","没有找到子元素"}. {"No data form found","没有找到数据表单"}. {"No Data","没有数据"}. @@ -313,27 +282,27 @@ {"No element found","未找到 元素"}. {"No hook has processed this command","没有任何钩子已处理此命令"}. {"No info about last activity found","未找到上次活动的信息"}. -{"No 'item' element found","没有找到 'item' 元素"}. -{"No items found in this query","此查询中没发现任何项"}. +{"No 'item' element found","未找到“item”元素"}. +{"No items found in this query","在此查询中找不到任何项目"}. {"No limit","不限"}. -{"No module is handling this query","没有正在处理此查询的模块"}. -{"No node specified","无指定节点"}. -{"No 'password' found in data form","数据表单中未发现 'password'"}. -{"No 'password' found in this query","此查询中未发现 'password'"}. -{"No 'path' found in data form","数据表单中未发现 'path'"}. -{"No pending subscriptions found","未发现挂起的订阅"}. +{"No module is handling this query","没有模块正在处理此查询"}. +{"No node specified","未指定节点"}. +{"No 'password' found in data form","在数据表单中找不到“password”"}. +{"No 'password' found in this query","在此查询中找不到“password”"}. +{"No 'path' found in data form","在数据表单中找不到“path”"}. +{"No pending subscriptions found","未找到待处理的订阅"}. {"No privacy list with this name found","未找到带此名称的隐私列表"}. {"No private data found in this query","此查询中未发现私有数据"}. {"No running node found","没有找到运行中的节点"}. {"No services available","无可用服务"}. {"No statistics found for this item","未找到此项的统计数据"}. -{"No 'to' attribute found in the invitation","邀请中未发现 'to' 标签"}. +{"No 'to' attribute found in the invitation","邀请中未找到“to”属性"}. {"Nobody","没有人"}. {"Node already exists","节点已存在"}. {"Node ID","节点 ID"}. {"Node index not found","没有找到节点索引"}. {"Node not found","没有找到节点"}. -{"Node ~p","节点~p"}. +{"Node ~p","节点 ~p"}. {"Nodeprep has failed","Nodeprep 已失效"}. {"Nodes","节点"}. {"Node","节点"}. @@ -345,7 +314,7 @@ {"Notify subscribers when the node configuration changes","当节点设置改变时通知订阅者"}. {"Notify subscribers when the node is deleted","当节点被删除时通知订阅者"}. {"November","十一月"}. -{"Number of answers required","需要的回答数量"}. +{"Number of answers required","所需答案数量"}. {"Number of occupants","占用人数"}. {"Number of Offline Messages","离线消息数量"}. {"Number of online users","在线用户数"}. @@ -353,23 +322,19 @@ {"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","等待多少秒后自动清除项目,“max”表示除服务器施加的最大值外没有特定限制"}. {"Occupants are allowed to invite others","允许占用者邀请别人"}. {"Occupants are allowed to query others","允许占用者查询别人"}. -{"Occupants May Change the Subject","占用者可以修改主题"}. +{"Occupants May Change the Subject","占用者可以更改话题"}. {"October","十月"}. -{"Offline Messages","离线消息"}. -{"Offline Messages:","离线消息:"}. {"OK","确定"}. {"Old Password:","旧密码:"}. {"Online Users","在线用户"}. -{"Online Users:","在线用户:"}. {"Online","在线"}. -{"Only admins can see this","仅管理员可以看见此内容"}. {"Only collection node owners may associate leaf nodes with the collection","只有集合节点所有者可以将叶子节点与集合关联"}. {"Only deliver notifications to available users","仅将通知发送给可发送的用户"}. {"Only or tags are allowed","仅允许 标签"}. {"Only element is allowed in this query","此查询中只允许 元素"}. -{"Only members may query archives of this room","只有成员才能查询此房间的存档"}. -{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与者才允许在此房间更改主题"}. -{"Only moderators are allowed to change the subject in this room","只有主持人才允许在此房间更改主题"}. +{"Only members may query archives of this room","只有成员才能查询此群聊的存档"}. +{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与者才允许在此群聊更改话题"}. +{"Only moderators are allowed to change the subject in this room","只有主持人才允许在此群聊更改话题"}. {"Only moderators are allowed to retract messages","只有主持人才允许撤回消息"}. {"Only moderators can approve voice requests","只有主持人才能批准发言请求"}. {"Only occupants are allowed to send messages to the conference","只有占用者才允许向群聊发送消息"}. @@ -382,20 +347,17 @@ {"Organization Unit","组织单位"}. {"Other Modules Available:","其他可用模块:"}. {"Outgoing s2s Connections","出站 s2s 连接"}. -{"Outgoing s2s Connections:","出站 s2s 连接:"}. {"Owner privileges required","需要所有者权限"}. {"Packet relay is denied by service policy","包中继被服务策略拒绝"}. -{"Packet","数据包"}. {"Participant ID","参与者 ID"}. {"Participant","参与者"}. -{"Password Verification:","密码确认:"}. -{"Password Verification","确认密码"}. +{"Password Verification","密码验证"}. +{"Password Verification:","密码验证:"}. {"Password","密码"}. {"Password:","密码:"}. {"Path to Dir","目录的路径"}. {"Path to File","文件路径"}. {"Payload semantic type information","有效载荷语义类型信息"}. -{"Pending","挂起"}. {"Period: ","持续时间: "}. {"Persist items to storage","持久化内容条目"}. {"Persistent","持久"}. @@ -404,17 +366,17 @@ {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库。如果您正在使用 ODBC 模块,您还需要分别备份您的数据库。"}. {"Please, wait for a while before sending new voice request","请稍候,然后再发送新的发言请求"}. {"Pong","Pong"}. -{"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许处理 'ask' 属性"}. -{"Present real Jabber IDs to","将真实 Jabber ID 显示给"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许有“ask”属性"}. +{"Present real Jabber IDs to","将用户真实 JID 显示给"}. {"Previous session not found","上一个会话未找到"}. {"Previous session PID has been killed","上一个会话的 PID 已被杀掉"}. {"Previous session PID has exited","上一个会话的 PID 已退出"}. {"Previous session PID is dead","上一个会话的 PID 已死"}. {"Previous session timed out","上一个会话已超时"}. -{"private, ","保密, "}. +{"private, ","私人, "}. {"Public","公开"}. {"Publish model","发布模型"}. -{"Publish-Subscribe","发布-订阅"}. +{"Publish-Subscribe","发布—订阅"}. {"PubSub subscriber request","PubSub 订阅者请求"}. {"Purge all items when the relevant publisher goes offline","相关发布人离线后清除所有选项"}. {"Push record not found","没有找到推送记录"}. @@ -427,52 +389,42 @@ {"Receive notification from direct child nodes only","仅接收所有直接子节点的通知"}. {"Receive notification of new items only","仅接收新内容项的通知"}. {"Receive notification of new nodes only","仅接收新节点的通知"}. -{"Recipient is not in the conference room","接收人不在会议室"}. +{"Recipient is not in the conference room","接收者不在群聊"}. {"Register an XMPP account","注册 XMPP 账号"}. -{"Registered Users","注册用户"}. -{"Registered Users:","注册用户:"}. {"Register","注册"}. {"Remote copy","远程复制"}. {"Remove a hat from a user","移除用户头衔"}. -{"Remove All Offline Messages","移除所有离线消息"}. -{"Remove User","删除用户"}. -{"Remove","移除"}. +{"Remove User","移除用户"}. {"Replaced by new connection","被新的连接替换"}. {"Request has timed out","请求已超时"}. {"Request is ignored","请求被忽略"}. {"Requested role","请求的角色"}. {"Resources","资源"}. {"Restart Service","重启服务"}. -{"Restart","重启"}. {"Restore Backup from File at ","从以下位置的文件恢复备份 "}. -{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. +{"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(所需内存较少):"}. {"Restore binary backup immediately:","立即恢复二进制备份:"}. -{"Restore plain text backup immediately:","立即恢复普通文本备份:"}. +{"Restore plain text backup immediately:","立即恢复明文备份:"}. {"Restore","恢复"}. {"Roles and Affiliations that May Retrieve Member List","可以检索成员列表的角色和从属关系"}. -{"Roles for which Presence is Broadcasted","被广播状态的角色"}. +{"Roles for which Presence is Broadcasted","广播在线状态的角色"}. {"Roles that May Send Private Messages","可以发送私信的角色"}. -{"Room Configuration","房间配置"}. -{"Room creation is denied by service policy","创建房间被服务策略拒绝"}. -{"Room description","房间描述"}. -{"Room Occupants","房间占用者"}. -{"Room terminates","房间终止"}. -{"Room title","房间标题"}. +{"Room Configuration","群聊配置"}. +{"Room creation is denied by service policy","群聊创建被服务策略拒绝"}. +{"Room description","群聊描述"}. +{"Room Occupants","群聊占用者"}. +{"Room terminates","群聊终止"}. +{"Room title","群聊标题"}. {"Roster groups allowed to subscribe","允许订阅的花名册组"}. -{"Roster of ~ts","~ts的花名册"}. {"Roster size","花名册大小"}. -{"Roster:","花名册:"}. -{"RPC Call Error","RPC 调用错误"}. {"Running Nodes","运行中的节点"}. -{"~s invites you to the room ~s","~s 邀请您到房间 ~s"}. +{"~s invites you to the room ~s","~s 邀请您加入群聊 ~s"}. {"Saturday","星期六"}. -{"Script check","脚本检查"}. {"Search from the date","从日期搜索"}. -{"Search Results for ","搜索结果属于关键词 "}. +{"Search Results for ","搜索结果 "}. {"Search the text","搜索文本"}. {"Search until the date","搜索截至日期"}. {"Search users in ","在以下位置搜索用户 "}. -{"Select All","全选"}. {"Send announcement to all online users on all hosts","发送通知给所有主机的在线用户"}. {"Send announcement to all online users","发送通知给所有在线用户"}. {"Send announcement to all users on all hosts","发送通知给所有主机上的所有用户"}. @@ -481,14 +433,14 @@ {"Server:","服务器:"}. {"Service list retrieval timed out","服务列表检索超时"}. {"Session state copying timed out","会话状态复制超时"}. -{"Set message of the day and send to online users","设定每日消息并发送给所有在线用户"}. -{"Set message of the day on all hosts and send to online users","设置所有主机上的每日消息并发送给在线用户"}. +{"Set message of the day and send to online users","设置每日消息并发送给在线用户"}. +{"Set message of the day on all hosts and send to online users","在所有主机上设置每日消息并发送给在线用户"}. {"Shared Roster Groups","共享的花名册组群"}. {"Show Integral Table","显示完整列表"}. {"Show Ordinary Table","显示普通列表"}. {"Shut Down Service","关闭服务"}. {"SOCKS5 Bytestreams","SOCKS5 字节流"}. -{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以将您的密码存储在计算机中,但出于安全原因,您应该仅在个人计算机中这样做。"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以将您的密码存储在计算机中,但出于安全考虑,您应该仅在个人计算机中存储密码。"}. {"Sources Specs:","源参数:"}. {"Specify the access model","指定访问范例"}. {"Specify the event message type","指定事件消息类型"}. @@ -496,23 +448,17 @@ {"Stanza id is not valid","节 id 无效"}. {"Stanza ID","节 ID"}. {"Statically specify a replyto of the node owner(s)","静态指定节点所有者的回复"}. -{"Statistics of ~p","~p的统计"}. -{"Statistics","统计"}. {"Stopped Nodes","已经停止的节点"}. -{"Stop","停止"}. -{"Storage Type","存储类型"}. {"Store binary backup:","存储为二进制备份:"}. {"Store plain text backup:","存储为普通文本备份:"}. {"Stream management is already enabled","流管理已启用"}. {"Stream management is not enabled","流管理未启用"}. -{"Subject","标题"}. +{"Subject","话题"}. {"Submitted","已提交"}. -{"Submit","提交"}. {"Subscriber Address","订阅者地址"}. {"Subscribers may publish","订阅者可以发布"}. {"Subscription requests must be approved and only subscribers may retrieve items","订阅请求必须得到批准,只有订阅者才能检索项目"}. {"Subscriptions are not allowed","不允许订阅"}. -{"Subscription","订阅"}. {"Sunday","星期天"}. {"Text associated with a picture","与图片相关的文字"}. {"Text associated with a sound","与声音相关的文字"}. @@ -525,17 +471,17 @@ {"The body text of the last received message","最后收到的消息的正文"}. {"The CAPTCHA is valid.","验证码有效。"}. {"The CAPTCHA verification has failed","验证码检查失败"}. -{"The captcha you entered is wrong","您输入的验证码有误"}. +{"The captcha you entered is wrong","您输入的验证码错误"}. {"The child nodes (leaf or collection) associated with a collection","关联集合的字节点(叶子或集合)"}. {"The collections with which a node is affiliated","加入结点的集合"}. {"The DateTime at which a leased subscription will end or has ended","租赁订阅将结束或已结束的日期时间"}. {"The datetime when the node was created","节点创建的日期时间"}. {"The default language of the node","该节点的默认语言"}. -{"The feature requested is not supported by the conference","会议不支持所请求的特征"}. -{"The JID of the node creator","节点创建人的 JID"}. +{"The feature requested is not supported by the conference","群聊不支持请求的功能"}. +{"The JID of the node creator","节点创建者的 JID"}. {"The JIDs of those to contact with questions","问题联系人的 JID"}. {"The JIDs of those with an affiliation of owner","拥有所有者从属关系的用户的 JID"}. -{"The JIDs of those with an affiliation of publisher","隶属发布人的 JID"}. +{"The JIDs of those with an affiliation of publisher","与发布者的从属关系有关的用户 JID"}. {"The list of all online users","所有在线用户列表"}. {"The list of all users","所有用户列表"}. {"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的 JID 列表"}. @@ -552,30 +498,29 @@ {"The password is too weak","密码强度太弱"}. {"the password is","密码是"}. {"The password of your XMPP account was successfully changed.","您的 XMPP 账号密码已成功更改。"}. -{"The password was not changed","密码未更新"}. +{"The password was not changed","密码未更改"}. {"The passwords are different","密码不一致"}. {"The presence states for which an entity wants to receive notifications","实体要为其接收通知的状态"}. -{"The query is only allowed from local users","仅本地用户可以查询"}. +{"The query is only allowed from local users","仅允许本地用户查询"}. {"The query must not contain elements","查询不能包含 元素"}. -{"The room subject can be modified by participants","房间主题可由参与者修改"}. -{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. +{"The room subject can be modified by participants","群聊话题可由参与者修改"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. {"The sender of the last received message","最后收到的消息的发送者"}. -{"The stanza MUST contain only one element, one element, or one element","本节必须只含一个 元素, 元素,或 元素"}. +{"The stanza MUST contain only one element, one element, or one element","节必须仅包含一个 元素、一个 元素或一个 元素"}. {"The subscription identifier associated with the subscription request","与订阅请求关联的订阅标识符"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL 转换的 URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL 转换的 URL,可以将其应用于有效负载格式,以生成有效的数据表单结果,客户端可以使用通用数据表单呈现引擎来显示该结果"}. -{"There was an error changing the password: ","修改密码出错: "}. +{"There was an error changing the password: ","更改密码时出错: "}. {"There was an error creating the account: ","创建账号时出错: "}. {"There was an error deleting the account: ","删除账号时出错: "}. -{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:macbeth 与 MacBeth 和 Macbeth 是一样的。"}. -{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上注册 XMPP 账号。您的 JID (Jabber ID) 的形式如下:username@server。请仔细阅读说明以正确填写字段。"}. -{"This page allows to unregister an XMPP account in this XMPP server.","此页面允许在此 XMPP 服务器上注销 XMPP 账号。"}. -{"This room is not anonymous","此房间不是匿名房间"}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:MacBeth 和 Macbeth 都是 macbeth。"}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器中注册 XMPP 账号,您的 JID(Jabber ID)的格式为:用户名@服务器。请仔细阅读说明以正确填写字段。"}. +{"This page allows to unregister an XMPP account in this XMPP server.","本页面允许在此 XMPP 服务器中注销 XMPP 账号。"}. +{"This room is not anonymous","此群聊不是匿名的"}. {"This service can not process the address: ~s","此服务无法处理地址:~s"}. {"Thursday","星期四"}. {"Time delay","时间延迟"}. {"Timed out waiting for stream resumption","等待流恢复超时"}. -{"Time","时间"}. {"To register, visit ~s","要注册,请访问 ~s"}. {"To ~ts","发送到~ts"}. {"Token TTL","TTL 令牌"}. @@ -587,15 +532,9 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自 IP 地址(~p)的(~s)失败认证太多。将在 UTC 时间 ~s 解除对该地址的封锁"}. {"Too many receiver fields were specified","指定的接收者字段太多"}. {"Too many unacked stanzas","未被确认的节太多"}. -{"Too many users in this conference","该会议的用户太多"}. -{"Total rooms","所有房间"}. -{"To","到"}. +{"Too many users in this conference","此群聊中的用户太多"}. {"Traffic rate limit is exceeded","已经超过传输率限制"}. -{"Transactions Aborted:","取消的事务:"}. -{"Transactions Committed:","提交的事务:"}. -{"Transactions Logged:","记入日志的事务:"}. -{"Transactions Restarted:","重启的事务:"}. -{"~ts's Offline Messages Queue","~ts的离线消息队列"}. +{"~ts's Offline Messages Queue","~ts 的离线消息队列"}. {"Tuesday","星期二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. {"Unable to register route on existing local domain","在已存在的本地域上无法注册路由"}. @@ -605,20 +544,14 @@ {"Uninstall","卸载"}. {"Unregister an XMPP account","注销 XMPP 账号"}. {"Unregister","注销"}. -{"Unselect All","取消全选"}. {"Unsupported element","不支持的 元素"}. {"Unsupported version","不支持的版本"}. -{"Update message of the day (don't send)","更新每日消息(不发送)"}. -{"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. -{"Update plan","更新计划"}. -{"Update ~p","更新~p"}. -{"Update script","更新脚本"}. +{"Update message of the day (don't send)","更新每日消息(不发送)"}. +{"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. {"Update specs to get modules source, then install desired ones.","更新参数获取模块源,然后安装所需的模块。"}. {"Update Specs","更新参数"}. -{"Update","更新"}. {"Upgrade","升级"}. -{"Uptime:","正常运行时间:"}. -{"URL for Archived Discussion Logs","已存档对话日志的 URL"}. +{"URL for Archived Discussion Logs","存档讨论日志的 URL"}. {"User already exists","用户已存在"}. {"User (jid)","用户 (jid)"}. {"User JID","用户 JID"}. @@ -626,25 +559,22 @@ {"User removed","用户已移除"}. {"User session not found","用户会话未找到"}. {"User session terminated","用户会话已终止"}. -{"User ~ts","用户~ts"}. +{"User ~ts","用户 ~ts"}. {"Username:","用户名:"}. {"Users are not allowed to register accounts so quickly","不允许用户太频繁地注册账号"}. {"Users Last Activity","用户上次活动"}. {"Users","用户"}. {"User","用户"}. -{"Validate","确认"}. -{"Value 'get' of 'type' attribute is not allowed","不允许 'type' 属性的 'get' 值"}. +{"Value 'get' of 'type' attribute is not allowed","不允许“type”属性的“get”值"}. {"Value of '~s' should be boolean","'~s' 的值应为布尔型"}. {"Value of '~s' should be datetime string","'~s' 的值应为日期时间字符串"}. {"Value of '~s' should be integer","'~s' 的值应为整数"}. -{"Value 'set' of 'type' attribute is not allowed","不允许 'type' 属性的 'set' 值"}. +{"Value 'set' of 'type' attribute is not allowed","不允许“type”属性的“set”值"}. {"vCard User Search","vCard 用户搜索"}. {"View joined MIX channels","查看已加入的 MIX 频道"}. -{"View Queue","查看队列"}. -{"View Roster","查看花名册"}. {"Virtual Hosts","虚拟主机"}. -{"Visitors are not allowed to change their nicknames in this room","访客不允许在此房间更改其昵称"}. -{"Visitors are not allowed to send messages to all occupants","访客不允许向所有占用者发送信息"}. +{"Visitors are not allowed to change their nicknames in this room","不允许访客在此群聊中更改其昵称"}. +{"Visitors are not allowed to send messages to all occupants","不允许访客向所有占用者发送信息"}. {"Visitor","访客"}. {"Voice requests are disabled in this conference","此群聊中禁用发言请求"}. {"Voice request","发言请求"}. @@ -668,22 +598,22 @@ {"XMPP Domains","XMPP 域"}. {"XMPP Show Value of Away","XMPP 的离开显示值"}. {"XMPP Show Value of Chat","XMPP 的聊天显示值"}. -{"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND (勿扰)显示值"}. -{"XMPP Show Value of XA (Extended Away)","XMPP 的 XA (延长离开)显示值"}. -{"XMPP URI of Associated Publish-Subscribe Node","发布-订阅节点关联的 XMPP URI"}. -{"You are being removed from the room because of a system shutdown","由于系统关闭,您将被从房间中移除"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND(请勿打扰)显示值"}. +{"XMPP Show Value of XA (Extended Away)","XMPP 的 XA(延长离开)显示值"}. +{"XMPP URI of Associated Publish-Subscribe Node","关联发布—订阅节点的 XMPP URI"}. +{"You are being removed from the room because of a system shutdown","由于系统关闭,您将会从群聊中移除"}. {"You are not allowed to send private messages","不允许您发送私信"}. {"You are not joined to the channel","您未加入频道"}. -{"You can later change your password using an XMPP client.","您稍后可以使用 XMPP 客户端更改密码。"}. -{"You have been banned from this room","您已被禁止进入该房间"}. -{"You have joined too many conferences","您加入的会议太多"}. -{"You must fill in field \"Nickname\" in the form","您必须填充表单中“昵称”项"}. -{"You need a client that supports x:data and CAPTCHA to register","您需要一个支持 x:data 和验证码的客户端进行注册"}. -{"You need a client that supports x:data to register the nickname","您需要一个支持 x:data 的客户端来注册昵称"}. -{"You need an x:data capable client to search","您需要一个兼容 x:data 的客户端来搜索"}. +{"You can later change your password using an XMPP client.","您之后可以使用 XMPP 客户端更改密码。"}. +{"You have been banned from this room","您已被此群聊封禁"}. +{"You have joined too many conferences","您加入了太多群聊"}. +{"You must fill in field \"Nickname\" in the form","您必须在表单中填写“昵称”字段"}. +{"You need a client that supports x:data and CAPTCHA to register","您需要支持 x:data 和验证码的客户端来注册"}. +{"You need a client that supports x:data to register the nickname","您需要支持 x:data 的客户端来注册昵称"}. +{"You need an x:data capable client to search","您需要支持 x:data 的客户端来搜索"}. {"Your active privacy list has denied the routing of this stanza.","您的活动隐私列表已拒绝路由此节。"}. {"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满。消息已被丢弃。"}. -{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发送给~s的消息已被阻止。要解除阻止,请访问 ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您对 ~s 的订阅请求和/或消息已被屏蔽。若要解除屏蔽您的订阅请求,请访问 ~s"}. {"Your XMPP account was successfully registered.","您的 XMPP 账号注册成功。"}. {"Your XMPP account was successfully unregistered.","您的 XMPP 账号注销成功。"}. -{"You're not allowed to create nodes","您不可以创建节点"}. +{"You're not allowed to create nodes","不允许您创建节点"}. From 9e4cccd4dbdcb6a7755e73e5a2fd461a28115516 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 11:35:07 +0200 Subject: [PATCH 0642/1302] Update Polish translation (thanks to Wojciech Teichert) --- priv/msgs/pl.msg | 56 +++--------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/priv/msgs/pl.msg b/priv/msgs/pl.msg index 1490cd22b..e4015091c 100644 --- a/priv/msgs/pl.msg +++ b/priv/msgs/pl.msg @@ -6,11 +6,11 @@ {" has set the subject to: "," zmienił temat na: "}. {"A friendly name for the node","Przyjazna nazwa węzła"}. {"A password is required to enter this room","Aby wejść do pokoju wymagane jest hasło"}. +{"A Web Page","Strona sieci Web"}. {"Accept","Zaakceptuj"}. {"Access denied by service policy","Dostęp zabroniony zgodnie z zasadami usługi"}. {"Action on user","Wykonaj na użytkowniku"}. -{"Add Jabber ID","Dodaj Jabber ID"}. -{"Add New","Dodaj nowe"}. +{"Add a hat to a user","Dodaj kapelusz do użytkownika"}. {"Add User","Dodaj użytkownika"}. {"Administration of ","Zarządzanie "}. {"Administration","Administracja"}. @@ -18,6 +18,7 @@ {"All activity","Cała aktywność"}. {"All Users","Wszyscy użytkownicy"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Pozwól temu Jabber ID na zapisanie się do tego węzła PubSub"}. +{"Allow this person to register with the room?","Pozwolić tej osobie zarejestrować się w tym pokoju?"}. {"Allow users to change the subject","Pozwól użytkownikom zmieniać temat"}. {"Allow users to query other users","Pozwól użytkownikom pobierać informacje o innych użytkownikach"}. {"Allow users to send invites","Pozwól użytkownikom wysyłać zaproszenia"}. @@ -60,22 +61,17 @@ {"Conference room does not exist","Pokój konferencyjny nie istnieje"}. {"Configuration of room ~s","Konfiguracja pokoju ~s"}. {"Configuration","Konfiguracja"}. -{"Connected Resources:","Zasoby zalogowane:"}. {"Country","Państwo"}. -{"CPU Time:","Czas CPU:"}. {"Database failure","Błąd bazy danych"}. -{"Database Tables at ~p","Tabele bazy na ~p"}. {"Database Tables Configuration at ","Konfiguracja tabel bazy na "}. {"Database","Baza danych"}. {"December","Grudzień"}. {"Default users as participants","Domyślni użytkownicy jako uczestnicy"}. {"Delete message of the day on all hosts","Usuń wiadomość dnia ze wszystkich hostów"}. {"Delete message of the day","Usuń wiadomość dnia"}. -{"Delete Selected","Usuń zaznaczone"}. {"Delete User","Usuń użytkownika"}. {"Deliver event notifications","Dostarczaj powiadomienia o zdarzeniach"}. {"Deliver payloads with event notifications","Dostarczaj zawartość publikacji wraz z powiadomieniami o zdarzeniach"}. -{"Description:","Opis:"}. {"Disc only copy","Kopia tylko na dysku"}. {"Dump Backup to Text File at ","Zapisz kopię zapasową w pliku tekstowym na "}. {"Dump to Text File","Wykonaj kopie do pliku tekstowego"}. @@ -87,7 +83,6 @@ {"ejabberd SOCKS5 Bytestreams module","Moduł SOCKS5 Bytestreams"}. {"ejabberd vCard module","Moduł vCard ejabberd"}. {"ejabberd Web Admin","ejabberd: Panel Administracyjny"}. -{"Elements","Elementy"}. {"Email","Email"}. {"Enable logging","Włącz logowanie"}. {"Enable message archiving","Włącz archiwizowanie rozmów"}. @@ -99,7 +94,6 @@ {"Enter path to jabberd14 spool file","Wprowadź ścieżkę do roboczego pliku serwera jabberd14"}. {"Enter path to text file","Wprowadź scieżkę do pliku tekstowego"}. {"Enter the text you see","Przepisz tekst z obrazka"}. -{"Error","Błąd"}. {"Exclude Jabber IDs from CAPTCHA challenge","Pomiń Jabber ID z żądania CAPTCHA"}. {"Export all tables as SQL queries to a file:","Wyeksportuj wszystkie tabele jako zapytania SQL do pliku:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportuj dane wszystkich użytkowników serwera do plików w formacie PIEFXIS (XEP-0227):"}. @@ -115,24 +109,19 @@ {"February","Luty"}. {"File larger than ~w bytes","Plik jest większy niż ~w bajtów"}. {"Friday","Piątek"}. -{"From","Od"}. {"Full Name","Pełna nazwa"}. {"Get Number of Online Users","Pokaż liczbę zalogowanych użytkowników"}. {"Get Number of Registered Users","Pokaż liczbę zarejestrowanych użytkowników"}. {"Get User Last Login Time","Pokaż czas ostatniego zalogowania uzytkownika"}. -{"Get User Password","Pobierz hasło użytkownika"}. {"Get User Statistics","Pobierz statystyki użytkownika"}. {"Given Name","Imię"}. {"Grant voice to this person?","Udzielić głosu tej osobie?"}. -{"Group","Grupa"}. -{"Groups","Grupy"}. {"has been banned","został wykluczony"}. {"has been kicked because of a system shutdown","został wyrzucony z powodu wyłączenia systemu"}. {"has been kicked because of an affiliation change","został wyrzucony z powodu zmiany przynależności"}. {"has been kicked because the room has been changed to members-only","został wyrzucony z powodu zmiany pokoju na \"Tylko dla Członków\""}. {"has been kicked","został wyrzucony"}. {"Host unknown","Nieznany host"}. -{"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jeśli nie widzisz obrazka CAPTCHA, odwiedź stronę internetową."}. {"Import Directory","Importuj katalog"}. {"Import File","Importuj plik"}. @@ -144,7 +133,6 @@ {"Import Users From jabberd14 Spool Files","Importuj użytkowników z plików roboczych serwera jabberd14"}. {"Improper domain part of 'from' attribute","Nieprawidłowa domena atrybutu 'from'"}. {"Improper message type","Nieprawidłowy typ wiadomości"}. -{"Incoming s2s Connections:","Przychodzące połączenia s2s:"}. {"Incorrect CAPTCHA submit","Nieprawidłowa odpowiedz dla CAPTCHA"}. {"Incorrect data form","Nieprawidłowe dane w formatce"}. {"Incorrect password","Nieprawidłowe hasło"}. @@ -169,8 +157,6 @@ {"Last month","Miniony miesiąc"}. {"Last year","Miniony rok"}. {"leaves the room","opuszcza pokój"}. -{"List of rooms","Lista pokoi"}. -{"Low level update script","Skrypt aktualizacji niskiego poziomu"}. {"Make participants list public","Upublicznij listę uczestników"}. {"Make room CAPTCHA protected","Pokój zabezpieczony captchą"}. {"Make room members-only","Pokój tylko dla członków"}. @@ -183,22 +169,18 @@ {"Max payload size in bytes","Maksymalna wielkość powiadomienia w bajtach"}. {"Maximum Number of Occupants","Maksymalna liczba uczestników"}. {"May","Maj"}. -{"Members:","Członkowie:"}. {"Membership is required to enter this room","Musisz być na liście członków tego pokoju aby do niego wejść"}. -{"Memory","Pamięć"}. {"Message body","Treść wiadomości"}. {"Message not found in forwarded payload","Nie znaleziona wiadomości w przesyłanych dalej danych"}. {"Middle Name","Drugie imię"}. {"Minimum interval between voice requests (in seconds)","Minimalny odstęp między żądaniami głosowymi (w sekundach)"}. {"Moderator privileges required","Wymagane uprawnienia moderatora"}. {"Moderator","Moderatorzy"}. -{"Modified modules","Zmodyfikowane moduły"}. {"Module failed to handle the query","Moduł nie był wstanie przetworzyć zapytania"}. {"Monday","Poniedziałek"}. {"Multicast","Multicast"}. {"Multi-User Chat","Wieloosobowa rozmowa"}. {"Name","Imię"}. -{"Name:","Nazwa:"}. {"Neither 'jid' nor 'nick' attribute found","Brak zarówno atrybutu 'jid' jak i 'nick'"}. {"Neither 'role' nor 'affiliation' attribute found","Brak zarówno atrybutu 'role' jak i 'affiliation'"}. {"Never","Nigdy"}. @@ -246,12 +228,9 @@ {"Number of online users","Liczba zalogowanych użytkowników"}. {"Number of registered users","Liczba zarejestrowanych użytkowników"}. {"October","Październik"}. -{"Offline Messages","Wiadomości offline"}. -{"Offline Messages:","Wiadomości offline:"}. {"OK","OK"}. {"Old Password:","Stare hasło:"}. {"Online Users","Użytkownicy zalogowani"}. -{"Online Users:","Użytkownicy zalogowani:"}. {"Online","Dostępny"}. {"Only deliver notifications to available users","Dostarczaj powiadomienia tylko dostępnym użytkownikom"}. {"Only or tags are allowed","Dozwolone są wyłącznie elementy lub "}. @@ -266,9 +245,7 @@ {"Organization Name","Nazwa organizacji"}. {"Organization Unit","Dział"}. {"Outgoing s2s Connections","Wychodzące połączenia s2s"}. -{"Outgoing s2s Connections:","Wychodzące połączenia s2s:"}. {"Owner privileges required","Wymagane uprawnienia właściciela"}. -{"Packet","Pakiet"}. {"Participant","Uczestnicy"}. {"Password Verification","Weryfikacja hasła"}. {"Password Verification:","Weryfikacja hasła:"}. @@ -276,7 +253,6 @@ {"Password:","Hasło:"}. {"Path to Dir","Ścieżka do katalogu"}. {"Path to File","Scieżka do pliku"}. -{"Pending","Oczekuje"}. {"Period: ","Przedział czasu: "}. {"Persist items to storage","Przechowuj na stałe dane PubSub"}. {"Ping query is incorrect","Żądanie 'ping' nie jest prawidłowe"}. @@ -295,17 +271,12 @@ {"RAM copy","Kopia w pamięci RAM"}. {"Really delete message of the day?","Na pewno usunąć wiadomość dnia?"}. {"Recipient is not in the conference room","Odbiorcy nie ma w pokoju"}. -{"Registered Users","Użytkownicy zarejestrowani"}. -{"Registered Users:","Użytkownicy zarejestrowani:"}. {"Register","Zarejestruj"}. {"Remote copy","Kopia zdalna"}. -{"Remove All Offline Messages","Usuń wszystkie wiadomości typu 'Offline'"}. {"Remove User","Usuń użytkownika"}. -{"Remove","Usuń"}. {"Replaced by new connection","Połączenie zostało zastąpione"}. {"Resources","Zasoby"}. {"Restart Service","Restart usługi"}. -{"Restart","Uruchom ponownie"}. {"Restore Backup from File at ","Odtwórz bazę danych z kopii zapasowej na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Odtwórz kopię binarną podczas następnego uruchomienia ejabberd (wymaga mniej zasobów):"}. {"Restore binary backup immediately:","Natychmiast odtwórz kopię binarną:"}. @@ -319,10 +290,8 @@ {"Room title","Tytuł pokoju"}. {"Roster groups allowed to subscribe","Grupy kontaktów uprawnione do subskrypcji"}. {"Roster size","Rozmiar listy kontaktów"}. -{"RPC Call Error","Błąd żądania RPC"}. {"Running Nodes","Uruchomione węzły"}. {"Saturday","Sobota"}. -{"Script check","Sprawdź skrypt"}. {"Search Results for ","Wyniki wyszukiwania dla "}. {"Search users in ","Wyszukaj użytkowników w "}. {"Send announcement to all online users on all hosts","Wyślij powiadomienie do wszystkich zalogowanych użytkowników na wszystkich hostach"}. @@ -340,19 +309,13 @@ {"Specify the access model","Określ model dostępu"}. {"Specify the event message type","Określ typ wiadomości"}. {"Specify the publisher model","Określ model publikującego"}. -{"Statistics of ~p","Statystyki ~p"}. -{"Statistics","Statystyki"}. {"Stopped Nodes","Zatrzymane węzły"}. -{"Stop","Zatrzymaj"}. -{"Storage Type","Typ bazy"}. {"Store binary backup:","Zachowaj kopię binarną:"}. {"Store plain text backup:","Zachowaj kopię w postaci tekstowej:"}. {"Subject","Temat"}. {"Submitted","Wprowadzone"}. -{"Submit","Wyślij"}. {"Subscriber Address","Adres subskrybenta"}. {"Subscriptions are not allowed","Subskrypcje nie są dozwolone"}. -{"Subscription","Subskrypcja"}. {"Sunday","Niedziela"}. {"That nickname is already in use by another occupant","Ta nazwa użytkownika jest używana przez kogoś innego"}. {"That nickname is registered by another person","Ta nazwa użytkownika jest już zarejestrowana przez inną osobę"}. @@ -371,9 +334,7 @@ {"This room is not anonymous","Ten pokój nie jest anonimowy"}. {"Thursday","Czwartek"}. {"Time delay","Opóźnienie"}. -{"Time","Czas"}. {"To register, visit ~s","Żeby się zarejestrować odwiedź ~s"}. -{"To","Do"}. {"Token TTL","Limit czasu tokenu"}. {"Too many active bytestreams","Zbyt wiele strumieni danych"}. {"Too many CAPTCHA requests","Za dużo żądań CAPTCHA"}. @@ -382,12 +343,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Zbyt wiele (~p) nieudanych prób logowanie z tego adresu IP (~s). Ten adres zostanie odblokowany o ~s UTC"}. {"Too many unacked stanzas","Zbyt wiele niepotwierdzonych pakietów"}. {"Too many users in this conference","Zbyt wielu użytkowników konferencji"}. -{"Total rooms","Wszystkich pokoi"}. {"Traffic rate limit is exceeded","Limit transferu przekroczony"}. -{"Transactions Aborted:","Transakcje anulowane:"}. -{"Transactions Committed:","Transakcje zakończone:"}. -{"Transactions Logged:","Transakcje zalogowane:"}. -{"Transactions Restarted:","Transakcje uruchomione ponownie:"}. {"Tuesday","Wtorek"}. {"Unable to generate a CAPTCHA","Nie można wygenerować CAPTCHA"}. {"Unable to register route on existing local domain","Nie można zarejestrować trasy dla lokalnej domeny"}. @@ -397,11 +353,6 @@ {"Unsupported element","Nieobsługiwany element "}. {"Update message of the day (don't send)","Aktualizuj wiadomość dnia (bez wysyłania)"}. {"Update message of the day on all hosts (don't send)","Aktualizuj wiadomość dnia na wszystkich hostach (bez wysyłania)"}. -{"Update plan","Plan aktualizacji"}. -{"Update ~p","Uaktualnij ~p"}. -{"Update script","Skrypt aktualizacji"}. -{"Update","Aktualizuj"}. -{"Uptime:","Czas pracy:"}. {"User already exists","Użytkownik już istnieje"}. {"User JID","Użytkownik "}. {"User (jid)","Użytkownik (jid)"}. @@ -413,7 +364,6 @@ {"Users Last Activity","Ostatnia aktywność użytkowników"}. {"Users","Użytkownicy"}. {"User","Użytkownik"}. -{"Validate","Potwierdź"}. {"Value 'get' of 'type' attribute is not allowed","Wartość 'get' dla atrybutu 'type' jest niedozwolona"}. {"Value of '~s' should be boolean","Wartość '~s' powinna być typu logicznego"}. {"Value of '~s' should be datetime string","Wartość '~s' powinna być typu daty"}. From 22cefeb2bdde52b23ba73755ef09e1c7e189e23e Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 11:35:36 +0200 Subject: [PATCH 0643/1302] Update German translation (thanks to Nautilusx) --- priv/msgs/de.msg | 69 +----------------------------------------------- 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index 88e1d7c2d..484522c2a 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","Konto existiert nicht"}. {"Action on user","Aktion auf Benutzer"}. {"Add a hat to a user","Funktion zu einem Benutzer hinzufügen"}. -{"Add Jabber ID","Jabber-ID hinzufügen"}. -{"Add New","Neue(n) hinzufügen"}. {"Add User","Benutzer hinzufügen"}. {"Administration of ","Administration von "}. {"Administration","Verwaltung"}. @@ -92,29 +90,20 @@ {"Conference room does not exist","Konferenzraum existiert nicht"}. {"Configuration of room ~s","Konfiguration des Raumes ~s"}. {"Configuration","Konfiguration"}. -{"Connected Resources:","Verbundene Ressourcen:"}. {"Contact Addresses (normally, room owner or owners)","Kontaktadresse (normalerweise Raumbesitzer)"}. {"Country","Land"}. -{"CPU Time:","CPU-Zeit:"}. {"Current Discussion Topic","Aktuelles Diskussionsthema"}. {"Database failure","Datenbankfehler"}. -{"Database Tables at ~p","Datenbanktabellen bei ~p"}. {"Database Tables Configuration at ","Datenbanktabellen-Konfiguration bei "}. {"Database","Datenbank"}. {"December","Dezember"}. {"Default users as participants","Benutzer werden standardmäßig Teilnehmer"}. -{"Delete content","Inhalt löschen"}. {"Delete message of the day on all hosts","Lösche Nachricht des Tages auf allen Hosts"}. {"Delete message of the day","Lösche Nachricht des Tages"}. -{"Delete Selected","Markierte löschen"}. -{"Delete table","Tabelle löschen"}. {"Delete User","Benutzer löschen"}. {"Deliver event notifications","Ereignisbenachrichtigungen zustellen"}. {"Deliver payloads with event notifications","Nutzdaten mit Ereignisbenachrichtigungen zustellen"}. -{"Description:","Beschreibung:"}. {"Disc only copy","Nur auf Festplatte"}. -{"'Displayed groups' not added (they do not exist!): ","'Angezeigte Gruppen' nicht hinzugefügt (sie existieren nicht!): "}. -{"Displayed:","Angezeigt:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Geben Sie niemandem Ihr Passwort, auch nicht den Administratoren des XMPP-Servers."}. {"Dump Backup to Text File at ","Gib Backup in Textdatei aus bei "}. {"Dump to Text File","Ausgabe in Textdatei"}. @@ -130,7 +119,6 @@ {"ejabberd vCard module","ejabberd vCard-Modul"}. {"ejabberd Web Admin","ejabberd Web-Admin"}. {"ejabberd","ejabberd"}. -{"Elements","Elemente"}. {"Email Address","E-Mail-Adresse"}. {"Email","E-Mail"}. {"Enable hats","Funktion einschalten"}. @@ -145,7 +133,6 @@ {"Enter path to text file","Geben Sie den Pfad zur Textdatei ein"}. {"Enter the text you see","Geben Sie den Text ein den Sie sehen"}. {"Erlang XMPP Server","Erlang XMPP-Server"}. -{"Error","Fehler"}. {"Exclude Jabber IDs from CAPTCHA challenge","Jabber-IDs von CAPTCHA-Herausforderung ausschließen"}. {"Export all tables as SQL queries to a file:","Alle Tabellen als SQL-Abfragen in eine Datei exportieren:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Alle Benutzerdaten des Servers in PIEFXIS-Dateien (XEP-0227) exportieren:"}. @@ -164,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Füllen Sie das Formular aus, um nach jeglichen passenden XMPP-Benutzern zu suchen"}. {"Friday","Freitag"}. {"From ~ts","Von ~ts"}. -{"From","Von"}. {"Full List of Room Admins","Vollständige Liste der Raumadmins"}. {"Full List of Room Owners","Vollständige Liste der Raumbesitzer"}. {"Full Name","Vollständiger Name"}. @@ -174,13 +160,9 @@ {"Get Number of Registered Users","Anzahl der registrierten Benutzer abrufen"}. {"Get Pending","Ausstehende abrufen"}. {"Get User Last Login Time","letzte Anmeldezeit des Benutzers abrufen"}. -{"Get User Password","Benutzerpasswort abrufen"}. {"Get User Statistics","Benutzerstatistiken abrufen"}. {"Given Name","Vorname"}. {"Grant voice to this person?","Dieser Person Sprachrechte erteilen?"}. -{"Group","Gruppe"}. -{"Groups that will be displayed to the members","Gruppen, die den Mitgliedern angezeigt werden"}. -{"Groups","Gruppen"}. {"has been banned","wurde gebannt"}. {"has been kicked because of a system shutdown","wurde wegen einer Systemabschaltung hinausgeworfen"}. {"has been kicked because of an affiliation change","wurde wegen einer Änderung der Zugehörigkeit hinausgeworfen"}. @@ -190,7 +172,6 @@ {"Hat URI","Funktions-URI"}. {"Hats limit exceeded","Funktionslimit wurde überschritten"}. {"Host unknown","Host unbekannt"}. -{"Host","Host"}. {"HTTP File Upload","HTTP-Dateiupload"}. {"Idle connection","Inaktive Verbindung"}. {"If you don't see the CAPTCHA image here, visit the web page.","Wenn Sie das CAPTCHA-Bild nicht sehen, besuchen Sie die Webseite."}. @@ -204,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importiere Benutzer aus jabberd14-Spooldateien"}. {"Improper domain part of 'from' attribute","Falscher Domänenteil des 'from'-Attributs"}. {"Improper message type","Unzulässiger Nachrichtentyp"}. -{"Incoming s2s Connections:","Eingehende s2s-Verbindungen:"}. {"Incorrect CAPTCHA submit","Falsche CAPTCHA-Eingabe"}. {"Incorrect data form","Falsches Datenformular"}. {"Incorrect password","Falsches Passwort"}. @@ -234,7 +214,6 @@ {"July","Juli"}. {"June","Juni"}. {"Just created","Gerade erstellt"}. -{"Label:","Label:"}. {"Last Activity","Letzte Aktivität"}. {"Last login","Letzte Anmeldung"}. {"Last message","Letzte Nachricht"}. @@ -242,11 +221,9 @@ {"Last year","Letztes Jahr"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Niederwertigstes Bit des SHA-256-Hashes des Textes sollte hexadezimalem Label gleichen"}. {"leaves the room","verlässt den Raum"}. -{"List of rooms","Liste von Räumen"}. {"List of users with hats","Liste der Benutzer mit Funktionen"}. {"List users with hats","Benutzer mit Funktionen auflisten"}. {"Logging","Protokollierung"}. -{"Low level update script","Low-Level-Aktualisierungsscript"}. {"Make participants list public","Teilnehmerliste öffentlich machen"}. {"Make room CAPTCHA protected","Raum mittels CAPTCHA schützen"}. {"Make room members-only","Raum nur für Mitglieder zugänglich machen"}. @@ -264,11 +241,8 @@ {"Maximum number of items to persist","Maximale Anzahl persistenter Items"}. {"Maximum Number of Occupants","Maximale Anzahl der Teilnehmer"}. {"May","Mai"}. -{"Members not added (inexistent vhost!): ","Mitglieder nicht hinzugefügt (nicht existierender vhost!): "}. {"Membership is required to enter this room","Mitgliedschaft ist erforderlich um diesen Raum zu betreten"}. -{"Members:","Mitglieder:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Merken Sie sich Ihr Passwort, oder schreiben Sie es auf einen Zettel den Sie sicher verwahren. Bei XMPP gibt es keine automatische Möglichkeit, das Passwort wiederherzustellen falls Sie es vergessen."}. -{"Memory","Speicher"}. {"Mere Availability in XMPP (No Show Value)","Bloße Verfügbarkeit in XMPP (kein Anzeigewert)"}. {"Message body","Nachrichtentext"}. {"Message not found in forwarded payload","Nachricht nicht in weitergeleiteten Nutzdaten gefunden"}. @@ -280,14 +254,11 @@ {"Moderator privileges required","Moderatorrechte erforderlich"}. {"Moderator","Moderator"}. {"Moderators Only","nur Moderatoren"}. -{"Modified modules","Geänderte Module"}. {"Module failed to handle the query","Modul konnte die Anfrage nicht verarbeiten"}. {"Monday","Montag"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","Mehrere -Elemente sind laut RFC6121 nicht erlaubt"}. {"Multi-User Chat","Mehrbenutzer-Chat (MUC)"}. -{"Name in the rosters where this group will be displayed","Name in den Kontaktlisten wo diese Gruppe angezeigt werden wird"}. -{"Name:","Name:"}. {"Name","Vorname"}. {"Natural Language for Room Discussions","Natürliche Sprache für Raumdiskussionen"}. {"Natural-Language Room Name","Raumname in natürlicher Sprache"}. @@ -353,14 +324,10 @@ {"Occupants are allowed to query others","Teilnehmer dürfen andere abfragen"}. {"Occupants May Change the Subject","Teilnehmer dürfen das Thema ändern"}. {"October","Oktober"}. -{"Offline Messages","Offline-Nachrichten"}. -{"Offline Messages:","Offline-Nachrichten:"}. {"OK","OK"}. {"Old Password:","Altes Passwort:"}. {"Online Users","Angemeldete Benutzer"}. -{"Online Users:","Angemeldete Benutzer:"}. {"Online","Angemeldet"}. -{"Only admins can see this","Nur Admins können dies sehen"}. {"Only collection node owners may associate leaf nodes with the collection","Nur Sammlungsknoten-Besitzer dürfen Blattknoten mit der Sammlung verknüpfen"}. {"Only deliver notifications to available users","Benachrichtigungen nur an verfügbare Benutzer schicken"}. {"Only or tags are allowed","Nur - oder -Tags sind erlaubt"}. @@ -380,10 +347,8 @@ {"Organization Unit","Abteilung"}. {"Other Modules Available:","Andere Module verfügbar:"}. {"Outgoing s2s Connections","Ausgehende s2s-Verbindungen"}. -{"Outgoing s2s Connections:","Ausgehende s2s-Verbindungen:"}. {"Owner privileges required","Besitzerrechte erforderlich"}. {"Packet relay is denied by service policy","Paket-Relay aufgrund der Dienstrichtlinien verweigert"}. -{"Packet","Paket"}. {"Participant ID","Teilnehmer-ID"}. {"Participant","Teilnehmer"}. {"Password Verification","Passwort bestätigen"}. @@ -392,7 +357,6 @@ {"Password:","Passwort:"}. {"Path to Dir","Pfad zum Verzeichnis"}. {"Path to File","Pfad zur Datei"}. -{"Pending","Ausstehend"}. {"Period: ","Zeitraum: "}. {"Persist items to storage","Items dauerhaft speichern"}. {"Persistent","Persistent"}. @@ -427,20 +391,15 @@ {"Recipient is not in the conference room","Empfänger ist nicht im Konferenzraum"}. {"Register an XMPP account","Ein XMPP-Konto registrieren"}. {"Register","Anmelden"}. -{"Registered Users","Registrierte Benutzer"}. -{"Registered Users:","Registrierte Benutzer:"}. {"Remote copy","Fernkopie"}. {"Remove a hat from a user","Eine Funktion bei einem Benutzer entfernen"}. -{"Remove All Offline Messages","Alle Offline-Nachrichten löschen"}. {"Remove User","Benutzer löschen"}. -{"Remove","Entfernen"}. {"Replaced by new connection","Durch neue Verbindung ersetzt"}. {"Request has timed out","Zeitüberschreitung bei Anforderung"}. {"Request is ignored","Anforderung wird ignoriert"}. {"Requested role","Angeforderte Rolle"}. {"Resources","Ressourcen"}. {"Restart Service","Dienst neustarten"}. -{"Restart","Neustart"}. {"Restore Backup from File at ","Backup wiederherstellen aus Datei bei "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Stelle binäres Backup beim nächsten ejabberd-Neustart wieder her (benötigt weniger Speicher):"}. {"Restore binary backup immediately:","Stelle binäres Backup sofort wieder her:"}. @@ -456,20 +415,15 @@ {"Room terminates","Raum wird beendet"}. {"Room title","Raumname"}. {"Roster groups allowed to subscribe","Kontaktlistengruppen die abonnieren dürfen"}. -{"Roster of ~ts","Kontaktliste von ~ts"}. {"Roster size","Kontaktlistengröße"}. -{"Roster:","Kontaktliste:"}. -{"RPC Call Error","Fehler bei RPC-Aufruf"}. {"Running Nodes","Laufende Knoten"}. {"~s invites you to the room ~s","~s lädt Sie in den Raum ~s ein"}. {"Saturday","Samstag"}. -{"Script check","Script-Überprüfung"}. {"Search from the date","Suche ab Datum"}. {"Search Results for ","Suchergebnisse für "}. {"Search the text","Text durchsuchen"}. {"Search until the date","Suche bis Datum"}. {"Search users in ","Suche Benutzer in "}. -{"Select All","Alles auswählen"}. {"Send announcement to all online users on all hosts","Ankündigung an alle angemeldeten Benutzer auf allen Hosts senden"}. {"Send announcement to all online users","Ankündigung an alle angemeldeten Benutzer senden"}. {"Send announcement to all users on all hosts","Ankündigung an alle Benutzer auf allen Hosts senden"}. @@ -493,22 +447,16 @@ {"Stanza id is not valid","Stanza-ID ist ungültig"}. {"Stanza ID","Stanza-ID"}. {"Statically specify a replyto of the node owner(s)","Ein 'replyto' des/der Nodebesitzer(s) statisch angeben"}. -{"Statistics of ~p","Statistiken von ~p"}. -{"Statistics","Statistiken"}. -{"Stop","Anhalten"}. {"Stopped Nodes","Angehaltene Knoten"}. -{"Storage Type","Speichertyp"}. {"Store binary backup:","Speichere binäres Backup:"}. {"Store plain text backup:","Speichere Klartext-Backup:"}. {"Stream management is already enabled","Stream-Verwaltung ist bereits aktiviert"}. {"Stream management is not enabled","Stream-Verwaltung ist nicht aktiviert"}. {"Subject","Betreff"}. -{"Submit","Senden"}. {"Submitted","Gesendet"}. {"Subscriber Address","Abonnenten-Adresse"}. {"Subscribers may publish","Abonnenten dürfen veröffentlichen"}. {"Subscription requests must be approved and only subscribers may retrieve items","Abonnement-Anforderungen müssen genehmigt werden und nur Abonnenten dürfen Items abrufen"}. -{"Subscription","Abonnement"}. {"Subscriptions are not allowed","Abonnements sind nicht erlaubt"}. {"Sunday","Sonntag"}. {"Text associated with a picture","Text verbunden mit einem Bild"}. @@ -571,10 +519,8 @@ {"Thursday","Donnerstag"}. {"Time delay","Zeitverzögerung"}. {"Timed out waiting for stream resumption","Zeitüberschreitung beim Warten auf Streamfortsetzung"}. -{"Time","Zeit"}. {"To register, visit ~s","Um sich zu registrieren, besuchen Sie ~s"}. {"To ~ts","An ~ts"}. -{"To","An"}. {"Token TTL","Token-TTL"}. {"Too many active bytestreams","Zu viele aktive Bytestreams"}. {"Too many CAPTCHA requests","Zu viele CAPTCHA-Anforderungen"}. @@ -585,12 +531,7 @@ {"Too many receiver fields were specified","Zu viele Empfängerfelder wurden angegeben"}. {"Too many unacked stanzas","Zu viele unbestätigte Stanzas"}. {"Too many users in this conference","Zu viele Benutzer in dieser Konferenz"}. -{"Total rooms","Gesamte Räume"}. {"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}. -{"Transactions Aborted:","Abgebrochene Transaktionen:"}. -{"Transactions Committed:","Übergebene Transaktionen:"}. -{"Transactions Logged:","Protokollierte Transaktionen:"}. -{"Transactions Restarted:","Neu gestartete Transaktionen:"}. {"~ts's Offline Messages Queue","Offline-Nachrichten-Warteschlange von ~ts"}. {"Tuesday","Dienstag"}. {"Unable to generate a CAPTCHA","Konnte kein CAPTCHA erstellen"}. @@ -601,19 +542,13 @@ {"Uninstall","Deinstallieren"}. {"Unregister an XMPP account","Ein XMPP-Konto entfernen"}. {"Unregister","Deregistrieren"}. -{"Unselect All","Alle abwählen"}. {"Unsupported element","Nicht unterstütztes -Element"}. {"Unsupported version","Nicht unterstützte Version"}. {"Update message of the day (don't send)","Aktualisiere Nachricht des Tages (nicht senden)"}. {"Update message of the day on all hosts (don't send)","Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)"}. -{"Update plan","Aktualisierungsplan"}. -{"Update ~p","~p aktualisieren"}. -{"Update script","Aktualisierungsscript"}. {"Update specs to get modules source, then install desired ones.","Aktualisieren Sie die Spezifikationen, um den Quellcode der Module zu erhalten und installieren Sie dann die gewünschten Module."}. {"Update Specs","Spezifikationen aktualisieren"}. -{"Update","Aktualisieren"}. {"Upgrade","Upgrade"}. -{"Uptime:","Betriebszeit:"}. {"URL for Archived Discussion Logs","URL für archivierte Diskussionsprotokolle"}. {"User already exists","Benutzer existiert bereits"}. {"User (jid)","Benutzer (JID)"}. @@ -628,7 +563,6 @@ {"Users are not allowed to register accounts so quickly","Benutzer dürfen Konten nicht so schnell registrieren"}. {"Users Last Activity","Letzte Benutzeraktivität"}. {"Users","Benutzer"}. -{"Validate","Validieren"}. {"Value 'get' of 'type' attribute is not allowed","Wert 'get' des 'type'-Attributs ist nicht erlaubt"}. {"Value of '~s' should be boolean","Wert von '~s' sollte boolesch sein"}. {"Value of '~s' should be datetime string","Wert von '~s' sollte DateTime-Zeichenkette sein"}. @@ -636,8 +570,6 @@ {"Value 'set' of 'type' attribute is not allowed","Wert 'set' des 'type'-Attributs ist nicht erlaubt"}. {"vCard User Search","vCard-Benutzer-Suche"}. {"View joined MIX channels","Beitretene MIX-Channel ansehen"}. -{"View Queue","Warteschlange ansehen"}. -{"View Roster","Kontaktliste ansehen"}. {"Virtual Hosts","Virtuelle Hosts"}. {"Visitor","Besucher"}. {"Visitors are not allowed to change their nicknames in this room","Besucher dürfen in diesem Raum ihren Spitznamen nicht ändern"}. @@ -667,6 +599,7 @@ {"XMPP Show Value of XA (Extended Away)","XMPP-Anzeigewert von XA (Extended Away/für längere Zeit abwesend)"}. {"XMPP URI of Associated Publish-Subscribe Node","XMPP-URI des verknüpften Publish-Subscribe-Knotens"}. {"You are being removed from the room because of a system shutdown","Sie werden wegen einer Systemabschaltung aus dem Raum entfernt"}. +{"You are not allowed to send private messages","Sie dürfen keine privaten Nachrichten senden"}. {"You are not joined to the channel","Sie sind dem Raum nicht beigetreten"}. {"You can later change your password using an XMPP client.","Sie können Ihr Passwort später mit einem XMPP-Client ändern."}. {"You have been banned from this room","Sie wurden aus diesem Raum verbannt"}. From cafe28fdea2545bbf63d5b8956607a7db52fad92 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 11:35:55 +0200 Subject: [PATCH 0644/1302] Update other translations --- priv/msgs/ar.msg | 1 - priv/msgs/bg.msg | 70 --------------------------------------------- priv/msgs/ca.msg | 70 --------------------------------------------- priv/msgs/cs.msg | 55 ----------------------------------- priv/msgs/el.msg | 68 ------------------------------------------- priv/msgs/eo.msg | 53 ---------------------------------- priv/msgs/es.msg | 70 --------------------------------------------- priv/msgs/fr.msg | 65 ----------------------------------------- priv/msgs/gl.msg | 53 ---------------------------------- priv/msgs/he.msg | 53 ---------------------------------- priv/msgs/hu.msg | 58 ------------------------------------- priv/msgs/id.msg | 62 --------------------------------------- priv/msgs/it.msg | 70 --------------------------------------------- priv/msgs/ja.msg | 62 --------------------------------------- priv/msgs/nl.msg | 52 --------------------------------- priv/msgs/no.msg | 56 ------------------------------------ priv/msgs/pt-br.msg | 70 --------------------------------------------- priv/msgs/pt.msg | 70 --------------------------------------------- priv/msgs/ru.msg | 57 ------------------------------------ priv/msgs/sk.msg | 48 ------------------------------- priv/msgs/sq.msg | 50 -------------------------------- priv/msgs/sv.msg | 47 ------------------------------ priv/msgs/th.msg | 44 ---------------------------- priv/msgs/tr.msg | 47 ------------------------------ priv/msgs/uk.msg | 68 ------------------------------------------- priv/msgs/vi.msg | 44 ---------------------------- priv/msgs/wa.msg | 53 ---------------------------------- 27 files changed, 1516 deletions(-) diff --git a/priv/msgs/ar.msg b/priv/msgs/ar.msg index b33565af4..5b7af3184 100644 --- a/priv/msgs/ar.msg +++ b/priv/msgs/ar.msg @@ -8,4 +8,3 @@ {"# participants","# المشاركين"}. {"A description of the node","وصف العقدة"}. {"A Web Page","موقع الكتروني"}. -{"'Displayed groups' not added (they do not exist!): ","لم تتم إضافة \"المجموعات المعروضة\" (فهي غير موجودة!): "}. diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg index e19799fd4..93e4d13dd 100644 --- a/priv/msgs/bg.msg +++ b/priv/msgs/bg.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","Профилът не съществува"}. {"Action on user","Действие върху потребител"}. {"Add a hat to a user","Добави шапка към потребител"}. -{"Add Jabber ID","Добави Jabber ID"}. -{"Add New","Добави нов"}. {"Add User","Добави потребител"}. {"Administration of ","Администриране на "}. {"Administration","Администриране"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Всеки, който има абонамент за присъствие на двете или от: може да се абонира и да извлича елементи"}. {"Anyone with Voice","Всеки, с възможност за гласово обаждане"}. {"Anyone","Всеки"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Очевидно Вашият акаунт няма административни права за този сървър. Моля, проверете как да предоставите администраторски права на: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Април"}. {"Attribute 'channel' is required for this request","Атрибутът 'канал' е задължителен за тази заявка"}. {"Attribute 'id' is mandatory for MIX messages","Атрибутът 'id' е задължителен за MIX съобщения"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","Конферентната стая не съществува"}. {"Configuration of room ~s","Конфигурация на стая ~s"}. {"Configuration","Конфигурация"}. -{"Connected Resources:","Свързани ресурси:"}. {"Contact Addresses (normally, room owner or owners)","Адреси за контакт (обикновено собственик или собственици на стая)"}. -{"Contrib Modules","Сътруднически модули"}. {"Country","Държава"}. -{"CPU Time:","Процесорно време:"}. {"Current Discussion Topic","Текуща тема на дискусита"}. {"Database failure","Грешка в базата данни"}. -{"Database Tables at ~p","Таблици на базата данни при ~p"}. {"Database Tables Configuration at ","Конфигурация на таблиците в базата данни при "}. {"Database","База данни"}. {"December","Декември"}. {"Default users as participants","Потребители по подразбиране като участници"}. -{"Delete content","Изтрий съдържанието"}. {"Delete message of the day on all hosts","Изтрий съобщението на деня от всички нодове"}. {"Delete message of the day","Изтрий съобщението на деня"}. -{"Delete Selected","Изтрий избраните"}. -{"Delete table","Изтрий таблицата"}. {"Delete User","Изтрий потребителя"}. {"Deliver event notifications","Достави известията за събития"}. {"Deliver payloads with event notifications","Достави прикачените обекти с известията за събития"}. -{"Description:","Описание:"}. {"Disc only copy","Копие само на диска"}. -{"'Displayed groups' not added (they do not exist!): ","'Показаните групи' не са добавени (не съществуват!): "}. -{"Displayed:","Показва се:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Не казвайте паролата си на никого, дори на администраторите на XMPP сървъра."}. {"Dump Backup to Text File at ","Архивиране в текстов файл при "}. {"Dump to Text File","Архивиране в текстов файл"}. @@ -127,7 +114,6 @@ {"ejabberd vCard module","ejabberd vCard модул"}. {"ejabberd Web Admin","Уеб администрация на ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Елементи"}. {"Email Address","Имейл адрес"}. {"Email","Илейл"}. {"Enable hats","Активиране на шапки"}. @@ -142,7 +128,6 @@ {"Enter path to text file","Въведете пътя към текстовия файл"}. {"Enter the text you see","Въведете текста, който виждате"}. {"Erlang XMPP Server","Erlang XMPP сървър"}. -{"Error","Грешка"}. {"Exclude Jabber IDs from CAPTCHA challenge","Изключи CAPTCHA предизвикателство за следните Jabber ID-та"}. {"Export all tables as SQL queries to a file:","Експортирай всички таблици като SQL заявки във файл:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Експортирай данните за всички потребители на сървъра в PIEFXIS файлове (XEP-0227):"}. @@ -161,7 +146,6 @@ {"Fill in the form to search for any matching XMPP User","Попълнете формата, за да търсите съвпадащ XMPP потребител"}. {"Friday","Петък"}. {"From ~ts","От ~ts"}. -{"From","От"}. {"Full List of Room Admins","Пълен списък на администраторите на стаята"}. {"Full List of Room Owners","Пълен списък на собствениците на стаята"}. {"Full Name","Пълно име"}. @@ -171,18 +155,13 @@ {"Get Number of Registered Users","Брой на регистрираните потребители"}. {"Get Pending","Виж чакащи"}. {"Get User Last Login Time","Покажи времето, когато потребителят е влязъл за последно"}. -{"Get User Password","Покажи паролата на потребителя"}. {"Get User Statistics","Покажи статистика за потребителя"}. {"Given Name","Име"}. {"Grant voice to this person?","Предоставяне на глас за потребителя?"}. -{"Groups that will be displayed to the members","Групи, които ще се показват на членовете"}. -{"Groups","Групи"}. -{"Group","Група"}. {"Hat title","Заглавие на шапката"}. {"Hat URI","URI адрес за шапка"}. {"Hats limit exceeded","Превишен е лимитът за шапка"}. {"Host unknown","Неизвестен хост"}. -{"Host","Хост"}. {"HTTP File Upload","Качване на файл по HTTP"}. {"Idle connection","Неактивна връзка"}. {"If you don't see the CAPTCHA image here, visit the web page.","Ако не виждате CAPTCHA изображението, посетете уеб страницата."}. @@ -196,7 +175,6 @@ {"Import Users From jabberd14 Spool Files","Импорт на потребители от jabberd14 Spool файлове"}. {"Improper domain part of 'from' attribute","Неправилна част за домейн в атрибута 'from'"}. {"Improper message type","Неправилен тип съобщение"}. -{"Incoming s2s Connections:","Входящи s2s връзки:"}. {"Incorrect CAPTCHA submit","Неправилно CAPTCHA въвеждане"}. {"Incorrect data form","Неправилна форма на данните"}. {"Incorrect password","Грешна парола"}. @@ -224,18 +202,15 @@ {"July","Юли"}. {"June","Юни"}. {"Just created","Току що създаден"}. -{"Label:","Етикет:"}. {"Last Activity","Последна активност"}. {"Last login","Последно влизане"}. {"Last message","Последно съобщение"}. {"Last month","Миналия месец"}. {"Last year","Миналата година"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Най-малко значимите битове SHA-256 хеш на текст трябва да са равни на шестнайсетичния етикет"}. -{"List of rooms","Списък на стаите"}. {"List of users with hats","Списък на потребителите с шапки"}. {"List users with hats","Избройте потребителите с шапки"}. {"Logging","Регистриране на събития"}. -{"Low level update script","Скрипт за актуализация на ниско ниво"}. {"Make participants list public","Направи списъка с участниците публичен"}. {"Make room CAPTCHA protected","Защити стаята с CAPTCHA"}. {"Make room members-only","Направи стаята само за членове"}. @@ -253,11 +228,8 @@ {"Maximum number of items to persist","Максимален брой елементи за запазване"}. {"Maximum Number of Occupants","Максимален брой участници"}. {"May","Май"}. -{"Members not added (inexistent vhost!): ","Членовете не са добавени (несъществуващ vhost!): "}. {"Membership is required to enter this room","Изисква се членство, за вход в тази стая"}. -{"Members:","Членове:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запомнете паролата си или я запишете на лист хартия, поставен на сигурно място. В XMPP няма автоматичен начин за възстановяване на паролата в случай, че я забравите."}. -{"Memory","Памет"}. {"Mere Availability in XMPP (No Show Value)","Наличност в XMPP (Не показвай стойност)"}. {"Message body","Текст на съобщението"}. {"Message not found in forwarded payload","Съобщението не е намерено в препратения прикачен елемент"}. @@ -269,15 +241,12 @@ {"Moderator privileges required","Изискват се права на модератор"}. {"Moderators Only","Само модератори"}. {"Moderator","Модератор"}. -{"Modified modules","Модифицирани модули"}. {"Module failed to handle the query","Модулът не успя да обработи заявката"}. {"Monday","Понеделник"}. {"Multicast","Мултикаст"}. {"Multiple elements are not allowed by RFC6121","Повече от един елемента не се разрешават от RFC6121"}. {"Multi-User Chat","Групов чат (MUC)"}. -{"Name in the rosters where this group will be displayed","Име в списъците с контакти, където ще се показва тази група"}. {"Name","Име"}. -{"Name:","Име:"}. {"Natural Language for Room Discussions","Език за дискусии в стаята"}. {"Natural-Language Room Name","Име на стаята на предпочитания език"}. {"Neither 'jid' nor 'nick' attribute found","Атрибутите 'jid' и 'nick' не са намерени"}. @@ -342,14 +311,10 @@ {"Occupants are allowed to query others","Участниците могат да отправят заявки към други лица"}. {"Occupants May Change the Subject","Участниците могат да променят темата"}. {"October","Октомври"}. -{"Offline Messages","Офлайн съобщения"}. -{"Offline Messages:","Офлайн съобщения:"}. {"OK","ДОБРЕ"}. {"Old Password:","Стара парола:"}. {"Online Users","Онлайн потребители"}. -{"Online Users:","Онлайн потребители:"}. {"Online","Онлайн"}. -{"Only admins can see this","Само администратори могат да видят това"}. {"Only collection node owners may associate leaf nodes with the collection","Само собственици на колекционни нодове имат право да свързват листови нодове към колекцията"}. {"Only deliver notifications to available users","Доставяне на известия само до наличните потребители"}. {"Only or tags are allowed","Само тагове и са разрешени"}. @@ -369,10 +334,8 @@ {"Organization Unit","Отдел"}. {"Other Modules Available:","Други налични модули:"}. {"Outgoing s2s Connections","Изходящи s2s връзки"}. -{"Outgoing s2s Connections:","Изходящи s2s връзки:"}. {"Owner privileges required","Изискват се привилегии на собственик"}. {"Packet relay is denied by service policy","Предаването на пакети е отказано от политиката на услугата"}. -{"Packet","Пакет"}. {"Participant ID","ID на участник"}. {"Participant","Участник"}. {"Password Verification","Проверка на паролата"}. @@ -382,7 +345,6 @@ {"Path to Dir","Път към директория"}. {"Path to File","Път до файл"}. {"Payload semantic type information","Информация за семантичен тип полезен товар"}. -{"Pending","В очакване"}. {"Period: ","Период: "}. {"Persist items to storage","Запазване на елементите в хранилището"}. {"Persistent","Постоянен"}. @@ -415,21 +377,16 @@ {"Receive notification of new nodes only","Получаване на известия само за нови нодове"}. {"Recipient is not in the conference room","Получателят не е в конферентната стая"}. {"Register an XMPP account","Регистрирай XMPP акаунт"}. -{"Registered Users","Регистрирани потребители"}. -{"Registered Users:","Регистрирани потребители:"}. {"Register","Регистрирай"}. {"Remote copy","Отдалечено копие"}. {"Remove a hat from a user","Премахни шапка от потребител"}. -{"Remove All Offline Messages","Премахни всички офлайн съобщения"}. {"Remove User","Премахни потребител"}. -{"Remove","Премахни"}. {"Replaced by new connection","Заменен от нова връзка"}. {"Request has timed out","Времето за заявка изтече"}. {"Request is ignored","Заявката е игнорирано"}. {"Requested role","Заявена роля"}. {"Resources","Ресурси"}. {"Restart Service","Рестартирай услугата"}. -{"Restart","Рестартирай"}. {"Restore Backup from File at ","Възстанови резервно копие от файл в "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Възстановяване на бинарно копие след следващото рестартиране на ejabberd (изисква по-малко памет):"}. {"Restore binary backup immediately:","Възстанови незабавно двоично копие:"}. @@ -445,20 +402,15 @@ {"Room terminates","Стаята се прекратява"}. {"Room title","Заглавие на стаята"}. {"Roster groups allowed to subscribe","Групи от списъци с контакти, на които е разрешено да се абонират"}. -{"Roster of ~ts","Списък с контакти на ~ts"}. {"Roster size","Размер на списъка с контакти"}. -{"Roster:","Списък с контакти:"}. -{"RPC Call Error","Грешка при RPC повикване"}. {"Running Nodes","Работещи нодове"}. {"~s invites you to the room ~s","~s ви кани в стая ~s"}. {"Saturday","Събота"}. -{"Script check","Проверка на скрипт"}. {"Search from the date","Търси от дата"}. {"Search Results for ","Резултати от търсенето за "}. {"Search the text","Търси текста"}. {"Search until the date","Търси до дата"}. {"Search users in ","Търси потребители в "}. -{"Select All","Избери всички"}. {"Send announcement to all online users on all hosts","Изпрати съобщение до всички онлайн потребители на всички хостове"}. {"Send announcement to all online users","Изпрати съобщение до всички онлайн потребители"}. {"Send announcement to all users on all hosts","Изпрати съобщение до всички потребители на всички хостове"}. @@ -482,23 +434,17 @@ {"Stanza id is not valid","Невалидно ID на строфата"}. {"Stanza ID","ID на строфа"}. {"Statically specify a replyto of the node owner(s)","Статично задаване на replyto на собственика(ците) на нода"}. -{"Statistics of ~p","Статистики на ~p"}. -{"Statistics","Статистики"}. {"Stopped Nodes","Спрени нодове"}. -{"Stop","Спри"}. -{"Storage Type","Тип хранилище"}. {"Store binary backup:","Запази бинарен архив:"}. {"Store plain text backup:","Запази архив като обикновен текст:"}. {"Stream management is already enabled","Управлението на потока вече е активирано"}. {"Stream management is not enabled","Управлението на потока не е активирано"}. {"Subject","Тема"}. {"Submitted","Изпратено"}. -{"Submit","Изпрати"}. {"Subscriber Address","Адрес на абоната"}. {"Subscribers may publish","Абонатите могат да публикуват"}. {"Subscription requests must be approved and only subscribers may retrieve items","Заявките за абонамент трябва да бъдат одобрени и само абонатите могат да извличат елементи"}. {"Subscriptions are not allowed","Абонаментите не са разрешени"}. -{"Subscription","Абонамент"}. {"Sunday","Неделя"}. {"Text associated with a picture","Текст, свързан със снимка"}. {"Text associated with a sound","Текст, свързан със звук"}. @@ -560,7 +506,6 @@ {"Thursday","Четвъртък"}. {"Time delay","Закъснение"}. {"Timed out waiting for stream resumption","Времето за изчакване за възобновяване на потока изтече"}. -{"Time","Час"}. {"To register, visit ~s","За да се регистрирате, посетете ~s"}. {"To ~ts","До ~ts"}. {"Token TTL","Токен TTL"}. @@ -573,13 +518,7 @@ {"Too many receiver fields were specified","Посочени са твърде много полета за получател"}. {"Too many unacked stanzas","Твърде много непотвърдени строфи"}. {"Too many users in this conference","Твърде много потребители в тази конференция"}. -{"Total rooms","Общо стаи"}. -{"To","До"}. {"Traffic rate limit is exceeded","Лимитът за трафик е надвишен"}. -{"Transactions Aborted:","Прекратени транзакции:"}. -{"Transactions Committed:","Извършени транзакции:"}. -{"Transactions Logged:","Регистрирани транзакции:"}. -{"Transactions Restarted:","Рестартирани транзакции:"}. {"~ts's Offline Messages Queue","Офлайн съобщения на ~ts"}. {"Tuesday","Вторник"}. {"Unable to generate a CAPTCHA","Не може да се генерира CAPTCHA"}. @@ -590,19 +529,13 @@ {"Uninstall","Деинсталирай"}. {"Unregister an XMPP account","Дерегистрирай XMPP профил"}. {"Unregister","Дерегистрирай"}. -{"Unselect All","Размаркирай всички"}. {"Unsupported element","Неподдържан елемент "}. {"Unsupported version","Неподдържана версия"}. {"Update message of the day (don't send)","Актуализирай съобщението на деня (не изпращай)"}. {"Update message of the day on all hosts (don't send)","Актуализирай съобщението на деня на всички хостове (не изпращай)"}. -{"Update plan","План за актуализация"}. -{"Update ~p","Актуализирай ~p"}. -{"Update script","Актуализиращ скрипт"}. {"Update specs to get modules source, then install desired ones.","Актуализирайте спецификациите, за да получите източник на модули, след което инсталирайте желаните."}. {"Update Specs","Актуализирай спецификациите"}. -{"Update","Актуализирай"}. {"Upgrade","Обнови"}. -{"Uptime:","Време на работа:"}. {"URL for Archived Discussion Logs","URL адрес за дневници на архивирани дискусии"}. {"User already exists","Потребителят вече съществува"}. {"User (jid)","Потребител (jid)"}. @@ -617,15 +550,12 @@ {"Users Last Activity","Последна активност на потребителите"}. {"Users","Потребители"}. {"User","Потребител"}. -{"Validate","Валидирай"}. {"Value 'get' of 'type' attribute is not allowed","Стойността 'get' на атрибут 'type' не е разрешена"}. {"Value of '~s' should be boolean","Стойността на '~s' трябва да е булева"}. {"Value of '~s' should be datetime string","Стойността на '~s' трябва да бъде низ за дата и час"}. {"Value of '~s' should be integer","Стойността на '~s' трябва да бъде цяло число"}. {"Value 'set' of 'type' attribute is not allowed","Стойността 'set' на атрибут 'type' не е разрешена"}. {"View joined MIX channels","Вижте присъединените MIX канали"}. -{"View Queue","Вижте опашката"}. -{"View Roster","Преглед на списъка с контакти"}. {"Virtual Hosts","Виртуални хостове"}. {"Visitors are not allowed to change their nicknames in this room","Посетителите нямат право да променят псевдонимите си в тази стая"}. {"Visitors are not allowed to send messages to all occupants","На посетителите не е разрешено да изпращат съобщения до всички участници"}. diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index da668af9b..b90cbbd56 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","El compte no existeix"}. {"Action on user","Acció en l'usuari"}. {"Add a hat to a user","Afegir un barret a un usuari"}. -{"Add Jabber ID","Afegir Jabber ID"}. -{"Add New","Afegir nou"}. {"Add User","Afegir usuari"}. {"Administration of ","Administració de "}. {"Administration","Administració"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualsevol amb una subscripció de presencia de 'both' o 'from' pot subscriure's i publicar elements"}. {"Anyone with Voice","Qualsevol amb Veu"}. {"Anyone","Qualsevol"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentment el teu compte no te privilegis d'administrador en este servidor. Per favor consulta com obtindre privilegis d'administrador en: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","L'atribut 'channel' és necessari per a aquesta petició"}. {"Attribute 'id' is mandatory for MIX messages","L'atribut 'id' es necessari per a missatges MIX"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","La sala de conferències no existeix"}. {"Configuration of room ~s","Configuració de la sala ~s"}. {"Configuration","Configuració"}. -{"Connected Resources:","Recursos connectats:"}. {"Contact Addresses (normally, room owner or owners)","Adreces de contacte (normalment, propietaris de la sala)"}. -{"Contrib Modules","Mòduls Contrib"}. {"Country","Pais"}. -{"CPU Time:","Temps de CPU:"}. {"Current Discussion Topic","Assumpte de discussió actual"}. {"Database failure","Error a la base de dades"}. -{"Database Tables at ~p","Taules de la base de dades en ~p"}. {"Database Tables Configuration at ","Configuració de la base de dades en "}. {"Database","Base de dades"}. {"December","Decembre"}. {"Default users as participants","Els usuaris són participants per defecte"}. -{"Delete content","Eliminar contingut"}. {"Delete message of the day on all hosts","Elimina el missatge del dis de tots els hosts"}. {"Delete message of the day","Eliminar el missatge del dia"}. -{"Delete Selected","Eliminar els seleccionats"}. -{"Delete table","Eliminar taula"}. {"Delete User","Eliminar Usuari"}. {"Deliver event notifications","Entrega de notificacions d'events"}. {"Deliver payloads with event notifications","Enviar payloads junt a les notificacions d'events"}. -{"Description:","Descripció:"}. {"Disc only copy","Còpia sols en disc"}. -{"'Displayed groups' not added (they do not exist!): ","'Mostrats' no afegits (no existeixen!): "}. -{"Displayed:","Mostrats:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","No li donis la teva contrasenya a ningú, ni tan sols als administradors del servidor XMPP."}. {"Dump Backup to Text File at ","Exporta còpia de seguretat a fitxer de text en "}. {"Dump to Text File","Exportar a fitxer de text"}. @@ -132,7 +119,6 @@ {"ejabberd vCard module","ejabberd mòdul vCard"}. {"ejabberd Web Admin","ejabberd Web d'administració"}. {"ejabberd","ejabberd"}. -{"Elements","Elements"}. {"Email Address","Adreça de correu"}. {"Email","Correu"}. {"Enable hats","Activar barrets"}. @@ -147,7 +133,6 @@ {"Enter path to text file","Introdueix ruta al fitxer de text"}. {"Enter the text you see","Introdueix el text que veus"}. {"Erlang XMPP Server","Servidor Erlang XMPP"}. -{"Error","Error"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excloure Jabber IDs de la comprovació CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exporta totes les taules a un fitxer SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar dades de tots els usuaris del servidor a arxius PIEFXIS (XEP-0227):"}. @@ -166,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Emplena camps per a buscar usuaris XMPP que concorden"}. {"Friday","Divendres"}. {"From ~ts","De ~ts"}. -{"From","De"}. {"Full List of Room Admins","Llista completa de administradors de la sala"}. {"Full List of Room Owners","Llista completa de propietaris de la sala"}. {"Full Name","Nom complet"}. @@ -176,13 +160,9 @@ {"Get Number of Registered Users","Obtenir Número d'Usuaris Registrats"}. {"Get Pending","Obtenir Pendents"}. {"Get User Last Login Time","Obtenir la última connexió d'Usuari"}. -{"Get User Password","Obtenir Contrasenya d'usuari"}. {"Get User Statistics","Obtenir Estadístiques d'Usuari"}. {"Given Name","Nom propi"}. {"Grant voice to this person?","Concedir veu a aquesta persona?"}. -{"Group","Grup"}. -{"Groups that will be displayed to the members","Grups que seran mostrats als membres"}. -{"Groups","Grups"}. {"has been banned","ha sigut bloquejat"}. {"has been kicked because of a system shutdown","ha sigut expulsat perquè el sistema va a apagar-se"}. {"has been kicked because of an affiliation change","ha sigut expulsat a causa d'un canvi d'afiliació"}. @@ -192,7 +172,6 @@ {"Hat URI","URI del barret"}. {"Hats limit exceeded","El límit de tràfic ha sigut sobrepassat"}. {"Host unknown","Host desconegut"}. -{"Host","Host"}. {"HTTP File Upload","HTTP File Upload"}. {"Idle connection","Connexió sense us"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no veus la imatge CAPTCHA açí, visita la pàgina web."}. @@ -206,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importar usuaris de jabberd14"}. {"Improper domain part of 'from' attribute","La part de domini de l'atribut 'from' es impròpia"}. {"Improper message type","Tipus de missatge incorrecte"}. -{"Incoming s2s Connections:","Connexions s2s d'entrada:"}. {"Incorrect CAPTCHA submit","El CAPTCHA proporcionat és incorrecte"}. {"Incorrect data form","El formulari de dades és incorrecte"}. {"Incorrect password","Contrasenya incorrecta"}. @@ -236,7 +214,6 @@ {"July","Juliol"}. {"June","Juny"}. {"Just created","Creació recent"}. -{"Label:","Etiqueta:"}. {"Last Activity","Última activitat"}. {"Last login","Últim login"}. {"Last message","Últim missatge"}. @@ -244,11 +221,9 @@ {"Last year","Últim any"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Els bits menys significants del hash SHA-256 del text deurien ser iguals a l'etiqueta hexadecimal"}. {"leaves the room","surt de la sala"}. -{"List of rooms","Llista de sales"}. {"List of users with hats","Llista d'usuaris amb barrets"}. {"List users with hats","Llista d'usuaris amb barrets"}. {"Logging","Registre"}. -{"Low level update script","Script d'actualització de baix nivell"}. {"Make participants list public","Crear una llista de participants pública"}. {"Make room CAPTCHA protected","Crear una sala protegida per CAPTCHA"}. {"Make room members-only","Crear una sala només per a membres"}. @@ -266,11 +241,8 @@ {"Maximum number of items to persist","Número màxim d'elements que persistixen"}. {"Maximum Number of Occupants","Número màxim d'ocupants"}. {"May","Maig"}. -{"Members not added (inexistent vhost!): ","Membres no afegits (perquè el vhost no existeix): "}. {"Membership is required to enter this room","Necessites ser membre d'aquesta sala per a poder entrar"}. -{"Members:","Membre:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memoritza la teva contrasenya, o escriu-la en un paper guardat a un lloc segur. A XMPP no hi ha una forma automatitzada de recuperar la teva contrasenya si la oblides."}. -{"Memory","Memòria"}. {"Mere Availability in XMPP (No Show Value)","Simplement disponibilitat a XMPP (sense valor de 'show')"}. {"Message body","Missatge"}. {"Message not found in forwarded payload","Missatge no trobat al contingut reenviat"}. @@ -282,15 +254,12 @@ {"Moderator privileges required","Es necessita tenir privilegis de moderador"}. {"Moderator","Moderador"}. {"Moderators Only","Només moderadors"}. -{"Modified modules","Mòduls modificats"}. {"Module failed to handle the query","El modul ha fallat al gestionar la petició"}. {"Monday","Dilluns"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","No estan permesos múltiples elements per RFC6121"}. {"Multi-User Chat","Multi-Usuari Converses"}. -{"Name in the rosters where this group will be displayed","Nom a les llistes de contactes on es mostrarà aquest grup"}. {"Name","Nom"}. -{"Name:","Nom:"}. {"Natural Language for Room Discussions","Llengua natural per a les discussions a les sales"}. {"Natural-Language Room Name","Nom de la sala a la seua llengua natural"}. {"Neither 'jid' nor 'nick' attribute found","No s'han trobat els atributs 'jid' ni 'nick'"}. @@ -355,14 +324,10 @@ {"Occupants are allowed to query others","Els ocupants poden enviar peticions a altres"}. {"Occupants May Change the Subject","Els ocupants poden canviar el Tema"}. {"October","Octubre"}. -{"Offline Messages:","Missatges fora de línia:"}. -{"Offline Messages","Missatges offline"}. {"OK","Acceptar"}. {"Old Password:","Antiga contrasenya:"}. {"Online Users","Usuaris conectats"}. -{"Online Users:","Usuaris en línia:"}. {"Online","Connectat"}. -{"Only admins can see this","Només els administradors poden veure esto"}. {"Only collection node owners may associate leaf nodes with the collection","Només els propietaris de la col·lecció de nodes poden associar nodes fulla amb la col·lecció"}. {"Only deliver notifications to available users","Sols enviar notificacions als usuaris disponibles"}. {"Only or tags are allowed","Només es permeten etiquetes o "}. @@ -381,11 +346,9 @@ {"Organization Name","Nom de la organizació"}. {"Organization Unit","Unitat de la organizació"}. {"Other Modules Available:","Altres mòduls disponibles:"}. -{"Outgoing s2s Connections:","Connexions d'eixida s2s:"}. {"Outgoing s2s Connections","Connexions s2s d'eixida"}. {"Owner privileges required","Es requerixen privilegis de propietari de la sala"}. {"Packet relay is denied by service policy","S'ha denegat el reenviament del paquet per política del servei"}. -{"Packet","Paquet"}. {"Participant ID","ID del Participant"}. {"Participant","Participant"}. {"Password Verification","Verificació de la Contrasenya"}. @@ -395,7 +358,6 @@ {"Path to Dir","Ruta al directori"}. {"Path to File","Ruta al fitxer"}. {"Payload semantic type information","Informació sobre el tipus semàntic de la carrega útil"}. -{"Pending","Pendent"}. {"Period: ","Període: "}. {"Persist items to storage","Persistir elements al guardar"}. {"Persistent","Persistent"}. @@ -429,21 +391,16 @@ {"Receive notification of new nodes only","Rebre notificació només de nous nodes"}. {"Recipient is not in the conference room","El receptor no està en la sala de conferència"}. {"Register an XMPP account","Registrar un compte XMPP"}. -{"Registered Users","Usuaris registrats"}. -{"Registered Users:","Usuaris registrats:"}. {"Register","Registrar"}. {"Remote copy","Còpia remota"}. {"Remove a hat from a user","Eliminar un barret d'un usuari"}. -{"Remove All Offline Messages","Eliminar tots els missatges offline"}. {"Remove User","Eliminar usuari"}. -{"Remove","Borrar"}. {"Replaced by new connection","Reemplaçat per una nova connexió"}. {"Request has timed out","La petició ha caducat"}. {"Request is ignored","La petició ha sigut ignorada"}. {"Requested role","Rol sol·licitat"}. {"Resources","Recursos"}. {"Restart Service","Reiniciar el Servei"}. -{"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaura còpia de seguretat des del fitxer en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar una còpia de seguretat binària després de reiniciar el ejabberd (requereix menys memòria:"}. {"Restore binary backup immediately:","Restaurar una còpia de seguretat binària ara mateix:"}. @@ -459,20 +416,15 @@ {"Room terminates","La sala està terminant"}. {"Room title","Títol de la sala"}. {"Roster groups allowed to subscribe","Llista de grups que tenen permés subscriures"}. -{"Roster of ~ts","Llista de contactes de ~ts"}. {"Roster size","Mida de la llista"}. -{"Roster:","Llista de contactes:"}. -{"RPC Call Error","Error de cridada RPC"}. {"Running Nodes","Nodes funcionant"}. {"~s invites you to the room ~s","~s et convida a la sala ~s"}. {"Saturday","Dissabte"}. -{"Script check","Comprovar script"}. {"Search from the date","Buscar des de la data"}. {"Search Results for ","Resultats de la búsqueda "}. {"Search the text","Buscar el text"}. {"Search until the date","Buscar fins la data"}. {"Search users in ","Cerca usuaris en "}. -{"Select All","Seleccionar Tots"}. {"Send announcement to all online users on all hosts","Enviar anunci a tots els usuaris connectats a tots els hosts"}. {"Send announcement to all online users","Enviar anunci a tots els usuaris connectats"}. {"Send announcement to all users on all hosts","Enviar anunci a tots els usuaris de tots els hosts"}. @@ -496,23 +448,17 @@ {"Stanza id is not valid","L'identificador del paquet no es vàlid"}. {"Stanza ID","ID del paquet"}. {"Statically specify a replyto of the node owner(s)","Especifica estaticament una adreça on respondre al propietari del node"}. -{"Statistics of ~p","Estadístiques de ~p"}. -{"Statistics","Estadístiques"}. -{"Stop","Detindre"}. {"Stopped Nodes","Nodes parats"}. -{"Storage Type","Tipus d'emmagatzematge"}. {"Store binary backup:","Guardar una còpia de seguretat binària:"}. {"Store plain text backup:","Guardar una còpia de seguretat en format de text pla:"}. {"Stream management is already enabled","L'administració de la connexió (stream management) ja està activada"}. {"Stream management is not enabled","L'administració de la conexió (stream management) no està activada"}. {"Subject","Tema"}. -{"Submit","Enviar"}. {"Submitted","Enviat"}. {"Subscriber Address","Adreça del Subscriptor"}. {"Subscribers may publish","Els subscriptors poden publicar"}. {"Subscription requests must be approved and only subscribers may retrieve items","Les peticiones de subscripció han de ser aprovades i només els subscriptors poden recuperar elements"}. {"Subscriptions are not allowed","Les subscripcions no estan permeses"}. -{"Subscription","Subscripció"}. {"Sunday","Diumenge"}. {"Text associated with a picture","Text associat amb una imatge"}. {"Text associated with a sound","Text associat amb un so"}. @@ -575,7 +521,6 @@ {"Thursday","Dijous"}. {"Time delay","Temps de retard"}. {"Timed out waiting for stream resumption","Massa temps esperant que es resumisca la connexió"}. -{"Time","Data"}. {"To register, visit ~s","Per a registrar-te, visita ~s"}. {"To ~ts","A ~ts"}. {"Token TTL","Token TTL"}. @@ -588,13 +533,7 @@ {"Too many receiver fields were specified","S'han especificat massa camps de receptors"}. {"Too many unacked stanzas","Massa missatges sense haver reconegut la seva recepció"}. {"Too many users in this conference","N'hi ha massa usuaris en esta sala de conferència"}. -{"To","Per a"}. -{"Total rooms","Sales totals"}. {"Traffic rate limit is exceeded","El límit de tràfic ha sigut sobrepassat"}. -{"Transactions Aborted:","Transaccions Avortades:"}. -{"Transactions Committed:","Transaccions Realitzades:"}. -{"Transactions Logged:","Transaccions registrades:"}. -{"Transactions Restarted:","Transaccions reiniciades:"}. {"~ts's Offline Messages Queue","~ts's cua de missatges offline"}. {"Tuesday","Dimarts"}. {"Unable to generate a CAPTCHA","No s'ha pogut generar un CAPTCHA"}. @@ -605,19 +544,13 @@ {"Uninstall","Desinstal·lar"}. {"Unregister an XMPP account","Anul·lar el registre d'un compte XMPP"}. {"Unregister","Anul·lar el registre"}. -{"Unselect All","Deseleccionar tots"}. {"Unsupported element","Element no soportat"}. {"Unsupported version","Versió no suportada"}. {"Update message of the day (don't send)","Actualitzar el missatge del dia (no enviar)"}. {"Update message of the day on all hosts (don't send)","Actualitza el missatge del dia en tots els hosts (no enviar)"}. -{"Update ~p","Actualitzar ~p"}. -{"Update plan","Pla d'actualització"}. -{"Update script","Script d'actualització"}. {"Update specs to get modules source, then install desired ones.","Actualitza les especificacions per obtindre el codi font dels mòduls, després instal·la els que vulgues."}. {"Update Specs","Actualitzar Especificacions"}. -{"Update","Actualitzar"}. {"Upgrade","Actualitza"}. -{"Uptime:","Temps en marxa:"}. {"URL for Archived Discussion Logs","URL dels Arxius de Discussions"}. {"User already exists","El usuari ja existeix"}. {"User JID","JID del usuari"}. @@ -632,7 +565,6 @@ {"Users Last Activity","Última activitat d'usuari"}. {"Users","Usuaris"}. {"User","Usuari"}. -{"Validate","Validar"}. {"Value 'get' of 'type' attribute is not allowed","El valor 'get' a l'atribut 'type' no és permès"}. {"Value of '~s' should be boolean","El valor de '~s' deuria ser booleà"}. {"Value of '~s' should be datetime string","El valor de '~s' deuria ser una data"}. @@ -640,8 +572,6 @@ {"Value 'set' of 'type' attribute is not allowed","El valor 'set' a l'atribut 'type' no és permès"}. {"vCard User Search","vCard recerca d'usuari"}. {"View joined MIX channels","Vore els canals MIX units"}. -{"View Queue","Vore Cua"}. -{"View Roster","Vore Llista de contactes"}. {"Virtual Hosts","Hosts virtuals"}. {"Visitors are not allowed to change their nicknames in this room","Els visitants no tenen permés canviar el seus Nicknames en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Els visitants no poden enviar missatges a tots els ocupants"}. diff --git a/priv/msgs/cs.msg b/priv/msgs/cs.msg index 991eb2871..cd57ebefb 100644 --- a/priv/msgs/cs.msg +++ b/priv/msgs/cs.msg @@ -9,8 +9,6 @@ {"Accept","Přijmout"}. {"Access denied by service policy","Přístup byl zamítnut nastavením služby"}. {"Action on user","Akce aplikovaná na uživatele"}. -{"Add Jabber ID","Přidat Jabber ID"}. -{"Add New","Přidat nový"}. {"Add User","Přidat uživatele"}. {"Administration of ","Administrace "}. {"Administration","Administrace"}. @@ -60,22 +58,17 @@ {"Conference room does not exist","Místnost neexistuje"}. {"Configuration of room ~s","Konfigurace místnosti ~s"}. {"Configuration","Konfigurace"}. -{"Connected Resources:","Připojené zdroje:"}. {"Country","Země"}. -{"CPU Time:","Čas procesoru:"}. {"Database failure","Chyba databáze"}. -{"Database Tables at ~p","Databázové tabulky na ~p"}. {"Database Tables Configuration at ","Konfigurace databázových tabulek "}. {"Database","Databáze"}. {"December",". prosince"}. {"Default users as participants","Uživatelé jsou implicitně členy"}. {"Delete message of the day on all hosts","Smazat zprávu dne na všech hostitelích"}. {"Delete message of the day","Smazat zprávu dne"}. -{"Delete Selected","Smazat vybrané"}. {"Delete User","Smazat uživatele"}. {"Deliver event notifications","Doručovat upozornění na události"}. {"Deliver payloads with event notifications","Doručovat náklad s upozorněním na událost"}. -{"Description:","Popis:"}. {"Disc only copy","Jen kopie disku"}. {"Dump Backup to Text File at ","Uložit zálohu do textového souboru na "}. {"Dump to Text File","Uložit do textového souboru"}. @@ -87,7 +80,6 @@ {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","Webová administrace ejabberd"}. -{"Elements","Položek"}. {"Email","E-mail"}. {"Enable logging","Zaznamenávat konverzace"}. {"Enable message archiving","Povolit ukládání historie zpráv"}. @@ -99,7 +91,6 @@ {"Enter path to jabberd14 spool file","Zadejte cestu k spool souboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému souboru"}. {"Enter the text you see","Zadejte text, který vidíte"}. -{"Error","Chyba"}. {"Exclude Jabber IDs from CAPTCHA challenge","Vyloučit Jabber ID z procesu CAPTCHA ověřování"}. {"Export all tables as SQL queries to a file:","Zálohovat všechny tabulky jako SQL dotazy do souboru:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovat všechny uživatele do souboru ve formátu PIEFXIS (XEP-0227):"}. @@ -116,24 +107,19 @@ {"File larger than ~w bytes","Soubor větší než ~w bytů"}. {"Friday","Pátek"}. {"From ~ts","Od ~ts"}. -{"From","Od"}. {"Full Name","Celé jméno"}. {"Get Number of Online Users","Získat počet online uživatelů"}. {"Get Number of Registered Users","Získat počet registrovaných uživatelů"}. {"Get User Last Login Time","Získat čas podleního přihlášení uživatele"}. -{"Get User Password","Získat heslo uživatele"}. {"Get User Statistics","Získat statistiky uživatele"}. {"Given Name","Křestní jméno"}. {"Grant voice to this person?","Udělit voice práva této osobě?"}. -{"Group","Skupina"}. -{"Groups","Skupiny"}. {"has been banned","byl(a) zablokován(a)"}. {"has been kicked because of a system shutdown","byl(a) vyhozen(a), protože dojde k vypnutí systému"}. {"has been kicked because of an affiliation change","byl(a) vyhozen(a) kvůli změně přiřazení"}. {"has been kicked because the room has been changed to members-only","byl(a) vyhozen(a), protože mísnost je nyní pouze pro členy"}. {"has been kicked","byl(a) vyhozen(a) z místnosti"}. {"Host unknown","Neznámý hostitel"}. -{"Host","Hostitel"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokud zde nevidíte obrázek CAPTCHA, přejděte na webovou stránku."}. {"Import Directory","Import adresáře"}. {"Import File","Import souboru"}. @@ -145,7 +131,6 @@ {"Import Users From jabberd14 Spool Files","Importovat uživatele z jabberd14 spool souborů"}. {"Improper domain part of 'from' attribute","Nesprávná část s doménou atributu 'from'"}. {"Improper message type","Nesprávný typ zprávy"}. -{"Incoming s2s Connections:","Příchozí s2s spojení:"}. {"Incorrect CAPTCHA submit","Nesprávné odeslání CAPTCHA"}. {"Incorrect data form","Nesprávný datový formulář"}. {"Incorrect password","Nesprávné heslo"}. @@ -172,8 +157,6 @@ {"Last month","Poslední měsíc"}. {"Last year","Poslední rok"}. {"leaves the room","opustil(a) místnost"}. -{"List of rooms","Seznam místností"}. -{"Low level update script","Nízkoúrovňový aktualizační skript"}. {"Make participants list public","Nastavit seznam účastníků jako veřejný"}. {"Make room CAPTCHA protected","Chránit místnost pomocí CAPTCHA"}. {"Make room members-only","Zpřístupnit místnost jen členům"}. @@ -186,22 +169,18 @@ {"Max payload size in bytes","Maximální náklad v bajtech"}. {"Maximum Number of Occupants","Maximální počet účastníků"}. {"May",". května"}. -{"Members:","Členové:"}. {"Membership is required to enter this room","Pro vstup do místnosti musíte být členem"}. -{"Memory","Paměť"}. {"Message body","Tělo zprávy"}. {"Message not found in forwarded payload","Zpráva nebyla nalezena v přeposlaném obsahu"}. {"Middle Name","Druhé jméno"}. {"Minimum interval between voice requests (in seconds)","Minimální interval mezi žádostmi o voice práva (v sekundách)"}. {"Moderator privileges required","Potřebujete práva moderátora"}. {"Moderator","Moderátor"}. -{"Modified modules","Aktualizované moduly"}. {"Module failed to handle the query","Modul chyboval při zpracování dotazu"}. {"Monday","Pondělí"}. {"Multicast","Multicast"}. {"Multi-User Chat","Víceuživatelský chat"}. {"Name","Jméno"}. -{"Name:","Jméno:"}. {"Neither 'jid' nor 'nick' attribute found","Nebyl nalezen atribut 'jid' ani 'nick'"}. {"Neither 'role' nor 'affiliation' attribute found","Nebyl nalezen atribut 'role' ani 'affiliation'"}. {"Never","Nikdy"}. @@ -250,12 +229,9 @@ {"Number of online users","Počet online uživatelů"}. {"Number of registered users","Počet registrovaných uživatelů"}. {"October",". října"}. -{"Offline Messages","Offline zprávy"}. -{"Offline Messages:","Offline zprávy:"}. {"OK","OK"}. {"Old Password:","Současné heslo:"}. {"Online Users","Připojení uživatelé"}. -{"Online Users:","Připojení uživatelé:"}. {"Online","Online"}. {"Only deliver notifications to available users","Doručovat upozornění jen právě přihlášeným uživatelům"}. {"Only or tags are allowed","Pouze značky nebo jsou povoleny"}. @@ -271,9 +247,7 @@ {"Organization Unit","Oddělení"}. {"Other Modules Available:","Ostatní dostupné moduly:"}. {"Outgoing s2s Connections","Odchozí s2s spojení"}. -{"Outgoing s2s Connections:","Odchozí s2s spojení:"}. {"Owner privileges required","Jsou vyžadována práva vlastníka"}. -{"Packet","Paket"}. {"Participant","Účastník"}. {"Password Verification","Ověření hesla"}. {"Password Verification:","Ověření hesla:"}. @@ -281,7 +255,6 @@ {"Password:","Heslo:"}. {"Path to Dir","Cesta k adresáři"}. {"Path to File","Cesta k souboru"}. -{"Pending","Čekající"}. {"Period: ","Čas: "}. {"Persist items to storage","Uložit položky natrvalo do úložiště"}. {"Ping query is incorrect","Ping dotaz je nesprávný"}. @@ -300,17 +273,12 @@ {"RAM copy","Kopie RAM"}. {"Really delete message of the day?","Skutečně smazat zprávu dne?"}. {"Recipient is not in the conference room","Příjemce se nenachází v místnosti"}. -{"Registered Users","Registrovaní uživatelé"}. -{"Registered Users:","Registrovaní uživatelé:"}. {"Register","Zaregistrovat se"}. {"Remote copy","Vzdálená kopie"}. -{"Remove All Offline Messages","Odstranit všechny offline zprávy"}. {"Remove User","Odstranit uživatele"}. -{"Remove","Odstranit"}. {"Replaced by new connection","Nahrazeno novým spojením"}. {"Resources","Zdroje"}. {"Restart Service","Restartovat službu"}. -{"Restart","Restart"}. {"Restore Backup from File at ","Obnovit zálohu ze souboru na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Obnovit binární zálohu při následujícím restartu ejabberd (vyžaduje méně paměti):"}. {"Restore binary backup immediately:","Okamžitě obnovit binární zálohu:"}. @@ -323,13 +291,9 @@ {"Room Occupants","Počet účastníků"}. {"Room title","Název místnosti"}. {"Roster groups allowed to subscribe","Skupiny kontaktů, které mohou odebírat"}. -{"Roster of ~ts","Seznam kontaktů ~ts"}. {"Roster size","Velikost seznamu kontaktů"}. -{"Roster:","Seznam kontaktů:"}. -{"RPC Call Error","Chyba RPC volání"}. {"Running Nodes","Běžící uzly"}. {"Saturday","Sobota"}. -{"Script check","Kontrola skriptu"}. {"Search Results for ","Výsledky hledání pro "}. {"Search users in ","Hledat uživatele v "}. {"Send announcement to all online users on all hosts","Odeslat oznámení všem online uživatelům na všech hostitelích"}. @@ -347,18 +311,12 @@ {"Specify the access model","Uveďte přístupový model"}. {"Specify the event message type","Zvolte typ zpráv pro události"}. {"Specify the publisher model","Specifikovat model pro publikování"}. -{"Statistics of ~p","Statistiky ~p"}. -{"Statistics","Statistiky"}. {"Stopped Nodes","Zastavené uzly"}. -{"Stop","Stop"}. -{"Storage Type","Typ úložiště"}. {"Store binary backup:","Uložit binární zálohu:"}. {"Store plain text backup:","Uložit zálohu do textového souboru:"}. {"Subject","Předmět"}. -{"Submit","Odeslat"}. {"Submitted","Odeslané"}. {"Subscriber Address","Adresa odběratele"}. -{"Subscription","Přihlášení"}. {"Subscriptions are not allowed","Předplatné není povoleno"}. {"Sunday","Neděle"}. {"That nickname is already in use by another occupant","Přezdívka je již používána jiným členem"}. @@ -381,7 +339,6 @@ {"This room is not anonymous","Tato místnost není anonymní"}. {"Thursday","Čtvrtek"}. {"Time delay","Časový posun"}. -{"Time","Čas"}. {"To register, visit ~s","Pokud se chcete zaregistrovat, navštivte ~s"}. {"Token TTL","Token TTL"}. {"Too many active bytestreams","Příliš mnoho aktivních bytestreamů"}. @@ -391,13 +348,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Příliš mnoho (~p) chybných pokusů o přihlášení z této IP adresy (~s). Adresa bude zablokována do ~s UTC"}. {"Too many unacked stanzas","Příliš mnoho nepotvrzených stanz"}. {"Too many users in this conference","Přiliš mnoho uživatelů v této místnosti"}. -{"To","Pro"}. -{"Total rooms","Celkem místností"}. {"Traffic rate limit is exceeded","Byl překročen limit"}. -{"Transactions Aborted:","Transakcí zrušených:"}. -{"Transactions Committed:","Transakcí potvrzených:"}. -{"Transactions Logged:","Transakcí zaznamenaných:"}. -{"Transactions Restarted:","Transakcí restartovaných:"}. {"Tuesday","Úterý"}. {"Unable to generate a CAPTCHA","Nebylo možné vygenerovat CAPTCHA"}. {"Unable to register route on existing local domain","Není možné zaregistrovat routu na existující místní doménu"}. @@ -407,11 +358,6 @@ {"Unsupported element","Nepodporovaný element"}. {"Update message of the day (don't send)","Aktualizovat zprávu dne (neodesílat)"}. {"Update message of the day on all hosts (don't send)","Aktualizovat zprávu dne pro všechny hostitele (neodesílat)"}. -{"Update ~p","Aktualizovat ~p"}. -{"Update plan","Aktualizovat plán"}. -{"Update script","Aktualizované skripty"}. -{"Update","Aktualizovat"}. -{"Uptime:","Čas běhu:"}. {"User already exists","Uživatel již existuje"}. {"User JID","Jabber ID uživatele"}. {"User (jid)","Uživatel (JID)"}. @@ -423,7 +369,6 @@ {"Users Last Activity","Poslední aktivita uživatele"}. {"Users","Uživatelé"}. {"User","Uživatel"}. -{"Validate","Ověřit"}. {"Value 'get' of 'type' attribute is not allowed","Hodnota 'get' atrubutu 'type' není povolena"}. {"Value of '~s' should be boolean","Hodnota '~s' by měla být boolean"}. {"Value of '~s' should be datetime string","Hodnota '~s' by měla být datetime řetězec"}. diff --git a/priv/msgs/el.msg b/priv/msgs/el.msg index d49ae04fc..8f905164a 100644 --- a/priv/msgs/el.msg +++ b/priv/msgs/el.msg @@ -15,8 +15,6 @@ {"Access model","Καθορίστε το μοντέλο πρόσβασης"}. {"Account doesn't exist","Ο λογαριασμός δεν υπάρχει"}. {"Action on user","Eνέργεια για το χρήστη"}. -{"Add Jabber ID","Προσθήκη Jabber Ταυτότητας"}. -{"Add New","Προσθήκη νέου"}. {"Add User","Προσθήκη Χρήστη"}. {"Administration of ","Διαχείριση του "}. {"Administration","Διαχείριση"}. @@ -90,29 +88,20 @@ {"Conference room does not exist","Η αίθουσα σύνεδριασης δεν υπάρχει"}. {"Configuration of room ~s","Διαμόρφωση δωματίου ~ s"}. {"Configuration","Ρύθμιση παραμέτρων"}. -{"Connected Resources:","Συνδεδεμένοι Πόροι:"}. {"Contact Addresses (normally, room owner or owners)","Διευθύνσεις της Επαφής (κανονικά, ιδιοκτήτης (-ες) αίθουσας)"}. {"Country","Χώρα"}. -{"CPU Time:","Ώρα CPU:"}. {"Current Discussion Topic","Τρέχων θέμα συζήτησης"}. {"Database failure","Αποτυχία βάσης δεδομένων"}. -{"Database Tables at ~p","Πίνακες βάσης δεδομένων στο ~p"}. {"Database Tables Configuration at ","Διαμόρφωση Πίνακων βάσης δεδομένων στο "}. {"Database","Βάση δεδομένων"}. {"December","Δεκέμβριος"}. {"Default users as participants","Προρυθμισμένοι χρήστες ως συμμετέχοντες"}. -{"Delete content","Διαγραφή περιεχομένων"}. {"Delete message of the day on all hosts","Διαγράψτε το μήνυμα της ημέρας σε όλους τους κεντρικούς υπολογιστές"}. {"Delete message of the day","Διαγράψτε το μήνυμα της ημέρας"}. -{"Delete Selected","Διαγραφή επιλεγμένων"}. -{"Delete table","Διαγραφή Πίνακα"}. {"Delete User","Διαγραφή Χρήστη"}. {"Deliver event notifications","Παράδοση ειδοποιήσεων συμβάντων"}. {"Deliver payloads with event notifications","Κοινοποίηση φόρτου εργασιών με τις ειδοποιήσεις συμβάντων"}. -{"Description:","Περιγραφή:"}. {"Disc only copy","Αντίγραφο μόνο σε δίσκο"}. -{"'Displayed groups' not added (they do not exist!): ","'Οι εμφανιζόμενες ομάδες' δεν προστέθηκαν (δεν υπάρχουν!): "}. -{"Displayed:","Απεικονίζεται:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Μην πείτε τον κωδικό πρόσβασής σας σε κανέναν, ούτε στους διαχειριστές του διακομιστή XMPP."}. {"Dump Backup to Text File at ","Αποθήκευση Αντιγράφου Ασφαλείας σε αρχείο κειμένου στο "}. {"Dump to Text File","Αποθήκευση σε αρχείο κειμένου"}. @@ -128,7 +117,6 @@ {"ejabberd vCard module","ejabberd vCard module"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"ejabberd","ejabberd"}. -{"Elements","Στοιχεία"}. {"Email Address","Ηλεκτρονική Διεύθυνση"}. {"Email","Ηλεκτρονικό ταχυδρομείο"}. {"Enable logging","Ενεργοποίηση καταγραφής"}. @@ -142,7 +130,6 @@ {"Enter path to text file","Εισάγετε Τοποθεσία Αρχείου Κειμένου"}. {"Enter the text you see","Πληκτρολογήστε το κείμενο που βλέπετε"}. {"Erlang XMPP Server","Διακομιστής Erlang XMPP"}. -{"Error","Σφάλμα"}. {"Exclude Jabber IDs from CAPTCHA challenge","Εξαίρεσε αυτές τις ταυτότητες Jabber από την CAPTCHA πρόκληση"}. {"Export all tables as SQL queries to a file:","Εξαγωγή όλων των πινάκων ως ερωτημάτων SQL σε ένα αρχείο:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Εξαγωγή δεδομένων όλων των χρηστών του διακομιστή σε PIEFXIS αρχεία (XEP-0227):"}. @@ -161,7 +148,6 @@ {"Fill in the form to search for any matching XMPP User","Συμπληρώστε την φόρμα για αναζήτηση χρηστών XMPP"}. {"Friday","Παρασκευή"}. {"From ~ts","Από ~ts"}. -{"From","Από"}. {"Full List of Room Admins","Πλήρης Κατάλογος Διαχειριστών αιθουσών"}. {"Full List of Room Owners","Πλήρης Κατάλογος Ιδιοκτητών αιθουσών"}. {"Full Name","Ονοματεπώνυμο"}. @@ -169,20 +155,15 @@ {"Get Number of Registered Users","Έκθεση αριθμού εγγεγραμμένων χρηστών"}. {"Get Pending","Λήψη των εκκρεμοτήτων"}. {"Get User Last Login Time","Έκθεση Τελευταίας Ώρας Σύνδεσης Χρήστη"}. -{"Get User Password","Έκθεση Κωδικού Πρόσβασης Χρήστη"}. {"Get User Statistics","Έκθεση Στατιστικών Χρήστη"}. {"Given Name","Όνομα"}. {"Grant voice to this person?","Παραχώρηση φωνής σε αυτό το άτομο;"}. -{"Groups that will be displayed to the members","Ομάδες που θα εμφανίζονται στα μέλη"}. -{"Groups","Ομάδες"}. -{"Group","Ομάδα"}. {"has been banned","έχει αποβληθεί διαπαντώς"}. {"has been kicked because of a system shutdown","αποβλήθηκε λόγω τερματισμού συστήματος"}. {"has been kicked because of an affiliation change","έχει αποβληθεί λόγω αλλαγής υπαγωγής"}. {"has been kicked because the room has been changed to members-only","αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο"}. {"has been kicked","αποβλήθηκε"}. {"Host unknown","Άγνωστος εξυπηρετητής"}. -{"Host","Εξυπηρετητής"}. {"HTTP File Upload","Ανέβασμα αρχείου"}. {"Idle connection","Αδρανής σύνδεση"}. {"If you don't see the CAPTCHA image here, visit the web page.","Εάν δεν βλέπετε την εικόνα CAPTCHA εδώ, επισκεφθείτε την ιστοσελίδα."}. @@ -196,7 +177,6 @@ {"Import Users From jabberd14 Spool Files","Εισαγωγή Χρηστών από αρχεία σειράς jabberd14"}. {"Improper domain part of 'from' attribute","Ανάρμοστο τμήμα τομέα του χαρακτηριστικού 'from'"}. {"Improper message type","Ακατάλληλο είδος μηνύματος"}. -{"Incoming s2s Connections:","Εισερχόμενες συνδέσεις s2s:"}. {"Incorrect CAPTCHA submit","Λάθος υποβολή CAPTCHA"}. {"Incorrect data form","Εσφαλμένη φόρμα δεδομένων"}. {"Incorrect password","Εσφαλμένος κωδικός πρόσβασης"}. @@ -222,7 +202,6 @@ {"July","Ιούλιος"}. {"June","Ιούνιος"}. {"Just created","Μόλις δημιουργήθηκε"}. -{"Label:","Ετικέτα:"}. {"Last Activity","Τελευταία Δραστηριότητα"}. {"Last login","Τελευταία σύνδεση"}. {"Last message","Τελευταίο μήνυμα"}. @@ -230,9 +209,7 @@ {"Last year","Πέρυσι"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Τα ψηφία μικρότερης αξίας του αθροίσματος SHA-256 του κειμένου θα έπρεπε να ισούνται με την δεκαεξαδική ετικέτα"}. {"leaves the room","εγκαταλείπει την αίθουσα"}. -{"List of rooms","Κατάλογος αιθουσών"}. {"Logging","Καταγραφή"}. -{"Low level update script","Προγράμα ενημέρωσης χαμηλού επίπεδου"}. {"Make participants list public","Κάντε δημόσιο τον κατάλογο συμμετεχόντων"}. {"Make room CAPTCHA protected","Κάντε την αίθουσα προστατεύομενη με CAPTCHA"}. {"Make room members-only","Κάντε την αίθουσα μόνο για μέλη"}. @@ -249,11 +226,8 @@ {"Maximum number of items to persist","Μέγιστος αριθμός μόνιμων στοιχείων"}. {"Maximum Number of Occupants","Μέγιστος αριθμός συμμετεχόντων"}. {"May","Μάιος"}. -{"Members not added (inexistent vhost!): ","Τα μέλη δεν προστέθηκαν (ανύπαρκτος vhost!): "}. {"Membership is required to enter this room","Απαιτείται αίτηση συμετοχής για είσοδο σε αυτή την αίθουσα"}. -{"Members:","Μέλη:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Απομνημονεύστε τον κωδικό πρόσβασής σας ή γράψτε τον σε χαρτί που βρίσκεται σε ασφαλές μέρος. Στο XMPP δεν υπάρχει αυτοματοποιημένος τρόπος ανάκτησης του κωδικού πρόσβασής σας εάν τον ξεχάσετε."}. -{"Memory","Μνήμη"}. {"Mere Availability in XMPP (No Show Value)","Διαθεσιμότητα στο XMPP (Χωρίς ένδειξη)"}. {"Message body","Περιεχόμενο μηνύματος"}. {"Message not found in forwarded payload","Δεν βρέθηκε μήνυμα στον προωθημένο φόρτο εργασίας"}. @@ -265,15 +239,12 @@ {"Moderator privileges required","Aπαιτούνται προνόμια επόπτου"}. {"Moderators Only","Επόπτες μόμον"}. {"Moderator","Επόπτης"}. -{"Modified modules","Τροποποιημένα modules"}. {"Module failed to handle the query","Το module απέτυχε να χειριστεί το ερώτημα"}. {"Monday","Δευτέρα"}. {"Multicast","Πολλαπλή διανομή (Multicast)"}. {"Multiple elements are not allowed by RFC6121","Πολλαπλά στοιχεία δεν επιτρέπονται από το RFC6121"}. {"Multi-User Chat","Συνομιλία με πολλούς χρήστες"}. -{"Name in the rosters where this group will be displayed","Όνομα στις λίστες όπου αυτή η ομάδα θα εμφανίζεται"}. {"Name","Όνομα"}. -{"Name:","Όνομα:"}. {"Natural Language for Room Discussions","Μητρική Γλώσσα για τις Συζητήσεις Αιθουσών"}. {"Natural-Language Room Name","Αίθουσα Μητρικής Γλώσσας"}. {"Neither 'jid' nor 'nick' attribute found","Δεν βρέθηκε κανένα χαρακτηριστικό 'jid' ούτε 'nick'"}. @@ -335,14 +306,10 @@ {"Occupants are allowed to invite others","Οι συμμετέχοντες μπορούν να προσκαλέσουν και άλλους"}. {"Occupants May Change the Subject","Επιτρέψτε στους χρήστες να αλλάζουν το Θέμα"}. {"October","Οκτώβριος"}. -{"Offline Messages","Χωρίς Σύνδεση Μηνύματα"}. -{"Offline Messages:","Χωρίς Σύνδεση Μηνύματα:"}. {"OK","Εντάξει"}. {"Old Password:","Παλαιός κωδικός πρόσβασης:"}. -{"Online Users:","Online Χρήστες:"}. {"Online Users","Συνδεμένοι χρήστες"}. {"Online","Συνδεδεμένο"}. -{"Only admins can see this","Μόνον οι διαχειριστές μπορούν να το δουν αυτό"}. {"Only collection node owners may associate leaf nodes with the collection","Μόνον οι ιδιοκτήτες των κόμβων μπορούν να συσχετίσουν leaf nodes με την Συλλογή"}. {"Only deliver notifications to available users","Παράδοση ειδοποιήσεων μόνο σε διαθέσιμους χρήστες"}. {"Only or tags are allowed","Επιτρέπονται μόνο tags ή "}. @@ -360,10 +327,8 @@ {"Organization Name","Όνομα Οργανισμού"}. {"Organization Unit","Μονάδα Οργανισμού"}. {"Outgoing s2s Connections","Εξερχόμενες S2S Συνδέσεις"}. -{"Outgoing s2s Connections:","Εξερχόμενες S2S Συνδέσεις:"}. {"Owner privileges required","Aπαιτούνται προνόμια ιδιοκτήτη"}. {"Packet relay is denied by service policy","Απαγορεύεται η αναμετάδοση πακέτων, λόγω της τακτικής Παροχής Υπηρεσιών"}. -{"Packet","Πακέτο"}. {"Participant","Συμμετέχων"}. {"Password Verification","Επαλήθευση κωδικού πρόσβασης"}. {"Password Verification:","Επαλήθευση κωδικού πρόσβασης:"}. @@ -371,7 +336,6 @@ {"Password:","Κωδικός πρόσβασης:"}. {"Path to Dir","Τοποθεσία κατάλογου αρχείων"}. {"Path to File","Τοποθεσία Αρχείου"}. -{"Pending","Εκκρεμεί"}. {"Period: ","Περίοδος: "}. {"Persist items to storage","Μόνιμη αποθήκευση στοιχείων"}. {"Persistent","Μόνιμη"}. @@ -405,20 +369,15 @@ {"Receive notification of new nodes only","Λάβετε ειδοποίηση μόνο από νέους κόμβους"}. {"Recipient is not in the conference room","Ο παραλήπτης δεν είναι στην αίθουσα συνεδριάσεων"}. {"Register an XMPP account","Καταχωρείστε έναν XMPP λογαριασμό χρήστη"}. -{"Registered Users","Εγγεγραμμένοι Χρήστες"}. -{"Registered Users:","Εγγεγραμμένοι Χρήστες:"}. {"Register","Καταχωρήστε"}. {"Remote copy","Εξ αποστάσεως αντίγραφο"}. -{"Remove All Offline Messages","Αφαίρεση όλων των μηνυμάτων χωρίς σύνδεση"}. {"Remove User","Αφαίρεση χρήστη"}. -{"Remove","Αφαίρεση"}. {"Replaced by new connection","Αντικαταστάθηκε από μια νέα σύνδεση"}. {"Request has timed out","Το αίτημα έληξε"}. {"Request is ignored","Το αίτημα θα αγνοηθεί"}. {"Requested role","Αιτούμενος ρόλος"}. {"Resources","Πόροι"}. {"Restart Service","Επανεκκίνηση Υπηρεσίας"}. -{"Restart","Επανεκκίνηση"}. {"Restore Backup from File at ","Επαναφορά Αντιγράφου Ασφαλείας από αρχείο στο "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Επαναφορά δυαδικού αντιγράφου ασφαλείας μετά την επόμενη επανεκκίνηση του ejabberd (απαιτεί λιγότερη μνήμη):"}. {"Restore binary backup immediately:","Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:"}. @@ -434,20 +393,15 @@ {"Room terminates","Τερματισμός Αίθουσας"}. {"Room title","Τίτλος Αίθουσας"}. {"Roster groups allowed to subscribe","Ομάδες Καταλόγου Επαφών μπορούν να εγγραφούν"}. -{"Roster of ~ts","Καταλόγου Επαφών του ~ts"}. {"Roster size","Μέγεθος Καταλόγου Επαφών"}. -{"Roster:","Καταλόγος Επαφών:"}. -{"RPC Call Error","Σφάλμα RPC Κλήσης"}. {"Running Nodes","Ενεργοί Κόμβοι"}. {"~s invites you to the room ~s","~s Σας καλεί στο δωμάτιο ~s"}. {"Saturday","Σάββατο"}. -{"Script check","Script ελέγχου"}. {"Search from the date","Αναζήτηση από της"}. {"Search Results for ","Αποτελέσματα αναζήτησης για "}. {"Search the text","Αναζήτηση του κειμένου"}. {"Search until the date","Αναζήτηση μέχρι της"}. {"Search users in ","Αναζήτηση χρηστών στο "}. -{"Select All","Επιλογή όλων"}. {"Send announcement to all online users on all hosts","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Send announcement to all online users","Αποστολή ανακοίνωσης σε όλους τους συνδεδεμένους χρήστες"}. {"Send announcement to all users on all hosts","Αποστολή ανακοίνωσης σε όλους τους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. @@ -469,23 +423,17 @@ {"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}. {"Stanza ID","Ταυτότητα Δωματίου"}. {"Statically specify a replyto of the node owner(s)","Προσδιορίστε (στατικά) το Απάντηση Προς του ιδιοκτήτη-ων του κόμβου"}. -{"Statistics of ~p","Στατιστικές του ~p"}. -{"Statistics","Στατιστικές"}. {"Stopped Nodes","Σταματημένοι Κόμβοι"}. -{"Stop","Σταμάτημα"}. -{"Storage Type","Τύπος Αποθήκευσης"}. {"Store binary backup:","Αποθηκεύση δυαδικού αντιγράφου ασφαλείας:"}. {"Store plain text backup:","Αποθηκεύση αντιγράφου ασφαλείας σε αρχείο κειμένου:"}. {"Stream management is already enabled","Η διαχείριση Ροών επιτρέπεται ηδη"}. {"Stream management is not enabled","Η διαχείριση Ροών δεν είναι ενεργοποιημένη"}. {"Subject","Θέμα"}. {"Submitted","Υποβλήθηκε"}. -{"Submit","Υποβολή"}. {"Subscriber Address","Διεύθυνση Συνδρομητή"}. {"Subscribers may publish","Οι συνδρομητές μπορούν να δημοσιεύσουν"}. {"Subscription requests must be approved and only subscribers may retrieve items","Τα αιτήματα για συνδρομή πρέπει να εγκριθούν και μόνο οι συνδρομητές μπορούν να λάβουν αντικείμενα"}. {"Subscriptions are not allowed","Οι συνδρομές δεν επιτρέπονται"}. -{"Subscription","Συνδρομή"}. {"Sunday","Κυριακή"}. {"Text associated with a picture","Το κείμενο σχετίστηκε με μία εικόνα"}. {"Text associated with a sound","Το κείμενο σχετίστηκε με έναν ήχο"}. @@ -544,7 +492,6 @@ {"Thursday","Πέμπτη"}. {"Time delay","Χρόνος καθυστέρησης"}. {"Timed out waiting for stream resumption","Υπερέβην το όριο αναμονής για επανασύνδεση της Ροής"}. -{"Time","Χρόνος"}. {"To register, visit ~s","Για να εγγραφείτε, επισκεφθείτε το ~s"}. {"To ~ts","Προς ~ts"}. {"Token TTL","Διακριτικό TTL"}. @@ -557,13 +504,7 @@ {"Too many receiver fields were specified","Πάρα πολλά πεδία δεκτών προσδιορίστηκαν"}. {"Too many unacked stanzas","Πάρα πολλές μη αναγνωρισμένες stanzas"}. {"Too many users in this conference","Πάρα πολλοί χρήστες σε αυτή τη διάσκεψη"}. -{"Total rooms","Συνολικές Αίθουσες σύνεδριασης"}. -{"To","Προς"}. {"Traffic rate limit is exceeded","Υπέρφορτωση"}. -{"Transactions Aborted:","Αποτυχημένες συναλλαγές:"}. -{"Transactions Committed:","Παραδοθείσες συναλλαγές:"}. -{"Transactions Logged:","Καταγεγραμμένες συναλλαγές:"}. -{"Transactions Restarted:","Επανειλημμένες συναλλαγές:"}. {"~ts's Offline Messages Queue","~ts's Χωρίς Σύνδεση Μηνύματα"}. {"Tuesday","Τρίτη"}. {"Unable to generate a CAPTCHA","Αδύνατη η δημιουργία CAPTCHA"}. @@ -573,16 +514,10 @@ {"Unexpected error condition: ~p","Απροσδόκητες συνθήκες σφάλματος: ~p"}. {"Unregister an XMPP account","Καταργήση λογαριασμού XMPP"}. {"Unregister","Καταργήση εγγραφής"}. -{"Unselect All","Αποεπιλογή όλων"}. {"Unsupported element","Μη υποστηριζόμενο στοιχείο "}. {"Unsupported version","Μη υποστηριζόμενη έκδοση"}. {"Update message of the day (don't send)","Ενημέρωση μηνύματος ημέρας (χωρίς άμεση αποστολή)"}. {"Update message of the day on all hosts (don't send)","Ενημέρωση μηνύματος ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση αποστολή)"}. -{"Update plan","Σχέδιο ενημέρωσης"}. -{"Update ~p","Ενημέρωση ~p"}. -{"Update script","Προγράμα ενημέρωσης"}. -{"Update","Ενημέρωση"}. -{"Uptime:","Χρόνος σε λειτουργία:"}. {"URL for Archived Discussion Logs","URL αρχειοθετημένων καταγραφών συζητήσεων"}. {"User already exists","Ο χρήστης υπάρχει ήδη"}. {"User JID","JID Χρήστη"}. @@ -597,15 +532,12 @@ {"Users Last Activity","Τελευταία Δραστηριότητα Χρήστη"}. {"Users","Χρήστες"}. {"User","Χρήστης"}. -{"Validate","Επαληθεύστε"}. {"Value 'get' of 'type' attribute is not allowed","Η τιμή 'get' του 'type' δεν επιτρέπεται"}. {"Value of '~s' should be boolean","Η τιμή του '~s' πρέπει να είναι boolean"}. {"Value of '~s' should be datetime string","Η τιμή του '~s' θα πρέπει να είναι χρονοσειρά"}. {"Value of '~s' should be integer","Η τιμή του '~s' θα πρέπει να είναι ακέραιος"}. {"Value 'set' of 'type' attribute is not allowed","Δεν επιτρέπεται η παράμετρος 'set' του 'type'"}. {"vCard User Search","vCard Αναζήτηση χρηστών"}. -{"View Queue","Εμφάνιση λίστας αναμονής"}. -{"View Roster","Εμφάνιση λίστας Επαφών"}. {"Virtual Hosts","Eικονικοί κεντρικοί υπολογιστές"}. {"Visitors are not allowed to change their nicknames in this room","Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την αίθουσα"}. {"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχοντες"}. diff --git a/priv/msgs/eo.msg b/priv/msgs/eo.msg index c0a6be1f0..553980422 100644 --- a/priv/msgs/eo.msg +++ b/priv/msgs/eo.msg @@ -15,8 +15,6 @@ {"Access model","Atingomodelo"}. {"Account doesn't exist","Konto ne ekzistas"}. {"Action on user","Ago je uzanto"}. -{"Add Jabber ID","Aldonu Jabber ID"}. -{"Add New","Aldonu novan"}. {"Add User","Aldonu Uzanton"}. {"Administration of ","Mastrumado de "}. {"Administration","Administro"}. @@ -76,22 +74,17 @@ {"Conference room does not exist","Babilejo ne ekzistas"}. {"Configuration of room ~s","Agordo de babilejo ~s"}. {"Configuration","Agordo"}. -{"Connected Resources:","Konektataj risurcoj:"}. {"Country","Lando"}. -{"CPU Time:","CPU-tempo"}. {"Current Discussion Topic","Aktuala Diskuta Temo"}. -{"Database Tables at ~p","Datumbaz-tabeloj je ~p"}. {"Database Tables Configuration at ","Agordo de datumbaz-tabeloj je "}. {"Database","Datumbazo"}. {"December","Decembro"}. {"Default users as participants","Kutime farigu uzantojn kiel partpoprenantoj"}. {"Delete message of the day on all hosts","Forigu mesaĝo de la tago je ĉiu gastigo"}. {"Delete message of the day","Forigu mesaĝo de la tago"}. -{"Delete Selected","Forigu elektata(j)n"}. {"Delete User","Forigu Uzanton"}. {"Deliver event notifications","Liveru event-sciigojn"}. {"Deliver payloads with event notifications","Liveru aĵojn de event-sciigoj"}. -{"Description:","Priskribo:"}. {"Disc only copy","Nur disk-kopio"}. {"Dump Backup to Text File at ","Skribu sekurkopion en plata teksto al "}. {"Dump to Text File","Skribu en plata tekst-dosiero"}. @@ -105,7 +98,6 @@ {"ejabberd vCard module","ejabberd vCard-modulo"}. {"ejabberd Web Admin","ejabberd Teksaĵa Administro"}. {"ejabberd","ejabberd"}. -{"Elements","Eroj"}. {"Email Address","Retpoŝta Adreso"}. {"Email","Retpoŝto"}. {"Enable logging","Ŝaltu protokoladon"}. @@ -117,7 +109,6 @@ {"Enter path to jabberd14 spool file","Enmetu vojon al jabberd14-uzantdosiero"}. {"Enter path to text file","Enmetu vojon al plata teksto"}. {"Enter the text you see","Enmetu montrita teksto"}. -{"Error","Eraro"}. {"Exclude Jabber IDs from CAPTCHA challenge","Esceptu Ĵabber-identigilojn je CAPTCHA-defio"}. {"Export all tables as SQL queries to a file:","Eksportu ĉiuj tabeloj kiel SQL-informmendo al dosierujo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksportu datumojn de ĉiuj uzantoj en servilo al PIEFXIS dosieroj (XEP-0227):"}. @@ -127,23 +118,18 @@ {"February","Februaro"}. {"File larger than ~w bytes","Dosiero pli granda ol ~w bajtoj"}. {"Friday","Vendredo"}. -{"From","De"}. {"Full Name","Plena Nomo"}. {"Get Number of Online Users","Montru nombron de konektataj uzantoj"}. {"Get Number of Registered Users","Montru nombron de registritaj uzantoj"}. {"Get User Last Login Time","Montru tempon de lasta ensaluto"}. -{"Get User Password","Montru pasvorton de uzanto"}. {"Get User Statistics","Montru statistikojn de uzanto"}. {"Given Name","Persona Nomo"}. {"Grant voice to this person?","Koncedu voĉon al ĉi-persono?"}. -{"Group","Grupo"}. -{"Groups","Grupoj"}. {"has been banned","estas forbarita"}. {"has been kicked because of a system shutdown","estas forpelita pro sistem-haltigo"}. {"has been kicked because of an affiliation change","estas forpelita pro aparteneca ŝanĝo"}. {"has been kicked because the room has been changed to members-only","estas forpelita ĉar la babilejo fariĝis sole por membroj"}. {"has been kicked","estas forpelita"}. -{"Host","Gastigo"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se vi ne vidas la CAPTCHA-imagon jene, vizitu la teksaĵ-paĝon."}. {"Import Directory","Importu dosierujo"}. {"Import File","Importu dosieron"}. @@ -166,14 +152,11 @@ {"July","Julio"}. {"June","Junio"}. {"Just created","Ĵus kreita"}. -{"Label:","Etikedo:"}. {"Last Activity","Lasta aktiveco"}. {"Last login","Lasta ensaluto"}. {"Last month","Lasta monato"}. {"Last year","Lasta jaro"}. {"leaves the room","eliras la babilejo"}. -{"List of rooms","Listo de babilejoj"}. -{"Low level update script","Bazanivela ĝisdatigo-skripto"}. {"Make participants list public","Farigu partoprento-liston publika"}. {"Make room CAPTCHA protected","Protektu babilejon per CAPTCHA"}. {"Make room members-only","Farigu babilejon sole por membroj"}. @@ -187,20 +170,16 @@ {"Maximum Number of Occupants","Limigo de nombro de partoprenantoj"}. {"May","Majo"}. {"Membership is required to enter this room","Membreco estas bezonata por eniri ĉi tiun babilejon"}. -{"Members:","Membroj:"}. -{"Memory","Memoro"}. {"Message body","Teksto de mesaĝo"}. {"Middle Name","Meza Nomo"}. {"Minimum interval between voice requests (in seconds)","Minimuma intervalo inter voĉ-petoj (je sekundoj)"}. {"Moderator privileges required","Moderantaj rajtoj bezonata"}. -{"Modified modules","Ĝisdatigitaj moduloj"}. {"Module failed to handle the query","Modulo malsukcesis trakti la informpeton"}. {"Monday","Lundo"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","RFC 6121 ne permesas plurajn -elementojn"}. {"Multi-User Chat","Grupbabilado"}. {"Name","Nomo"}. -{"Name:","Nomo:"}. {"Natural Language for Room Discussions","Homa Lingvo por Diskutoj en Babilejo"}. {"Never","Neniam"}. {"New Password:","Nova Pasvorto:"}. @@ -232,11 +211,8 @@ {"Number of online users","Nombro de konektataj uzantoj"}. {"Number of registered users","Nombro de registritaj uzantoj"}. {"October","Oktobro"}. -{"Offline Messages","Liverontaj mesaĝoj"}. -{"Offline Messages:","Liverontaj mesaĝoj"}. {"OK","Bone"}. {"Old Password:","Malnova Pasvorto:"}. -{"Online Users:","Konektataj uzantoj:"}. {"Online Users","Konektataj Uzantoj"}. {"Online","Konektata"}. {"Only deliver notifications to available users","Nur liveru sciigojn al konektataj uzantoj"}. @@ -253,16 +229,13 @@ {"Organization Name","Organiz-nomo"}. {"Organization Unit","Organiz-parto"}. {"Outgoing s2s Connections","Elirantaj s-al-s-konektoj"}. -{"Outgoing s2s Connections:","Elirantaj s-al-s-konektoj:"}. {"Owner privileges required","Mastraj rajtoj bezonata"}. -{"Packet","Pakaĵo"}. {"Password Verification","Pasvortkontrolo"}. {"Password Verification:","Pasvortkontrolo:"}. {"Password","Pasvorto"}. {"Password:","Pasvorto:"}. {"Path to Dir","Vojo al dosierujo"}. {"Path to File","Voje de dosiero"}. -{"Pending","Atendanta"}. {"Period: ","Periodo: "}. {"Persist items to storage","Savu erojn en konservado"}. {"Ping","Sondaĵo"}. @@ -281,17 +254,12 @@ {"RAM copy","RAM-kopio"}. {"Really delete message of the day?","Ĉu vere forigi mesaĝon de la tago?"}. {"Recipient is not in the conference room","Ricevanto ne ĉeestas en la babilejo"}. -{"Registered Users","Registritaj uzantoj"}. -{"Registered Users:","Registritaj uzantoj:"}. {"Register","Registru"}. {"Remote copy","Fora kopio"}. -{"Remove All Offline Messages","Forigu ĉiujn liverontajn mesaĝojn"}. {"Remove User","Forigu uzanton"}. -{"Remove","Forigu"}. {"Replaced by new connection","Anstataŭigita je nova konekto"}. {"Resources","Risurcoj"}. {"Restart Service","Restartu Servon"}. -{"Restart","Restartu"}. {"Restore Backup from File at ","Restaŭrigu de dosiero el "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaŭrigu duuman sekurkopion post sekvonta ejabberd-restarto"}. {"Restore binary backup immediately:","Restaŭrigu duuman sekurkopion tuj:"}. @@ -304,10 +272,8 @@ {"Room title","Babilejo-nomo"}. {"Roster groups allowed to subscribe","Kontaktlist-grupoj kiuj rajtas aboni"}. {"Roster size","Kontaktlist-grando"}. -{"RPC Call Error","Eraro de RPC-alvoko"}. {"Running Nodes","Funkciantaj Nodoj"}. {"Saturday","Sabato"}. -{"Script check","Skript-kontrolo"}. {"Search Results for ","Serĉ-rezultoj de "}. {"Search users in ","Serĉu uzantojn en "}. {"Send announcement to all online users on all hosts","Sendu anoncon al ĉiu konektata uzanto de ĉiu gastigo"}. @@ -325,19 +291,13 @@ {"Specify the access model","Specifu atingo-modelon"}. {"Specify the event message type","Specifu tipo de event-mesaĝo"}. {"Specify the publisher model","Enmetu publikadan modelon"}. -{"Statistics of ~p","Statistikoj de ~p"}. -{"Statistics","Statistikoj"}. -{"Stop","Haltigu"}. {"Stopped Nodes","Neaktivaj Nodoj"}. -{"Storage Type","Konserv-tipo"}. {"Store binary backup:","Konservu duuman sekurkopion:"}. {"Store plain text backup:","Skribu sekurkopion en plata tekstdosiero"}. {"Subject","Temo"}. -{"Submit","Sendu"}. {"Submitted","Sendita"}. {"Subscriber Address","Abonanta adreso"}. {"Subscribers may publish","Abonantoj rajtas publici"}. -{"Subscription","Abono"}. {"Sunday","Dimanĉo"}. {"That nickname is already in use by another occupant","Tiu kaŝnomo jam estas uzata de alia partoprenanto"}. {"That nickname is registered by another person","Kaŝnomo estas registrita de alia persono"}. @@ -354,28 +314,16 @@ {"This room is not anonymous","Ĉi tiu babilejo ne estas anonima"}. {"Thursday","Ĵaŭdo"}. {"Time delay","Prokrasto"}. -{"Time","Tempo"}. -{"To","Ĝis"}. {"Too many CAPTCHA requests","Tro multaj CAPTCHA-petoj"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Tro da malsukcesaj aŭtentprovoj (~p) de ĉi tiu IP-adreso (~s). La adreso estos malbarata je ~s UTC."}. {"Too many unacked stanzas","Tro da neagnoskitaj stancoj"}. -{"Total rooms","Babilejoj"}. {"Traffic rate limit is exceeded","Trafikrapida limigo superita"}. -{"Transactions Aborted:","Transakcioj nuligitaj"}. -{"Transactions Committed:","Transakcioj enmetitaj"}. -{"Transactions Logged:","Transakcioj protokolitaj"}. -{"Transactions Restarted:","Transakcioj restartitaj"}. {"Tuesday","Mardo"}. {"Unable to generate a CAPTCHA","Ne eblis krei CAPTCHA"}. {"Unauthorized","Nepermesita"}. {"Unregister","Malregistru"}. {"Update message of the day (don't send)","Ŝanĝu mesaĝon de la tago (ne sendu)"}. {"Update message of the day on all hosts (don't send)","Ŝanĝu mesaĝon de la tago je ĉiu gastigo (ne sendu)"}. -{"Update ~p","Ĝisdatigu ~p-n"}. -{"Update plan","Ĝisdatigo-plano"}. -{"Update script","Ĝisdatigo-skripto"}. -{"Update","Ĝisdatigu"}. -{"Uptime:","Daŭro de funkciado"}. {"URL for Archived Discussion Logs","Retpaĝa adreso de Enarkivigitaj Diskutprotokoloj"}. {"User JID","Uzant-JID"}. {"User Management","Uzanto-administrado"}. @@ -384,7 +332,6 @@ {"Users Last Activity","Lasta aktiveco de uzanto"}. {"Users","Uzantoj"}. {"User","Uzanto"}. -{"Validate","Validigu"}. {"vCard User Search","Serĉado de vizitkartoj"}. {"Virtual Hosts","Virtual-gastigoj"}. {"Visitors are not allowed to change their nicknames in this room","Ne estas permesata al vizitantoj ŝanĝi siajn kaŝnomojn en ĉi tiu ĉambro"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index af0f0c283..be06874f6 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","La cuenta no existe"}. {"Action on user","Acción en el usuario"}. {"Add a hat to a user","Añade un sombrero a un usuario"}. -{"Add Jabber ID","Añadir Jabber ID"}. -{"Add New","Añadir nuevo"}. {"Add User","Añadir usuario"}. {"Administration of ","Administración de "}. {"Administration","Administración"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Cualquiera con una suscripción a la presencia de 'ambos' o 'de' puede suscribirse y recibir elementos"}. {"Anyone with Voice","Cualquiera con Voz"}. {"Anyone","Cualquiera"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente tu cuenta no tiene permisos de administración en este servidor. Por favor consulta cómo concederle privilegios de administrador en: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","El atributo 'channel' es necesario para esta petición"}. {"Attribute 'id' is mandatory for MIX messages","El atributo 'id' es necesario para mensajes MIX"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","La sala de conferencias no existe"}. {"Configuration of room ~s","Configuración para la sala ~s"}. {"Configuration","Configuración"}. -{"Connected Resources:","Recursos conectados:"}. {"Contact Addresses (normally, room owner or owners)","Direcciones de contacto (normalmente la del dueño o dueños de la sala)"}. -{"Contrib Modules","Módulos Contrib"}. {"Country","País"}. -{"CPU Time:","Tiempo consumido de CPU:"}. {"Current Discussion Topic","Tema de discusión actual"}. {"Database failure","Error en la base de datos"}. -{"Database Tables at ~p","Tablas de la base de datos en ~p"}. {"Database Tables Configuration at ","Configuración de tablas de la base de datos en "}. {"Database","Base de datos"}. {"December","Diciembre"}. {"Default users as participants","Los usuarios son participantes por defecto"}. -{"Delete content","Borrar contenido"}. {"Delete message of the day on all hosts","Borrar el mensaje del día en todos los dominios"}. {"Delete message of the day","Borrar mensaje del dia"}. -{"Delete Selected","Borrar los seleccionados"}. -{"Delete table","Borrar tabla"}. {"Delete User","Borrar usuario"}. {"Deliver event notifications","Entregar notificaciones de eventos"}. {"Deliver payloads with event notifications","Enviar contenidos junto con las notificaciones de eventos"}. -{"Description:","Descripción:"}. {"Disc only copy","Copia en disco solamente"}. -{"'Displayed groups' not added (they do not exist!): ","'Mostrados' que no han sido añadidos (¡no existen!): "}. -{"Displayed:","Mostrados:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","No le digas tu contraseña a nadie, ni siquiera a los administradores del servidor XMPP."}. {"Dump Backup to Text File at ","Exporta copia de seguridad a fichero de texto en "}. {"Dump to Text File","Exportar a fichero de texto"}. @@ -132,7 +119,6 @@ {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"ejabberd","ejabberd"}. -{"Elements","Elementos"}. {"Email Address","Dirección de correo electrónico"}. {"Email","Correo electrónico"}. {"Enable hats","Activar sombreros"}. @@ -147,7 +133,6 @@ {"Enter path to text file","Introduce ruta al fichero de texto"}. {"Enter the text you see","Teclea el texto que ves"}. {"Erlang XMPP Server","Servidor XMPP en Erlang"}. -{"Error","Error"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluir Jabber IDs de las pruebas de CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas las tablas a un fichero SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos los usuarios del servidor a ficheros PIEFXIS (XEP-0227):"}. @@ -166,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Rellena campos para buscar usuarios XMPP que concuerden"}. {"Friday","Viernes"}. {"From ~ts","De ~ts"}. -{"From","De"}. {"Full List of Room Admins","Lista completa de administradores de la sala"}. {"Full List of Room Owners","Lista completa de dueños de la sala"}. {"Full Name","Nombre completo"}. @@ -176,13 +160,9 @@ {"Get Number of Registered Users","Ver número de usuarios registrados"}. {"Get Pending","Obtener pendientes"}. {"Get User Last Login Time","Ver fecha de la última conexión de usuario"}. -{"Get User Password","Ver contraseña de usuario"}. {"Get User Statistics","Ver estadísticas de usuario"}. {"Given Name","Nombre"}. {"Grant voice to this person?","¿Conceder voz a esta persona?"}. -{"Group","Grupo"}. -{"Groups that will be displayed to the members","Grupos que se mostrarán a los miembros"}. -{"Groups","Grupos"}. {"has been banned","ha sido bloqueado"}. {"has been kicked because of a system shutdown","ha sido expulsado porque el sistema se va a detener"}. {"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}. @@ -192,7 +172,6 @@ {"Hat URI","Dirección del sombrero"}. {"Hats limit exceeded","Se ha excedido el límite de sombreros"}. {"Host unknown","Dominio desconocido"}. -{"Host","Dominio"}. {"HTTP File Upload","Subir fichero por HTTP"}. {"Idle connection","Conexión sin uso"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si no ves la imagen CAPTCHA aquí, visita la página web."}. @@ -206,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheros spool de jabberd-1.4"}. {"Improper domain part of 'from' attribute","Parte de dominio impropia en el atributo 'from'"}. {"Improper message type","Tipo de mensaje incorrecto"}. -{"Incoming s2s Connections:","Conexiones S2S entrantes:"}. {"Incorrect CAPTCHA submit","El CAPTCHA proporcionado es incorrecto"}. {"Incorrect data form","Formulario de datos incorrecto"}. {"Incorrect password","Contraseña incorrecta"}. @@ -236,7 +214,6 @@ {"July","Julio"}. {"June","Junio"}. {"Just created","Recién creada"}. -{"Label:","Etiqueta:"}. {"Last Activity","Última actividad"}. {"Last login","Última conexión"}. {"Last message","Último mensaje"}. @@ -244,11 +221,9 @@ {"Last year","Último año"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Los bits menos significativos del hash SHA-256 del texto deberían ser iguales a la etiqueta hexadecimal"}. {"leaves the room","sale de la sala"}. -{"List of rooms","Lista de salas"}. {"List of users with hats","Lista de usuarios con sombreros"}. {"List users with hats","Listar usuarios con sombreros"}. {"Logging","Histórico de mensajes"}. -{"Low level update script","Script de actualización a bajo nivel"}. {"Make participants list public","La lista de participantes es pública"}. {"Make room CAPTCHA protected","Proteger la sala con CAPTCHA"}. {"Make room members-only","Sala sólo para miembros"}. @@ -266,11 +241,8 @@ {"Maximum number of items to persist","Máximo número de elementos que persisten"}. {"Maximum Number of Occupants","Número máximo de ocupantes"}. {"May","Mayo"}. -{"Members not added (inexistent vhost!): ","Miembros no añadidos (el vhost no existe): "}. {"Membership is required to enter this room","Necesitas ser miembro de esta sala para poder entrar"}. -{"Members:","Miembros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memoriza tu contraseña, o apúntala en un papel en un lugar seguro. En XMPP no hay un método automatizado para recuperar la contraseña si la olvidas."}. -{"Memory","Memoria"}. {"Mere Availability in XMPP (No Show Value)","Disponible en XMPP (sin valor de Mostrado)"}. {"Message body","Cuerpo del mensaje"}. {"Message not found in forwarded payload","Mensaje no encontrado en el contenido reenviado"}. @@ -282,15 +254,12 @@ {"Moderator privileges required","Se necesita privilegios de moderador"}. {"Moderator","Moderador"}. {"Moderators Only","Solo moderadores"}. -{"Modified modules","Módulos modificados"}. {"Module failed to handle the query","El módulo falló al gestionar la petición"}. {"Monday","Lunes"}. {"Multicast","Multidifusión"}. {"Multiple elements are not allowed by RFC6121","No se permiten múltiples elementos en RFC6121"}. {"Multi-User Chat","Salas de Charla"}. -{"Name in the rosters where this group will be displayed","Nombre del grupo con que aparecerá en las listas de contactos"}. {"Name","Nombre"}. -{"Name:","Nombre:"}. {"Natural Language for Room Discussions","Idioma natural en las charlas de la sala"}. {"Natural-Language Room Name","Nombre de la sala en el idioma natural de la sala"}. {"Neither 'jid' nor 'nick' attribute found","No se encontraron los atributos 'jid' ni 'nick'"}. @@ -355,14 +324,10 @@ {"Occupants are allowed to query others","Los ocupantes pueden enviar peticiones a otros"}. {"Occupants May Change the Subject","Los ocupantes pueden cambiar el Asunto"}. {"October","Octubre"}. -{"Offline Messages","Mensajes diferidos"}. -{"Offline Messages:","Mensajes diferidos:"}. {"OK","Aceptar"}. {"Old Password:","Contraseña antigua:"}. {"Online Users","Usuarios conectados"}. -{"Online Users:","Usuarios conectados:"}. {"Online","Conectado"}. -{"Only admins can see this","Solo los administradores pueden ver esto"}. {"Only collection node owners may associate leaf nodes with the collection","Solo los dueños e la colección de nodos pueden asociar nodos hoja a la colección"}. {"Only deliver notifications to available users","Solo enviar notificaciones a los usuarios disponibles"}. {"Only or tags are allowed","Solo se permiten las etiquetas o "}. @@ -382,10 +347,8 @@ {"Organization Unit","Unidad de la organización"}. {"Other Modules Available:","Otros módulos disponibles:"}. {"Outgoing s2s Connections","Conexiones S2S salientes"}. -{"Outgoing s2s Connections:","Conexiones S2S salientes:"}. {"Owner privileges required","Se requieren privilegios de propietario de la sala"}. {"Packet relay is denied by service policy","Se ha denegado el reenvío del paquete por política del servicio"}. -{"Packet","Paquete"}. {"Participant ID","ID del Participante"}. {"Participant","Participante"}. {"Password Verification","Verificación de la contraseña"}. @@ -395,7 +358,6 @@ {"Path to Dir","Ruta al directorio"}. {"Path to File","Ruta al fichero"}. {"Payload semantic type information","Información sobre el tipo semántico de la carga útil"}. -{"Pending","Pendiente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Persistir elementos al almacenar"}. {"Persistent","Permanente"}. @@ -429,21 +391,16 @@ {"Receive notification of new nodes only","Recibir notificaciones solo de nuevos nodos"}. {"Recipient is not in the conference room","El receptor no está en la sala de conferencia"}. {"Register an XMPP account","Registrar una cuenta XMPP"}. -{"Registered Users","Usuarios registrados"}. -{"Registered Users:","Usuarios registrados:"}. {"Register","Registrar"}. {"Remote copy","Copia remota"}. {"Remove a hat from a user","Quitarle un sombrero a un usuario"}. -{"Remove All Offline Messages","Borrar todos los mensajes diferidos"}. {"Remove User","Eliminar usuario"}. -{"Remove","Borrar"}. {"Replaced by new connection","Reemplazado por una nueva conexión"}. {"Request has timed out","La petición ha caducado"}. {"Request is ignored","La petición ha sido ignorada"}. {"Requested role","Rol solicitado"}. {"Resources","Recursos"}. {"Restart Service","Reiniciar el servicio"}. -{"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaura copia de seguridad desde el fichero en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridad binaria en el siguiente reinicio de ejabberd (requiere menos memoria que si instantánea):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridad binaria:"}. @@ -459,20 +416,15 @@ {"Room terminates","Cerrando la sala"}. {"Room title","Título de la sala"}. {"Roster groups allowed to subscribe","Grupos de contactos que pueden suscribirse"}. -{"Roster of ~ts","Lista de contactos de ~ts"}. {"Roster size","Tamaño de la lista de contactos"}. -{"Roster:","Lista de contactos:"}. -{"RPC Call Error","Error en la llamada RPC"}. {"Running Nodes","Nodos funcionando"}. {"~s invites you to the room ~s","~s te invita a la sala ~s"}. {"Saturday","Sábado"}. -{"Script check","Comprobación de script"}. {"Search from the date","Buscar desde la fecha"}. {"Search Results for ","Buscar resultados por "}. {"Search the text","Buscar el texto"}. {"Search until the date","Buscar hasta la fecha"}. {"Search users in ","Buscar usuarios en "}. -{"Select All","Seleccionar todo"}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos los usuarios conectados en todos los dominios"}. {"Send announcement to all online users","Enviar anuncio a todos los usuarios conectados"}. {"Send announcement to all users on all hosts","Enviar anuncio a todos los usuarios en todos los dominios"}. @@ -496,23 +448,17 @@ {"Stanza id is not valid","El identificador de la estrofa no es válido"}. {"Stanza ID","ID del paquete"}. {"Statically specify a replyto of the node owner(s)","Especificar de forma estática un 'replyto' de dueño(s) del nodo"}. -{"Statistics of ~p","Estadísticas de ~p"}. -{"Statistics","Estadísticas"}. -{"Stop","Detener"}. {"Stopped Nodes","Nodos detenidos"}. -{"Storage Type","Tipo de almacenamiento"}. {"Store binary backup:","Guardar copia de seguridad binaria:"}. {"Store plain text backup:","Guardar copia de seguridad en texto plano:"}. {"Stream management is already enabled","Ya está activada la administración de la conexión"}. {"Stream management is not enabled","No está activada la administración de la conexión"}. {"Subject","Asunto"}. -{"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscriber Address","Dirección del subscriptor"}. {"Subscribers may publish","Los suscriptores pueden publicar"}. {"Subscription requests must be approved and only subscribers may retrieve items","Las peticiones de suscripción deben ser aprobadas y solo los suscriptores pueden obtener elementos"}. {"Subscriptions are not allowed","Las subscripciones no están permitidas"}. -{"Subscription","Subscripción"}. {"Sunday","Domingo"}. {"Text associated with a picture","Texto asociado con una imagen"}. {"Text associated with a sound","Texto asociado con un sonido"}. @@ -575,7 +521,6 @@ {"Thursday","Jueves"}. {"Time delay","Retraso temporal"}. {"Timed out waiting for stream resumption","Ha pasado demasiado tiempo esperando que la conexión se restablezca"}. -{"Time","Fecha"}. {"To register, visit ~s","Para registrarte, visita ~s"}. {"To ~ts","A ~ts"}. {"Token TTL","Token TTL"}. @@ -588,13 +533,7 @@ {"Too many receiver fields were specified","Se han especificado demasiados campos de destinatario"}. {"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}. {"Too many users in this conference","Demasiados usuarios en esta sala"}. -{"To","Para"}. -{"Total rooms","Salas totales"}. {"Traffic rate limit is exceeded","Se ha excedido el límite de tráfico"}. -{"Transactions Aborted:","Transacciones abortadas:"}. -{"Transactions Committed:","Transacciones finalizadas:"}. -{"Transactions Logged:","Transacciones registradas:"}. -{"Transactions Restarted:","Transacciones reiniciadas:"}. {"~ts's Offline Messages Queue","Cola de mensajes diferidos de ~ts"}. {"Tuesday","Martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. @@ -605,19 +544,13 @@ {"Uninstall","Desinstalar"}. {"Unregister an XMPP account","Borrar una cuenta XMPP"}. {"Unregister","Borrar"}. -{"Unselect All","Deseleccionar todo"}. {"Unsupported element","Elemento no soportado"}. {"Unsupported version","Versión no soportada"}. {"Update message of the day (don't send)","Actualizar mensaje del dia, pero no enviarlo"}. {"Update message of the day on all hosts (don't send)","Actualizar el mensaje del día en todos los dominos (pero no enviarlo)"}. -{"Update ~p","Actualizar ~p"}. -{"Update plan","Plan de actualización"}. -{"Update script","Script de actualización"}. {"Update specs to get modules source, then install desired ones.","Actualizar Especificaciones para conseguir el código fuente de los módulos, luego instala los que quieras."}. {"Update Specs","Actualizar Especificaciones"}. -{"Update","Actualizar"}. {"Upgrade","Actualizar"}. -{"Uptime:","Tiempo desde el inicio:"}. {"URL for Archived Discussion Logs","URL del registro de discusiones archivadas"}. {"User already exists","El usuario ya existe"}. {"User JID","Jabber ID del usuario"}. @@ -632,7 +565,6 @@ {"Users Last Activity","Última actividad de los usuarios"}. {"Users","Usuarios"}. {"User","Usuario"}. -{"Validate","Validar"}. {"Value 'get' of 'type' attribute is not allowed","El valor 'get' del atributo 'type' no está permitido"}. {"Value of '~s' should be boolean","El valor de '~s' debería ser booleano"}. {"Value of '~s' should be datetime string","El valor de '~s' debería ser una fecha"}. @@ -640,8 +572,6 @@ {"Value 'set' of 'type' attribute is not allowed","El valor 'set' del atributo 'type' no está permitido"}. {"vCard User Search","Búsqueda de vCard de usuarios"}. {"View joined MIX channels","Ver los canales MIX unidos"}. -{"View Queue","Ver Cola"}. -{"View Roster","Ver Lista de contactos"}. {"Virtual Hosts","Dominios Virtuales"}. {"Visitors are not allowed to change their nicknames in this room","Los visitantes no tienen permitido cambiar sus apodos en esta sala"}. {"Visitors are not allowed to send messages to all occupants","Los visitantes no pueden enviar mensajes a todos los ocupantes"}. diff --git a/priv/msgs/fr.msg b/priv/msgs/fr.msg index 1ebac0f14..7d092971b 100644 --- a/priv/msgs/fr.msg +++ b/priv/msgs/fr.msg @@ -15,8 +15,6 @@ {"Access model","Modèle d’accès"}. {"Account doesn't exist","Le compte n’existe pas"}. {"Action on user","Action sur l'utilisateur"}. -{"Add Jabber ID","Ajouter un Jabber ID"}. -{"Add New","Ajouter"}. {"Add User","Ajouter un utilisateur"}. {"Administration of ","Administration de "}. {"Administration","Administration"}. @@ -90,29 +88,20 @@ {"Conference room does not exist","Le salon de discussion n'existe pas"}. {"Configuration of room ~s","Configuration pour le salon ~s"}. {"Configuration","Configuration"}. -{"Connected Resources:","Ressources connectées :"}. {"Contact Addresses (normally, room owner or owners)","Adresses de contact (normalement les administrateurs du salon)"}. {"Country","Pays"}. -{"CPU Time:","Temps CPU :"}. {"Current Discussion Topic","Sujet de discussion courant"}. {"Database failure","Échec sur la base de données"}. -{"Database Tables at ~p","Tables de base de données sur ~p"}. {"Database Tables Configuration at ","Configuration des tables de base de données sur "}. {"Database","Base de données"}. {"December","Décembre"}. {"Default users as participants","Les utilisateurs sont participant par défaut"}. -{"Delete content","Supprimer le contenu"}. {"Delete message of the day on all hosts","Supprimer le message du jour sur tous les domaines"}. {"Delete message of the day","Supprimer le message du jour"}. -{"Delete Selected","Suppression des éléments sélectionnés"}. -{"Delete table","Supprimer la table"}. {"Delete User","Supprimer l'utilisateur"}. {"Deliver event notifications","Envoyer les notifications d'événement"}. {"Deliver payloads with event notifications","Inclure le contenu du message avec la notification"}. -{"Description:","Description :"}. {"Disc only copy","Copie sur disque uniquement"}. -{"'Displayed groups' not added (they do not exist!): ","« Groupes affichés » non ajoutés (ils n’existent pas !) : "}. -{"Displayed:","Affichés :"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Ne révélez votre mot de passe à personne, pas même aux administrateurs du serveur XMPP."}. {"Dump Backup to Text File at ","Enregistrer la sauvegarde dans un fichier texte sur "}. {"Dump to Text File","Sauvegarder dans un fichier texte"}. @@ -127,7 +116,6 @@ {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Console Web d'administration de ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Éléments"}. {"Email Address","Adresse courriel"}. {"Email","Courriel"}. {"Enable logging","Activer l'archivage"}. @@ -141,7 +129,6 @@ {"Enter path to text file","Entrez le chemin vers le fichier texte"}. {"Enter the text you see","Tapez le texte que vous voyez"}. {"Erlang XMPP Server","Serveur XMPP Erlang"}. -{"Error","Erreur"}. {"Exclude Jabber IDs from CAPTCHA challenge","Exempter des Jabberd IDs du test CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exporter toutes les tables vers un fichier SQL :"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporter les données de tous les utilisateurs du serveur vers un fichier PIEFXIS (XEP-0227) :"}. @@ -160,7 +147,6 @@ {"Fill in the form to search for any matching XMPP User","Complétez le formulaire pour rechercher un utilisateur XMPP correspondant"}. {"Friday","Vendredi"}. {"From ~ts","De ~ts"}. -{"From","De"}. {"Full List of Room Admins","Liste complète des administrateurs des salons"}. {"Full List of Room Owners","Liste complète des propriétaires des salons"}. {"Full Name","Nom complet"}. @@ -169,13 +155,9 @@ {"Get Number of Online Users","Récupérer le nombre d'utilisateurs en ligne"}. {"Get Number of Registered Users","Récupérer le nombre d'utilisateurs enregistrés"}. {"Get User Last Login Time","Récupérer la dernière date de connexion de l'utilisateur"}. -{"Get User Password","Récupérer le mot de passe de l'utilisateur"}. {"Get User Statistics","Récupérer les statistiques de l'utilisateur"}. {"Given Name","Nom"}. {"Grant voice to this person?","Accorder le droit de parole à cet utilisateur ?"}. -{"Group","Groupe"}. -{"Groups that will be displayed to the members","Groupes qui seront affichés aux membres"}. -{"Groups","Groupes"}. {"has been banned","a été banni"}. {"has been kicked because of a system shutdown","a été éjecté en raison de l'arrêt du système"}. {"has been kicked because of an affiliation change","a été éjecté à cause d'un changement d'autorisation"}. @@ -183,7 +165,6 @@ {"has been kicked","a été expulsé"}. {"Hats limit exceeded","La limite a été dépassée"}. {"Host unknown","Serveur inconnu"}. -{"Host","Serveur"}. {"HTTP File Upload","Téléversement de fichier HTTP"}. {"Idle connection","Connexion inactive"}. {"If you don't see the CAPTCHA image here, visit the web page.","SI vous ne voyez pas l'image CAPTCHA ici, visitez la page web."}. @@ -197,7 +178,6 @@ {"Import Users From jabberd14 Spool Files","Importer des utilisateurs depuis un fichier spool Jabberd 1.4"}. {"Improper domain part of 'from' attribute","Le domaine de l'attribut 'from' est incorrect"}. {"Improper message type","Mauvais type de message"}. -{"Incoming s2s Connections:","Connexions s2s entrantes :"}. {"Incorrect CAPTCHA submit","Entrée CAPTCHA incorrecte"}. {"Incorrect data form","Formulaire incorrect"}. {"Incorrect password","Mot de passe incorrect"}. @@ -221,15 +201,12 @@ {"July","Juillet"}. {"June","Juin"}. {"Just created","Vient d'être créé"}. -{"Label:","Étiquette :"}. {"Last Activity","Dernière activité"}. {"Last login","Dernière connexion"}. {"Last message","Dernier message"}. {"Last month","Dernier mois"}. {"Last year","Dernière année"}. {"leaves the room","quitte le salon"}. -{"List of rooms","Liste des salons"}. -{"Low level update script","Script de mise à jour de bas-niveau"}. {"Make participants list public","Rendre la liste des participants publique"}. {"Make room CAPTCHA protected","Protéger le salon par un CAPTCHA"}. {"Make room members-only","Réserver le salon aux membres uniquement"}. @@ -246,9 +223,7 @@ {"Maximum Number of Occupants","Nombre maximal d'occupants"}. {"May","Mai"}. {"Membership is required to enter this room","Vous devez être membre pour accèder à ce salon"}. -{"Members:","Membres :"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Mémorisez votre mot de passe, ou écrivez-le sur un papier conservé dans un endroit secret. Dans XMPP il n'y a pas de mécanisme pour retrouver votre mot de passe si vous l'avez oublié."}. -{"Memory","Mémoire"}. {"Message body","Corps du message"}. {"Message not found in forwarded payload","Message non trouvé dans l'enveloppe transférée"}. {"Messages from strangers are rejected","Les messages d'étrangers sont rejetés"}. @@ -259,14 +234,12 @@ {"Moderator privileges required","Les droits de modérateur sont nécessaires"}. {"Moderator","Modérateur"}. {"Moderators Only","Modérateurs uniquement"}. -{"Modified modules","Modules mis à jour"}. {"Module failed to handle the query","Échec de traitement de la demande"}. {"Monday","Lundi"}. {"Multicast","Multidiffusion"}. {"Multiple elements are not allowed by RFC6121","Les multiples éléments ne sont pas autorisés avec RFC6121"}. {"Multi-User Chat","Discussion de groupe"}. {"Name","Nom"}. -{"Name:","Nom :"}. {"Natural Language for Room Discussions","Langue naturelle pour les discussions en salle"}. {"Natural-Language Room Name","Nom de la salle en langue naturelle"}. {"Neither 'jid' nor 'nick' attribute found","Attribut 'jid' ou 'nick' absent"}. @@ -329,14 +302,10 @@ {"Occupants are allowed to invite others","Les occupants sont autorisés à inviter d’autres personnes"}. {"Occupants May Change the Subject","Les occupants peuvent changer le sujet"}. {"October","Octobre"}. -{"Offline Messages","Messages en attente"}. -{"Offline Messages:","Messages hors ligne :"}. {"OK","OK"}. {"Old Password:","Ancien mot de passe :"}. -{"Online Users:","Utilisateurs connectés :"}. {"Online Users","Utilisateurs en ligne"}. {"Online","En ligne"}. -{"Only admins can see this","Seuls les administrateurs peuvent voir cela"}. {"Only deliver notifications to available users","Envoyer les notifications uniquement aux utilisateurs disponibles"}. {"Only or tags are allowed","Seul le tag ou est autorisé"}. {"Only element is allowed in this query","Seul l'élément est autorisé dans cette requête"}. @@ -351,9 +320,7 @@ {"Organization Name","Nom de l'organisation"}. {"Organization Unit","Unité de l'organisation"}. {"Outgoing s2s Connections","Connexions s2s sortantes"}. -{"Outgoing s2s Connections:","Connexions s2s sortantes :"}. {"Owner privileges required","Les droits de propriétaire sont nécessaires"}. -{"Packet","Paquet"}. {"Participant","Participant"}. {"Password Verification","Vérification du mot de passe"}. {"Password Verification:","Vérification du mot de passe :"}. @@ -361,7 +328,6 @@ {"Password:","Mot de passe :"}. {"Path to Dir","Chemin vers le répertoire"}. {"Path to File","Chemin vers le fichier"}. -{"Pending","En suspens"}. {"Period: ","Période : "}. {"Persist items to storage","Stockage persistant des éléments"}. {"Persistent","Persistant"}. @@ -393,20 +359,15 @@ {"Receive notification of new nodes only","Recevoir les notifications de tous les nouveaux nœuds descendants"}. {"Recipient is not in the conference room","Le destinataire n'est pas dans la conférence"}. {"Register an XMPP account","Inscrire un compte XMPP"}. -{"Registered Users","Utilisateurs enregistrés"}. -{"Registered Users:","Utilisateurs enregistrés :"}. {"Register","Enregistrer"}. {"Remote copy","Copie distante"}. -{"Remove All Offline Messages","Effacer tous les messages hors ligne"}. {"Remove User","Supprimer l'utilisateur"}. -{"Remove","Supprimer"}. {"Replaced by new connection","Remplacé par une nouvelle connexion"}. {"Request has timed out","La demande a expiré"}. {"Request is ignored","La demande est ignorée"}. {"Requested role","Rôle demandé"}. {"Resources","Ressources"}. {"Restart Service","Redémarrer le service"}. -{"Restart","Redémarrer"}. {"Restore Backup from File at ","Restaurer la sauvegarde depuis le fichier sur "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restauration de la sauvegarde binaire après redémarrage (nécessite moins de mémoire) :"}. {"Restore binary backup immediately:","Restauration immédiate d'une sauvegarde binaire :"}. @@ -418,19 +379,14 @@ {"Room Occupants","Occupants du salon"}. {"Room title","Titre du salon"}. {"Roster groups allowed to subscribe","Groupes de liste de contact autorisés à s'abonner"}. -{"Roster of ~ts","Liste de contacts de ~ts"}. {"Roster size","Taille de la liste de contacts"}. -{"Roster:","Liste de contacts :"}. -{"RPC Call Error","Erreur d'appel RPC"}. {"Running Nodes","Nœuds actifs"}. {"~s invites you to the room ~s","~s vous invite dans la salle de discussion ~s"}. {"Saturday","Samedi"}. -{"Script check","Validation du script"}. {"Search Results for ","Résultats de recherche pour "}. {"Search the text","Recherche le texte"}. {"Search until the date","Rechercher jusqu’à la date"}. {"Search users in ","Rechercher des utilisateurs "}. -{"Select All","Tout sélectionner"}. {"Send announcement to all online users on all hosts","Envoyer l'annonce à tous les utilisateurs en ligne sur tous les serveurs"}. {"Send announcement to all online users","Envoyer l'annonce à tous les utilisateurs en ligne"}. {"Send announcement to all users on all hosts","Envoyer une annonce à tous les utilisateurs de tous les domaines"}. @@ -450,20 +406,14 @@ {"Specify the event message type","Définir le type de message d'événement"}. {"Specify the publisher model","Définir le modèle de publication"}. {"Stanza ID","Identifiant Stanza"}. -{"Statistics of ~p","Statistiques de ~p"}. -{"Statistics","Statistiques"}. -{"Stop","Arrêter"}. {"Stopped Nodes","Nœuds arrêtés"}. -{"Storage Type","Type de stockage"}. {"Store binary backup:","Sauvegarde binaire :"}. {"Store plain text backup:","Sauvegarde texte :"}. {"Stream management is already enabled","La gestion des flux est déjà activée"}. {"Subject","Sujet"}. -{"Submit","Soumettre"}. {"Submitted","Soumis"}. {"Subscriber Address","Adresse de l'abonné"}. {"Subscribers may publish","Les souscripteurs peuvent publier"}. -{"Subscription","Abonnement"}. {"Subscriptions are not allowed","Les abonnement ne sont pas autorisés"}. {"Sunday","Dimanche"}. {"Text associated with a picture","Texte associé à une image"}. @@ -512,10 +462,8 @@ {"Thursday","Jeudi"}. {"Time delay","Délais"}. {"Timed out waiting for stream resumption","Expiration du délai d’attente pour la reprise du flux"}. -{"Time","Heure"}. {"To register, visit ~s","Pour vous enregistrer, visitez ~s"}. {"To ~ts","À ~ts"}. -{"To","A"}. {"Token TTL","Jeton TTL"}. {"Too many active bytestreams","Trop de flux SOCKS5 actifs"}. {"Too many CAPTCHA requests","Trop de requêtes CAPTCHA"}. @@ -525,12 +473,7 @@ {"Too many receiver fields were specified","Trop de champs de récepteurs ont été spécifiés"}. {"Too many unacked stanzas","Trop de stanzas sans accusé de réception (ack)"}. {"Too many users in this conference","Trop d'utilisateurs dans cette conférence"}. -{"Total rooms","Nombre de salons"}. {"Traffic rate limit is exceeded","La limite de trafic a été dépassée"}. -{"Transactions Aborted:","Transactions annulées :"}. -{"Transactions Committed:","Transactions commitées :"}. -{"Transactions Logged:","Transactions journalisées :"}. -{"Transactions Restarted:","Transactions redémarrées :"}. {"Tuesday","Mardi"}. {"Unable to generate a CAPTCHA","Impossible de générer le CAPTCHA"}. {"Unable to register route on existing local domain","Impossible d'enregistrer la route sur un domaine locale existant"}. @@ -539,16 +482,10 @@ {"Unexpected error condition: ~p","Condition d’erreur inattendue : ~p"}. {"Unregister an XMPP account","Annuler l’enregistrement d’un compte XMPP"}. {"Unregister","Désinscrire"}. -{"Unselect All","Tout désélectionner"}. {"Unsupported element","Elément non supporté"}. {"Unsupported version","Version non prise en charge"}. {"Update message of the day (don't send)","Mise à jour du message du jour (pas d'envoi)"}. {"Update message of the day on all hosts (don't send)","Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)"}. -{"Update plan","Plan de mise à jour"}. -{"Update ~p","Mise à jour de ~p"}. -{"Update script","Script de mise à jour"}. -{"Update","Mettre à jour"}. -{"Uptime:","Temps depuis le démarrage :"}. {"URL for Archived Discussion Logs","URL des journaux de discussion archivés"}. {"User already exists","L'utilisateur existe déjà"}. {"User JID","JID de l'utilisateur"}. @@ -563,14 +500,12 @@ {"Users Last Activity","Dernière activité des utilisateurs"}. {"Users","Utilisateurs"}. {"User","Utilisateur"}. -{"Validate","Valider"}. {"Value 'get' of 'type' attribute is not allowed","La valeur de l'attribut 'type' ne peut être 'get'"}. {"Value of '~s' should be boolean","La valeur de '~s' ne peut être booléen"}. {"Value of '~s' should be datetime string","La valeur de '~s' doit être une chaine datetime"}. {"Value of '~s' should be integer","La valeur de '~s' doit être un entier"}. {"Value 'set' of 'type' attribute is not allowed","La valeur de l'attribut 'type' ne peut être 'set'"}. {"vCard User Search","Recherche dans l'annnuaire"}. -{"View Queue","Afficher la file d’attente"}. {"Virtual Hosts","Serveurs virtuels"}. {"Visitors are not allowed to change their nicknames in this room","Les visiteurs ne sont pas autorisés à changer de pseudo dans ce salon"}. {"Visitors are not allowed to send messages to all occupants","Les visiteurs ne sont pas autorisés à envoyer des messages à tout les occupants"}. diff --git a/priv/msgs/gl.msg b/priv/msgs/gl.msg index 099936c33..a72e07360 100644 --- a/priv/msgs/gl.msg +++ b/priv/msgs/gl.msg @@ -9,8 +9,6 @@ {"Accept","Aceptar"}. {"Access denied by service policy","Acceso denegado pola política do servizo"}. {"Action on user","Acción no usuario"}. -{"Add Jabber ID","Engadir ID Jabber"}. -{"Add New","Engadir novo"}. {"Add User","Engadir usuario"}. {"Administration of ","Administración de "}. {"Administration","Administración"}. @@ -60,22 +58,17 @@ {"Conference room does not exist","A sala de conferencias non existe"}. {"Configuration of room ~s","Configuración para a sala ~s"}. {"Configuration","Configuración"}. -{"Connected Resources:","Recursos conectados:"}. {"Country","País"}. -{"CPU Time:","Tempo da CPU:"}. {"Database failure","Erro na base de datos"}. -{"Database Tables at ~p","Táboas da base de datos en ~p"}. {"Database Tables Configuration at ","Configuración de táboas da base de datos en "}. {"Database","Base de datos"}. {"December","Decembro"}. {"Default users as participants","Os usuarios son participantes por defecto"}. {"Delete message of the day on all hosts","Borrar a mensaxe do día en todos os dominios"}. {"Delete message of the day","Borrar mensaxe do dia"}. -{"Delete Selected","Eliminar os seleccionados"}. {"Delete User","Borrar usuario"}. {"Deliver event notifications","Entregar notificacións de eventos"}. {"Deliver payloads with event notifications","Enviar payloads xunto coas notificacións de eventos"}. -{"Description:","Descrición:"}. {"Disc only copy","Copia en disco soamente"}. {"Dump Backup to Text File at ","Exporta copia de seguridade a ficheiro de texto en "}. {"Dump to Text File","Exportar a ficheiro de texto"}. @@ -87,7 +80,6 @@ {"ejabberd SOCKS5 Bytestreams module","Módulo SOCKS5 Bytestreams para ejabberd"}. {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Administrador Web"}. -{"Elements","Elementos"}. {"Email","Email"}. {"Enable logging","Gardar históricos"}. {"Enable message archiving","Activar o almacenamento de mensaxes"}. @@ -99,7 +91,6 @@ {"Enter path to jabberd14 spool file","Introduce ruta ao ficheiro jabberd14 spool"}. {"Enter path to text file","Introduce ruta ao ficheiro de texto"}. {"Enter the text you see","Introduza o texto que ves"}. -{"Error","Erro"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluír Jabber IDs das probas de CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas as táboas a un ficheiro SQL:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar datos de todos os usuarios do servidor a ficheros PIEFXIS (XEP-0227):"}. @@ -115,24 +106,19 @@ {"February","Febreiro"}. {"File larger than ~w bytes","O ficheiro é maior que ~w bytes"}. {"Friday","Venres"}. -{"From","De"}. {"Full Name","Nome completo"}. {"Get Number of Online Users","Ver número de usuarios conectados"}. {"Get Number of Registered Users","Ver número de usuarios rexistrados"}. {"Get User Last Login Time","Ver data da última conexión de usuario"}. -{"Get User Password","Ver contrasinal de usuario"}. {"Get User Statistics","Ver estatísticas de usuario"}. {"Given Name","Nome"}. {"Grant voice to this person?","¿Conceder voz a esta persoa?"}. -{"Group","Grupo"}. -{"Groups","Grupos"}. {"has been banned","foi bloqueado"}. {"has been kicked because of a system shutdown","foi expulsado porque o sistema vaise a deter"}. {"has been kicked because of an affiliation change","foi expulsado debido a un cambio de afiliación"}. {"has been kicked because the room has been changed to members-only","foi expulsado, porque a sala cambiouse a só-membros"}. {"has been kicked","foi expulsado"}. {"Host unknown","Dominio descoñecido"}. -{"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si non ves a imaxe CAPTCHA aquí, visita a páxina web."}. {"Import Directory","Importar directorio"}. {"Import File","Importar ficheiro"}. @@ -144,7 +130,6 @@ {"Import Users From jabberd14 Spool Files","Importar usuarios de ficheiros spool de jabberd-1.4"}. {"Improper domain part of 'from' attribute","Parte de dominio impropio no atributo 'from'"}. {"Improper message type","Tipo de mensaxe incorrecta"}. -{"Incoming s2s Connections:","Conexións S2S saíntes:"}. {"Incorrect CAPTCHA submit","O CAPTCHA proporcionado é incorrecto"}. {"Incorrect data form","Formulario de datos incorrecto"}. {"Incorrect password","Contrasinal incorrecta"}. @@ -169,8 +154,6 @@ {"Last month","Último mes"}. {"Last year","Último ano"}. {"leaves the room","sae da sala"}. -{"List of rooms","Lista de salas"}. -{"Low level update script","Script de actualización a baixo nivel"}. {"Make participants list public","A lista de participantes é pública"}. {"Make room CAPTCHA protected","Protexer a sala con CAPTCHA"}. {"Make room members-only","Sala só para membros"}. @@ -184,21 +167,17 @@ {"Maximum Number of Occupants","Número máximo de ocupantes"}. {"May","Maio"}. {"Membership is required to enter this room","Necesitas ser membro desta sala para poder entrar"}. -{"Members:","Membros:"}. -{"Memory","Memoria"}. {"Message body","Corpo da mensaxe"}. {"Message not found in forwarded payload","Mensaxe non atopada no contido reenviado"}. {"Middle Name","Segundo nome"}. {"Minimum interval between voice requests (in seconds)","Intervalo mínimo entre peticións de voz (en segundos)"}. {"Moderator privileges required","Necesítase privilexios de moderador"}. {"Moderator","Moderator"}. -{"Modified modules","Módulos Modificados"}. {"Module failed to handle the query","O módulo non puido xestionar a consulta"}. {"Monday","Luns"}. {"Multicast","Multicast"}. {"Multi-User Chat","Salas de Charla"}. {"Name","Nome"}. -{"Name:","Nome:"}. {"Neither 'jid' nor 'nick' attribute found","Non se atopou o atributo 'jid' nin 'nick'"}. {"Neither 'role' nor 'affiliation' attribute found","Non se atopou o atributo 'role' nin 'affiliation'"}. {"Never","Nunca"}. @@ -247,12 +226,9 @@ {"Number of online users","Número de usuarios conectados"}. {"Number of registered users","Número de usuarios rexistrados"}. {"October","Outubro"}. -{"Offline Messages","Mensaxes diferidas"}. -{"Offline Messages:","Mensaxes sen conexión:"}. {"OK","Aceptar"}. {"Old Password:","Contrasinal anterior:"}. {"Online Users","Usuarios conectados"}. -{"Online Users:","Usuarios conectados:"}. {"Online","Conectado"}. {"Only deliver notifications to available users","Só enviar notificacións aos usuarios dispoñibles"}. {"Only or tags are allowed","Só se permiten etiquetas ou "}. @@ -267,9 +243,7 @@ {"Organization Name","Nome da organización"}. {"Organization Unit","Unidade da organización"}. {"Outgoing s2s Connections","Conexións S2S saíntes"}. -{"Outgoing s2s Connections:","Conexións S2S saíntes:"}. {"Owner privileges required","Requírense privilexios de propietario da sala"}. -{"Packet","Paquete"}. {"Participant","Participante"}. {"Password Verification","Verificación da contrasinal"}. {"Password Verification:","Verificación da Contrasinal:"}. @@ -277,7 +251,6 @@ {"Password:","Contrasinal:"}. {"Path to Dir","Ruta ao directorio"}. {"Path to File","Ruta ao ficheiro"}. -{"Pending","Pendente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Persistir elementos ao almacenar"}. {"Ping query is incorrect","A solicitude de Ping é incorrecta"}. @@ -296,17 +269,12 @@ {"RAM copy","Copia en RAM"}. {"Really delete message of the day?","¿Está seguro que quere borrar a mensaxe do dia?"}. {"Recipient is not in the conference room","O receptor non está na sala de conferencia"}. -{"Registered Users","Usuarios rexistrados"}. -{"Registered Users:","Usuarios rexistrados:"}. {"Register","Rexistrar"}. {"Remote copy","Copia remota"}. -{"Remove All Offline Messages","Borrar Todas as Mensaxes Sen conexión"}. {"Remove User","Eliminar usuario"}. -{"Remove","Borrar"}. {"Replaced by new connection","Substituído por unha nova conexión"}. {"Resources","Recursos"}. {"Restart Service","Reiniciar o servizo"}. -{"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaura copia de seguridade desde o ficheiro en "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar copia de seguridade binaria no seguinte reinicio de ejabberd (require menos memoria):"}. {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridade binaria:"}. @@ -320,10 +288,8 @@ {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Lista de grupos autorizados a subscribir"}. {"Roster size","Tamaño da lista de contactos"}. -{"RPC Call Error","Erro na chamada RPC"}. {"Running Nodes","Nodos funcionando"}. {"Saturday","Sábado"}. -{"Script check","Comprobación de script"}. {"Search Results for ","Buscar resultados por "}. {"Search users in ","Buscar usuarios en "}. {"Send announcement to all online users on all hosts","Enviar anuncio a todos os usuarios conectados en todos os dominios"}. @@ -341,19 +307,13 @@ {"Specify the access model","Especifica o modelo de acceso"}. {"Specify the event message type","Especifica o tipo da mensaxe de evento"}. {"Specify the publisher model","Especificar o modelo do publicante"}. -{"Statistics of ~p","Estatísticas de ~p"}. -{"Statistics","Estatísticas"}. -{"Stop","Deter"}. {"Stopped Nodes","Nodos detidos"}. -{"Storage Type","Tipo de almacenamento"}. {"Store binary backup:","Gardar copia de seguridade binaria:"}. {"Store plain text backup:","Gardar copia de seguridade en texto plano:"}. {"Subject","Asunto"}. -{"Submit","Enviar"}. {"Submitted","Enviado"}. {"Subscriber Address","Dirección do subscriptor"}. {"Subscriptions are not allowed","Non se permiten subscricións"}. -{"Subscription","Subscripción"}. {"Sunday","Domingo"}. {"That nickname is already in use by another occupant","Ese alcume xa está a ser usado por outro ocupante"}. {"That nickname is registered by another person","O alcume xa está rexistrado por outra persoa"}. @@ -372,7 +332,6 @@ {"This room is not anonymous","Sala non anónima"}. {"Thursday","Xoves"}. {"Time delay","Atraso temporal"}. -{"Time","Data"}. {"To register, visit ~s","Para rexistrarse, visita ~s"}. {"Token TTL","Token TTL"}. {"Too many active bytestreams","Demasiados bytestreams activos"}. @@ -382,13 +341,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Demasiados (~p) fallou autenticaciones desde esta dirección IP (~s). A dirección será desbloqueada as ~s UTC"}. {"Too many unacked stanzas","Demasiadas mensaxes sen recoñecer recibilos"}. {"Too many users in this conference","Demasiados usuarios nesta sala"}. -{"To","Para"}. -{"Total rooms","Salas totais"}. {"Traffic rate limit is exceeded","Hase exedido o límite de tráfico"}. -{"Transactions Aborted:","Transaccións abortadas:"}. -{"Transactions Committed:","Transaccións finalizadas:"}. -{"Transactions Logged:","Transaccións rexistradas:"}. -{"Transactions Restarted:","Transaccións reiniciadas:"}. {"Tuesday","Martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. {"Unable to register route on existing local domain","Non se pode rexistrar a ruta no dominio local existente"}. @@ -398,11 +351,6 @@ {"Unsupported element","Elemento non soportado"}. {"Update message of the day (don't send)","Actualizar mensaxe do dia, pero non envialo"}. {"Update message of the day on all hosts (don't send)","Actualizar a mensaxe do día en todos os dominos (pero non envialo)"}. -{"Update ~p","Actualizar ~p"}. -{"Update plan","Plan de actualización"}. -{"Update script","Script de actualización"}. -{"Update","Actualizar"}. -{"Uptime:","Tempo desde o inicio:"}. {"User already exists","O usuario xa existe"}. {"User JID","Jabber ID do usuario"}. {"User (jid)","Usuario (jid)"}. @@ -414,7 +362,6 @@ {"Users Last Activity","Última actividade dos usuarios"}. {"Users","Usuarios"}. {"User","Usuario"}. -{"Validate","Validar"}. {"Value 'get' of 'type' attribute is not allowed","O valor \"get\" do atributo 'type' non está permitido"}. {"Value of '~s' should be boolean","O valor de '~s' debería ser booleano"}. {"Value of '~s' should be datetime string","O valor de '~s' debería ser unha data"}. diff --git a/priv/msgs/he.msg b/priv/msgs/he.msg index 83be202d5..1dabbd028 100644 --- a/priv/msgs/he.msg +++ b/priv/msgs/he.msg @@ -9,8 +9,6 @@ {"Accept","קבל"}. {"Access denied by service policy","גישה נדחתה על ידי פוליסת שירות"}. {"Action on user","פעולה על משתמש"}. -{"Add Jabber ID","הוסף מזהה Jabber"}. -{"Add New","הוסף חדש"}. {"Add User","הוסף משתמש"}. {"Administration of ","ניהול של "}. {"Administration","הנהלה"}. @@ -58,22 +56,17 @@ {"Conference room does not exist","חדר ועידה לא קיים"}. {"Configuration of room ~s","תצורת חדר ~s"}. {"Configuration","תצורה"}. -{"Connected Resources:","משאבים מחוברים:"}. {"Country","ארץ"}. -{"CPU Time:","זמן מחשב (CPU):"}. {"Database failure","כשל מסד נתונים"}. -{"Database Tables at ~p","טבלאות מסד נתונים אצל ~p"}. {"Database Tables Configuration at ","תצורת טבלאות מסד נתונים אצל "}. {"Database","מסד נתונים"}. {"December","דצמבר"}. {"Default users as participants","משתמשים שגרתיים כמשתתפים"}. {"Delete message of the day on all hosts","מחק את בשורת היום בכל המארחים"}. {"Delete message of the day","מחק את בשורת היום"}. -{"Delete Selected","מחק נבחרות"}. {"Delete User","מחק משתמש"}. {"Deliver event notifications","מסור התראות אירוע"}. {"Deliver payloads with event notifications","מסור מטעני ייעוד (מטע״ד) יחד עם התראות אירוע"}. -{"Description:","תיאור:"}. {"Disc only copy","העתק של תקליטור בלבד"}. {"Dump Backup to Text File at ","השלך גיבוי לקובץ טקסט אצל "}. {"Dump to Text File","השלך לקובץ טקסט"}. @@ -85,7 +78,6 @@ {"ejabberd SOCKS5 Bytestreams module","מודול SOCKS5 Bytestreams של ejabberd"}. {"ejabberd vCard module","מודול vCard של ejabberd"}. {"ejabberd Web Admin","מנהל רשת ejabberd"}. -{"Elements","אלמנטים"}. {"Email","דוא״ל"}. {"Enable logging","אפשר רישום פעילות"}. {"Enable message archiving","אפשר אחסון הודעות"}. @@ -96,7 +88,6 @@ {"Enter path to jabberd14 spool file","הזן נתיב לקובץ סליל (spool file) של jabberd14"}. {"Enter path to text file","הזן נתיב לקובץ טקסט"}. {"Enter the text you see","הזן את הכיתוב שאתה רואה"}. -{"Error","שגיאה"}. {"Exclude Jabber IDs from CAPTCHA challenge","הוצא כתובות Jabber מתוך אתגר CAPTCHA"}. {"Export all tables as SQL queries to a file:","יצא את כל הטבלאות בתור שאילתות SQL לתוך קובץ:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","יצא מידע של כל המשתמשים שבתוך שרת זה לתוך קבצי PIEFXIS ‏(XEP-0227):"}. @@ -109,24 +100,19 @@ {"February","פברואר"}. {"File larger than ~w bytes","קובץ גדול יותר משיעור של ~w בייטים"}. {"Friday","יום שישי"}. -{"From","מאת"}. {"Full Name","שם מלא"}. {"Get Number of Online Users","השג מספר של משתמשים מקוונים"}. {"Get Number of Registered Users","השג מספר של משתמשים רשומים"}. {"Get User Last Login Time","השג זמן כניסה אחרון של משתמש"}. -{"Get User Password","השג סיסמת משתמש"}. {"Get User Statistics","השג סטטיסטיקת משתמש"}. {"Given Name","שם פרטי"}. {"Grant voice to this person?","להעניק ביטוי לאישיות זו?"}. -{"Groups","קבוצות"}. -{"Group","קבוצה"}. {"has been banned","נאסר/ה"}. {"has been kicked because of a system shutdown","נבעט/ה משום כיבוי מערכת"}. {"has been kicked because of an affiliation change","נבעט/ה משום שינוי סינוף"}. {"has been kicked because the room has been changed to members-only","נבעט/ה משום שהחדר שונה אל חברים-בלבד"}. {"has been kicked","נבעט/ה"}. {"Host unknown","מארח לא ידוע"}. -{"Host","מארח"}. {"If you don't see the CAPTCHA image here, visit the web page.","אם אינך רואה תמונת CAPTCHA כאן, בקר בעמוד רשת."}. {"Import Directory","ייבוא מדור"}. {"Import File","ייבוא קובץ"}. @@ -137,7 +123,6 @@ {"Import Users from Dir at ","ייבוא משתמשים מתוך מדור אצל "}. {"Import Users From jabberd14 Spool Files","יבא משתמשים מתוך קבצי סליל (Spool Files) של jabberd14"}. {"Improper message type","טיפוס הודעה לא מתאים"}. -{"Incoming s2s Connections:","חיבורי s2s נכנסים:"}. {"Incorrect CAPTCHA submit","נשלחה CAPTCHA שגויה"}. {"Incorrect data form","טופס מידע לא תקין"}. {"Incorrect password","מילת מעבר שגויה"}. @@ -158,8 +143,6 @@ {"Last month","חודש אחרון"}. {"Last year","שנה אחרונה"}. {"leaves the room","עוזב/ת את החדר"}. -{"List of rooms","רשימה של חדרים"}. -{"Low level update script","תסריט עדכון Low level"}. {"Make participants list public","הפוך רשימת משתתפים לפומבית"}. {"Make room CAPTCHA protected","הפוך חדר לחדר מוגן CAPTCHA"}. {"Make room members-only","הפוך חדר לחדר עבור חברים-בלבד"}. @@ -173,20 +156,16 @@ {"Maximum Number of Occupants","מספר מרבי של נוכחים"}. {"May","מאי"}. {"Membership is required to enter this room","נדרשת חברות כדי להיכנס אל חדר זה"}. -{"Members:","חברים:"}. -{"Memory","זיכרון"}. {"Message body","גוף הודעה"}. {"Middle Name","שם אמצעי"}. {"Minimum interval between voice requests (in seconds)","תדירות מינימלית בין בקשות ביטוי (בשניות)"}. {"Moderator privileges required","נדרשות הרשאות אחראי"}. {"Moderator","אחראי"}. -{"Modified modules","מודולים שהותאמו"}. {"Module failed to handle the query","מודול נכשל לטפל בשאילתא"}. {"Monday","יום שני"}. {"Multicast","שידור מרובב"}. {"Multi-User Chat","שיחה מרובת משתמשים"}. {"Name","שם"}. -{"Name:","שם:"}. {"Never","אף פעם"}. {"New Password:","סיסמה חדשה:"}. {"Nickname Registration at ","רישום שם כינוי אצל "}. @@ -223,12 +202,9 @@ {"Number of online users","מספר של משתמשים מקוונים"}. {"Number of registered users","מספר של משתמשים רשומים"}. {"October","אוקטובר"}. -{"Offline Messages","הודעות לא מקוונות"}. -{"Offline Messages:","הודעות לא מקוונות:"}. {"OK","אישור"}. {"Old Password:","סיסמה ישנה:"}. {"Online Users","משתמשים מקוונים"}. -{"Online Users:","משתמשים מקוונים:"}. {"Online","מקוון"}. {"Only deliver notifications to available users","מסור התראות למשתמשים זמינים בלבד"}. {"Only or tags are allowed","רק תגיות או הינן מורשות"}. @@ -242,9 +218,7 @@ {"Organization Name","שם ארגון"}. {"Organization Unit","יחידת איגוד"}. {"Outgoing s2s Connections","חיבורי s2s יוצאים"}. -{"Outgoing s2s Connections:","חיבורי s2s יוצאים:"}. {"Owner privileges required","נדרשות הרשאות בעלים"}. -{"Packet","חבילת מידע"}. {"Participant","משתתף"}. {"Password Verification","אימות סיסמה"}. {"Password Verification:","אימות סיסמה:"}. @@ -252,7 +226,6 @@ {"Password:","סיסמה:"}. {"Path to Dir","נתיב למדור"}. {"Path to File","נתיב לקובץ"}. -{"Pending","ממתינות"}. {"Period: ","משך זמן: "}. {"Persist items to storage","פריטים קבועים לאחסון"}. {"Ping query is incorrect","שאילתת פינג הינה שגויה"}. @@ -270,17 +243,12 @@ {"RAM copy","העתק RAM"}. {"Really delete message of the day?","באמת למחוק את בשורת היום?"}. {"Recipient is not in the conference room","מקבל אינו מצוי בחדר הועידה"}. -{"Registered Users","משתמשים רשומים"}. -{"Registered Users:","משתמשים רשומים:"}. {"Register","הרשם"}. {"Remote copy","העתק מרוחק"}. -{"Remove All Offline Messages","הסר את כל ההודעות הלא מקוונות"}. {"Remove User","הסר משתמש"}. -{"Remove","הסר"}. {"Replaced by new connection","הוחלף בחיבור חדש"}. {"Resources","משאבים"}. {"Restart Service","אתחל שירות"}. -{"Restart","אתחל"}. {"Restore Backup from File at ","שחזר גיבוי מתוך קובץ אצל "}. {"Restore binary backup after next ejabberd restart (requires less memory):","שחזר גיבוי בינארי לאחר האתחול הבא של ejabberd (מצריך פחות זיכרון):"}. {"Restore binary backup immediately:","שחזר גיבוי בינארי לאלתר:"}. @@ -294,10 +262,8 @@ {"Room title","כותרת חדר"}. {"Roster groups allowed to subscribe","קבוצות רשימה מורשות להירשם"}. {"Roster size","גודל רשימה"}. -{"RPC Call Error","שגיאת קריאת RPC"}. {"Running Nodes","צמתים מורצים"}. {"Saturday","יום שבת"}. -{"Script check","בדיקת תסריט"}. {"Search Results for ","תוצאות חיפוש עבור "}. {"Search users in ","חיפוש משתמשים אצל "}. {"Send announcement to all online users on all hosts","שלח בשורה לכל המשתמשים המקוונים בכל המארחים"}. @@ -315,19 +281,13 @@ {"Specify the access model","ציין מודל גישה"}. {"Specify the event message type","ציין טיפוס הודעת אירוע"}. {"Specify the publisher model","ציין מודל פרסום"}. -{"Statistics of ~p","סטטיסטיקות של ~p"}. -{"Statistics","סטטיסטיקה"}. {"Stopped Nodes","צמתים שנפסקו"}. -{"Stop","הפסק"}. -{"Storage Type","טיפוס אחסון"}. {"Store binary backup:","אחסן גיבוי בינארי:"}. {"Store plain text backup:","אחסן גיבוי טקסט גלוי (plain text):"}. {"Subject","נושא"}. {"Submitted","נשלח"}. -{"Submit","שלח"}. {"Subscriber Address","כתובת מנוי"}. {"Subscriptions are not allowed","הרשמות אינן מורשות"}. -{"Subscription","הרשמה"}. {"Sunday","יום ראשון"}. {"That nickname is already in use by another occupant","שם כינוי זה כבר מצוי בשימוש על ידי נוכח אחר"}. {"That nickname is registered by another person","שם כינוי זה הינו רשום על ידי מישהו אחר"}. @@ -341,7 +301,6 @@ {"This room is not anonymous","חדר זה אינו אנונימי"}. {"Thursday","יום חמישי"}. {"Time delay","זמן שיהוי"}. -{"Time","זמן"}. {"To register, visit ~s","כדי להירשם, בקרו ~s"}. {"Token TTL","סימן TTL"}. {"Too many active bytestreams","יותר מדי יחידות bytestream פעילות"}. @@ -349,13 +308,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל גישה בשעה ~s UTC"}. {"Too many unacked stanzas","יותר מדי סטנזות בלי אישורי קבלה"}. {"Too many users in this conference","יותר מדי משתמשים בועידה זו"}. -{"Total rooms","חדרים סה״כ"}. -{"To","לכבוד"}. {"Traffic rate limit is exceeded","מגבלת שיעור תעבורה נחצתה"}. -{"Transactions Aborted:","טרנזקציות שבוטלו:"}. -{"Transactions Committed:","טרנזקציות שבוצעו:"}. -{"Transactions Logged:","טרנזקציות שנרשמו:"}. -{"Transactions Restarted:","טרנזקציות שהותחלו מחדש:"}. {"Tuesday","יום שלישי"}. {"Unable to generate a CAPTCHA","אין אפשרות להפיק CAPTCHA"}. {"Unauthorized","לא מורשה"}. @@ -363,11 +316,6 @@ {"Unregister","בטל רישום"}. {"Update message of the day (don't send)","עדכן את בשורת היום (אל תשלח)"}. {"Update message of the day on all hosts (don't send)","עדכן את בשורת היום בכל המארחים (אל תשלח)"}. -{"Update plan","תכנית עדכון"}. -{"Update ~p","עדכון ~p"}. -{"Update script","תסריט עדכון"}. -{"Update","עדכן"}. -{"Uptime:","זמן פעילות:"}. {"User already exists","משתמש כבר קיים"}. {"User JID","‏JID משתמש"}. {"User (jid)","משתמש (jid)"}. @@ -379,7 +327,6 @@ {"Users Last Activity","פעילות משתמשים אחרונה"}. {"Users","משתמשים"}. {"User","משתמש"}. -{"Validate","הענק תוקף"}. {"Value of '~s' should be boolean","ערך של '~s' צריך להיות boolean"}. {"Value of '~s' should be datetime string","ערך של '~s' צריך להיות מחרוזת datetime"}. {"Value of '~s' should be integer","ערך של '~s' צריך להיות integer"}. diff --git a/priv/msgs/hu.msg b/priv/msgs/hu.msg index ee9c3af0f..ca0dc5e77 100644 --- a/priv/msgs/hu.msg +++ b/priv/msgs/hu.msg @@ -10,8 +10,6 @@ {"Access denied by service policy","Hozzáférés megtagadva a szolgáltatási irányelv miatt"}. {"Account doesn't exist","A fiók nem létezik"}. {"Action on user","Művelet a felhasználón"}. -{"Add Jabber ID","Jabber-azonosító hozzáadása"}. -{"Add New","Új hozzáadása"}. {"Add User","Felhasználó hozzáadása"}. {"Administration of ","Adminisztrációja ennek: "}. {"Administration","Adminisztráció"}. @@ -66,22 +64,15 @@ {"Conference room does not exist","A konferenciaszoba nem létezik"}. {"Configuration of room ~s","A(z) ~s szoba beállítása"}. {"Configuration","Beállítás"}. -{"Connected Resources:","Kapcsolódott erőforrások:"}. {"Country","Ország"}. -{"CPU Time:","Processzoridő:"}. {"Database failure","Adatbázishiba"}. -{"Database Tables at ~p","Adatbázistáblák itt: ~p"}. {"Database Tables Configuration at ","Adatbázistáblák beállítása itt: "}. {"Database","Adatbázis"}. {"December","december"}. {"Default users as participants","Alapértelmezett felhasználók mint résztvevők"}. -{"Delete content","Tartalom törlése"}. {"Delete message of the day on all hosts","Napi üzenet törlése az összes gépen"}. {"Delete message of the day","Napi üzenet törlése"}. -{"Delete Selected","Kijelöltek törlése"}. -{"Delete table","Tábla törlése"}. {"Delete User","Felhasználó törlése"}. -{"Description:","Leírás:"}. {"Disc only copy","Csak lemez másolása"}. {"Dump Backup to Text File at ","Biztonsági mentés kiírása szövegfájlba itt: "}. {"Dump to Text File","Kiírás szövegfájlba"}. @@ -95,7 +86,6 @@ {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","ejabberd webes adminisztráció"}. {"ejabberd","ejabberd"}. -{"Elements","Elemek"}. {"Email","E-mail"}. {"Enable logging","Naplózás engedélyezése"}. {"Enabling push without 'node' attribute is not supported","A „node” attribútum nélküli felküldés engedélyezése nem támogatott"}. @@ -106,7 +96,6 @@ {"Enter path to jabberd14 spool file","Adja meg a jabberd14 tárolófájl útvonalát"}. {"Enter path to text file","Adja meg a szövegfájl útvonalát"}. {"Enter the text you see","Írja be a látott szöveget"}. -{"Error","Hiba"}. {"Export all tables as SQL queries to a file:","Összes tábla exportálása SQL-lekérdezésekként egy fájlba:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","A kiszolgálón lévő összes felhasználó adatainak exportálása PIEFXIS-fájlokba (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Egy gépen lévő felhasználók adatainak exportálása PIEFXIS-fájlokba (XEP-0227):"}. @@ -121,24 +110,19 @@ {"File larger than ~w bytes","A fájl nagyobb ~w bájtnál"}. {"Friday","péntek"}. {"From ~ts","Feladó: ~ts"}. -{"From","Feladó"}. {"Full Name","Teljes név"}. {"Get Number of Online Users","Elérhető felhasználók számának lekérése"}. {"Get Number of Registered Users","Regisztrált felhasználók számának lekérése"}. {"Get Pending","Függőben lévő lekérése"}. {"Get User Last Login Time","Felhasználó legutolsó bejelentkezési idejének lekérése"}. -{"Get User Password","Felhasználó jelszavának lekérése"}. {"Get User Statistics","Felhasználói statisztikák lekérése"}. {"Given Name","Keresztnév"}. -{"Group","Csoport"}. -{"Groups","Csoportok"}. {"has been banned","ki lett tiltva"}. {"has been kicked because of a system shutdown","ki lett rúgva egy rendszerleállítás miatt"}. {"has been kicked because of an affiliation change","ki lett rúgva egy hovatartozás megváltozása miatt"}. {"has been kicked because the room has been changed to members-only","ki lett rúgva, mert a szobát megváltoztatták csak tagok részére"}. {"has been kicked","ki lett rúgva"}. {"Host unknown","Gép ismeretlen"}. -{"Host","Gép"}. {"HTTP File Upload","HTTP fájlfeltöltés"}. {"Idle connection","Tétlen kapcsolat"}. {"If you don't see the CAPTCHA image here, visit the web page.","Ha nem látja itt a CAPTCHA képet, akkor látogassa meg a weboldalt."}. @@ -152,7 +136,6 @@ {"Import Users From jabberd14 Spool Files","Felhasználók importálása jabberd14 tárolófájlokból"}. {"Improper domain part of 'from' attribute","A „from” attribútum tartományrésze helytelen"}. {"Improper message type","Helytelen üzenettípus"}. -{"Incoming s2s Connections:","Bejövő s2s kapcsolatok:"}. {"Incorrect CAPTCHA submit","Hibás CAPTCHA beküldés"}. {"Incorrect data form","Hibás adatűrlap"}. {"Incorrect password","Hibás jelszó"}. @@ -182,8 +165,6 @@ {"Last month","Múlt hónap"}. {"Last year","Múlt év"}. {"leaves the room","elhagyta a szobát"}. -{"List of rooms","Szobák listája"}. -{"Low level update script","Alacsony szintű frissítő parancsfájl"}. {"Make participants list public","Résztvevőlista nyilvánossá tétele"}. {"Make room CAPTCHA protected","Szoba CAPTCHA-védetté tétele"}. {"Make room members-only","Szoba beállítása csak tagoknak"}. @@ -197,20 +178,16 @@ {"Maximum Number of Occupants","Résztvevők legnagyobb száma"}. {"May","május"}. {"Membership is required to enter this room","Tagság szükséges a szobába lépéshez"}. -{"Members:","Tagok:"}. -{"Memory","Memória"}. {"Message body","Üzenettörzs"}. {"Message not found in forwarded payload","Nem található üzenet a továbbított adatokban"}. {"Messages from strangers are rejected","Idegenektől származó üzenetek vissza vannak utasítva"}. {"Middle Name","Középső név"}. {"Moderator privileges required","Moderátori jogosultságok szükségesek"}. -{"Modified modules","Módosított modulok"}. {"Module failed to handle the query","A modul nem tudta kezelni a lekérdezést"}. {"Monday","hétfő"}. {"Multicast","Csoportcímzés"}. {"Multi-User Chat","Többfelhasználós csevegés"}. {"Name","Név"}. -{"Name:","Név:"}. {"Neither 'jid' nor 'nick' attribute found","Sem a „jid”, sem a „nick” attribútum nem található"}. {"Neither 'role' nor 'affiliation' attribute found","Sem a „role”, sem az „affiliation” attribútum nem található"}. {"Never","Soha"}. @@ -259,12 +236,9 @@ {"Number of online users","Elérhető felhasználók száma"}. {"Number of registered users","Regisztrált felhasználók száma"}. {"October","október"}. -{"Offline Messages","Kapcsolat nélküli üzenetek"}. -{"Offline Messages:","Kapcsolat nélküli üzenetek:"}. {"OK","Rendben"}. {"Old Password:","Régi jelszó:"}. {"Online Users","Elérhető felhasználók"}. -{"Online Users:","Elérhető felhasználók:"}. {"Online","Elérhető"}. {"Only or tags are allowed","Csak az vagy címkék engedélyezettek"}. {"Only element is allowed in this query","Csak a elem engedélyezett ebben a lekérdezésben"}. @@ -278,17 +252,14 @@ {"Organization Name","Szervezet neve"}. {"Organization Unit","Szervezeti egység"}. {"Outgoing s2s Connections","Kimenő s2s kapcsolatok"}. -{"Outgoing s2s Connections:","Kimenő s2s kapcsolatok:"}. {"Owner privileges required","Tulajdonosi jogosultságok szükségesek"}. {"Packet relay is denied by service policy","Csomagátjátszás megtagadva a szolgáltatási irányelv miatt"}. -{"Packet","Csomag"}. {"Password Verification","Jelszó ellenőrzése"}. {"Password Verification:","Jelszó ellenőrzése:"}. {"Password","Jelszó"}. {"Password:","Jelszó:"}. {"Path to Dir","Útvonal a könyvtárhoz"}. {"Path to File","Útvonal a fájlhoz"}. -{"Pending","Függőben"}. {"Period: ","Időszak: "}. {"Ping query is incorrect","A lekérdezés pingelése hibás"}. {"Ping","Ping"}. @@ -310,19 +281,14 @@ {"RAM copy","RAM másolás"}. {"Really delete message of the day?","Valóban törli a napi üzenetet?"}. {"Recipient is not in the conference room","A címzett nincs a konferenciaszobában"}. -{"Registered Users","Regisztrált felhasználók"}. -{"Registered Users:","Regisztrált felhasználók:"}. {"Register","Regisztráció"}. {"Remote copy","Távoli másolás"}. -{"Remove All Offline Messages","Összes kapcsolat nélküli üzenet eltávolítása"}. {"Remove User","Felhasználó eltávolítása"}. -{"Remove","Eltávolítás"}. {"Replaced by new connection","Kicserélve egy új kapcsolattal"}. {"Request has timed out","A kérés túllépte az időkorlátot"}. {"Request is ignored","A kérés mellőzve lett"}. {"Resources","Erőforrások"}. {"Restart Service","Szolgáltatás újraindítása"}. -{"Restart","Újraindítás"}. {"Restore Backup from File at ","Biztonsági mentés visszaállítása fájlból itt: "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Bináris biztonsági mentés visszaállítása az ejabberd következő újraindítása után (kevesebb memóriát igényel):"}. {"Restore binary backup immediately:","Bináris biztonsági mentés visszaállítása azonnal:"}. @@ -334,15 +300,11 @@ {"Room Occupants","Szoba résztvevői"}. {"Room terminates","Szoba megszűnik"}. {"Room title","Szoba címe"}. -{"Roster of ~ts","~ts névsora"}. {"Roster size","Névsor mérete"}. -{"RPC Call Error","RPC hívási hiba"}. {"Running Nodes","Futó csomópontok"}. {"Saturday","szombat"}. -{"Script check","Parancsfájl-ellenőrzés"}. {"Search Results for ","Keresési eredménye ennek: "}. {"Search users in ","Felhasználók keresése ebben: "}. -{"Select All","Összes kijelölése"}. {"Send announcement to all online users on all hosts","Közlemény küldése az összes elérhető felhasználónak az összes gépen"}. {"Send announcement to all online users","Közlemény küldése az összes elérhető felhasználónak"}. {"Send announcement to all users on all hosts","Közlemény küldése az összes felhasználónak az összes gépen"}. @@ -357,19 +319,13 @@ {"Show Ordinary Table","Szokásos táblázat megjelenítése"}. {"Shut Down Service","Szolgáltatás leállítása"}. {"SOCKS5 Bytestreams","SOCKS5 bájtfolyamok"}. -{"Statistics of ~p","~p statisztikái"}. -{"Statistics","Statisztikák"}. -{"Stop","Leállítás"}. {"Stopped Nodes","Leállított csomópontok"}. -{"Storage Type","Tárolótípus"}. {"Store binary backup:","Bináris biztonsági mentés tárolása:"}. {"Store plain text backup:","Egyszerű szöveges biztonsági mentés tárolása:"}. {"Stream management is already enabled","A folyamkezelés már engedélyezve van"}. {"Stream management is not enabled","A folyamkezelés nincs engedélyezve"}. {"Subject","Tárgy"}. -{"Submit","Elküldés"}. {"Submitted","Elküldve"}. -{"Subscription","Feliratkozás"}. {"Subscriptions are not allowed","Feliratkozások nem engedélyezettek"}. {"Sunday","vasárnap"}. {"That nickname is already in use by another occupant","Ezt a becenevet már használja egy másik résztvevő"}. @@ -394,10 +350,8 @@ {"Thursday","csütörtök"}. {"Time delay","Időkésleltetés"}. {"Timed out waiting for stream resumption","Időtúllépés a folyam újrakezdésére várakozásnál"}. -{"Time","Idő"}. {"To register, visit ~s","Regisztráláshoz látogassa meg ezt az oldalt: ~s"}. {"To ~ts","Címzett: ~ts"}. -{"To","Címzett"}. {"Token TTL","Token élettartama"}. {"Too many active bytestreams","Túl sok aktív bájtfolyam"}. {"Too many CAPTCHA requests","Túl sok CAPTCHA kérés"}. @@ -408,12 +362,7 @@ {"Too many receiver fields were specified","Túl sok fogadómező lett meghatározva"}. {"Too many unacked stanzas","Túl sok nyugtázatlan stanza"}. {"Too many users in this conference","Túl sok felhasználó ebben a konferenciában"}. -{"Total rooms","Szobák összesen"}. {"Traffic rate limit is exceeded","Forgalom sebességkorlátja elérve"}. -{"Transactions Aborted:","Megszakított tranzakciók:"}. -{"Transactions Committed:","Véglegesített tranzakciók:"}. -{"Transactions Logged:","Naplózott tranzakciók:"}. -{"Transactions Restarted:","Újraindított tranzakciók:"}. {"~ts's Offline Messages Queue","~ts kapcsolat nélküli üzeneteinek tárolója"}. {"Tuesday","kedd"}. {"Unable to generate a CAPTCHA","Nem lehet előállítani CAPTCHA-t"}. @@ -422,16 +371,10 @@ {"Unexpected action","Váratlan művelet"}. {"Unexpected error condition: ~p","Váratlan hibafeltétel: ~p"}. {"Unregister","Regisztráció törlése"}. -{"Unselect All","Összes kijelölésének megszüntetése"}. {"Unsupported element","Nem támogatott elem"}. {"Unsupported version","Nem támogatott verzió"}. {"Update message of the day (don't send)","Napi üzenet frissítése (ne küldje el)"}. {"Update message of the day on all hosts (don't send)","Napi üzenet frissítése az összes gépen (ne küldje el)"}. -{"Update plan","Frissítési terv"}. -{"Update ~p","~p frissítése"}. -{"Update script","Frissítő parancsfájl"}. -{"Update","Frissítés"}. -{"Uptime:","Működési idő:"}. {"User already exists","A felhasználó már létezik"}. {"User (jid)","Felhasználó (JID)"}. {"User Management","Felhasználó-kezelés"}. @@ -444,7 +387,6 @@ {"Users are not allowed to register accounts so quickly","A felhasználóknak nem engedélyezett fiókokat regisztrálni ilyen gyorsan"}. {"Users Last Activity","Felhasználók utolsó tevékenysége"}. {"Users","Felhasználók"}. -{"Validate","Ellenőrzés"}. {"Value 'get' of 'type' attribute is not allowed","A „type” attribútum „get” értéke nem engedélyezett"}. {"Value of '~s' should be boolean","A(z) „~s” értéke csak logikai lehet"}. {"Value of '~s' should be datetime string","A(z) „~s” értéke csak dátum és idő karakterlánc lehet"}. diff --git a/priv/msgs/id.msg b/priv/msgs/id.msg index 29befa63d..ac6db281c 100644 --- a/priv/msgs/id.msg +++ b/priv/msgs/id.msg @@ -15,8 +15,6 @@ {"Access model","Model akses"}. {"Account doesn't exist","Akun tidak ada"}. {"Action on user","Tindakan pada pengguna"}. -{"Add Jabber ID","Tambah Jabber ID"}. -{"Add New","Tambah Baru"}. {"Add User","Tambah Pengguna"}. {"Administration of ","Administrasi "}. {"Administration","Administrasi"}. @@ -87,28 +85,20 @@ {"Conference room does not exist","Ruang Konferensi tidak ada"}. {"Configuration of room ~s","Pengaturan ruangan ~s"}. {"Configuration","Pengaturan"}. -{"Connected Resources:","Sumber Daya Terhubung:"}. {"Contact Addresses (normally, room owner or owners)","Alamat Kontak (biasanya, pemilik atau pemilik kamar)"}. {"Country","Negara"}. -{"CPU Time:","Waktu CPU:"}. {"Current Discussion Topic","Topik diskusi saat ini"}. {"Database failure","Kegagalan database"}. -{"Database Tables at ~p","Tabel Database pada ~p"}. {"Database Tables Configuration at ","Konfigurasi Tabel Database pada "}. {"Database","Database"}. {"December","Desember"}. {"Default users as participants","pengguna pertama kali masuk sebagai participant"}. -{"Delete content","Hapus isi"}. {"Delete message of the day on all hosts","Hapus pesan harian pada semua host"}. {"Delete message of the day","Hapus pesan harian"}. -{"Delete Selected","Hapus Yang Terpilih"}. -{"Delete table","Hapus tabel"}. {"Delete User","Hapus Pengguna"}. {"Deliver event notifications","Memberikan pemberitahuan acara"}. {"Deliver payloads with event notifications","Memberikan muatan dengan pemberitahuan acara"}. -{"Description:","Keterangan:"}. {"Disc only copy","Hanya salinan dari disc"}. -{"Displayed:","Tampilkan:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Jangan beritahukan kata sandi Anda ke siapapun, bahkan ke administrator layanan XMPP."}. {"Dump Backup to Text File at ","Dump Backup menjadi File Teks di "}. {"Dump to Text File","Dump menjadi File Teks"}. @@ -123,7 +113,6 @@ {"ejabberd vCard module","Modul ejabberd vCard"}. {"ejabberd Web Admin","Admin Web ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Elemen-elemen"}. {"Email Address","Alamat email"}. {"Email","Email"}. {"Enable logging","Aktifkan log"}. @@ -137,7 +126,6 @@ {"Enter path to text file","Masukkan path ke file teks"}. {"Enter the text you see","Masukkan teks yang Anda lihat"}. {"Erlang XMPP Server","Server Erlang XMPP"}. -{"Error","Kesalahan"}. {"Exclude Jabber IDs from CAPTCHA challenge","Kecualikan Jabber IDs dari tantangan CAPTCHA"}. {"Export all tables as SQL queries to a file:","Ekspor semua tabel sebagai kueri SQL ke file:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Ekspor data dari semua pengguna pada layanan ke berkas PIEFXIS (XEP-0227):"}. @@ -155,7 +143,6 @@ {"Fill in the form to search for any matching XMPP User","Isi kolom untuk mencari pengguna XMPP"}. {"Friday","Jumat"}. {"From ~ts","Dari ~ts"}. -{"From","Dari"}. {"Full List of Room Admins","Daftar Lengkap Admin Kamar"}. {"Full List of Room Owners","Daftar Lengkap Pemilik Kamar"}. {"Full Name","Nama Lengkap"}. @@ -163,20 +150,15 @@ {"Get Number of Registered Users","Dapatkan Jumlah Pengguna Yang Terdaftar"}. {"Get Pending","Lihat yang tertunda"}. {"Get User Last Login Time","Lihat Waktu Login Terakhir Pengguna"}. -{"Get User Password","Dapatkan User Password"}. {"Get User Statistics","Dapatkan Statistik Pengguna"}. {"Given Name","Nama"}. {"Grant voice to this person?","Ijinkan akses suara kepadanya?"}. -{"Group","Grup"}. -{"Groups that will be displayed to the members","Grup yang akan ditampilkan kepada anggota"}. -{"Groups","Grup"}. {"has been banned","telah dibanned"}. {"has been kicked because of a system shutdown","telah dikick karena sistem shutdown"}. {"has been kicked because of an affiliation change","telah dikick karena perubahan afiliasi"}. {"has been kicked because the room has been changed to members-only","telah dikick karena ruangan telah diubah menjadi hanya untuk member"}. {"has been kicked","telah dikick"}. {"Host unknown","Host tidak dikenal"}. -{"Host","Host"}. {"HTTP File Upload","Unggah Berkas HTTP"}. {"Idle connection","Koneksi menganggur"}. {"If you don't see the CAPTCHA image here, visit the web page.","Jika Anda tidak melihat gambar CAPTCHA disini, silahkan kunjungi halaman web."}. @@ -189,7 +171,6 @@ {"Import Users from Dir at ","Impor Pengguna dari Dir di "}. {"Import Users From jabberd14 Spool Files","Impor Pengguna Dari jabberd14 Spool File"}. {"Improper message type","Jenis pesan yang tidak benar"}. -{"Incoming s2s Connections:","Koneksi s2s masuk:"}. {"Incorrect CAPTCHA submit","Isian CAPTCHA salah"}. {"Incorrect data form","Formulir data salah"}. {"Incorrect password","Kata sandi salah"}. @@ -212,7 +193,6 @@ {"Last month","Akhir bulan"}. {"Last year","Akhir tahun"}. {"leaves the room","meninggalkan ruangan"}. -{"Low level update script","Perbaruan naskah tingkat rendah"}. {"Make participants list public","Buat daftar participant diketahui oleh public"}. {"Make room CAPTCHA protected","Buat ruangan dilindungi dengan CAPTCHA"}. {"Make room members-only","Buat ruangan hanya untuk member saja"}. @@ -224,17 +204,13 @@ {"Max payload size in bytes","Max kapasitas ukuran dalam bytes"}. {"Maximum Number of Occupants","Maksimum Jumlah Penghuni"}. {"May","Mei"}. -{"Members:","Anggota:"}. {"Membership is required to enter this room","Hanya Member yang dapat masuk ruangan ini"}. -{"Memory","Memori"}. {"Message body","Isi Pesan"}. {"Middle Name","Nama Tengah"}. {"Moderator privileges required","Hak istimewa moderator dibutuhkan"}. -{"Modified modules","Modifikasi modul-modul"}. {"Monday","Senin"}. {"Multiple elements are not allowed by RFC6121","Beberapa elemen tidak diizinkan oleh RFC6121"}. {"Name","Nama"}. -{"Name:","Nama:"}. {"Never","Tidak Pernah"}. {"New Password:","Password Baru:"}. {"Nickname Registration at ","Pendaftaran Julukan pada "}. @@ -257,11 +233,8 @@ {"Number of online users","Jumlah pengguna online"}. {"Number of registered users","Jumlah pengguna terdaftar"}. {"October","Oktober"}. -{"Offline Messages","Pesan Offline"}. -{"Offline Messages:","Pesan Offline:"}. {"OK","YA"}. {"Old Password:","Password Lama:"}. -{"Online Users:","Pengguna Online:"}. {"Online Users","Pengguna Yang Online"}. {"Online","Online"}. {"Only deliver notifications to available users","Hanya mengirimkan pemberitahuan kepada pengguna yang tersedia"}. @@ -273,9 +246,7 @@ {"Organization Name","Nama Organisasi"}. {"Organization Unit","Unit Organisasi"}. {"Outgoing s2s Connections","Koneksi Keluar s2s"}. -{"Outgoing s2s Connections:","Koneksi s2s yang keluar:"}. {"Owner privileges required","Hak istimewa owner dibutuhkan"}. -{"Packet","Paket"}. {"Participant","Partisipan"}. {"Password Verification:","Verifikasi Kata Sandi:"}. {"Password Verification","Verifikasi Sandi"}. @@ -283,7 +254,6 @@ {"Password","Sandi"}. {"Path to Dir","Jalur ke Dir"}. {"Path to File","Jalur ke File"}. -{"Pending","Tertunda"}. {"Period: ","Periode: "}. {"Persist items to storage","Pertahankan item ke penyimpanan"}. {"Persistent","Persisten"}. @@ -314,20 +284,15 @@ {"Receive notification of new nodes only","Terima notifikasi dari node baru saja"}. {"Recipient is not in the conference room","Penerima tidak berada di ruangan konferensi"}. {"Register an XMPP account","Daftarkan sebuah akun XMPP"}. -{"Registered Users","Pengguna Terdaftar"}. -{"Registered Users:","Pengguna Terdaftar:"}. {"Register","Mendaftar"}. {"Remote copy","Salinan Remote"}. -{"Remove All Offline Messages","Hapus Semua Pesan Offline"}. {"Remove User","Hapus Pengguna"}. -{"Remove","Menghapus"}. {"Replaced by new connection","Diganti dengan koneksi baru"}. {"Request has timed out","Waktu permintaan telah habis"}. {"Request is ignored","Permintaan diabaikan"}. {"Requested role","Peran yang diminta"}. {"Resources","Sumber daya"}. {"Restart Service","Restart Layanan"}. -{"Restart","Jalankan Ulang"}. {"Restore Backup from File at ","Kembalikan Backup dari File pada "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Mengembalikan cadangan yang berpasanagn setelah ejabberd berikutnya dijalankan ulang (memerlukan memori lebih sedikit):"}. {"Restore binary backup immediately:","Segera mengembalikan cadangan yang berpasangan:"}. @@ -341,20 +306,15 @@ {"Room terminates","Ruang dihentikan"}. {"Room title","Nama Ruangan"}. {"Roster groups allowed to subscribe","Kelompok kontak yang diizinkan untuk berlangganan"}. -{"Roster of ~ts","Daftar ~ts"}. {"Roster size","Ukuran Daftar Kontak"}. -{"Roster:","Daftar:"}. -{"RPC Call Error","Panggilan Kesalahan RPC"}. {"Running Nodes","Menjalankan Node"}. {"~s invites you to the room ~s","~s mengundang anda masuk kamar ~s"}. {"Saturday","Sabtu"}. -{"Script check","Periksa naskah"}. {"Search from the date","Cari dari tanggal"}. {"Search Results for ","Hasil Pencarian untuk "}. {"Search the text","Cari teks"}. {"Search until the date","Cari sampai tanggal"}. {"Search users in ","Pencarian pengguna dalam "}. -{"Select All","Pilih Semua"}. {"Send announcement to all online users on all hosts","Kirim pengumuman untuk semua pengguna yang online pada semua host"}. {"Send announcement to all online users","Kirim pengumuman untuk semua pengguna yang online"}. {"Send announcement to all users on all hosts","Kirim pengumuman untuk semua pengguna pada semua host"}. @@ -372,21 +332,15 @@ {"Specify the event message type","Tentukan jenis acara pesan"}. {"Specify the publisher model","Tentukan model penerbitan"}. {"Stanza ID","ID Stanza"}. -{"Statistics of ~p","statistik dari ~p"}. -{"Statistics","Statistik"}. -{"Stop","Hentikan"}. {"Stopped Nodes","Menghentikan node"}. -{"Storage Type","Jenis Penyimpanan"}. {"Store binary backup:","Penyimpanan cadangan yang berpasangan:"}. {"Store plain text backup:","Simpan cadangan teks biasa:"}. {"Stream management is already enabled","Manajemen stream sudah diaktifkan"}. {"Stream management is not enabled","Manajemen stream tidak diaktifkan"}. {"Subject","Subyek"}. -{"Submit","Serahkan"}. {"Submitted","Ulangi masukan"}. {"Subscriber Address","Alamat Pertemanan"}. {"Subscribers may publish","Pelanggan dapat mempublikasikan"}. -{"Subscription","Berlangganan"}. {"Subscriptions are not allowed","Langganan tidak diperbolehkan"}. {"Sunday","Minggu"}. {"Text associated with a picture","Teks yang terkait dengan gambar"}. @@ -422,11 +376,9 @@ {"This service can not process the address: ~s","Layanan ini tidak dapat memproses alamat: ~s"}. {"Thursday","Kamis"}. {"Time delay","Waktu tunda"}. -{"Time","Waktu"}. {"To register, visit ~s","Untuk mendaftar, kunjungi ~s"}. {"To ~ts","Kepada ~ts"}. {"Token TTL","TTL Token"}. -{"To","Kepada"}. {"Too many active bytestreams","Terlalu banyak bytestream aktif"}. {"Too many CAPTCHA requests","Terlalu banyak permintaan CAPTCHA"}. {"Too many child elements","Terlalu banyak elemen turunan"}. @@ -435,12 +387,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Terlalu banyak (~p) percobaan otentifikasi yang gagal dari alamat IP (~s). Alamat akan di unblok pada ~s UTC"}. {"Too many receiver fields were specified","Terlalu banyak bidang penerima yang ditentukan"}. {"Too many users in this conference","Terlalu banyak pengguna di grup ini"}. -{"Total rooms","Total kamar"}. {"Traffic rate limit is exceeded","Batas tingkat lalu lintas terlampaui"}. -{"Transactions Aborted:","Transaksi dibatalkan:"}. -{"Transactions Committed:","Transaksi yang dilakukan:"}. -{"Transactions Logged:","Transaksi yang ditempuh:"}. -{"Transactions Restarted:","Transaksi yang dijalankan ulang:"}. {"~ts's Offline Messages Queue","~ts's antrian Pesan Offline"}. {"Tuesday","Selasa"}. {"Unable to generate a CAPTCHA","Tidak dapat menghasilkan CAPTCHA"}. @@ -450,16 +397,10 @@ {"Unexpected error condition: ~p","Kondisi kerusakan yang tidak diduga: ~p"}. {"Unregister an XMPP account","Nonaktifkan akun XMPP"}. {"Unregister","Nonaktifkan"}. -{"Unselect All","Batalkan semua"}. {"Unsupported element","Elemen tidak didukung"}. {"Unsupported version","Versi tidak didukung"}. {"Update message of the day (don't send)","Rubah pesan harian (tidak dikirim)"}. {"Update message of the day on all hosts (don't send)","Rubah pesan harian pada semua host (tidak dikirim)"}. -{"Update plan","Rencana Perubahan"}. -{"Update ~p","Memperbaharui ~p"}. -{"Update script","Perbarui naskah"}. -{"Update","Memperbarui"}. -{"Uptime:","Sampai saat:"}. {"User already exists","Pengguna sudah ada"}. {"User (jid)","Pengguna (jid)"}. {"User JID","Pengguna JID"}. @@ -473,15 +414,12 @@ {"Users are not allowed to register accounts so quickly","Pengguna tidak diperkenankan untuk mendaftar akun begitu cepat"}. {"Users Last Activity","Aktifitas terakhir para pengguna"}. {"Users","Pengguna"}. -{"Validate","Mengesahkan"}. {"Value 'get' of 'type' attribute is not allowed","Nilai 'get' dari 'type' atribut tidak diperbolehkan"}. {"Value of '~s' should be boolean","Nilai '~ s' harus boolean"}. {"Value of '~s' should be datetime string","Nilai '~s' harus string datetime"}. {"Value of '~s' should be integer","Nilai '~ s' harus integer"}. {"Value 'set' of 'type' attribute is not allowed","Nilai 'set' dari 'type' atribut tidak diperbolehkan"}. {"vCard User Search","vCard Pencarian Pengguna"}. -{"View Queue","Lihat antrian"}. -{"View Roster","Lihat daftar kontak"}. {"Virtual Hosts","Host Virtual"}. {"Visitors are not allowed to change their nicknames in this room","Tamu tidak diperbolehkan untuk mengubah nama panggilan di ruangan ini"}. {"Visitors are not allowed to send messages to all occupants","Tamu tidak diperbolehkan untuk mengirim pesan ke semua penghuni"}. diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index 218815bdc..b50e82688 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","L'account non esiste"}. {"Action on user","Azione sull'utente"}. {"Add a hat to a user","Aggiungere un cappello a un utente"}. -{"Add Jabber ID","Aggiungere un Jabber ID"}. -{"Add New","Aggiungere Nuovo"}. {"Add User","Aggiungere un Utente"}. {"Administration of ","Amministrazione di "}. {"Administration","Amministrazione"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Chiunque abbia un abbonamento di presenza di entrambi o di può sottoscrivere e recuperare elementi"}. {"Anyone with Voice","Chiunque abbia la Voce"}. {"Anyone","Chiunque"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","A quanto pare il tuo account non ha diritti di amministrazione su questo server. Controlla come concedere i diritti di amministratore in: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Aprile"}. {"Attribute 'channel' is required for this request","Per questa richiesta è richiesto l'attributo 'canale'"}. {"Attribute 'id' is mandatory for MIX messages","L'attributo 'id' è obbligatorio per i messaggi MIX"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","La stanza per conferenze non esiste"}. {"Configuration of room ~s","Configurazione per la stanza ~s"}. {"Configuration","Configurazione"}. -{"Connected Resources:","Risorse connesse:"}. {"Contact Addresses (normally, room owner or owners)","Indirizzi di contatto (normalmente, proprietario o proprietari della stanza)"}. -{"Contrib Modules","Moduli di Contributo"}. {"Country","Paese"}. -{"CPU Time:","Tempo CPU:"}. {"Current Discussion Topic","Argomento di discussione attuale"}. {"Database failure","Errore del database"}. -{"Database Tables at ~p","Tabelle del database a ~p"}. {"Database Tables Configuration at ","Configurazione delle tabelle del database su "}. {"Database","Database"}. {"December","Dicembre"}. {"Default users as participants","Definire per default gli utenti come partecipanti"}. -{"Delete content","Elimina contenuto"}. {"Delete message of the day on all hosts","Eliminare il messaggio del giorno (MOTD) su tutti gli host"}. {"Delete message of the day","Eliminare il messaggio del giorno (MOTD)"}. -{"Delete Selected","Elimina Selezionato"}. -{"Delete table","Elimina tabella"}. {"Delete User","Eliminare l'utente"}. {"Deliver event notifications","Inviare notifiche degli eventi"}. {"Deliver payloads with event notifications","Inviare il contenuto del messaggio con la notifica dell'evento"}. -{"Description:","Descrizione:"}. {"Disc only copy","Copia solo su disco"}. -{"'Displayed groups' not added (they do not exist!): ","'Gruppi visualizzati' non aggiunti (non esistono!): "}. -{"Displayed:","Visualizzato:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Non rivelare la tua password a nessuno, nemmeno agli amministratori del server XMPP."}. {"Dump Backup to Text File at ","Trascrivere il salvataggio sul file di testo "}. {"Dump to Text File","Trascrivere su file di testo"}. @@ -132,7 +119,6 @@ {"ejabberd vCard module","Modulo vCard per ejabberd"}. {"ejabberd Web Admin","Amministrazione web ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Elementi"}. {"Email Address","Indirizzo di Posta Elettronica"}. {"Email","E-mail"}. {"Enable hats","Abilitare i cappelli"}. @@ -147,7 +133,6 @@ {"Enter path to text file","Immettere il percorso del file di testo"}. {"Enter the text you see","Immettere il testo visibile"}. {"Erlang XMPP Server","Server XMPP Erlang"}. -{"Error","Errore"}. {"Exclude Jabber IDs from CAPTCHA challenge","Escludi degli ID Jabber dal passaggio CAPTCHA"}. {"Export all tables as SQL queries to a file:","Esporta tutte le tabelle come query SQL in un file:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Esportare i dati di tutti gli utenti nel server in file PIEFXIS (XEP-0227):"}. @@ -166,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Compila il modulo per cercare qualsiasi utente XMPP corrispondente"}. {"Friday","Venerdì"}. {"From ~ts","Da ~ts"}. -{"From","Da"}. {"Full List of Room Admins","Elenco Completo degli Amministratori delle Stanze"}. {"Full List of Room Owners","Elenco Completo dei Proprietari delle Stanze"}. {"Full Name","Nome completo"}. @@ -176,13 +160,9 @@ {"Get Number of Registered Users","Ottenere il numero di utenti registrati"}. {"Get Pending","Ottieni in sospeso"}. {"Get User Last Login Time","Ottenere la data di ultimo accesso dell'utente"}. -{"Get User Password","Ottenere la password dell'utente"}. {"Get User Statistics","Ottenere le statistiche dell'utente"}. {"Given Name","Nome di battesimo"}. {"Grant voice to this person?","Dare parola a questa persona?"}. -{"Group","Gruppo"}. -{"Groups that will be displayed to the members","Gruppi che verranno visualizzati ai membri"}. -{"Groups","Gruppi"}. {"has been banned","è stata/o bandita/o"}. {"has been kicked because of a system shutdown","è stato espulso a causa dello spegnimento del sistema"}. {"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. @@ -192,7 +172,6 @@ {"Hat URI","URI Cappello"}. {"Hats limit exceeded","Limite di cappelli superato"}. {"Host unknown","Host sconosciuto"}. -{"Host","Host"}. {"HTTP File Upload","Caricamento file HTTP"}. {"Idle connection","Connessione inattiva"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se qui non vedi l'immagine CAPTCHA, visita la pagina web."}. @@ -206,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importare utenti da file di spool di jabberd14"}. {"Improper domain part of 'from' attribute","Parte del dominio non corretta dell'attributo 'da'"}. {"Improper message type","Tipo di messaggio non corretto"}. -{"Incoming s2s Connections:","Connessioni s2s in Entrata:"}. {"Incorrect CAPTCHA submit","Invio CAPTCHA errato"}. {"Incorrect data form","Modulo dati errato"}. {"Incorrect password","Password non esatta"}. @@ -236,7 +214,6 @@ {"July","Luglio"}. {"June","Giugno"}. {"Just created","Appena creato"}. -{"Label:","Etichetta:"}. {"Last Activity","Ultima attività"}. {"Last login","Ultimo accesso"}. {"Last message","Ultimo messaggio"}. @@ -244,11 +221,9 @@ {"Last year","Ultimo anno"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","I bit meno significativi dell'hash di testo SHA-256 devono corrispondere all'etichetta esadecimale"}. {"leaves the room","esce dalla stanza"}. -{"List of rooms","Elenco delle stanze"}. {"List of users with hats","Elenco degli utenti con cappelli"}. {"List users with hats","Elenca gli utenti con cappelli"}. {"Logging","Registrazione"}. -{"Low level update script","Script di aggiornamento di basso livello"}. {"Make participants list public","Rendere pubblica la lista dei partecipanti"}. {"Make room CAPTCHA protected","Rendere la stanza protetta da CAPTCHA"}. {"Make room members-only","Rendere la stanza riservata ai membri"}. @@ -266,11 +241,8 @@ {"Maximum number of items to persist","Numero massimo di elementi da persistere"}. {"Maximum Number of Occupants","Numero massimo di occupanti"}. {"May","Maggio"}. -{"Members not added (inexistent vhost!): ","Membri non aggiunti (vhost inesistente!): "}. {"Membership is required to enter this room","Per entrare in questa stanza è necessario essere membro"}. -{"Members:","Membri:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorizza la tua password, oppure scrivila su un foglio riposto in un luogo sicuro. In XMPP non esiste un modo automatico per recuperare la password se la dimentichi."}. -{"Memory","Memoria"}. {"Mere Availability in XMPP (No Show Value)","Mera disponibilità in XMPP (Nessun Valore Mostrato)"}. {"Message body","Corpo del messaggio"}. {"Message not found in forwarded payload","Messaggio non trovato nel payload inoltrato"}. @@ -282,15 +254,12 @@ {"Moderator privileges required","Necessari i privilegi di moderatore"}. {"Moderator","Moderatore/Moderatrice"}. {"Moderators Only","Solo i Moderatori"}. -{"Modified modules","Moduli modificati"}. {"Module failed to handle the query","Il modulo non è riuscito a gestire la query"}. {"Monday","Lunedì"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","Più elementi non sono consentiti da RFC6121"}. {"Multi-User Chat","Chat Multiutente"}. -{"Name in the rosters where this group will be displayed","Nome nei roster in cui verrà visualizzato questo gruppo"}. {"Name","Nome"}. -{"Name:","Nome:"}. {"Natural Language for Room Discussions","Linguaggio Naturale per le Discussioni in Sala"}. {"Natural-Language Room Name","Nome della Stanza in Linguaggio Naturale"}. {"Neither 'jid' nor 'nick' attribute found","Né l'attributo 'jid' né quello 'nick' sono stati trovati"}. @@ -355,14 +324,10 @@ {"Occupants are allowed to query others","Gli occupanti possono interrogare gli altri"}. {"Occupants May Change the Subject","Gli Occupanti Possono Cambiare il Soggetto"}. {"October","Ottobre"}. -{"Offline Messages","Messaggi offline"}. -{"Offline Messages:","Messaggi offline:"}. {"OK","OK"}. {"Old Password:","Vecchia password:"}. -{"Online Users:","Utenti connessi:"}. {"Online Users","Utenti online"}. {"Online","Online"}. -{"Only admins can see this","Solo gli amministratori possono vedere questo"}. {"Only collection node owners may associate leaf nodes with the collection","Solo i proprietari dei nodi di raccolta possono associare i nodi foglia alla collezione"}. {"Only deliver notifications to available users","Inviare le notifiche solamente agli utenti disponibili"}. {"Only or tags are allowed","Sono consentiti solo i tag o "}. @@ -382,10 +347,8 @@ {"Organization Unit","Unità dell'organizzazione"}. {"Other Modules Available:","Altri Moduli Disponibili:"}. {"Outgoing s2s Connections","Connessioni s2s in uscita"}. -{"Outgoing s2s Connections:","Connessioni s2s in uscita:"}. {"Owner privileges required","Necessari i privilegi di proprietario"}. {"Packet relay is denied by service policy","Il relay dei pacchetti è negato dalla politica di servizio"}. -{"Packet","Pacchetto"}. {"Participant ID","ID Partecipante"}. {"Participant","Partecipante"}. {"Password Verification","Verifica della password"}. @@ -395,7 +358,6 @@ {"Path to Dir","Percorso della directory"}. {"Path to File","Percorso del file"}. {"Payload semantic type information","Informazioni sul tipo semantico del payload"}. -{"Pending","Pendente"}. {"Period: ","Periodo: "}. {"Persist items to storage","Conservazione persistente degli elementi"}. {"Persistent","Persistente"}. @@ -429,21 +391,16 @@ {"Receive notification of new nodes only","Ricevere solo la notifica di nuovi nodi"}. {"Recipient is not in the conference room","Il destinatario non è nella stanza per conferenze"}. {"Register an XMPP account","Registra un account XMPP"}. -{"Registered Users","Utenti registrati"}. -{"Registered Users:","Utenti registrati:"}. {"Register","Registra"}. {"Remote copy","Copia remota"}. {"Remove a hat from a user","Rimuovere un cappello da un utente"}. -{"Remove All Offline Messages","Eliminare Tutti i Messaggi Offline"}. {"Remove User","Rimuovere l'utente"}. -{"Remove","Eliminare"}. {"Replaced by new connection","Sostituito da una nuova connessione"}. {"Request has timed out","La richiesta è scaduta"}. {"Request is ignored","La richiesta viene ignorata"}. {"Requested role","Ruolo richiesto"}. {"Resources","Risorse"}. {"Restart Service","Riavviare il servizio"}. -{"Restart","Riavviare"}. {"Restore Backup from File at ","Recuperare il salvataggio dal file "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Recuperare un salvataggio binario dopo il prossimo riavvio di ejabberd (necessita di meno memoria):"}. {"Restore binary backup immediately:","Recuperare un salvataggio binario adesso:"}. @@ -459,20 +416,15 @@ {"Room terminates","La stanza termina"}. {"Room title","Titolo della stanza"}. {"Roster groups allowed to subscribe","Gruppi roster abilitati alla registrazione"}. -{"Roster of ~ts","Roster di ~ts"}. {"Roster size","Dimensione della lista dei contatti"}. -{"Roster:","Lista dei contatti:"}. -{"RPC Call Error","Errore di chiamata RPC"}. {"Running Nodes","Nodi attivi"}. {"~s invites you to the room ~s","~s ti invita nella stanza ~s"}. {"Saturday","Sabato"}. -{"Script check","Verifica dello script"}. {"Search from the date","Cerca dalla data"}. {"Search Results for ","Risultati della ricerca per "}. {"Search the text","Cerca nel testo"}. {"Search until the date","Cerca fino alla data"}. {"Search users in ","Cercare utenti in "}. -{"Select All","Seleziona tutto"}. {"Send announcement to all online users on all hosts","Inviare l'annuncio a tutti gli utenti online su tutti gli host"}. {"Send announcement to all online users","Inviare l'annuncio a tutti gli utenti online"}. {"Send announcement to all users on all hosts","Inviare l'annuncio a tutti gli utenti su tutti gli host"}. @@ -496,22 +448,16 @@ {"Stanza id is not valid","L'id della stanza non è valido"}. {"Stanza ID","Stanza ID"}. {"Statically specify a replyto of the node owner(s)","Specificare staticamente una risposta del proprietario(i) del nodo"}. -{"Statistics of ~p","Statistiche di ~p"}. -{"Statistics","Statistiche"}. -{"Stop","Arrestare"}. {"Stopped Nodes","Nodi arrestati"}. -{"Storage Type","Tipo di conservazione"}. {"Store binary backup:","Conservare un salvataggio binario:"}. {"Store plain text backup:","Conservare un salvataggio come semplice testo:"}. {"Stream management is already enabled","La gestione del flusso è già abilitata"}. {"Stream management is not enabled","La gestione del flusso non è abilitata"}. {"Subject","Oggetto"}. -{"Submit","Inviare"}. {"Submitted","Inviato"}. {"Subscriber Address","Indirizzo dell'iscritta/o"}. {"Subscribers may publish","I sottoscrittori possono pubblicare"}. {"Subscription requests must be approved and only subscribers may retrieve items","Le richieste di sottoscrizione devono essere approvate e solo i sottoscrittori possono recuperare le voci"}. -{"Subscription","Iscrizione"}. {"Subscriptions are not allowed","Le sottoscrizioni non sono consentite"}. {"Sunday","Domenica"}. {"Text associated with a picture","Testo associato a un'immagine"}. @@ -575,10 +521,8 @@ {"Thursday","Giovedì"}. {"Time delay","Ritardo"}. {"Timed out waiting for stream resumption","Timed out in attesa della ripresa dello stream"}. -{"Time","Ora"}. {"To register, visit ~s","Per registrarsi, visita ~s"}. {"To ~ts","A ~ts"}. -{"To","A"}. {"Token TTL","Gettone TTL"}. {"Too many active bytestreams","Troppi bytestream attivi"}. {"Too many CAPTCHA requests","Troppe richieste CAPTCHA"}. @@ -588,12 +532,7 @@ {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Troppe (~p) autenticazioni non riuscite da questo indirizzo IP (~s). L'indirizzo verrà sbloccato alle ~s UTC"}. {"Too many receiver fields were specified","Sono stati specificati troppi campi del ricevitore"}. {"Too many users in this conference","Troppi utenti in questa conferenza"}. -{"Total rooms","Stanze totali"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. -{"Transactions Aborted:","Transazioni abortite:"}. -{"Transactions Committed:","Transazioni avvenute:"}. -{"Transactions Logged:","Transazioni con log:"}. -{"Transactions Restarted:","Transazioni riavviate:"}. {"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}. {"Tuesday","Martedì"}. {"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}. @@ -604,19 +543,13 @@ {"Uninstall","Disinstallare"}. {"Unregister an XMPP account","Disregistrare un account XMPP"}. {"Unregister","Elimina"}. -{"Unselect All","Deseleziona Tutto"}. {"Unsupported element","Elemento non supportato"}. {"Unsupported version","Versione non supportata"}. {"Update message of the day (don't send)","Aggiornare il messaggio del giorno (MOTD) (non inviarlo)"}. {"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}. -{"Update ~p","Aggiorna ~p"}. -{"Update plan","Piano di aggiornamento"}. -{"Update script","Script di aggiornamento"}. {"Update specs to get modules source, then install desired ones.","Aggiorna le specifiche per ottenere il sorgente dei moduli, quindi installa quelli desiderati."}. {"Update Specs","Aggiorna Specifiche"}. -{"Update","Aggiornare"}. {"Upgrade","Aggiornamento"}. -{"Uptime:","Tempo dall'avvio:"}. {"URL for Archived Discussion Logs","URL per i Registri delle Discussioni Archiviati"}. {"User already exists","L'utente esiste già"}. {"User JID","JID utente"}. @@ -631,7 +564,6 @@ {"Users Last Activity","Ultima attività degli utenti"}. {"Users","Utenti"}. {"User","Utente"}. -{"Validate","Validare"}. {"Value 'get' of 'type' attribute is not allowed","Il valore 'get' dell'attributo 'type' non è consentito"}. {"Value of '~s' should be boolean","Il valore di '~s' dovrebbe essere booleano"}. {"Value of '~s' should be datetime string","Il valore di '~s' deve essere una stringa dataora"}. @@ -639,8 +571,6 @@ {"Value 'set' of 'type' attribute is not allowed","Il valore 'set' dell'attributo 'type' non è consentito"}. {"vCard User Search","Ricerca di utenti per vCard"}. {"View joined MIX channels","Visualizza i canali MIX uniti"}. -{"View Queue","Visualizza Coda"}. -{"View Roster","Visualizza il Roster"}. {"Virtual Hosts","Host Virtuali"}. {"Visitors are not allowed to change their nicknames in this room","Non è consentito ai visitatori cambiare il nickname in questa stanza"}. {"Visitors are not allowed to send messages to all occupants","Non è consentito ai visitatori l'invio di messaggi a tutti i presenti"}. diff --git a/priv/msgs/ja.msg b/priv/msgs/ja.msg index ddabad44a..bf1401f54 100644 --- a/priv/msgs/ja.msg +++ b/priv/msgs/ja.msg @@ -14,8 +14,6 @@ {"Access model","アクセスモデル"}. {"Account doesn't exist","アカウントは存在しません"}. {"Action on user","ユーザー操作"}. -{"Add Jabber ID","Jabber ID を追加"}. -{"Add New","新規追加"}. {"Add User","ユーザーを追加"}. {"Administration of ","管理: "}. {"Administration","管理"}. @@ -68,26 +66,19 @@ {"Conference room does not exist","会議室は存在しません"}. {"Configuration of room ~s","チャットルーム ~s の設定"}. {"Configuration","設定"}. -{"Connected Resources:","接続リソース:"}. {"Contact Addresses (normally, room owner or owners)","連絡先 (通常は会議室の主宰者またはその複数)"}. {"Country","国"}. -{"CPU Time:","CPU時間:"}. {"Current Discussion Topic","現在の話題"}. {"Database failure","データーベース障害"}. -{"Database Tables at ~p","データーベーステーブル: ~p"}. {"Database Tables Configuration at ","データーベーステーブル設定 "}. {"Database","データーベース"}. {"December","12月"}. {"Default users as participants","デフォルトのユーザーは参加者"}. -{"Delete content","内容を削除"}. {"Delete message of the day on all hosts","全ホストのお知らせメッセージを削除"}. {"Delete message of the day","お知らせメッセージを削除"}. -{"Delete Selected","選択した項目を削除"}. -{"Delete table","テーブルを削除"}. {"Delete User","ユーザーを削除"}. {"Deliver event notifications","イベント通知を配送する"}. {"Deliver payloads with event notifications","イベント通知と同時にペイロードを配送する"}. -{"Description:","説明:"}. {"Disc only copy","ディスクだけのコピー"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","パスワードは誰にも(たとえ XMPP サーバーの管理者でも)教えないようにしてください。"}. {"Dump Backup to Text File at ","テキストファイルにバックアップ: "}. @@ -103,7 +94,6 @@ {"ejabberd vCard module","ejabberd vCard モジュール"}. {"ejabberd Web Admin","ejabberd ウェブ管理"}. {"ejabberd","ejabberd"}. -{"Elements","要素"}. {"Email Address","メールアドレス"}. {"Email","メール"}. {"Enable logging","ロギングを有効"}. @@ -116,7 +106,6 @@ {"Enter path to text file","テキストファイルのパスを入力してください"}. {"Enter the text you see","見えているテキストを入力してください"}. {"Erlang XMPP Server","Erlang XMPP サーバー"}. -{"Error","エラー"}. {"Exclude Jabber IDs from CAPTCHA challenge","CAPTCHA 入力を免除する Jabber ID"}. {"Export all tables as SQL queries to a file:","すべてのテーブルをSQL形式でファイルにエクスポート: "}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","サーバーにあるすべてのユーザーデータを PIEFXIS ファイルにエクスポート (XEP-0227):"}. @@ -131,7 +120,6 @@ {"Fill in the form to search for any matching XMPP User","XMPP ユーザーを検索するには欄に入力してください"}. {"Friday","金曜日"}. {"From ~ts","From ~ts"}. -{"From","差出人"}. {"Full List of Room Admins","チャットルーム管理者の一覧"}. {"Full List of Room Owners","チャットルーム主宰者の一覧"}. {"Full Name","氏名"}. @@ -141,19 +129,15 @@ {"Get Number of Registered Users","登録ユーザー数を取得"}. {"Get Pending","保留中の取得"}. {"Get User Last Login Time","最終ログイン時間を取得"}. -{"Get User Password","パスワードを取得"}. {"Get User Statistics","ユーザー統計を取得"}. {"Given Name","名"}. {"Grant voice to this person?","この人に発言権を与えますか ?"}. -{"Groups","グループ"}. -{"Group","グループ"}. {"has been banned","はバンされました"}. {"has been kicked because of a system shutdown","はシステムシャットダウンのためキックされました"}. {"has been kicked because of an affiliation change","は分掌が変更されたためキックされました"}. {"has been kicked because the room has been changed to members-only","はチャットルームがメンバー制に変更されたためキックされました"}. {"has been kicked","はキックされました"}. {"Host unknown","不明なホスト"}. -{"Host","ホスト"}. {"HTTP File Upload","HTTP ファイルアップロード"}. {"If you don't see the CAPTCHA image here, visit the web page.","ここに CAPTCHA 画像が表示されない場合、ウェブページを参照してください。"}. {"Import Directory","ディレクトリインポート"}. @@ -165,7 +149,6 @@ {"Import Users from Dir at ","ディレクトリからユーザーをインポート: "}. {"Import Users From jabberd14 Spool Files","jabberd14 Spool ファイルからユーザーをインポート"}. {"Improper message type","誤ったメッセージタイプです"}. -{"Incoming s2s Connections:","内向き s2s コネクション:"}. {"Incorrect data form","データ形式が違います"}. {"Incorrect password","パスワードが違います"}. {"Installed Modules:","インストールされているモジュール:"}. @@ -187,15 +170,12 @@ {"July","7月"}. {"June","6月"}. {"Just created","作成しました"}. -{"Label:","ラベル:"}. {"Last Activity","活動履歴"}. {"Last login","最終ログイン"}. {"Last message","最終メッセージ"}. {"Last month","先月"}. {"Last year","去年"}. {"leaves the room","がチャットルームから退出しました"}. -{"List of rooms","チャットルームの一覧"}. -{"Low level update script","低レベル更新スクリプト"}. {"Make participants list public","参加者一覧を公開"}. {"Make room CAPTCHA protected","チャットルームを CAPTCHA で保護"}. {"Make room members-only","チャットルームをメンバーのみに制限"}. @@ -210,20 +190,16 @@ {"Maximum Number of Occupants","最大在室者数"}. {"May","5月"}. {"Membership is required to enter this room","このチャットルームに入るにはメンバーでなければなりません"}. -{"Members:","メンバー:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","パスワードは記憶するか、紙に書いて安全な場所に保管してください。もしあなたがパスワードを忘れてしまった場合、XMPP ではパスワードのリカバリを自動的に行うことはできません。"}. -{"Memory","メモリ"}. {"Message body","本文"}. {"Middle Name","ミドルネーム"}. {"Minimum interval between voice requests (in seconds)","発言権の要求の最小時間間隔 (秒)"}. {"Moderator privileges required","モデレーター権限が必要です"}. {"Moderator","モデレーター"}. -{"Modified modules","更新されたモジュール"}. {"Monday","月曜日"}. {"Multicast","マルチキャスト"}. {"Multi-User Chat","マルチユーザーチャット"}. {"Name","名"}. -{"Name:","名前:"}. {"Natural-Language Room Name","自然言語での会議室名"}. {"Never","なし"}. {"New Password:","新しいパスワード:"}. @@ -260,12 +236,9 @@ {"Occupants are allowed to invite others","在室者は誰かを招待することができます"}. {"Occupants May Change the Subject","ユーザーによる件名の変更を許可"}. {"October","10月"}. -{"Offline Messages","オフラインメッセージ"}. -{"Offline Messages:","オフラインメッセージ:"}. {"OK","OK"}. {"Old Password:","古いパスワード:"}. {"Online Users","オンラインユーザー"}. -{"Online Users:","オンラインユーザー:"}. {"Online","オンライン"}. {"Only deliver notifications to available users","有効なユーザーにのみ告知を送信する"}. {"Only or tags are allowed"," タグまたは タグのみが許可されます"}. @@ -280,9 +253,7 @@ {"Organization Name","会社名"}. {"Organization Unit","部署名"}. {"Outgoing s2s Connections","外向き s2s コネクション"}. -{"Outgoing s2s Connections:","外向き s2s コネクション:"}. {"Owner privileges required","主宰者の権限が必要です"}. -{"Packet","パケット"}. {"Participant ID","参加者 ID"}. {"Participant","参加者"}. {"Password Verification","パスワード (確認)"}. @@ -291,7 +262,6 @@ {"Password:","パスワード:"}. {"Path to Dir","ディレクトリのパス"}. {"Path to File","ファイルのパス"}. -{"Pending","保留"}. {"Period: ","期間: "}. {"Persist items to storage","アイテムをストレージに保存する"}. {"Persistent","チャットルームを永続化"}. @@ -313,18 +283,13 @@ {"Really delete message of the day?","本当にお知らせメッセージを削除しますか ?"}. {"Recipient is not in the conference room","受信者はこの会議室にいません"}. {"Register an XMPP account","XMPP アカウントを登録"}. -{"Registered Users","登録ユーザー"}. -{"Registered Users:","登録ユーザー:"}. {"Register","登録"}. {"Remote copy","リモートコピー"}. -{"Remove All Offline Messages","すべてのオフラインメッセージを削除"}. {"Remove User","ユーザーを削除"}. -{"Remove","削除"}. {"Replaced by new connection","新しいコネクションによって置き換えられました"}. {"Request is ignored","リクエストは無視されます"}. {"Resources","リソース"}. {"Restart Service","サービスを再起動"}. -{"Restart","再起動"}. {"Restore Backup from File at ","ファイルからバックアップをリストア: "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd の再起動時にバイナリバックアップからリストア (メモリ少):"}. {"Restore binary backup immediately:","直ちにバイナリバックアップからリストア:"}. @@ -337,17 +302,12 @@ {"Room Occupants","在室者"}. {"Room title","チャットルームのタイトル"}. {"Roster groups allowed to subscribe","名簿グループは購読を許可しました"}. -{"Roster of ~ts","~ts の名簿"}. {"Roster size","名簿サイズ"}. -{"Roster:","名簿:"}. -{"RPC Call Error","RPC 呼び出しエラー"}. {"Running Nodes","起動ノード"}. {"~s invites you to the room ~s","~s はあなたをチャットルーム ~s に招待しています"}. {"Saturday","土曜日"}. -{"Script check","スクリプトチェック"}. {"Search Results for ","検索結果: "}. {"Search users in ","ユーザーの検索: "}. -{"Select All","すべて選択"}. {"Send announcement to all online users on all hosts","全ホストのオンラインユーザーにアナウンスを送信"}. {"Send announcement to all online users","すべてのオンラインユーザーにアナウンスを送信"}. {"Send announcement to all users on all hosts","全ホストのユーザーにアナウンスを送信"}. @@ -366,18 +326,12 @@ {"Specify the event message type","イベントメッセージ種別を設定"}. {"Specify the publisher model","公開モデルを指定する"}. {"Stanza ID","スタンザ ID"}. -{"Statistics of ~p","~p の統計"}. -{"Statistics","統計"}. {"Stopped Nodes","停止ノード"}. -{"Stop","停止"}. -{"Storage Type","ストレージタイプ"}. {"Store binary backup:","バイナリバックアップを保存:"}. {"Store plain text backup:","プレーンテキストバックアップを保存:"}. {"Subject","件名"}. {"Submitted","送信完了"}. -{"Submit","送信"}. {"Subscriber Address","購読者のアドレス"}. -{"Subscription","認可"}. {"Sunday","日曜日"}. {"That nickname is already in use by another occupant","そのニックネームは既にほかの在室者によって使用されています"}. {"That nickname is registered by another person","ニックネームはほかの人によって登録されています"}. @@ -404,18 +358,11 @@ {"This room is not anonymous","このチャットルームは非匿名です"}. {"Thursday","木曜日"}. {"Time delay","遅延時間"}. -{"Time","時間"}. {"Too many CAPTCHA requests","CAPTCHA 要求が多すぎます"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。"}. {"Too many unacked stanzas","多くのスタンザが応答していません"}. {"Too many users in this conference","この会議にはユーザーが多すぎます"}. -{"Total rooms","チャットルーム数"}. -{"To","To"}. {"Traffic rate limit is exceeded","トラフィックレートの制限を超えました"}. -{"Transactions Aborted:","トランザクションの失敗:"}. -{"Transactions Committed:","トランザクションのコミット:"}. -{"Transactions Logged:","トランザクションのログ: "}. -{"Transactions Restarted:","トランザクションの再起動:"}. {"~ts's Offline Messages Queue","~ts のオフラインメッセージキュー"}. {"Tuesday","火曜日"}. {"Unable to generate a CAPTCHA","CAPTCHA を生成できません"}. @@ -425,16 +372,10 @@ {"Uninstall","アンインストール"}. {"Unregister an XMPP account","XMPP アカウントを削除"}. {"Unregister","削除"}. -{"Unselect All","すべての選択を解除"}. {"Unsupported version","対応していないバージョン"}. {"Update message of the day (don't send)","お知らせメッセージを更新 (送信しない)"}. {"Update message of the day on all hosts (don't send)","全ホストのお知らせメッセージを更新 (送信しない)"}. -{"Update plan","更新計画"}. -{"Update ~p","更新 ~p"}. -{"Update script","スクリプトの更新"}. -{"Update","更新"}. {"Upgrade","アップグレード"}. -{"Uptime:","起動時間:"}. {"User already exists","ユーザーは既に存在しています"}. {"User (jid)","ユーザー (JID)"}. {"User JID","ユーザー JID"}. @@ -448,12 +389,9 @@ {"Users Last Activity","ユーザーの活動履歴"}. {"Users","ユーザー"}. {"User","ユーザー"}. -{"Validate","検証"}. {"Value of '~s' should be boolean","'~s' の値はブール値である必要があります"}. {"Value of '~s' should be integer","'~s' の値は整数である必要があります"}. {"vCard User Search","vCard検索"}. -{"View Queue","キューを表示"}. -{"View Roster","名簿を表示"}. {"Virtual Hosts","バーチャルホスト"}. {"Visitors are not allowed to change their nicknames in this room","傍聴者はこのチャットルームでニックネームを変更することはできません"}. {"Visitors are not allowed to send messages to all occupants","傍聴者はすべての在室者にメッセージを送信することはできません"}. diff --git a/priv/msgs/nl.msg b/priv/msgs/nl.msg index 64fbbdab2..d7799f9c5 100644 --- a/priv/msgs/nl.msg +++ b/priv/msgs/nl.msg @@ -9,8 +9,6 @@ {"A password is required to enter this room","U hebt een wachtwoord nodig om deze chatruimte te kunnen betreden"}. {"Access denied by service policy","De toegang werd geweigerd door het beleid van deze dienst"}. {"Action on user","Actie op gebruiker"}. -{"Add Jabber ID","Jabber ID toevoegen"}. -{"Add New","Toevoegen"}. {"Add User","Gebruiker toevoegen"}. {"Administration of ","Beheer van "}. {"Administration","Beheer"}. @@ -53,21 +51,16 @@ {"Conference room does not exist","De chatruimte bestaat niet"}. {"Configuration of room ~s","Instellingen van chatruimte ~s"}. {"Configuration","Instellingen"}. -{"Connected Resources:","Verbonden bronnen:"}. {"Country","Land"}. -{"CPU Time:","Processortijd:"}. -{"Database Tables at ~p","Databasetabellen van ~p"}. {"Database Tables Configuration at ","Instellingen van databasetabellen op "}. {"Database","Database"}. {"December","December"}. {"Default users as participants","Gebruikers standaard instellen als deelnemers"}. {"Delete message of the day on all hosts","Verwijder bericht-van-de-dag op alle hosts"}. {"Delete message of the day","Bericht van de dag verwijderen"}. -{"Delete Selected","Geselecteerde verwijderen"}. {"Delete User","Verwijder Gebruiker"}. {"Deliver event notifications","Gebeurtenisbevestigingen Sturen"}. {"Deliver payloads with event notifications","Berichten bezorgen samen met gebeurtenisnotificaties"}. -{"Description:","Beschrijving:"}. {"Disc only copy","Harde schijf"}. {"Dump Backup to Text File at ","Backup naar een tekstbestand schrijven op "}. {"Dump to Text File","Backup naar een tekstbestand schrijven"}. @@ -79,7 +72,6 @@ {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams module"}. {"ejabberd vCard module","ejabberd's vCard-module"}. {"ejabberd Web Admin","ejabberd Webbeheer"}. -{"Elements","Elementen"}. {"Email","E-mail"}. {"Enable logging","Logs aanzetten"}. {"Enable message archiving","Zet bericht-archivering aan"}. @@ -90,7 +82,6 @@ {"Enter path to jabberd14 spool file","Voer pad naar jabberd14-spool-bestand in"}. {"Enter path to text file","Voer pad naar backupbestand in"}. {"Enter the text you see","Voer de getoonde tekst in"}. -{"Error","Fout"}. {"Exclude Jabber IDs from CAPTCHA challenge","Geen CAPTCHA test voor Jabber IDs"}. {"Export all tables as SQL queries to a file:","Exporteer alle tabellen als SQL-queries naar een bestand:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exporteer data van alle gebruikers in de server naar PIEFXIS-bestanden (XEP-0227):"}. @@ -99,22 +90,17 @@ {"Family Name","Achternaam"}. {"February","Februari"}. {"Friday","Vrijdag"}. -{"From","Van"}. {"Full Name","Volledige naam"}. {"Get Number of Online Users","Aantal Aanwezige Gebruikers Opvragen"}. {"Get Number of Registered Users","Aantal Geregistreerde Gebruikers Opvragen"}. {"Get User Last Login Time","Tijd van Laatste Aanmelding Opvragen"}. -{"Get User Password","Gebruikerswachtwoord Opvragen"}. {"Get User Statistics","Gebruikers-statistieken Opvragen"}. {"Grant voice to this person?","Stemaanvraag honoreren voor deze persoon?"}. -{"Group","Groep"}. -{"Groups","Groepen"}. {"has been banned","is verbannen"}. {"has been kicked because of a system shutdown","is weggestuurd omdat het systeem gestopt wordt"}. {"has been kicked because of an affiliation change","is weggestuurd vanwege een affiliatieverandering"}. {"has been kicked because the room has been changed to members-only","is weggestuurd omdat de chatruimte vanaf heden alleen toegankelijk is voor leden"}. {"has been kicked","is weggestuurd"}. -{"Host","Host"}. {"If you don't see the CAPTCHA image here, visit the web page.","Als U het CAPTCHA-plaatje niet ziet, bezoek dan de webpagina."}. {"Import Directory","Directory importeren"}. {"Import File","Bestand importeren"}. @@ -140,8 +126,6 @@ {"Last month","Afgelopen maand"}. {"Last year","Afgelopen jaar"}. {"leaves the room","verliet de chatruimte"}. -{"List of rooms","Lijst van groepsgesprekken"}. -{"Low level update script","Lowlevel script voor de opwaardering"}. {"Make participants list public","Deelnemerslijst publiek maken"}. {"Make room CAPTCHA protected","Chatruimte beveiligen met een geautomatiseerde Turing test"}. {"Make room members-only","Chatruimte enkel toegankelijk maken voor leden"}. @@ -153,19 +137,15 @@ {"Max payload size in bytes","Maximumgrootte van bericht in bytes"}. {"Maximum Number of Occupants","Maximum aantal aanwezigen"}. {"May","Mei"}. -{"Members:","Groepsleden:"}. {"Membership is required to enter this room","U moet lid zijn om deze chatruimte te kunnen betreden"}. -{"Memory","Geheugen"}. {"Message body","Bericht"}. {"Middle Name","Tussennaam"}. {"Minimum interval between voice requests (in seconds)","Minimale interval tussen stemaanvragen (in seconden)"}. {"Moderator privileges required","U hebt moderatorprivileges nodig"}. -{"Modified modules","Gewijzigde modules"}. {"Monday","Maandag"}. {"Multicast","Multicast"}. {"Multi-User Chat","Groepschat"}. {"Name","Naam"}. -{"Name:","Naam:"}. {"Never","Nooit"}. {"New Password:","Nieuw Wachtwoord:"}. {"Nickname Registration at ","Registratie van een bijnaam op "}. @@ -188,12 +168,9 @@ {"Number of online users","Aantal Aanwezige Gebruikers"}. {"Number of registered users","Aantal Geregistreerde Gebruikers"}. {"October","Oktober"}. -{"Offline Messages","Offline berichten"}. -{"Offline Messages:","Offline berichten:"}. {"OK","OK"}. {"Old Password:","Oud Wachtwoord:"}. {"Online Users","Online gebruikers"}. -{"Online Users:","Online gebruikers:"}. {"Online","Online"}. {"Only deliver notifications to available users","Notificaties alleen verzenden naar online gebruikers"}. {"Only moderators and participants are allowed to change the subject in this room","Alleen moderators en deelnemers mogen het onderwerp van deze chatruimte veranderen"}. @@ -205,16 +182,13 @@ {"Organization Name","Organisatie"}. {"Organization Unit","Afdeling"}. {"Outgoing s2s Connections","Uitgaande s2s-verbindingen"}. -{"Outgoing s2s Connections:","Uitgaande s2s-verbindingen:"}. {"Owner privileges required","U hebt eigenaarsprivileges nodig"}. -{"Packet","Pakket"}. {"Password Verification","Wachtwoord Bevestiging"}. {"Password Verification:","Wachtwoord Bevestiging:"}. {"Password","Wachtwoord"}. {"Password:","Wachtwoord:"}. {"Path to Dir","Pad naar directory"}. {"Path to File","Pad naar bestand"}. -{"Pending","Bezig"}. {"Period: ","Periode: "}. {"Persist items to storage","Items in het geheugen bewaren"}. {"Ping","Ping"}. @@ -231,17 +205,12 @@ {"RAM copy","RAM"}. {"Really delete message of the day?","Wilt u het bericht van de dag verwijderen?"}. {"Recipient is not in the conference room","De ontvanger is niet in de chatruimte"}. -{"Registered Users","Geregistreerde gebruikers"}. -{"Registered Users:","Geregistreerde gebruikers:"}. {"Register","Registreer"}. {"Remote copy","Op andere nodes in de cluster"}. -{"Remove All Offline Messages","Verwijder alle offline berichten"}. {"Remove User","Gebruiker verwijderen"}. -{"Remove","Verwijderen"}. {"Replaced by new connection","Vervangen door een nieuwe verbinding"}. {"Resources","Bronnen"}. {"Restart Service","Herstart Service"}. -{"Restart","Herstarten"}. {"Restore Backup from File at ","Binaire backup direct herstellen op "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Binaire backup herstellen na herstart van ejabberd (vereist minder geheugen):"}. {"Restore binary backup immediately:","Binaire backup direct herstellen:"}. @@ -254,10 +223,8 @@ {"Room title","Naam van de chatruimte"}. {"Roster groups allowed to subscribe","Contactlijst-groepen die mogen abonneren"}. {"Roster size","Contactlijst Groote"}. -{"RPC Call Error","RPC-oproepfout"}. {"Running Nodes","Draaiende nodes"}. {"Saturday","Zaterdag"}. -{"Script check","Controle van script"}. {"Search Results for ","Zoekresultaten voor "}. {"Search users in ","Gebruikers zoeken in "}. {"Send announcement to all online users on all hosts","Mededeling verzenden naar alle online gebruikers op alle virtuele hosts"}. @@ -275,18 +242,12 @@ {"Specify the access model","Geef toegangsmodel"}. {"Specify the event message type","Geef type van eventbericht"}. {"Specify the publisher model","Publicatietype opgeven"}. -{"Statistics of ~p","Statistieken van ~p"}. -{"Statistics","Statistieken"}. {"Stopped Nodes","Gestopte nodes"}. -{"Stop","Stoppen"}. -{"Storage Type","Opslagmethode"}. {"Store binary backup:","Binaire backup maken:"}. {"Store plain text backup:","Backup naar een tekstbestand schrijven:"}. {"Subject","Onderwerp"}. {"Submitted","Verzonden"}. -{"Submit","Verzenden"}. {"Subscriber Address","Abonnee Adres"}. -{"Subscription","Inschrijving"}. {"Sunday","Zondag"}. {"That nickname is already in use by another occupant","Deze bijnaam is al in gebruik door een andere aanwezige"}. {"That nickname is registered by another person","Deze bijnaam is al geregistreerd door iemand anders"}. @@ -300,28 +261,16 @@ {"This room is not anonymous","Deze chatruimte is niet anoniem"}. {"Thursday","Donderdag"}. {"Time delay","Vertraging"}. -{"Time","Tijd"}. -{"To","Aan"}. {"Too many CAPTCHA requests","Te veel CAPTCHA-aanvragen"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Te veel (~p) mislukte authenticatie-pogingen van dit IP-adres (~s). Dit adres zal worden gedeblokkeerd om ~s UTC"}. {"Too many unacked stanzas","Te veel niet-bevestigde stanzas"}. -{"Total rooms","Aantal groepsgesprekken"}. {"Traffic rate limit is exceeded","Dataverkeerslimiet overschreden"}. -{"Transactions Aborted:","Afgebroken transacties:"}. -{"Transactions Committed:","Bevestigde transacties:"}. -{"Transactions Logged:","Gelogde transacties:"}. -{"Transactions Restarted:","Herstarte transacties:"}. {"Tuesday","Dinsdag"}. {"Unable to generate a CAPTCHA","Het generen van een CAPTCHA is mislukt"}. {"Unauthorized","Niet geautoriseerd"}. {"Unregister","Opheffen"}. {"Update message of the day (don't send)","Bericht van de dag bijwerken (niet verzenden)"}. {"Update message of the day on all hosts (don't send)","Verander bericht-van-de-dag op alle hosts (niet versturen)"}. -{"Update plan","Plan voor de opwaardering"}. -{"Update ~p","Opwaarderen van ~p"}. -{"Update script","Script voor de opwaardering"}. -{"Update","Bijwerken"}. -{"Uptime:","Uptime:"}. {"User JID","JID Gebruiker"}. {"User Management","Gebruikersbeheer"}. {"User","Gebruiker"}. @@ -329,7 +278,6 @@ {"Users are not allowed to register accounts so quickly","Het is gebruikers niet toegestaan zo snel achter elkaar te registreren"}. {"Users Last Activity","Laatste activiteit van gebruikers"}. {"Users","Gebruikers"}. -{"Validate","Bevestigen"}. {"vCard User Search","Gebruikers zoeken"}. {"Virtual Hosts","Virtuele hosts"}. {"Visitors are not allowed to change their nicknames in this room","Het is bezoekers niet toegestaan hun naam te veranderen in dit kanaal"}. diff --git a/priv/msgs/no.msg b/priv/msgs/no.msg index 98e48b6d6..b24b9eca9 100644 --- a/priv/msgs/no.msg +++ b/priv/msgs/no.msg @@ -9,8 +9,6 @@ {"Accept","Godta"}. {"Access denied by service policy","Tilgang nektes på grunn av en tjenesteregel"}. {"Action on user","Handling på bruker"}. -{"Add Jabber ID","Legg til Jabber-ID"}. -{"Add New","Legg til ny"}. {"Add User","Legg til bruker"}. {"Administration of ","Administrasjon av "}. {"Administration","Administrasjon"}. @@ -59,23 +57,17 @@ {"Conference room does not exist","Konferanserommet finnes ikke"}. {"Configuration of room ~s","Oppsett for rom ~s"}. {"Configuration","Oppsett"}. -{"Connected Resources:","Tilkoblede ressurser:"}. {"Country","Land"}. -{"CPU Time:","Prosessortid:"}. {"Current Discussion Topic","Nåværende diskusjonstema"}. -{"Database Tables at ~p","Databasetabeller på ~p"}. {"Database Tables Configuration at ","Database-tabelloppsett på "}. {"Database","Database"}. {"December","desember"}. {"Default users as participants","Standard brukere som deltakere"}. {"Delete message of the day","Slett melding for dagen"}. -{"Delete Selected","Slett valgte"}. {"Delete User","Slett bruker"}. {"Deliver event notifications","Lever begivenhetskunngjøringer"}. {"Deliver payloads with event notifications","Send innhold sammen med hendelsesmerknader"}. -{"Description:","Beskrivelse:"}. {"Disc only copy","Kun diskkopi"}. -{"'Displayed groups' not added (they do not exist!): ","«Viste grupper» ikke lagt til (de finnes ikke!): "}. {"Dump Backup to Text File at ","Dump sikkerhetskopi til tekstfil på "}. {"Dump to Text File","Dump til tekstfil"}. {"Edit Properties","Rediger egenskaper"}. @@ -84,7 +76,6 @@ {"ejabberd MUC module","ejabberd-MUC-modul"}. {"ejabberd Multicast service","ejabberd-multikastingstjeneste"}. {"ejabberd Publish-Subscribe module","ejabberd-Publish-Subscribe-modul"}. -{"Elements","Elementer"}. {"Email","E-post"}. {"Enable logging","Skru på loggføring"}. {"Enable message archiving","Skru på meldingsarkivering"}. @@ -95,7 +86,6 @@ {"Enter path to jabberd14 spool dir","Skriv inn sti til jabberd14 spoolkatalog"}. {"Enter path to text file","Skriv inn sti til tekstfil"}. {"Enter the text you see","Skriv inn teksten du ser"}. -{"Error","Feil"}. {"Exclude Jabber IDs from CAPTCHA challenge","Ekskluder Jabber-ID-er fra CAPTCHA-utfordring"}. {"Export all tables as SQL queries to a file:","Eksporter alle tabeller som SQL-spørringer til en fil:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Eksporter data om alle brukere på en tjener til PIEFXIS-filer (XEP-0227):"}. @@ -107,23 +97,18 @@ {"February","februar"}. {"File larger than ~w bytes","Fil større enn ~w byte"}. {"Friday","fredag"}. -{"From","Fra"}. {"Full List of Room Admins","Full liste over romadministratorer"}. {"Full List of Room Owners","Full liste over romeiere"}. {"Full Name","Fullt navn"}. {"Get Number of Online Users","Vis antall tilkoblede brukere"}. {"Get Number of Registered Users","Vis antall registrerte brukere"}. {"Get User Last Login Time","Vis brukers siste innloggingstidspunkt"}. -{"Get User Password","Hent brukers passord"}. {"Get User Statistics","Vis brukerstatistikk"}. -{"Group","Gruppe"}. -{"Groups","Grupper"}. {"has been banned","har blitt bannlyst"}. {"has been kicked because of a system shutdown","har blitt kastet ut på grunn av systemavstenging"}. {"has been kicked because of an affiliation change","har blitt kastet ut på grunn av en tilknytningsendring"}. {"has been kicked","har blitt kastet ut"}. {"Host unknown","Ukjent vert"}. -{"Host","Vert"}. {"HTTP File Upload","HTTP-filopplasting"}. {"If you don't see the CAPTCHA image here, visit the web page.","Dersom du ikke ser et CAPTCHA-bilde her, besøk nettsiden."}. {"Import Directory","Importer mappe"}. @@ -146,16 +131,13 @@ {"July","juli"}. {"June","juni"}. {"Just created","Akkurat opprettet"}. -{"Label:","Etikett:"}. {"Last Activity","Siste aktivitet"}. {"Last login","Siste innlogging"}. {"Last month","Siste måned"}. {"Last year","Siste år"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","De minst viktige bit-ene av SHA-256-sjekksummen for tekst skal tilsvare heksadesimal etikett"}. {"leaves the room","forlater rommet"}. -{"List of rooms","Romliste"}. {"Logging","Loggføring"}. -{"Low level update script","Lavnivå-oppdateringsskript"}. {"Make participants list public","Gjør deltakerlisten offentlig"}. {"Make room members-only","Gjør rommet tilgjengelig kun for medlemmer"}. {"Make room password protected","Passordbeskytt rommet"}. @@ -166,8 +148,6 @@ {"Maximum Number of History Messages Returned by Room","Maksimalt antall historikkmeldinger tilbudt av rommet"}. {"May","mai"}. {"Membership is required to enter this room","Medlemskap kreves for tilgang til dette rommet"}. -{"Members:","Medlemmer:"}. -{"Memory","Minne"}. {"Message body","Meldingskropp"}. {"Message not found in forwarded payload","Fant ikke melding i videresendt nyttelast"}. {"Messages from strangers are rejected","Meldinger fra ukjente avvises"}. @@ -175,13 +155,10 @@ {"Messages of type normal","Meldinger av normal type"}. {"Middle Name","Mellomnavn"}. {"Minimum interval between voice requests (in seconds)","Minimumsintervall mellom lydforespørsler (i sekunder)"}. -{"Modified modules","Endrede moduler"}. {"Monday","mandag"}. {"Multicast","Multikasting"}. {"Multi-User Chat","Multibrukersludring"}. -{"Name in the rosters where this group will be displayed","Navn i kontaktlistene der denne gruppen vises"}. {"Name","Navn"}. -{"Name:","Navn:"}. {"Never","Aldri"}. {"New Password:","Nytt passord:"}. {"Nickname can't be empty","Kallenavn kan ikke stå tomt"}. @@ -206,14 +183,10 @@ {"Number of online users","Antall tilkoblede brukere"}. {"Number of registered users","Antall registrerte brukere"}. {"October","oktober"}. -{"Offline Messages","Frakoblede meldinger"}. -{"Offline Messages:","Frakoblede meldinger:"}. {"OK","OK"}. {"Old Password:","Gammelt passord:"}. {"Online Users","Tilkoblede brukere"}. -{"Online Users:","Tilkoblede brukere:"}. {"Online","Tilkoblet"}. -{"Only admins can see this","Kun administratorer kan se dette"}. {"Only deliver notifications to available users","Kun send kunngjøringer til tilgjengelige brukere"}. {"Only occupants are allowed to send messages to the conference","Bare deltakere får sende normale meldinger til konferansen"}. {"Only occupants are allowed to send queries to the conference","Kun deltakere tillates å sende forespørsler til konferansen"}. @@ -222,9 +195,7 @@ {"Organization Name","Organisasjonsnavn"}. {"Organization Unit","Organisasjonsenhet"}. {"Outgoing s2s Connections","Utgående s2s-koblinger"}. -{"Outgoing s2s Connections:","Utgående s2s-koblinger"}. {"Owner privileges required","Eierprivilegier kreves"}. -{"Packet","Pakke"}. {"Participant","Deltager"}. {"Password Verification","Passordbekreftelse"}. {"Password Verification:","Passordbekreftelse:"}. @@ -232,7 +203,6 @@ {"Password:","Passord:"}. {"Path to Dir","Sti til mappe"}. {"Path to File","Sti til fil"}. -{"Pending","Ventende"}. {"Period: ","Periode: "}. {"Persist items to storage","Vedvarende elementer til lagring"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","Merk at disse valgene kun vil sikkerhetskopiere den innebygde Mnesia-databasen. Dersom du bruker ODBC-modulen må du også ta sikkerhetskopi av din SQL-database."}. @@ -249,19 +219,14 @@ {"RAM copy","Minnekopi"}. {"Really delete message of the day?","Vil du virkelig slette melding for dagen?"}. {"Recipient is not in the conference room","Mottakeren er ikke i konferanserommet"}. -{"Registered Users","Registrerte brukere"}. -{"Registered Users:","Registrerte brukere:"}. {"Register","Registrer"}. -{"Remove All Offline Messages","Fjern Alle frakoblede meldinger"}. {"Remove User","Fjern bruker"}. -{"Remove","Fjern"}. {"Replaced by new connection","Erstattet av en ny tilkobling"}. {"Request has timed out","Tidsavbrudd for forespørsel"}. {"Request is ignored","Forespørsel ignorert"}. {"Requested role","Forespurt rolle"}. {"Resources","Ressurser"}. {"Restart Service","Omstart av tjeneste"}. -{"Restart","Start på ny"}. {"Restore Backup from File at ","Gjenopprett fra sikkerhetskopifil på "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Gjenopprett binær sikkerhetskopi etter neste ejabberd-omstart (krever mindre minne):"}. {"Restore binary backup immediately:","Gjenopprett binær sikkerhetskopi umiddelbart:"}. @@ -276,10 +241,8 @@ {"Roster size","Kontaktlistestørrelse"}. {"Running Nodes","Kjørende noder"}. {"Saturday","lørdag"}. -{"Script check","Skript-sjekk"}. {"Search Results for ","Søkeresultater for "}. {"Search users in ","Søk etter brukere i "}. -{"Select All","Velg alt"}. {"Send announcement to all online users on all hosts","Send kunngjøring til alle tilkoblede brukere på alle verter"}. {"Send announcement to all online users","Send kunngjøring alle tilkoblede brukere"}. {"Send announcement to all users on all hosts","Send kunngjøring til alle brukere på alle verter"}. @@ -293,19 +256,13 @@ {"Specify the access model","Spesifiser tilgangsmodellen"}. {"Specify the event message type","Spesifiser hendelsesbeskjedtypen"}. {"Specify the publisher model","Angi publiseringsmodell"}. -{"Statistics of ~p","Statistikk for ~p"}. -{"Statistics","Statistikk"}. {"Stopped Nodes","Stoppede noder"}. -{"Stop","Stopp"}. -{"Storage Type","Lagringstype"}. {"Store binary backup:","Lagre binær sikkerhetskopi:"}. {"Store plain text backup:","Lagre klartekst-sikkerhetskopi:"}. {"Subject","Emne"}. -{"Submit","Send"}. {"Submitted","Innsendt"}. {"Subscriber Address","Abonnementsadresse"}. {"Subscribers may publish","Abonnenter kan publisere"}. -{"Subscription","Abonnement"}. {"Subscriptions are not allowed","Abonnementer tillates ikke"}. {"Sunday","søndag"}. {"Text associated with a picture","Tekst tilknyttet et bilde"}. @@ -334,17 +291,11 @@ {"This room is not anonymous","Dette rommet er ikke anonymt"}. {"Thursday","torsdag"}. {"Time delay","Tidsforsinkelse"}. -{"Time","Tid"}. {"To register, visit ~s","Besøk ~s for registrering"}. {"Too many CAPTCHA requests","For mange CAPTCHA-forespørsler"}. {"Too many elements","For mange -elementer"}. {"Too many elements","For mange -elementer"}. -{"To","Til"}. {"Traffic rate limit is exceeded","Grense for tillatt trafikkmengde overskredet"}. -{"Transactions Aborted:","Avbrutte transaksjoner:"}. -{"Transactions Committed:","Sendte transaksjoner:"}. -{"Transactions Logged:","Loggede transaksjoner:"}. -{"Transactions Restarted:","Omstartede transaksjoner:"}. {"Tuesday","tirsdag"}. {"Unable to generate a CAPTCHA","Kunne ikke generere CAPTCHA"}. {"Unauthorized","Uautorisert"}. @@ -353,11 +304,6 @@ {"Unsupported version","Ustøttet versjon"}. {"Update message of the day (don't send)","Oppdater melding for dagen (ikke send)"}. {"Update message of the day on all hosts (don't send)","Oppdater melding for dagen på alle verter (ikke send)"}. -{"Update plan","Oppdateringplan"}. -{"Update ~p","Oppdater ~p"}. -{"Update script","Oppdateringsskript"}. -{"Update","Oppdater"}. -{"Uptime:","Oppetid:"}. {"User already exists","Brukeren finnes allerede"}. {"User Management","Brukerhåndtering"}. {"User removed","Fjernet bruker"}. @@ -367,9 +313,7 @@ {"Users are not allowed to register accounts so quickly","Brukere har ikke lov til registrere kontoer så fort"}. {"Users Last Activity","Brukers siste aktivitet"}. {"Users","Brukere"}. -{"Validate","Bekrefte gyldighet"}. {"vCard User Search","vCard-brukersøk"}. -{"View Queue","Vis kø"}. {"Virtual Hosts","Virtuelle maskiner"}. {"Visitor","Besøker"}. {"Visitors are not allowed to change their nicknames in this room","Besøkende får ikke lov å endre kallenavn i dette rommet"}. diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index e3570f41b..76275ac63 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","A conta não existe"}. {"Action on user","Ação no usuário"}. {"Add a hat to a user","Adiciona um chapéu num usuário"}. -{"Add Jabber ID","Adicionar ID jabber"}. -{"Add New","Adicionar novo"}. {"Add User","Adicionar usuário"}. {"Administration of ","Administração de "}. {"Administration","Administração"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente, a sua conta não tem direitos de administração neste servidor. Verifique como conceder os direitos administrativos em: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","A sala de conferência não existe"}. {"Configuration of room ~s","Configuração para ~s"}. {"Configuration","Configuração"}. -{"Connected Resources:","Recursos conectados:"}. {"Contact Addresses (normally, room owner or owners)","Endereços de contato (normalmente, o proprietário ou os proprietários da sala)"}. -{"Contrib Modules","Módulos contrib"}. {"Country","País"}. -{"CPU Time:","Tempo da CPU:"}. {"Current Discussion Topic","Assunto em discussão"}. {"Database failure","Falha no banco de dados"}. -{"Database Tables at ~p","Tabelas da Base de dados em ~p"}. {"Database Tables Configuration at ","Configuração de Tabelas de Base de dados em "}. {"Database","Base de dados"}. {"December","Dezembro"}. {"Default users as participants","Usuários padrões como participantes"}. -{"Delete content","Excluir o conteúdo"}. {"Delete message of the day on all hosts","Apagar a mensagem do dia em todos os hosts"}. {"Delete message of the day","Apagar mensagem do dia"}. -{"Delete Selected","Remover os selecionados"}. -{"Delete table","Excluir a tabela"}. {"Delete User","Deletar Usuário"}. {"Deliver event notifications","Entregar as notificações de evento"}. {"Deliver payloads with event notifications","Enviar payloads junto com as notificações de eventos"}. -{"Description:","Descrição:"}. {"Disc only copy","Somente cópia em disco"}. -{"'Displayed groups' not added (they do not exist!): ","Os 'Grupos exibidos' não foi adicionado (eles não existem!): "}. -{"Displayed:","Exibido:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Não revele a sua senha para ninguém, nem mesmo para o administrador deste servidor XMPP."}. {"Dump Backup to Text File at ","Exportar backup para texto em "}. {"Dump to Text File","Exportar para arquivo texto"}. @@ -132,7 +119,6 @@ {"ejabberd vCard module","Módulo vCard para ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"ejabberd","ejabberd"}. -{"Elements","Elementos"}. {"Email Address","Endereço de e-mail"}. {"Email","Email"}. {"Enable hats","Ativa chapéus"}. @@ -147,7 +133,6 @@ {"Enter path to text file","Introduza caminho para o arquivo texto"}. {"Enter the text you see","Insira o texto que você vê"}. {"Erlang XMPP Server","Servidor XMPP Erlang"}. -{"Error","Erro"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluir IDs Jabber de serem submetidos ao CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas as tabelas como SQL para um arquivo:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar todos os dados de todos os usuários no servidor, para arquivos formato PIEFXIS (XEP-0227):"}. @@ -166,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Preencha campos para procurar por quaisquer usuários XMPP"}. {"Friday","Sexta"}. {"From ~ts","De ~s"}. -{"From","De"}. {"Full List of Room Admins","Lista completa dos administradores das salas"}. {"Full List of Room Owners","Lista completa dos proprietários das salas"}. {"Full Name","Nome completo"}. @@ -176,13 +160,9 @@ {"Get Number of Registered Users","Obter Número de Usuários Registrados"}. {"Get Pending","Obter os pendentes"}. {"Get User Last Login Time","Obter a Data do Último Login"}. -{"Get User Password","Obter Senha do Usuário"}. {"Get User Statistics","Obter Estatísticas do Usuário"}. {"Given Name","Prenome"}. {"Grant voice to this person?","Dar voz a esta pessoa?"}. -{"Group","Grupo"}. -{"Groups that will be displayed to the members","Os grupos que serão exibidos para os membros"}. -{"Groups","Grupos"}. {"has been banned","foi banido"}. {"has been kicked because of a system shutdown","foi desconectado porque o sistema foi desligado"}. {"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. @@ -192,7 +172,6 @@ {"Hat URI","URI do chapéu"}. {"Hats limit exceeded","O limite dos chapéus foi excedido"}. {"Host unknown","Máquina desconhecida"}. -{"Host","Máquina"}. {"HTTP File Upload","Upload de arquivo HTTP"}. {"Idle connection","Conexão inativa"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se você não conseguir ver o CAPTCHA aqui, visite a web page."}. @@ -206,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importar usuários de arquivos jabberd14 (spool files)"}. {"Improper domain part of 'from' attribute","Atributo 'from' contém domínio incorreto"}. {"Improper message type","Tipo de mensagem incorreto"}. -{"Incoming s2s Connections:","Conexões s2s de Entrada:"}. {"Incorrect CAPTCHA submit","CAPTCHA submetido incorretamente"}. {"Incorrect data form","Formulário dos dados incorreto"}. {"Incorrect password","Senha incorreta"}. @@ -236,7 +214,6 @@ {"July","Julho"}. {"June","Junho"}. {"Just created","Acabou de ser criado"}. -{"Label:","Rótulo:"}. {"Last Activity","Última atividade"}. {"Last login","Último login"}. {"Last message","Última mensagem"}. @@ -244,11 +221,9 @@ {"Last year","Último ano"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Bits menos significativos do hash sha-256 do texto devem ser iguais ao rótulo hexadecimal"}. {"leaves the room","Sair da sala"}. -{"List of rooms","Lista de salas"}. {"List of users with hats","Lista dos usuários com chapéus"}. {"List users with hats","Lista os usuários com chapéus"}. {"Logging","Registrando no log"}. -{"Low level update script","Script de atualização low level"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a senha da sala"}. {"Make room members-only","Tornar sala apenas para membros"}. @@ -266,11 +241,8 @@ {"Maximum number of items to persist","Quantidade máxima dos itens para manter"}. {"Maximum Number of Occupants","Número máximo de participantes"}. {"May","Maio"}. -{"Members not added (inexistent vhost!): ","Membros que não foram adicionados (o vhost não existe!): "}. {"Membership is required to enter this room","É necessário ser membro desta sala para poder entrar"}. -{"Members:","Membros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorize a sua senha ou anote-a em um papel guardado em um local seguro. No XMPP, não há uma maneira automatizada de recuperar a sua senha caso a esqueça."}. -{"Memory","Memória"}. {"Mere Availability in XMPP (No Show Value)","Mera disponibilidade no XMPP (Sem valor para ser exibido)"}. {"Message body","Corpo da mensagem"}. {"Message not found in forwarded payload","Mensagem não encontrada em conteúdo encaminhado"}. @@ -282,15 +254,12 @@ {"Moderator privileges required","Se necessita privilégios de moderador"}. {"Moderator","Moderador"}. {"Moderators Only","Somente moderadores"}. -{"Modified modules","Módulos atualizados"}. {"Module failed to handle the query","Módulo falhou ao processar a consulta"}. {"Monday","Segunda"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","Vários elementos não são permitidos pela RFC6121"}. {"Multi-User Chat","Chat multi-usuário"}. -{"Name in the rosters where this group will be displayed","O nome nas listas onde este grupo será exibido"}. {"Name","Nome"}. -{"Name:","Nome:"}. {"Natural Language for Room Discussions","Idioma nativo para as discussões na sala"}. {"Natural-Language Room Name","Nome da sala no idioma nativo"}. {"Neither 'jid' nor 'nick' attribute found","Nem o atributo 'jid' nem 'nick' foram encontrados"}. @@ -355,14 +324,10 @@ {"Occupants are allowed to query others","Os ocupantes estão autorizados a consultar os outros"}. {"Occupants May Change the Subject","As pessoas talvez possam alterar o assunto"}. {"October","Outubro"}. -{"Offline Messages","Mensagens offline"}. -{"Offline Messages:","Mensagens offline:"}. {"OK","OK"}. {"Old Password:","Senha Antiga:"}. {"Online Users","Usuários conectados"}. -{"Online Users:","Usuários online:"}. {"Online","Conectado"}. -{"Only admins can see this","Apenas administradores podem ver isso"}. {"Only collection node owners may associate leaf nodes with the collection","Apenas um grupo dos proprietários dos nós podem associar as páginas na coleção"}. {"Only deliver notifications to available users","Somente enviar notificações aos usuários disponíveis"}. {"Only or tags are allowed","Apenas tags ou são permitidas"}. @@ -382,10 +347,8 @@ {"Organization Unit","Departamento/Unidade"}. {"Other Modules Available:","Outros módulos disponíveis:"}. {"Outgoing s2s Connections","Conexões s2s de Saída"}. -{"Outgoing s2s Connections:","Saída das conexões s2s:"}. {"Owner privileges required","Se requer privilégios de proprietário da sala"}. {"Packet relay is denied by service policy","A retransmissão de pacote é negada por causa da política de serviço"}. -{"Packet","Pacote"}. {"Participant ID","ID do participante"}. {"Participant","Participante"}. {"Password Verification:","Verificação da Senha:"}. @@ -395,7 +358,6 @@ {"Path to Dir","Caminho para o diretório"}. {"Path to File","Caminho do arquivo"}. {"Payload semantic type information","Informações de tipo semântico de carga útil"}. -{"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. {"Persistent","Persistente"}. @@ -429,21 +391,16 @@ {"Receive notification of new nodes only","Receba apenas as notificações dos nós novos"}. {"Recipient is not in the conference room","O receptor não está na sala de conferência"}. {"Register an XMPP account","Registre uma conta XMPP"}. -{"Registered Users:","Usuários registrados:"}. -{"Registered Users","Usuários Registrados"}. {"Register","Registrar"}. {"Remote copy","Cópia remota"}. {"Remove a hat from a user","Remove um chapéu de um usuário"}. -{"Remove All Offline Messages","Remover Todas as Mensagens Offline"}. {"Remove User","Remover usuário"}. -{"Remove","Remover"}. {"Replaced by new connection","Substituído por nova conexão"}. {"Request has timed out","O pedido expirou"}. {"Request is ignored","O pedido foi ignorado"}. {"Requested role","Função solicitada"}. {"Resources","Recursos"}. {"Restart Service","Reiniciar Serviço"}. -{"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaurar backup a partir do arquivo em "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar backup binário após reinicialização do ejabberd (requer menos memória):"}. {"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. @@ -459,20 +416,15 @@ {"Room terminates","Terminação da sala"}. {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Listar grupos autorizados"}. -{"Roster of ~ts","Lista de ~ts"}. {"Roster size","Tamanho da Lista"}. -{"Roster:","Lista:"}. -{"RPC Call Error","Erro de chamada RPC"}. {"Running Nodes","Nós em execução"}. {"~s invites you to the room ~s","~s convidaram você para a sala ~s"}. {"Saturday","Sábado"}. -{"Script check","Verificação de Script"}. {"Search from the date","Pesquise a partir da data"}. {"Search Results for ","Resultados de pesquisa para "}. {"Search the text","Pesquise o texto"}. {"Search until the date","Pesquise até a data"}. {"Search users in ","Procurar usuários em "}. -{"Select All","Selecione tudo"}. {"Send announcement to all online users on all hosts","Enviar anúncio a todos usuários online em todas as máquinas"}. {"Send announcement to all online users","Enviar anúncio a todos os usuárions online"}. {"Send announcement to all users on all hosts","Enviar aviso para todos os usuários em todos os hosts"}. @@ -496,23 +448,17 @@ {"Stanza id is not valid","A Stanza ID não é válido"}. {"Stanza ID","ID da estrofe"}. {"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. -{"Statistics of ~p","Estatísticas de ~p"}. -{"Statistics","Estatísticas"}. -{"Stop","Parar"}. {"Stopped Nodes","Nós parados"}. -{"Storage Type","Tipo de armazenamento"}. {"Store binary backup:","Armazenar backup binário:"}. {"Store plain text backup:","Armazenar backup em texto:"}. {"Stream management is already enabled","A gestão do fluxo já está ativada"}. {"Stream management is not enabled","O gerenciamento do fluxo não está ativado"}. {"Subject","Assunto"}. -{"Submit","Enviar"}. {"Submitted","Submetido"}. {"Subscriber Address","Endereço dos Assinantes"}. {"Subscribers may publish","Os assinantes podem publicar"}. {"Subscription requests must be approved and only subscribers may retrieve items","Os pedidos de assinatura devem ser aprovados e apenas os assinantes podem recuperar os itens"}. {"Subscriptions are not allowed","Subscrições não estão permitidas"}. -{"Subscription","Subscrição"}. {"Sunday","Domingo"}. {"Text associated with a picture","Um texto associado a uma imagem"}. {"Text associated with a sound","Um texto associado a um som"}. @@ -575,7 +521,6 @@ {"Thursday","Quinta"}. {"Time delay","Intervalo (Tempo)"}. {"Timed out waiting for stream resumption","Tempo limite expirou durante à espera da retomada da transmissão"}. -{"Time","Tempo"}. {"To register, visit ~s","Para registrar, visite ~s"}. {"To ~ts","Para ~s"}. {"Token TTL","Token TTL"}. @@ -588,13 +533,7 @@ {"Too many receiver fields were specified","Foram definidos receptores demais nos campos"}. {"Too many unacked stanzas","Número excessivo de instâncias sem confirmação"}. {"Too many users in this conference","Há uma quantidade excessiva de usuários nesta conferência"}. -{"To","Para"}. -{"Total rooms","Salas no total"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. -{"Transactions Aborted:","Transações abortadas:"}. -{"Transactions Committed:","Transações salvas:"}. -{"Transactions Logged:","Transações de log:"}. -{"Transactions Restarted:","Transações reiniciadas:"}. {"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. @@ -605,19 +544,13 @@ {"Uninstall","Desinstalar"}. {"Unregister an XMPP account","Excluir uma conta XMPP"}. {"Unregister","Deletar registro"}. -{"Unselect All","Desmarcar todos"}. {"Unsupported element","Elemento não suportado"}. {"Unsupported version","Versão sem suporte"}. {"Update message of the day (don't send)","Atualizar mensagem do dia (não enviar)"}. {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. -{"Update ~p","Atualizar ~p"}. -{"Update plan","Plano de Atualização"}. -{"Update script","Script de atualização"}. {"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. {"Update Specs","Atualizar as especificações"}. -{"Update","Atualizar"}. {"Upgrade","Atualização"}. -{"Uptime:","Tempo de atividade:"}. {"URL for Archived Discussion Logs","A URL para o arquivamento dos registros da discussão"}. {"User already exists","Usuário já existe"}. {"User (jid)","Usuário (jid)"}. @@ -632,7 +565,6 @@ {"Users Last Activity","Últimas atividades dos usuários"}. {"Users","Usuários"}. {"User","Usuário"}. -{"Validate","Validar"}. {"Value 'get' of 'type' attribute is not allowed","Valor 'get' não permitido para atributo 'type'"}. {"Value of '~s' should be boolean","Value de '~s' deveria ser um booleano"}. {"Value of '~s' should be datetime string","Valor de '~s' deveria ser data e hora"}. @@ -640,8 +572,6 @@ {"Value 'set' of 'type' attribute is not allowed","Valor 'set' não permitido para atributo 'type'"}. {"vCard User Search","Busca de Usuário vCard"}. {"View joined MIX channels","Exibir os canais MIX aderidos"}. -{"View Queue","Exibir a fila"}. -{"View Roster","Ver a lista"}. {"Virtual Hosts","Hosts virtuais"}. {"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não podem mudar seus apelidos"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens a todos os ocupantes"}. diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index 8d247022b..4cc105458 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","A conta não existe"}. {"Action on user","Acção no utilizador"}. {"Add a hat to a user","Adiciona um chapéu num utilizador"}. -{"Add Jabber ID","Adicionar ID jabber"}. -{"Add New","Adicionar novo"}. {"Add User","Adicionar utilizador"}. {"Administration of ","Administração de "}. {"Administration","Administração"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Aparentemente, a sua conta não tem direitos de administração neste servidor. Verifique como conceder os direitos administrativos em: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Abril"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","A sala não existe"}. {"Configuration of room ~s","Configuração para ~s"}. {"Configuration","Configuração"}. -{"Connected Resources:","Recursos conectados:"}. {"Contact Addresses (normally, room owner or owners)","Endereços de contato (normalmente, o proprietário ou os proprietários da sala)"}. -{"Contrib Modules","Módulos contrib"}. {"Country","País"}. -{"CPU Time:","Tempo da CPU:"}. {"Current Discussion Topic","Assunto em discussão"}. {"Database failure","Falha no banco de dados"}. -{"Database Tables at ~p","Tabelas da Base de dados em ~p"}. {"Database Tables Configuration at ","Configuração de Tabelas de Base de dados em "}. {"Database","Base de dados"}. {"December","Dezembro"}. {"Default users as participants","Utilizadores padrões como participantes"}. -{"Delete content","Apagar o conteúdo"}. {"Delete message of the day on all hosts","Apagar a mensagem do dia em todos os hosts"}. {"Delete message of the day","Apagar mensagem do dia"}. -{"Delete Selected","Eliminar os seleccionados"}. -{"Delete table","Apagar a tabela"}. {"Delete User","Deletar Utilizador"}. {"Deliver event notifications","Entregar as notificações de evento"}. {"Deliver payloads with event notifications","Enviar payloads junto com as notificações de eventos"}. -{"Description:","Descrição:"}. {"Disc only copy","Cópia apenas em disco"}. -{"'Displayed groups' not added (they do not exist!): ","Os 'Grupos exibidos' não foi adicionado (eles não existem!): "}. -{"Displayed:","Exibido:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Não revele a sua palavra-passe a ninguém, nem mesmo para o administrador deste servidor XMPP."}. {"Dump Backup to Text File at ","Exporta cópia de segurança para ficheiro de texto em "}. {"Dump to Text File","Exportar para ficheiro de texto"}. @@ -132,7 +119,6 @@ {"ejabberd vCard module","Módulo vCard de ejabberd"}. {"ejabberd Web Admin","ejabberd Web Admin"}. {"ejabberd","ejabberd"}. -{"Elements","Elementos"}. {"Email Address","Endereço de e-mail"}. {"Email","Email"}. {"Enable hats","Ativa chapéus"}. @@ -147,7 +133,6 @@ {"Enter path to text file","Introduza caminho para o ficheiro de texto"}. {"Enter the text you see","Insira o texto que vê"}. {"Erlang XMPP Server","Servidor XMPP Erlang"}. -{"Error","Erro"}. {"Exclude Jabber IDs from CAPTCHA challenge","Excluir IDs Jabber de serem submetidos ao CAPTCHA"}. {"Export all tables as SQL queries to a file:","Exportar todas as tabelas como SQL para um ficheiro:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportar todos os dados de todos os utilizadores no servidor, para ficheiros de formato PIEFXIS (XEP-0227):"}. @@ -166,7 +151,6 @@ {"Fill in the form to search for any matching XMPP User","Preencha campos para procurar por quaisquer utilizadores XMPP"}. {"Friday","Sexta"}. {"From ~ts","De ~s"}. -{"From","De"}. {"Full List of Room Admins","Lista completa dos administradores das salas"}. {"Full List of Room Owners","Lista completa dos proprietários das salas"}. {"Full Name","Nome completo"}. @@ -176,13 +160,9 @@ {"Get Number of Registered Users","Obter quantidade de utilizadores registados"}. {"Get Pending","Obter os pendentes"}. {"Get User Last Login Time","Obter a data do último login"}. -{"Get User Password","Obter palavra-passe do utilizador"}. {"Get User Statistics","Obter estatísticas do utilizador"}. {"Given Name","Sobrenome"}. {"Grant voice to this person?","Dar voz a esta pessoa?"}. -{"Group","Grupo"}. -{"Groups that will be displayed to the members","Os grupos que serão exibidos para os membros"}. -{"Groups","Grupos"}. {"has been banned","foi banido"}. {"has been kicked because of a system shutdown","foi desconectado porque o sistema foi desligado"}. {"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. @@ -192,7 +172,6 @@ {"Hat URI","URI do chapéu"}. {"Hats limit exceeded","O limite dos chapéus foi excedido"}. {"Host unknown","Máquina desconhecida"}. -{"Host","Máquina"}. {"HTTP File Upload","Upload de ficheiros por HTTP"}. {"Idle connection","Conexão inativa"}. {"If you don't see the CAPTCHA image here, visit the web page.","Se não conseguir ver o CAPTCHA aqui, visite a web page."}. @@ -206,7 +185,6 @@ {"Import Users From jabberd14 Spool Files","Importar utilizadores de ficheiros de jabberd14 (spool files)"}. {"Improper domain part of 'from' attribute","Atributo 'from' contém domínio incorreto"}. {"Improper message type","Tipo de mensagem incorrecto"}. -{"Incoming s2s Connections:","Conexões s2s de Entrada:"}. {"Incorrect CAPTCHA submit","CAPTCHA submetido incorretamente"}. {"Incorrect data form","Formulário dos dados incorreto"}. {"Incorrect password","Palavra-chave incorrecta"}. @@ -236,7 +214,6 @@ {"July","Julho"}. {"June","Junho"}. {"Just created","Acabou de ser criado"}. -{"Label:","Rótulo:"}. {"Last Activity","Última actividade"}. {"Last login","Último login"}. {"Last message","Última mensagem"}. @@ -244,11 +221,9 @@ {"Last year","Último ano"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Bits menos significativos do hash sha-256 do texto devem ser iguais ao rótulo hexadecimal"}. {"leaves the room","Sair da sala"}. -{"List of rooms","Lista de salas"}. {"List of users with hats","Lista os utilizadores com chapéus"}. {"List users with hats","Lista os utilizadores com chapéus"}. {"Logging","Registando no log"}. -{"Low level update script","Script de atualização low level"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a palavra-passe da sala"}. {"Make room members-only","Tornar sala apenas para membros"}. @@ -266,11 +241,8 @@ {"Maximum number of items to persist","Quantidade máxima dos itens para manter"}. {"Maximum Number of Occupants","Quantidate máxima de participantes"}. {"May","Maio"}. -{"Members not added (inexistent vhost!): ","Membros que não foram adicionados (o vhost não existe!): "}. {"Membership is required to enter this room","É necessário ser membro desta sala para poder entrar"}. -{"Members:","Membros:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Memorize a sua palavra-passe ou anote-a num papel guardado num local seguro. No XMPP, não há uma maneira automatizada de recuperar a sua palavra-passe caso a esqueça."}. -{"Memory","Memória"}. {"Mere Availability in XMPP (No Show Value)","Mera disponibilidade no XMPP (Sem valor para ser exibido)"}. {"Message body","Corpo da mensagem"}. {"Message not found in forwarded payload","Mensagem não encontrada em conteúdo encaminhado"}. @@ -282,15 +254,12 @@ {"Moderator privileges required","São necessários privilégios de moderador"}. {"Moderator","Moderador"}. {"Moderators Only","Somente moderadores"}. -{"Modified modules","Módulos atualizados"}. {"Module failed to handle the query","Módulo falhou ao processar a consulta"}. {"Monday","Segunda"}. {"Multicast","Multicast"}. {"Multiple elements are not allowed by RFC6121","Vários elementos não são permitidos pela RFC6121"}. {"Multi-User Chat","Chat multi-utilizador"}. -{"Name in the rosters where this group will be displayed","O nome nas listas onde este grupo será exibido"}. {"Name","Nome"}. -{"Name:","Nome:"}. {"Natural Language for Room Discussions","Idioma nativo para as discussões na sala"}. {"Natural-Language Room Name","Nome da sala no idioma nativo"}. {"Neither 'jid' nor 'nick' attribute found","Nem o atributo 'jid' nem 'nick' foram encontrados"}. @@ -355,14 +324,10 @@ {"Occupants are allowed to query others","Os ocupantes estão autorizados a consultar os outros"}. {"Occupants May Change the Subject","As pessoas talvez possam alterar o assunto"}. {"October","Outubro"}. -{"Offline Messages","Mensagens offline"}. -{"Offline Messages:","Mensagens offline:"}. {"OK","OK"}. {"Old Password:","Palavra-passe Antiga:"}. {"Online Users","Utilizadores ligados"}. -{"Online Users:","Utilizadores online:"}. {"Online","Ligado"}. -{"Only admins can see this","Apenas administradores podem ver isso"}. {"Only collection node owners may associate leaf nodes with the collection","Apenas um grupo dos proprietários dos nós podem associar as páginas na coleção"}. {"Only deliver notifications to available users","Somente enviar notificações aos utilizadores disponíveis"}. {"Only or tags are allowed","Apenas tags ou são permitidas"}. @@ -382,10 +347,8 @@ {"Organization Unit","Unidade da organização"}. {"Other Modules Available:","Outros módulos disponíveis:"}. {"Outgoing s2s Connections","Conexões s2s de Saída"}. -{"Outgoing s2s Connections:","Saída das conexões s2s:"}. {"Owner privileges required","São necessários privilégios de dono"}. {"Packet relay is denied by service policy","A retransmissão de pacote é negada por causa da política de serviço"}. -{"Packet","Pacote"}. {"Participant ID","ID do participante"}. {"Participant","Participante"}. {"Password Verification:","Verificação da Palavra-passe:"}. @@ -395,7 +358,6 @@ {"Path to Dir","Caminho para o directório"}. {"Path to File","Caminho do ficheiro"}. {"Payload semantic type information","Informações de tipo semântico de carga útil"}. -{"Pending","Pendente"}. {"Period: ","Período: "}. {"Persist items to storage","Persistir elementos ao armazenar"}. {"Persistent","Persistente"}. @@ -429,21 +391,16 @@ {"Receive notification of new nodes only","Receba apenas as notificações dos nós novos"}. {"Recipient is not in the conference room","O destinatário não está na sala"}. {"Register an XMPP account","Registe uma conta XMPP"}. -{"Registered Users","Utilizadores registados"}. -{"Registered Users:","Utilizadores registados:"}. {"Register","Registar"}. {"Remote copy","Cópia remota"}. {"Remove a hat from a user","Remove um chapéu de um utilizador"}. -{"Remove All Offline Messages","Remover Todas as Mensagens Offline"}. {"Remove User","Eliminar utilizador"}. -{"Remove","Remover"}. {"Replaced by new connection","Substituído por nova conexão"}. {"Request has timed out","O pedido expirou"}. {"Request is ignored","O pedido foi ignorado"}. {"Requested role","Função solicitada"}. {"Resources","Recursos"}. {"Restart Service","Reiniciar Serviço"}. -{"Restart","Reiniciar"}. {"Restore Backup from File at ","Restaura cópia de segurança a partir do ficheiro em "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Restaurar backup binário após reinicialização do ejabberd (requer menos memória):"}. {"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. @@ -459,20 +416,15 @@ {"Room terminates","Terminação da sala"}. {"Room title","Título da sala"}. {"Roster groups allowed to subscribe","Listar grupos autorizados"}. -{"Roster of ~ts","Lista de ~ts"}. {"Roster size","Tamanho da Lista"}. -{"Roster:","Lista:"}. -{"RPC Call Error","Erro de chamada RPC"}. {"Running Nodes","Nodos a correr"}. {"~s invites you to the room ~s","~s convidaram-o à sala ~s"}. {"Saturday","Sábado"}. -{"Script check","Verificação de Script"}. {"Search from the date","Pesquise a partir da data"}. {"Search Results for ","Resultados de pesquisa para "}. {"Search the text","Pesquise o texto"}. {"Search until the date","Pesquise até a data"}. {"Search users in ","Procurar utilizadores em "}. -{"Select All","Selecione tudo"}. {"Send announcement to all online users on all hosts","Enviar anúncio a todos utilizadores online em todas as máquinas"}. {"Send announcement to all online users","Enviar anúncio a todos os utilizadorns online"}. {"Send announcement to all users on all hosts","Enviar aviso para todos os utilizadores em todos os hosts"}. @@ -496,23 +448,17 @@ {"Stanza id is not valid","A Stanza ID é inválido"}. {"Stanza ID","ID da estrofe"}. {"Statically specify a replyto of the node owner(s)","Defina uma resposta fixa do(s) proprietário(s) do nó"}. -{"Statistics of ~p","Estatísticas de ~p"}. -{"Statistics","Estatísticas"}. -{"Stop","Parar"}. {"Stopped Nodes","Nodos parados"}. -{"Storage Type","Tipo de armazenagem"}. {"Store binary backup:","Armazenar backup binário:"}. {"Store plain text backup:","Armazenar backup em texto:"}. {"Stream management is already enabled","A gestão do fluxo já está ativada"}. {"Stream management is not enabled","A gestão do fluxo não está ativada"}. {"Subject","Assunto"}. -{"Submit","Enviar"}. {"Submitted","Submetido"}. {"Subscriber Address","Endereço dos Assinantes"}. {"Subscribers may publish","Os assinantes podem publicar"}. {"Subscription requests must be approved and only subscribers may retrieve items","Os pedidos de assinatura devem ser aprovados e apenas os assinantes podem recuperar os itens"}. {"Subscriptions are not allowed","Subscrições não estão permitidas"}. -{"Subscription","Subscrição"}. {"Sunday","Domingo"}. {"Text associated with a picture","Um texto associado a uma imagem"}. {"Text associated with a sound","Um texto associado a um som"}. @@ -575,7 +521,6 @@ {"Thursday","Quinta"}. {"Time delay","Intervalo (Tempo)"}. {"Timed out waiting for stream resumption","Tempo limite expirou durante à espera da retomada da transmissão"}. -{"Time","Data"}. {"To register, visit ~s","Para registar, visite ~s"}. {"To ~ts","Para ~s"}. {"Token TTL","Token TTL"}. @@ -588,13 +533,7 @@ {"Too many receiver fields were specified","Foram definidos receptores demais nos campos"}. {"Too many unacked stanzas","Quantidade excessiva de instâncias sem confirmação"}. {"Too many users in this conference","Há uma quantidade excessiva de utilizadores nesta conferência"}. -{"To","Para"}. -{"Total rooms","Salas no total"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. -{"Transactions Aborted:","Transações abortadas:"}. -{"Transactions Committed:","Transações salvas:"}. -{"Transactions Logged:","Transações de log:"}. -{"Transactions Restarted:","Transações reiniciadas:"}. {"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. @@ -605,19 +544,13 @@ {"Uninstall","Desinstalar"}. {"Unregister an XMPP account","Excluir uma conta XMPP"}. {"Unregister","Deletar registo"}. -{"Unselect All","Desmarcar todos"}. {"Unsupported element","Elemento não suportado"}. {"Unsupported version","Versão sem suporte"}. {"Update message of the day (don't send)","Atualizar mensagem do dia (não enviar)"}. {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. -{"Update ~p","Atualizar ~p"}. -{"Update plan","Plano de atualização"}. -{"Update script","Script de atualização"}. {"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. {"Update Specs","Atualizar as especificações"}. -{"Update","Actualizar"}. {"Upgrade","Atualização"}. -{"Uptime:","Tempo de atividade:"}. {"URL for Archived Discussion Logs","A URL para o arquivamento dos registos da discussão"}. {"User already exists","Utilizador já existe"}. {"User (jid)","Utilizador (jid)"}. @@ -632,7 +565,6 @@ {"Users Last Activity","Últimas atividades dos utilizadores"}. {"Users","Utilizadores"}. {"User","Utilizador"}. -{"Validate","Validar"}. {"Value 'get' of 'type' attribute is not allowed","Valor 'get' não permitido para atributo 'type'"}. {"Value of '~s' should be boolean","Value de '~s' deveria ser um booleano"}. {"Value of '~s' should be datetime string","Valor de '~s' deveria ser data e hora"}. @@ -640,8 +572,6 @@ {"Value 'set' of 'type' attribute is not allowed","Valor 'set' não permitido para atributo 'type'"}. {"vCard User Search","Busca de Utilizador vCard"}. {"View joined MIX channels","Exibir os canais MIX aderidos"}. -{"View Queue","Exibir a fila"}. -{"View Roster","Ver a lista"}. {"Virtual Hosts","Hosts virtuais"}. {"Visitors are not allowed to change their nicknames in this room","Nesta sala, os visitantes não podem mudar os apelidos deles"}. {"Visitors are not allowed to send messages to all occupants","Os visitantes não podem enviar mensagens para todos os ocupantes"}. diff --git a/priv/msgs/ru.msg b/priv/msgs/ru.msg index d2e5e023c..ca23d2167 100644 --- a/priv/msgs/ru.msg +++ b/priv/msgs/ru.msg @@ -12,8 +12,6 @@ {"Access denied by service policy","Доступ запрещён политикой службы"}. {"Account doesn't exist","Учётная запись не существует"}. {"Action on user","Действие над пользователем"}. -{"Add Jabber ID","Добавить Jabber ID"}. -{"Add New","Добавить"}. {"Add User","Добавить пользователя"}. {"Administration of ","Администрирование "}. {"Administration","Администрирование"}. @@ -72,24 +70,17 @@ {"Conference room does not exist","Конференция не существует"}. {"Configuration of room ~s","Конфигурация комнаты ~s"}. {"Configuration","Конфигурация"}. -{"Connected Resources:","Подключённые ресурсы:"}. {"Country","Страна"}. -{"CPU Time:","Процессорное время:"}. {"Database failure","Ошибка базы данных"}. -{"Database Tables at ~p","Таблицы базы данных на ~p"}. {"Database Tables Configuration at ","Конфигурация таблиц базы данных на "}. {"Database","База данных"}. {"December","декабря"}. {"Default users as participants","Сделать пользователей участниками по умолчанию"}. -{"Delete content","Удалить содержимое"}. {"Delete message of the day on all hosts","Удалить сообщение дня со всех виртуальных серверов"}. {"Delete message of the day","Удалить сообщение дня"}. -{"Delete Selected","Удалить выделенные"}. -{"Delete table","Удалить таблицу"}. {"Delete User","Удалить пользователя"}. {"Deliver event notifications","Доставлять уведомления о событиях"}. {"Deliver payloads with event notifications","Доставлять вместе с уведомлениями o публикациях сами публикации"}. -{"Description:","Описание:"}. {"Disc only copy","только диск"}. {"Dump Backup to Text File at ","Копирование в текстовый файл на "}. {"Dump to Text File","Копирование в текстовый файл"}. @@ -102,7 +93,6 @@ {"ejabberd vCard module","ejabberd vCard модуль"}. {"ejabberd Web Admin","Web-интерфейс администрирования ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Элементы"}. {"Email","Электронная почта"}. {"Enable logging","Включить журналирование"}. {"Enable message archiving","Включить хранение сообщений"}. @@ -114,7 +104,6 @@ {"Enter path to jabberd14 spool file","Введите путь к файлу из спула jabberd14"}. {"Enter path to text file","Введите путь к текстовому файлу"}. {"Enter the text you see","Введите увиденный текст"}. -{"Error","Ошибка"}. {"Exclude Jabber IDs from CAPTCHA challenge","Исключить показ капчи для списка Jabber ID"}. {"Export all tables as SQL queries to a file:","Экспортировать все таблицы в виде SQL запросов в файл:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Экспорт данных всех пользователей сервера в файлы формата PIEFXIS (XEP-0227):"}. @@ -130,25 +119,20 @@ {"February","февраля"}. {"File larger than ~w bytes","Файл больше ~w байт"}. {"Friday","Пятница"}. -{"From","От кого"}. {"Full Name","Полное имя"}. {"Get Number of Online Users","Получить количество подключённых пользователей"}. {"Get Number of Registered Users","Получить количество зарегистрированных пользователей"}. {"Get Pending","Получить отложенные"}. {"Get User Last Login Time","Получить время последнего подключения пользователя"}. -{"Get User Password","Получить пароль пользователя"}. {"Get User Statistics","Получить статистику по пользователю"}. {"Given Name","Имя"}. {"Grant voice to this person?","Предоставить голос?"}. -{"Groups","Группы"}. -{"Group","Группа"}. {"has been banned","запретили входить в комнату"}. {"has been kicked because of a system shutdown","выгнали из комнаты из-за останова системы"}. {"has been kicked because of an affiliation change","выгнали из комнаты вследствие смены ранга"}. {"has been kicked because the room has been changed to members-only","выгнали из комнаты потому что она стала только для членов"}. {"has been kicked","выгнали из комнаты"}. {"Host unknown","Неизвестное имя сервера"}. -{"Host","Хост"}. {"HTTP File Upload","Передача файлов по HTTP"}. {"Idle connection","Неиспользуемое соединение"}. {"If you don't see the CAPTCHA image here, visit the web page.","Если вы не видите изображение капчи, перейдите по ссылке."}. @@ -162,7 +146,6 @@ {"Import Users From jabberd14 Spool Files","Импорт пользователей из спула jabberd14"}. {"Improper domain part of 'from' attribute","Неправильная доменная часть атрибута 'from'"}. {"Improper message type","Неправильный тип сообщения"}. -{"Incoming s2s Connections:","Входящие s2s соединения:"}. {"Incorrect CAPTCHA submit","Неверный ввод капчи"}. {"Incorrect data form","Некорректная форма данных"}. {"Incorrect password","Неправильный пароль"}. @@ -190,8 +173,6 @@ {"Last month","За последний месяц"}. {"Last year","За последний год"}. {"leaves the room","вышел(а) из комнаты"}. -{"List of rooms","Список комнат"}. -{"Low level update script","Низкоуровневый сценарий обновления"}. {"Make participants list public","Сделать список участников видимым всем"}. {"Make room CAPTCHA protected","Сделать комнату защищённой капчей"}. {"Make room members-only","Комната только для зарегистрированных участников"}. @@ -206,21 +187,17 @@ {"Maximum Number of Occupants","Максимальное количество участников"}. {"May","мая"}. {"Membership is required to enter this room","В эту конференцию могут входить только её члены"}. -{"Members:","Члены:"}. -{"Memory","Память"}. {"Message body","Тело сообщения"}. {"Message not found in forwarded payload","Сообщение не найдено в пересылаемом вложении"}. {"Messages from strangers are rejected","Сообщения от незнакомцев запрещены"}. {"Middle Name","Отчество"}. {"Minimum interval between voice requests (in seconds)","Минимальный интервал между запросами на право голоса"}. {"Moderator privileges required","Требуются права модератора"}. -{"Modified modules","Изменённые модули"}. {"Module failed to handle the query","Ошибка модуля при обработке запроса"}. {"Monday","Понедельник"}. {"Multicast","Мультикаст"}. {"Multi-User Chat","Конференция"}. {"Name","Название"}. -{"Name:","Название:"}. {"Neither 'jid' nor 'nick' attribute found","Не найден атрибут 'jid' или 'nick'"}. {"Neither 'role' nor 'affiliation' attribute found","Не найден атрибут 'role' или 'affiliation'"}. {"Never","Никогда"}. @@ -275,12 +252,9 @@ {"Number of online users","Количество подключённых пользователей"}. {"Number of registered users","Количество зарегистрированных пользователей"}. {"October","октября"}. -{"Offline Messages","Офлайновые сообщения"}. -{"Offline Messages:","Офлайновые сообщения:"}. {"OK","Продолжить"}. {"Old Password:","Старый пароль:"}. {"Online Users","Подключённые пользователи"}. -{"Online Users:","Подключённые пользователи:"}. {"Online","Подключён"}. {"Only deliver notifications to available users","Доставлять уведомления только доступным пользователям"}. {"Only or tags are allowed","Допустимы только тэги или "}. @@ -294,18 +268,15 @@ {"Only service administrators are allowed to send service messages","Только администратор службы может посылать служебные сообщения"}. {"Organization Name","Название организации"}. {"Organization Unit","Отдел организации"}. -{"Outgoing s2s Connections:","Исходящие s2s-серверы:"}. {"Outgoing s2s Connections","Исходящие s2s-соединения"}. {"Owner privileges required","Требуются права владельца"}. {"Packet relay is denied by service policy","Пересылка пакетов запрещена политикой службы"}. -{"Packet","Пакет"}. {"Password Verification","Проверка пароля"}. {"Password Verification:","Проверка пароля:"}. {"Password","Пароль"}. {"Password:","Пароль:"}. {"Path to Dir","Путь к директории"}. {"Path to File","Путь к файлу"}. -{"Pending","Ожидание"}. {"Period: ","Период"}. {"Persist items to storage","Сохранять публикации в хранилище"}. {"Ping query is incorrect","Некорректный пинг-запрос"}. @@ -330,18 +301,13 @@ {"RAM copy","ОЗУ"}. {"Really delete message of the day?","Действительно удалить сообщение дня?"}. {"Recipient is not in the conference room","Адресата нет в конференции"}. -{"Registered Users","Зарегистрированные пользователи"}. -{"Registered Users:","Зарегистрированные пользователи:"}. {"Register","Зарегистрировать"}. {"Remote copy","не хранится локально"}. -{"Remove All Offline Messages","Удалить все офлайновые сообщения"}. {"Remove User","Удалить пользователя"}. -{"Remove","Удалить"}. {"Replaced by new connection","Заменено новым соединением"}. {"Request has timed out","Истекло время ожидания запроса"}. {"Resources","Ресурсы"}. {"Restart Service","Перезапустить службу"}. -{"Restart","Перезапустить"}. {"Restore Backup from File at ","Восстановление из резервной копии на "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Восстановить из бинарной резервной копии при следующем запуске (требует меньше памяти):"}. {"Restore binary backup immediately:","Восстановить из бинарной резервной копии немедленно:"}. @@ -355,13 +321,10 @@ {"Room title","Название комнаты"}. {"Roster groups allowed to subscribe","Группы списка контактов, которым разрешена подписка"}. {"Roster size","Размер списка контактов"}. -{"RPC Call Error","Ошибка вызова RPC"}. {"Running Nodes","Работающие узлы"}. {"Saturday","Суббота"}. -{"Script check","Проверка сценария"}. {"Search Results for ","Результаты поиска в "}. {"Search users in ","Поиск пользователей в "}. -{"Select All","Выбрать всё"}. {"Send announcement to all online users on all hosts","Разослать объявление всем подключённым пользователям на всех виртуальных серверах"}. {"Send announcement to all online users","Разослать объявление всем подключённым пользователям"}. {"Send announcement to all users on all hosts","Разослать объявление всем пользователям на всех виртуальных серверах"}. @@ -379,21 +342,15 @@ {"Specify the access model","Укажите механизм управления доступом"}. {"Specify the event message type","Укажите тип сообщения о событии"}. {"Specify the publisher model","Условия публикации"}. -{"Statistics of ~p","статистика узла ~p"}. -{"Statistics","Статистика"}. {"Stopped Nodes","Остановленные узлы"}. -{"Stop","Остановить"}. -{"Storage Type","Тип таблицы"}. {"Store binary backup:","Сохранить бинарную резервную копию:"}. {"Store plain text backup:","Сохранить текстовую резервную копию:"}. {"Stream management is already enabled","Управление потоком уже активировано"}. {"Stream management is not enabled","Управление потоком не активировано"}. {"Subject","Тема"}. {"Submitted","Отправлено"}. -{"Submit","Отправить"}. {"Subscriber Address","Адрес подписчика"}. {"Subscriptions are not allowed","Подписки недопустимы"}. -{"Subscription","Подписка"}. {"Sunday","Воскресенье"}. {"That nickname is already in use by another occupant","Этот псевдоним уже занят другим участником"}. {"That nickname is registered by another person","Этот псевдоним зарегистрирован кем-то другим"}. @@ -418,7 +375,6 @@ {"Thursday","Четверг"}. {"Time delay","По истечение"}. {"Timed out waiting for stream resumption","Истекло время ожидания возобновления потока"}. -{"Time","Время"}. {"To register, visit ~s","Для регистрации посетите ~s"}. {"Token TTL","Токен TTL"}. {"Too many active bytestreams","Слишком много активных потоков данных"}. @@ -430,13 +386,7 @@ {"Too many receiver fields were specified","Указано слишком много получателей"}. {"Too many unacked stanzas","Слишком много неподтверждённых пакетов"}. {"Too many users in this conference","Слишком много пользователей в этой конференции"}. -{"Total rooms","Все комнаты"}. -{"To","Кому"}. {"Traffic rate limit is exceeded","Превышен лимит скорости посылки информации"}. -{"Transactions Aborted:","Транзакции отмененные:"}. -{"Transactions Committed:","Транзакции завершенные:"}. -{"Transactions Logged:","Транзакции запротоколированные:"}. -{"Transactions Restarted:","Транзакции перезапущенные:"}. {"Tuesday","Вторник"}. {"Unable to generate a CAPTCHA","Не получилось создать капчу"}. {"Unable to register route on existing local domain","Нельзя регистрировать маршруты на существующие локальные домены"}. @@ -444,16 +394,10 @@ {"Unexpected action","Неожиданное действие"}. {"Unexpected error condition: ~p","Неожиданная ошибка: ~p"}. {"Unregister","Удалить"}. -{"Unselect All","Снять всё выделение"}. {"Unsupported element","Элемент не поддерживается"}. {"Unsupported version","Неподдерживаемая версия"}. {"Update message of the day (don't send)","Обновить сообщение дня (не рассылать)"}. {"Update message of the day on all hosts (don't send)","Обновить сообщение дня на всех виртуальных серверах (не рассылать)"}. -{"Update plan","План обновления"}. -{"Update ~p","Обновление ~p"}. -{"Update script","Сценарий обновления"}. -{"Update","Обновить"}. -{"Uptime:","Время работы:"}. {"User already exists","Пользователь уже существует"}. {"User JID","JID пользователя"}. {"User (jid)","Пользователь (XMPP адрес)"}. @@ -466,7 +410,6 @@ {"Users Last Activity","Статистика последнего подключения пользователей"}. {"Users","Пользователи"}. {"User","Пользователь"}. -{"Validate","Утвердить"}. {"Value 'get' of 'type' attribute is not allowed","Значение 'get' атрибута 'type' недопустимо"}. {"Value of '~s' should be boolean","Значение '~s' должно быть булевым"}. {"Value of '~s' should be datetime string","Значение '~s' должно быть датой"}. diff --git a/priv/msgs/sk.msg b/priv/msgs/sk.msg index fedce900f..54f711610 100644 --- a/priv/msgs/sk.msg +++ b/priv/msgs/sk.msg @@ -8,8 +8,6 @@ {"A password is required to enter this room","Pre vstup do miestnosti je potrebné heslo"}. {"Access denied by service policy","Prístup bol zamietnutý nastavením služby"}. {"Action on user","Operácia aplikovaná na užívateľa"}. -{"Add Jabber ID","Pridať Jabber ID"}. -{"Add New","Pridať nový"}. {"Add User","Pridať používateľa"}. {"Administration of ","Administrácia "}. {"Administration","Administrácia"}. @@ -51,20 +49,16 @@ {"Conference room does not exist","Diskusná miestnosť neexistuje"}. {"Configuration of room ~s","Konfigurácia miestnosti ~s"}. {"Configuration","Konfigurácia"}. -{"Connected Resources:","Pripojené zdroje:"}. {"Country","Krajina"}. -{"CPU Time:","Čas procesoru"}. {"Database Tables Configuration at ","Konfigurácia databázových tabuliek "}. {"Database","Databáza"}. {"December","December"}. {"Default users as participants","Užívatelia sú implicitne členmi"}. {"Delete message of the day on all hosts","Zmazať správu dňa na všetkých serveroch"}. {"Delete message of the day","Zmazať správu dňa"}. -{"Delete Selected","Zmazať vybrané"}. {"Delete User","Vymazať užívateľa"}. {"Deliver event notifications","Doručiť oznamy o udalosti"}. {"Deliver payloads with event notifications","Doručiť náklad s upozornením na udalosť"}. -{"Description:","Popis:"}. {"Disc only copy","Len kópia disku"}. {"Dump Backup to Text File at ","Uložiť zálohu do textového súboru na "}. {"Dump to Text File","Uložiť do textového súboru"}. @@ -75,7 +69,6 @@ {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modul"}. {"ejabberd vCard module","ejabberd vCard modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Prvky"}. {"Email","E-mail"}. {"Enable logging","Zapnúť zaznamenávanie histórie"}. {"End User Session","Ukončiť reláciu užívateľa"}. @@ -85,7 +78,6 @@ {"Enter path to jabberd14 spool file","Zadajte cestu k spool súboru jabberd14"}. {"Enter path to text file","Zadajte cestu k textovému súboru"}. {"Enter the text you see","Zadajte zobrazený text"}. -{"Error","Chyba"}. {"Exclude Jabber IDs from CAPTCHA challenge","Nepoužívať CAPTCHA pre nasledujúce Jabber ID"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportovať dáta všetkých uživateľov na serveri do súborov PIEFXIS (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportovať dáta uživateľov na hostitelovi do súborov PIEFXIS (XEP-0227):"}. @@ -93,22 +85,17 @@ {"Family Name","Priezvisko"}. {"February","Február"}. {"Friday","Piatok"}. -{"From","Od"}. {"Full Name","Celé meno: "}. {"Get Number of Online Users","Zobraziť počet pripojených užívateľov"}. {"Get Number of Registered Users","Zobraziť počet registrovaných užívateľov"}. {"Get User Last Login Time","Zobraziť čas posledného prihlásenia"}. -{"Get User Password","Zobraziť heslo užívateľa"}. {"Get User Statistics","Zobraziť štatistiku užívateľa"}. {"Grant voice to this person?","Prideltiť Voice tejto osobe?"}. -{"Group","Skupina"}. -{"Groups","Skupiny"}. {"has been banned","bol(a) zablokovaný(á)"}. {"has been kicked because of a system shutdown","bol vyhodený(á) kvôli reštartu systému"}. {"has been kicked because of an affiliation change","bol vyhodený(á) kvôli zmene priradenia"}. {"has been kicked because the room has been changed to members-only","bol vyhodený(á), pretože miestnosť bola vyhradená len pre členov"}. {"has been kicked","bol(a) vyhodený(á) z miestnosti"}. -{"Host","Server"}. {"If you don't see the CAPTCHA image here, visit the web page.","Pokiaľ nevidíte obrázok CAPTCHA, navštívte webovú stránku."}. {"Import Directory","Import adresára"}. {"Import File","Import súboru"}. @@ -134,7 +121,6 @@ {"Last month","Posledný mesiac"}. {"Last year","Posledný rok"}. {"leaves the room","odišiel(a) z miestnosti"}. -{"Low level update script","Nízkoúrovňový aktualizačný skript"}. {"Make participants list public","Nastaviť zoznam zúčastnených ako verejný"}. {"Make room CAPTCHA protected","Chrániť miestnosť systémom CAPTCHA"}. {"Make room members-only","Nastaviť miestnosť len pre členov"}. @@ -146,17 +132,13 @@ {"Max payload size in bytes","Maximálny náklad v bajtoch"}. {"Maximum Number of Occupants","Počet účastníkov"}. {"May","Máj"}. -{"Members:","Členovia:"}. {"Membership is required to enter this room","Pre vstup do miestnosti je potrebné byť členom"}. -{"Memory","Pamäť"}. {"Message body","Telo správy"}. {"Middle Name","Prostredné meno: "}. {"Minimum interval between voice requests (in seconds)","Minimum interval between voice requests (in seconds)"}. {"Moderator privileges required","Sú potrebné práva moderátora"}. -{"Modified modules","Modifikované moduly"}. {"Monday","Pondelok"}. {"Name","Meno"}. -{"Name:","Meno:"}. {"Never","Nikdy"}. {"New Password:","Nové heslo:"}. {"Nickname Registration at ","Registrácia prezývky na "}. @@ -178,11 +160,8 @@ {"Number of online users","Počet online užívateľov"}. {"Number of registered users","Počet registrovaných užívateľov"}. {"October","Október"}. -{"Offline Messages","Offline správy"}. -{"Offline Messages:","Offline správy"}. {"OK","OK"}. {"Old Password:","Staré heslo:"}. -{"Online Users:","Online používatelia:"}. {"Online Users","Online užívatelia"}. {"Online","Online"}. {"Only deliver notifications to available users","Doručovať upozornenia len aktuálne prihláseným používateľom"}. @@ -195,16 +174,13 @@ {"Organization Name","Meno organizácie: "}. {"Organization Unit","Organizačná jednotka: "}. {"Outgoing s2s Connections","Odchádzajúce s2s spojenia"}. -{"Outgoing s2s Connections:","Odchádzajúce s2s spojenia:"}. {"Owner privileges required","Sú vyžadované práva vlastníka"}. -{"Packet","Paket"}. {"Password Verification","Overenie hesla"}. {"Password Verification:","Overenie hesla"}. {"Password","Heslo"}. {"Password:","Heslo:"}. {"Path to Dir","Cesta k adresáru"}. {"Path to File","Cesta k súboru"}. -{"Pending","Čakajúce"}. {"Period: ","Čas:"}. {"Persist items to storage","Uložiť položky natrvalo do úložiska"}. {"Ping","Ping"}. @@ -221,17 +197,12 @@ {"RAM copy","Kópia RAM"}. {"Really delete message of the day?","Skutočne zmazať správu dňa?"}. {"Recipient is not in the conference room","Príjemca sa nenachádza v konferenčnej miestnosti"}. -{"Registered Users","Registrovaní používatelia"}. -{"Registered Users:","Registrovaní používatelia:"}. {"Register","Zoznam kontaktov"}. {"Remote copy","Vzdialená kópia"}. -{"Remove All Offline Messages","Odstrániť všetky offline správy"}. {"Remove User","Odstrániť užívateľa"}. -{"Remove","Odstrániť"}. {"Replaced by new connection","Nahradené novým spojením"}. {"Resources","Zdroje"}. {"Restart Service","Reštartovať službu"}. -{"Restart","Reštart"}. {"Restore Backup from File at ","Obnoviť zálohu zo súboru na "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Obnoviť binárnu zálohu pri nasledujúcom reštarte ejabberd (vyžaduje menej pamäte)"}. {"Restore binary backup immediately:","Okamžite obnoviť binárnu zálohu:"}. @@ -244,10 +215,8 @@ {"Room title","Názov miestnosti"}. {"Roster groups allowed to subscribe","Skupiny kontaktov, ktoré môžu odoberať"}. {"Roster size","Počet kontaktov v zozname"}. -{"RPC Call Error","Chyba RPC volania"}. {"Running Nodes","Bežiace uzly"}. {"Saturday","Sobota"}. -{"Script check","Kontrola skriptu"}. {"Search Results for ","Hľadať výsledky pre "}. {"Search users in ","Hľadať užívateľov v "}. {"Send announcement to all online users on all hosts","Odoslať oznam všetkým online používateľom na všetkých serveroch"}. @@ -264,18 +233,12 @@ {"Specify the access model","Uveďte model prístupu"}. {"Specify the event message type","Uveďte typ pre správu o udalosti"}. {"Specify the publisher model","Špecifikovať model publikovania"}. -{"Statistics of ~p","Štatistiky ~p"}. -{"Statistics","Štatistiky"}. {"Stopped Nodes","Zastavené uzly"}. -{"Stop","Zastaviť"}. -{"Storage Type","Typ úložiska"}. {"Store binary backup:","Uložiť binárnu zálohu:"}. {"Store plain text backup:","Uložiť zálohu do textového súboru:"}. {"Subject","Predmet"}. -{"Submit","Odoslať"}. {"Submitted","Odoslané"}. {"Subscriber Address","Adresa odberateľa"}. -{"Subscription","Prihlásenie"}. {"Sunday","Nedeľa"}. {"That nickname is already in use by another occupant","Prezývka je už používaná iným členom"}. {"That nickname is registered by another person","Prezývka je už zaregistrovaná inou osobou"}. @@ -289,24 +252,14 @@ {"This room is not anonymous","Táto miestnosť nie je anonymná"}. {"Thursday","Štvrtok"}. {"Time delay","Časový posun"}. -{"Time","Čas"}. {"Too many CAPTCHA requests","Príliš veľa žiadostí o CAPTCHA"}. -{"To","Pre"}. {"Traffic rate limit is exceeded","Bol prekročený prenosový limit"}. -{"Transactions Aborted:","Transakcie zrušená"}. -{"Transactions Committed:","Transakcie potvrdená"}. -{"Transactions Logged:","Transakcie zaznamenaná"}. -{"Transactions Restarted:","Transakcie reštartovaná"}. {"Tuesday","Utorok"}. {"Unable to generate a CAPTCHA","Nepodarilo sa vygenerovat CAPTCHA"}. {"Unauthorized","Neautorizovaný"}. {"Unregister","Zrušiť účet"}. {"Update message of the day (don't send)","Aktualizovať správu dňa (neodosielať)"}. {"Update message of the day on all hosts (don't send)","Upraviť správu dňa na všetkých serveroch"}. -{"Update plan","Aktualizovať plán"}. -{"Update script","Aktualizované skripty"}. -{"Update","Aktualizovať"}. -{"Uptime:","Uptime:"}. {"User JID","Používateľ "}. {"User Management","Správa užívateľov"}. {"Username:","IRC prezývka"}. @@ -314,7 +267,6 @@ {"Users Last Activity","Posledná aktivita používateľa"}. {"Users","Používatelia"}. {"User","Užívateľ"}. -{"Validate","Overiť"}. {"vCard User Search","Hľadať užívateľov vo vCard"}. {"Virtual Hosts","Virtuálne servery"}. {"Visitors are not allowed to change their nicknames in this room","V tejto miestnosti nieje povolené meniť prezývky"}. diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index ba9f6cdfc..12f356901 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -11,8 +11,6 @@ {"A Web Page","Faqe Web"}. {"Accept","Pranoje"}. {"Account doesn't exist","Llogaria s’ekziston"}. -{"Add Jabber ID","Shtoni ID Jabber"}. -{"Add New","Shtoni të Ri"}. {"Add User","Shtoni Përdorues"}. {"Administration of ","Administrim i "}. {"Administration","Administrim"}. @@ -68,25 +66,18 @@ {"Configuration of room ~s","Formësim i dhomë ~s"}. {"Configuration","Formësim"}. {"Country","Vend"}. -{"CPU Time:","Kohë CPU-je:"}. {"Current Discussion Topic","Tema e Tanishme e Diskutimit"}. {"Database failure","Dështim baze të dhënash"}. -{"Database Tables at ~p","Tabela Baze të Dhënash te ~p"}. {"Database Tables Configuration at ","Formësim Tabelash Baze të Dhënash te "}. {"Database","Bazë të dhënash"}. {"December","Dhjetor"}. -{"Delete content","Fshini lëndë"}. {"Delete message of the day","Fshini mesazhin e ditës"}. -{"Delete Selected","Fshi të Përzgjedhurin"}. -{"Delete table","Fshini tabelën"}. {"Delete User","Fshi Përdorues"}. {"Deliver event notifications","Dërgo njoftime aktesh"}. -{"Description:","Përshkrim:"}. {"Disc only copy","Kopje vetëm në disk"}. {"Duplicated groups are not allowed by RFC6121","Grupe të përsëdytur s’lejohen nga RFC6121"}. {"Edit Properties","Përpunoni Veti"}. {"ejabberd","ejabberd"}. -{"Elements","Elementë"}. {"Email Address","Adresë Email"}. {"Email","Email"}. {"Enable logging","Aktivizo regjistrim"}. @@ -94,7 +85,6 @@ {"Enter path to backup file","Jepni shteg për te kartelë kopjeruajtje"}. {"Enter path to text file","Jepni shteg për te kartelë tekst"}. {"Enter the text you see","Jepni tekstin që shihni"}. -{"Error","Gabim"}. {"External component failure","Dështim përbërësi të jashtëm"}. {"External component timeout","Mbarim kohe për përbërës të jashtëm"}. {"Failed to parse HTTP response","S’u arrit të përtypet përgjigje HTTP"}. @@ -106,23 +96,17 @@ {"Fill in the form to search for any matching XMPP User","Plotësoni formularin që të kërkohet për çfarëdo përdoruesi XMPP me përputhje"}. {"Friday","E premte"}. {"From ~ts","Nga ~ts"}. -{"From","Nga"}. {"Full List of Room Admins","Listë e Plotë Përgjegjësish Dhome"}. {"Full List of Room Owners","Listë e Plotë të Zotësh Dhome"}. {"Full Name","Emër i Plotë"}. {"Get Number of Online Users","Merr Numër Përdoruesish Në Linjë"}. {"Get Number of Registered Users","Merr Numër Përdoruesish të Regjistruar"}. -{"Get User Password","Merr Fjalëkalim Përdoruesi"}. {"Get User Statistics","Merr Statistika Përdoruesi"}. {"Given Name","Emër"}. {"Grant voice to this person?","T’i akordohet zë këtij personi?"}. -{"Group","Grup"}. -{"Groups that will be displayed to the members","Grupe që do t’u shfaqen anëtarëve"}. -{"Groups","Grupe"}. {"has been banned","është dëbuar"}. {"has been kicked","është përzënë"}. {"Host unknown","Strehë e panjohur"}. -{"Host","Strehë"}. {"HTTP File Upload","Ngarkim Kartelash HTTP"}. {"Idle connection","Lidhje e plogësht"}. {"Import Directory","Importoni Drejtori"}. @@ -130,7 +114,6 @@ {"Import User from File at ","Importo Përdorues prej Kartele te "}. {"Import Users from Dir at ","Importo Përdorues nga Drejtori te "}. {"Improper message type","Lloj i pasaktë mesazhesh"}. -{"Incoming s2s Connections:","Lidhje s2s Ardhëse:"}. {"Incorrect CAPTCHA submit","Parashtim Captcha-je të pasaktë"}. {"Incorrect password","Fjalëkalim i pasaktë"}. {"Incorrect value of 'action' attribute","Vlerë e pavlefshme atributi 'action'"}. @@ -149,14 +132,12 @@ {"July","Korrik"}. {"June","Qershor"}. {"Just created","Të sapokrijuara"}. -{"Label:","Etiketë:"}. {"Last Activity","Veprimtaria e Fundit"}. {"Last login","Hyrja e fundit"}. {"Last message","Mesazhi i fundit"}. {"Last month","Muaji i fundit"}. {"Last year","Viti i shkuar"}. {"leaves the room","del nga dhoma"}. -{"List of rooms","Listë dhomash"}. {"Logging","Regjistrim"}. {"Make participants list public","Bëje publike listën e pjesëmarrësve"}. {"Make room CAPTCHA protected","Bëje dhomën të mbrojtur me CAPTCHA"}. @@ -171,10 +152,7 @@ {"Maximum file size","Madhësi maksimum kartelash"}. {"Maximum Number of Occupants","Numër Maksimum të Pranishmish"}. {"May","Maj"}. -{"Members not added (inexistent vhost!): ","S’u shtuan anëtarë (vhost joekzistuese!): "}. -{"Members:","Anëtarë:"}. {"Membership is required to enter this room","Lypset anëtarësim për të hyrë në këtë dhomë"}. -{"Memory","Kujtesë"}. {"Message body","Lëndë mesazhi"}. {"Messages from strangers are rejected","Mesazhet prej të panjohurish hidhen tej"}. {"Messages of type headline","Mesazhe të llojit titull"}. @@ -188,7 +166,6 @@ {"Multicast","Multikast"}. {"Multi-User Chat","Fjalosje Me Shumë Përdorues Njëherësh"}. {"Name","Emër"}. -{"Name:","Emër:"}. {"Natural-Language Room Name","Emër Dhome Në Gjuhë Natyrale"}. {"Never","Kurrë"}. {"New Password:","Fjalëkalim i Ri:"}. @@ -228,23 +205,17 @@ {"Occupants are allowed to invite others","Të pranishmëve u është lejuar të ftojnë të tjerë"}. {"Occupants May Change the Subject","Të pranishmit Mund të Ndryshojnë Subjektin"}. {"October","Tetor"}. -{"Offline Messages","Mesazhe Jo Në Linjë"}. -{"Offline Messages:","Mesazhe Jo Në Linjë:"}. {"OK","OK"}. {"Old Password:","Fjalëkalimi i Vjetër:"}. {"Online Users","Përdorues Në Linjë"}. -{"Online Users:","Përdorues Në Linjë:"}. {"Online","Në linjë"}. -{"Only admins can see this","Këtë mund ta shohin vetëm përgjegjësit"}. {"Only deliver notifications to available users","Dorëzo njoftime vetëm te përdoruesit e pranishëm"}. {"Only occupants are allowed to send messages to the conference","Vetëm të pranishmëve u lejohet të dërgojnë mesazhe te konferenca"}. {"Only publishers may publish","Vetëm botuesit mund të botojnë"}. {"Organization Name","Emër Enti"}. {"Organization Unit","Njësi Organizative"}. {"Outgoing s2s Connections","Lidhje s2s Ikëse"}. -{"Outgoing s2s Connections:","Lidhje s2s Ikëse:"}. {"Owner privileges required","Lypset privilegje të zoti"}. -{"Packet","Paketë"}. {"Participant","Pjesëmarrës"}. {"Password Verification","Verifikim Fjalëkalimi"}. {"Password Verification:","Verifikim Fjalëkalimi:"}. @@ -252,7 +223,6 @@ {"Password:","Fjalëkalim:"}. {"Path to Dir","Shteg për te Drejtori"}. {"Path to File","Shteg për te Kartelë"}. -{"Pending","Pezull"}. {"Period: ","Periudhë: "}. {"Ping","Ping"}. {"Pong","Pong"}. @@ -265,27 +235,21 @@ {"Really delete message of the day?","Të fshihet vërtet mesazhi i ditës?"}. {"Recipient is not in the conference room","Pjesëmarrësi s’është në dhomën e konferencës"}. {"Register an XMPP account","Regjistroni një llogari XMPP"}. -{"Registered Users","Përdorues të Regjistruar"}. -{"Registered Users:","Përdorues të Regjistruar:"}. {"Register","Regjistrohuni"}. {"Remote copy","Kopje e largët"}. -{"Remove All Offline Messages","Hiq Krejt Mesazhet Jo Në Linjë"}. {"Remove User","Hiqeni Përdoruesin"}. -{"Remove","Hiqe"}. {"Replaced by new connection","Zëvendësuar nga lidhje e re"}. {"Request has timed out","Kërkesës i mbaroi koha"}. {"Request is ignored","Kërkesa u shpërfill"}. {"Requested role","Rol i domosdoshëm"}. {"Resources","Burime"}. {"Restart Service","Rinise Shërbimin"}. -{"Restart","Rinise"}. {"Restore","Riktheje"}. {"Roles that May Send Private Messages","Role që Mund të Dërgojnë Mesazhe Private"}. {"Room Configuration","Formësim Dhome"}. {"Room description","Përshkrim i dhomës"}. {"Room Occupants","Të pranishëm Në Dhomë"}. {"Room title","Titull dhome"}. -{"RPC Call Error","Gabim Thirrjeje RPC"}. {"Running Nodes","Nyje Në Punë"}. {"Saturday","E shtunë"}. {"Search from the date","Kërko nga data"}. @@ -293,7 +257,6 @@ {"Search the text","Kërkoni për tekst"}. {"Search until the date","Kërko deri më datën"}. {"Search users in ","Kërko përdorues te "}. -{"Select All","Përzgjidheni Krejt"}. {"Send announcement to all users","Dërgo njoftim krejt përdoruesve"}. {"September","Shtator"}. {"Server:","Shërbyes:"}. @@ -303,16 +266,10 @@ {"Specify the access model","Specifikoni model hyrjeje"}. {"Specify the event message type","Përcaktoni llojin e mesazhit për aktin"}. {"Specify the publisher model","Përcaktoni model botuesi"}. -{"Statistics of ~p","Statistika për ~p"}. -{"Statistics","Statistika"}. -{"Stop","Ndale"}. {"Stopped Nodes","Nyja të Ndalura"}. -{"Storage Type","Lloj Depozitimi"}. {"Subject","Subjekti"}. -{"Submit","Parashtrojeni"}. {"Submitted","Parashtruar"}. {"Subscriber Address","Adresë e Pajtimtarit"}. -{"Subscription","Pajtim"}. {"Sunday","E diel"}. {"The account already exists","Ka tashmë një llogari të tillë"}. {"The account was not unregistered","Llogaria s’qe çregjistruar"}. @@ -334,23 +291,18 @@ {"This room is not anonymous","Kjo dhomë s’është anonime"}. {"Thursday","E enjte"}. {"Time delay","Vonesë kohore"}. -{"Time","Kohë"}. {"Too many CAPTCHA requests","Shumë kërkesa ndaj CAPTCHA-s"}. {"Too many child elements","Shumë elementë pjella"}. {"Too many elements","Shumë elementë "}. {"Too many elements","Shumë elementë "}. {"Too many users in this conference","Shumë përdorues në këtë konferencë"}. -{"Total rooms","Dhoma gjithsej"}. {"Tuesday","E martë"}. {"Unable to generate a CAPTCHA","S’arrihet të prodhohet një CAPTCHA"}. {"Unauthorized","E paautorizuar"}. {"Unexpected action","Veprim i papritur"}. {"Unregister an XMPP account","Çregjistroni një llogari XMPP"}. {"Unregister","Çregjistrohuni"}. -{"Unselect All","Shpërzgjidhi Krejt"}. {"Unsupported version","Version i pambuluar"}. -{"Update","Përditësoje"}. -{"Uptime:","Kohëpunim:"}. {"User already exists","Ka tashmë një përdorues të tillë"}. {"User JID","JID përdoruesi"}. {"User (jid)","Përdorues (jid)"}. @@ -362,8 +314,6 @@ {"User","Përdorues"}. {"Users Last Activity","Veprimtaria e Fundit Nga Përdorues"}. {"Users","Përdorues"}. -{"Validate","Vleftësoje"}. -{"View Queue","Shihni Radhën"}. {"Virtual Hosts","Streha Virtuale"}. {"Visitor","Vizitor"}. {"Wednesday","E mërkurë"}. diff --git a/priv/msgs/sv.msg b/priv/msgs/sv.msg index 08f560a3c..74337cd1f 100644 --- a/priv/msgs/sv.msg +++ b/priv/msgs/sv.msg @@ -7,8 +7,6 @@ {"A friendly name for the node","Ett vänligt namn for noden"}. {"Access denied by service policy","Åtkomst nekad enligt lokal policy"}. {"Action on user","Handling mot användare"}. -{"Add Jabber ID","Lägg till Jabber ID"}. -{"Add New","Lägg till ny"}. {"Add User","Lägg till användare"}. {"Administration of ","Administration av "}. {"Administration","Administration"}. @@ -42,20 +40,16 @@ {"Conference room does not exist","Rummet finns inte"}. {"Configuration of room ~s","Konfiguration för ~s"}. {"Configuration","Konfiguration"}. -{"Connected Resources:","Anslutna resurser:"}. {"Country","Land"}. -{"CPU Time:","CPU tid"}. {"Database Tables Configuration at ","Databastabellers konfiguration"}. {"Database","Databas"}. {"December","December"}. {"Default users as participants","Gör om användare till deltagare"}. {"Delete message of the day on all hosts","Ta bort dagens meddelande på alla värdar"}. {"Delete message of the day","Ta bort dagens meddelande"}. -{"Delete Selected","Tabort valda"}. {"Delete User","Ta bort användare"}. {"Deliver event notifications","Skicka eventnotifikation"}. {"Deliver payloads with event notifications","Skicka innehåll tillsammans med notifikationer"}. -{"Description:","Beskrivning:"}. {"Disc only copy","Endast diskkopia"}. {"Dump Backup to Text File at ","Dumpa säkerhetskopia till textfil på "}. {"Dump to Text File","Dumpa till textfil"}. @@ -65,7 +59,6 @@ {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestrem modul"}. {"ejabberd vCard module","ejabberd vCard-modul"}. {"ejabberd Web Admin","ejabberd Web Admin"}. -{"Elements","Elements"}. {"Email","Email"}. {"Enable logging","Möjliggör login"}. {"End User Session","Avsluta användarsession"}. @@ -75,27 +68,21 @@ {"Enter path to jabberd14 spool file","Skriv in sökväg till spoolfil från jabberd14"}. {"Enter path to text file","Skriv in sökväg till textfil"}. {"Enter the text you see","Skriv in sökväg till textfil"}. -{"Error","Fel"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Exportera data av alla användare i servern till en PIEFXIS fil (XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Exportera data av användare i en host till PIEFXIS fil (XEP-0227):"}. {"Family Name","Efternamn"}. {"February","Februari"}. {"Friday","Fredag"}. -{"From","Från"}. {"Full Name","Fullständigt namn"}. {"Get Number of Online Users","Hämta antal inloggade användare"}. {"Get Number of Registered Users","Hämta antal registrerade användare"}. {"Get User Last Login Time","Hämta användarens senast inloggade tid"}. -{"Get User Password","Hämta användarlösenord"}. {"Get User Statistics","Hämta användarstatistik"}. -{"Group","Grupp"}. -{"Groups","Grupper"}. {"has been banned","har blivit bannad"}. {"has been kicked because of a system shutdown","har blivit kickad p.g.a en systemnerstängning"}. {"has been kicked because of an affiliation change","har blivit kickad p.g.a en ändring av tillhörighet"}. {"has been kicked because the room has been changed to members-only","har blivit kickad p.g.a att rummet har ändrats till endast användare"}. {"has been kicked","har blivit kickad"}. -{"Host","Server"}. {"Import Directory","Importera katalog"}. {"Import File","Importera fil"}. {"Import user data from jabberd14 spool file:","Importera användare från jabberd14 Spool filer"}. @@ -120,7 +107,6 @@ {"Last month","Senaste månaden"}. {"Last year","Senaste året"}. {"leaves the room","lämnar rummet"}. -{"Low level update script","Uppdaterade laglevel skript"}. {"Make participants list public","Gör deltagarlistan publik"}. {"Make room members-only","Gör om rummet till endast medlemmar"}. {"Make room moderated","Gör rummet modererat"}. @@ -132,15 +118,11 @@ {"Maximum Number of Occupants","Maximalt antal av användare"}. {"May","Maj"}. {"Membership is required to enter this room","Du måste vara medlem för att komma in i det här rummet"}. -{"Members:","Medlemmar:"}. -{"Memory","Minne"}. {"Message body","Meddelande kropp"}. {"Middle Name","Mellannamn"}. {"Moderator privileges required","Moderatorprivilegier krävs"}. -{"Modified modules","Uppdaterade moduler"}. {"Monday","Måndag"}. {"Name","Namn"}. -{"Name:","Namn:"}. {"Never","Aldrig"}. {"Nickname Registration at ","Registrera smeknamn på "}. {"Nickname ~s does not exist in the room","Smeknamnet ~s existerar inte i det här rummet"}. @@ -161,11 +143,8 @@ {"Number of online users","Antal inloggade användare"}. {"Number of registered users","Antal registrerade användare"}. {"October","Oktober"}. -{"Offline Messages","Offline meddelanden"}. -{"Offline Messages:","Offline meddelanden:"}. {"OK","OK"}. {"Online Users","Anslutna användare"}. -{"Online Users:","Inloggade användare"}. {"Online","Ansluten"}. {"Only deliver notifications to available users","Skicka notifikationer bara till uppkopplade användare"}. {"Only moderators and participants are allowed to change the subject in this room","Endast moderatorer och deltagare har tillåtelse att ändra ämnet i det här rummet"}. @@ -175,15 +154,12 @@ {"Organization Name","Organisationsnamn"}. {"Organization Unit","Organisationsenhet"}. {"Outgoing s2s Connections","Utgaende s2s anslutning"}. -{"Outgoing s2s Connections:","Utgående s2s anslutning"}. {"Owner privileges required","Ägarprivilegier krävs"}. -{"Packet","Paket"}. {"Password Verification","Lösenordsverifikation"}. {"Password","Lösenord"}. {"Password:","Lösenord:"}. {"Path to Dir","Sökväg till katalog"}. {"Path to File","Sökväg till fil"}. -{"Pending","Ännu inte godkända"}. {"Period: ","Period: "}. {"Persist items to storage","Spara dataposter permanent"}. {"Ping","Ping"}. @@ -198,15 +174,11 @@ {"RAM copy","RAM-kopia"}. {"Really delete message of the day?","Verkligen ta bort dagens meddelanden?"}. {"Recipient is not in the conference room","Mottagaren finns inte i rummet"}. -{"Registered Users","Registrerade användare"}. -{"Registered Users:","Registrerade användare"}. {"Remote copy","Sparas inte lokalt"}. {"Remove User","Ta bort användare"}. -{"Remove","Ta bort"}. {"Replaced by new connection","Ersatt av ny anslutning"}. {"Resources","Resurser"}. {"Restart Service","Starta om servicen"}. -{"Restart","Omstart"}. {"Restore Backup from File at ","Återställ säkerhetskopia från fil på "}. {"Restore binary backup after next ejabberd restart (requires less memory):","återställ den binära backupen efter nästa ejabberd omstart"}. {"Restore binary backup immediately:","återställ den binära backupen omedelbart"}. @@ -218,10 +190,8 @@ {"Room title","Rumstitel"}. {"Roster groups allowed to subscribe","Rostergrupper tillåts att prenumerera"}. {"Roster size","Roster storlek"}. -{"RPC Call Error","RPC Uppringningserror"}. {"Running Nodes","Körande noder"}. {"Saturday","Lördag"}. -{"Script check","Skript kollat"}. {"Search Results for ","Sökresultat för"}. {"Search users in ","Sök efter användare på "}. {"Send announcement to all online users on all hosts","Sänd meddelanden till alla inloggade användare på alla värdar"}. @@ -237,18 +207,12 @@ {"Shut Down Service","Stäng ner servicen"}. {"Specify the access model","Specificera accessmodellen"}. {"Specify the publisher model","Ange publiceringsmodell"}. -{"Statistics of ~p","Statistik på ~p"}. -{"Statistics","Statistik"}. {"Stopped Nodes","Stannade noder"}. -{"Stop","Stoppa"}. -{"Storage Type","Lagringstyp"}. {"Store binary backup:","Lagra den binära backupen"}. {"Store plain text backup:","Lagra textbackup"}. {"Subject","Ämne"}. -{"Submit","Skicka"}. {"Submitted","Skicka in"}. {"Subscriber Address","Prenumerationsadress"}. -{"Subscription","Prenumeration"}. {"Sunday","Söndag"}. {"That nickname is registered by another person","Smeknamnet är reserverat"}. {"The CAPTCHA is valid.","Din CAPTCHA är godkänd."}. @@ -256,27 +220,16 @@ {"This room is not anonymous","Detta rum är inte anonymt"}. {"Thursday","Torsdag"}. {"Time delay","Tidsförsening"}. -{"Time","Tid"}. -{"To","Till"}. {"Traffic rate limit is exceeded","Trafikgränsen har överstigits"}. -{"Transactions Aborted:","Transaktioner borttagna"}. -{"Transactions Committed:","Transaktioner kommittade"}. -{"Transactions Logged:","Transaktioner loggade "}. -{"Transactions Restarted:","Transaktioner omstartade"}. {"Tuesday","Tisdag"}. {"Unauthorized","Ej auktoriserad"}. {"Update message of the day (don't send)","Uppdatera dagens status meddelande (skicka inte)"}. {"Update message of the day on all hosts (don't send)","Uppdatera dagens status meddelande på alla värdar (skicka inte)"}. -{"Update plan","Uppdateringsplan"}. -{"Update script","Uppdatera skript"}. -{"Update","Uppdatera"}. -{"Uptime:","Tid upp"}. {"User Management","Användarmanagement"}. {"User","Användarnamn"}. {"Users are not allowed to register accounts so quickly","Det är inte tillåtet för användare att skapa konton så fort"}. {"Users Last Activity","Användarens senaste aktivitet"}. {"Users","Användare"}. -{"Validate","Validera"}. {"vCard User Search","vCard användare sök"}. {"Virtual Hosts","Virtuella servrar"}. {"Visitors are not allowed to change their nicknames in this room","Det är inte tillåtet for gäster att ändra sina smeknamn i detta rummet"}. diff --git a/priv/msgs/th.msg b/priv/msgs/th.msg index 9ec823668..0def6490d 100644 --- a/priv/msgs/th.msg +++ b/priv/msgs/th.msg @@ -6,8 +6,6 @@ {" has set the subject to: "," ตั้งหัวข้อว่า: "}. {"Access denied by service policy","การเข้าถึงถูกปฏิเสธโดยนโยบายการบริการ"}. {"Action on user","การดำเนินการกับผู้ใช้"}. -{"Add Jabber ID","เพิ่ม Jabber ID"}. -{"Add New","เพิ่มผู้ใช้ใหม่"}. {"Add User","เพิ่มผู้ใช้"}. {"Administration of ","การดูแล "}. {"Administration","การดูแล"}. @@ -37,20 +35,16 @@ {"Commands","คำสั่ง"}. {"Conference room does not exist","ไม่มีห้องประชุม"}. {"Configuration","การกำหนดค่า"}. -{"Connected Resources:","ทรัพยากรที่เชื่อมต่อ:"}. {"Country","ประเทศ"}. -{"CPU Time:","เวลาการทำงานของ CPU:"}. {"Database Tables Configuration at ","การกำหนดค่าตารางฐานข้อมูลที่"}. {"Database","ฐานข้อมูล"}. {"December","ธันวาคม"}. {"Default users as participants","ผู้ใช้เริ่มต้นเป็นผู้เข้าร่วม"}. {"Delete message of the day on all hosts","ลบข้อความของวันบนโฮสต์ทั้งหมด"}. {"Delete message of the day","ลบข้อความของวัน"}. -{"Delete Selected","ลบข้อความที่เลือก"}. {"Delete User","ลบผู้ใช้"}. {"Deliver event notifications","ส่งการแจ้งเตือนเหตุการณ์"}. {"Deliver payloads with event notifications","ส่งส่วนของข้อมูล (payload) พร้อมกับการแจ้งเตือนเหตุการณ์"}. -{"Description:","รายละเอียด:"}. {"Disc only copy","คัดลอกเฉพาะดิสก์"}. {"Dump Backup to Text File at ","ถ่ายโอนการสำรองข้อมูลไปยังไฟล์ข้อความที่"}. {"Dump to Text File","ถ่ายโอนข้อมูลไปยังไฟล์ข้อความ"}. @@ -70,18 +64,13 @@ {"Family Name","นามสกุล"}. {"February","กุมภาพันธ์"}. {"Friday","วันศุกร์"}. -{"From","จาก"}. {"Full Name","ชื่อเต็ม"}. {"Get Number of Online Users","แสดงจำนวนผู้ใช้ออนไลน์"}. {"Get Number of Registered Users","แสดงจำนวนผู้ใช้ที่ลงทะเบียน"}. {"Get User Last Login Time","แสดงเวลาเข้าสู่ระบบครั้งล่าสุดของผู้ใช้"}. -{"Get User Password","ขอรับรหัสผ่านของผู้ใช้"}. {"Get User Statistics","แสดงสถิติของผู้ใช้"}. -{"Groups","กลุ่ม"}. -{"Group","กลุ่"}. {"has been banned","ถูกสั่งห้าม"}. {"has been kicked","ถูกไล่ออก"}. -{"Host","โฮสต์"}. {"Import Directory","อิมพอร์ตไดเร็กทอรี"}. {"Import File","อิมพอร์ตไฟล์"}. {"Import User from File at ","อิมพอร์ตผู้ใช้จากไฟล์ที่"}. @@ -103,7 +92,6 @@ {"Last month","เดือนที่แล้ว"}. {"Last year","ปีที่แล้ว"}. {"leaves the room","ออกจากห้อง"}. -{"Low level update script","อัพเดตสคริปต์ระดับต่ำ"}. {"Make participants list public","สร้างรายการผู้เข้าร่วมสำหรับใช้งานโดยบุคคลทั่วไป"}. {"Make room members-only","สร้างห้องสำหรับสมาชิกเท่านั้น"}. {"Make room password protected","สร้างห้องที่มีการป้องกันด้วยรหัสผ่าน"}. @@ -113,14 +101,11 @@ {"Max payload size in bytes","ขนาดสูงสุดของส่วนของข้อมูล (payload) มีหน่วยเป็นไบต์"}. {"Maximum Number of Occupants","จำนวนผู้ครอบครองห้องสูงสุด"}. {"May","พฤษภาคม"}. -{"Members:","สมาชิก:"}. -{"Memory","หน่วยความจำ"}. {"Message body","เนื้อหาของข้อความ"}. {"Middle Name","ชื่อกลาง"}. {"Moderator privileges required","ต้องมีสิทธิพิเศษของผู้ดูแลการสนทนา"}. {"Monday","วันจันทร์"}. {"Name","ชื่อ"}. -{"Name:","ชื่อ:"}. {"Never","ไม่เคย"}. {"Nickname Registration at ","การลงทะเบียนชื่อเล่นที่ "}. {"Nickname","ชื่อเล่น"}. @@ -139,11 +124,8 @@ {"Number of online users","จำนวนผู้ใช้ออนไลน์"}. {"Number of registered users","จำนวนผู้ใช้ที่ลงทะเบียน"}. {"October","ตุลาคม"}. -{"Offline Messages","ข้อความออฟไลน์"}. -{"Offline Messages:","ข้อความออฟไลน์:"}. {"OK","ตกลง"}. {"Online Users","ผู้ใช้ออนไลน์"}. -{"Online Users:","ผู้ใช้ออนไลน์:"}. {"Online","ออนไลน์"}. {"Only deliver notifications to available users","ส่งการแจ้งเตือนถึงผู้ใช้ที่สามารถติดต่อได้เท่านั้น"}. {"Only occupants are allowed to send messages to the conference","ผู้ครอบครองห้องเท่านั้นที่ได้รับอนุญาตให้ส่งข้อความไปยังห้องประชุม"}. @@ -152,15 +134,12 @@ {"Organization Name","ชื่อองค์กร"}. {"Organization Unit","หน่วยขององค์กร"}. {"Outgoing s2s Connections","การเชื่อมต่อ s2s ขาออก"}. -{"Outgoing s2s Connections:","การเชื่อมต่อ s2s ขาออก:"}. {"Owner privileges required","ต้องมีสิทธิพิเศษของเจ้าของ"}. -{"Packet","แพ็กเก็ต"}. {"Password Verification","การตรวจสอบรหัสผ่าน"}. {"Password","รหัสผ่าน"}. {"Password:","รหัสผ่าน:"}. {"Path to Dir","พาธไปยัง Dir"}. {"Path to File","พาธของไฟล์ข้อมูล"}. -{"Pending","ค้างอยู่"}. {"Period: ","ระยะเวลา:"}. {"Persist items to storage","ยืนยันรายการที่จะจัดเก็บ"}. {"Ping","Ping"}. @@ -174,15 +153,11 @@ {"RAM copy","คัดลอก RAM"}. {"Really delete message of the day?","แน่ใจว่าต้องการลบข้อความของวันหรือไม่"}. {"Recipient is not in the conference room","ผู้รับไม่ได้อยู่ในห้องประชุม"}. -{"Registered Users","ผู้ใช้ที่ลงทะเบียน"}. -{"Registered Users:","ผู้ใช้ที่ลงทะเบียน:"}. {"Remote copy","คัดลอกระยะไกล"}. {"Remove User","ลบผู้ใช้"}. -{"Remove","ลบ"}. {"Replaced by new connection","แทนที่ด้วยการเชื่อมต่อใหม่"}. {"Resources","ทรัพยากร"}. {"Restart Service","เริ่มต้นการบริการใหม่อีกครั้ง"}. -{"Restart","เริ่มต้นใหม่"}. {"Restore Backup from File at ","คืนค่าการสำรองข้อมูลจากไฟล์ที่"}. {"Restore binary backup after next ejabberd restart (requires less memory):","คืนค่าข้อมูลสำรองแบบไบนารีหลังจากที่ ejabberd ถัดไปเริ่มการทำงานใหม่ (ใช้หน่วยความจำน้อยลง):"}. {"Restore binary backup immediately:","คืนค่าข้อมูลสำรองแบบไบนารีโดยทันที:"}. @@ -192,10 +167,8 @@ {"Room creation is denied by service policy","การสร้างห้องสนทนาถูกปฏิเสธโดยนโยบายการบริการ"}. {"Room title","ชื่อห้อง"}. {"Roster size","ขนาดของบัญชีรายชื่อ"}. -{"RPC Call Error","ข้อผิดพลาดจากการเรียกใช้ RPC"}. {"Running Nodes","โหนดที่ทำงาน"}. {"Saturday","วันเสาร์"}. -{"Script check","ตรวจสอบคริปต์"}. {"Search Results for ","ผลการค้นหาสำหรับ "}. {"Search users in ","ค้นหาผู้ใช้ใน "}. {"Send announcement to all online users on all hosts","ส่งประกาศถึงผู้ใช้ออนไลน์ทั้งหมดบนโฮสต์ทั้งหมด"}. @@ -211,42 +184,25 @@ {"Shut Down Service","ปิดการบริการ"}. {"Specify the access model","ระบุโมเดลการเข้าถึง"}. {"Specify the publisher model","ระบุโมเดลผู้เผยแพร่"}. -{"Statistics of ~p","สถิติของ ~p"}. -{"Statistics","สถิติ"}. {"Stopped Nodes","โหนดที่หยุด"}. -{"Stop","หยุด"}. -{"Storage Type","ชนิดที่เก็บข้อมูล"}. {"Store binary backup:","จัดเก็บข้อมูลสำรองแบบไบนารี:"}. {"Store plain text backup:","จัดเก็บข้อมูลสำรองที่เป็นข้อความธรรมดา:"}. {"Subject","หัวเรื่อง"}. {"Submitted","ส่งแล้ว"}. -{"Submit","ส่ง"}. {"Subscriber Address","ที่อยู่ของผู้สมัคร"}. -{"Subscription","การสมัครสมาชิก"}. {"Sunday","วันอาทิตย์"}. {"the password is","รหัสผ่านคือ"}. {"This room is not anonymous","ห้องนี้ไม่ปิดบังชื่อ"}. {"Thursday","วันพฤหัสบดี"}. {"Time delay","การหน่วงเวลา"}. -{"Time","เวลา"}. -{"To","ถึง"}. {"Traffic rate limit is exceeded","อัตราของปริมาณการเข้าใช้เกินขีดจำกัด"}. -{"Transactions Aborted:","ทรานแซกชันที่ถูกยกเลิก:"}. -{"Transactions Committed:","ทรานแซกชันที่ได้รับมอบหมาย:"}. -{"Transactions Logged:","ทรานแซกชันที่บันทึก:"}. -{"Transactions Restarted:","ทรานแซกชันที่เริ่มทำงานใหม่อีกครั้ง:"}. {"Tuesday","วันอังคาร"}. {"Update message of the day (don't send)","อัพเดตข้อความของวัน (ไม่ต้องส่ง)"}. {"Update message of the day on all hosts (don't send)","อัพเดตข้อความของวันบนโฮสต์ทั้งหมด (ไม่ต้องส่ง) "}. -{"Update plan","แผนการอัพเดต"}. -{"Update script","อัพเดตสคริปต์"}. -{"Update","อัพเดต"}. -{"Uptime:","เวลาการทำงานต่อเนื่อง:"}. {"User Management","การจัดการผู้ใช้"}. {"Users Last Activity","กิจกรรมล่าสุดของผู้ใช้"}. {"Users","ผู้ใช้"}. {"User","ผู้ใช้"}. -{"Validate","ตรวจสอบ"}. {"vCard User Search","ค้นหาผู้ใช้ vCard "}. {"Virtual Hosts","โฮสต์เสมือน"}. {"Visitors are not allowed to send messages to all occupants","ผู้เยี่ยมเยือนไม่ได้รับอนุญาตให้ส่งข้อความถึงผู้ครอบครองห้องทั้งหมด"}. diff --git a/priv/msgs/tr.msg b/priv/msgs/tr.msg index 9b3cbd2c2..af108d514 100644 --- a/priv/msgs/tr.msg +++ b/priv/msgs/tr.msg @@ -8,8 +8,6 @@ {"A password is required to enter this room","Bu odaya girmek için parola gerekiyor"}. {"Access denied by service policy","Servis politikası gereği erişim engellendi"}. {"Action on user","Kullanıcıya uygulanacak eylem"}. -{"Add Jabber ID","Jabber ID'si Ekle"}. -{"Add New","Yeni Ekle"}. {"Add User","Kullanıcı Ekle"}. {"Administration of ","Yönetim : "}. {"Administration","Yönetim"}. @@ -51,20 +49,16 @@ {"Conference room does not exist","Konferans odası bulunamadı"}. {"Configuration of room ~s","~s odasının ayarları"}. {"Configuration","Ayarlar"}. -{"Connected Resources:","Bağlı Kaynaklar:"}. {"Country","Ülke"}. -{"CPU Time:","İşlemci Zamanı:"}. {"Database Tables Configuration at ","Veritabanı Tablo Ayarları : "}. {"Database","Veritabanı"}. {"December","Aralık"}. {"Default users as participants","Kullanıcılar öntanımlı olarak katılımcı olsun"}. {"Delete message of the day on all hosts","Tüm sunuculardaki günün mesajını sil"}. {"Delete message of the day","Günün mesajını sil"}. -{"Delete Selected","Seçilenleri Sil"}. {"Delete User","Kullanıcıyı Sil"}. {"Deliver event notifications","Olay uyarıları gönderilsin"}. {"Deliver payloads with event notifications","Yükleri (payload) olay uyarıları ile beraber gönder"}. -{"Description:","Tanım:"}. {"Disc only copy","Sadece disk kopyala"}. {"Dump Backup to Text File at ","Metin Dosyasına Döküm Alarak Yedekle : "}. {"Dump to Text File","Metin Dosyasına Döküm Al"}. @@ -75,7 +69,6 @@ {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams modülü"}. {"ejabberd vCard module","ejabberd vCard modülü"}. {"ejabberd Web Admin","ejabberd Web Yöneticisi"}. -{"Elements","Elementler"}. {"Email","E-posta"}. {"Enable logging","Kayıt tutma özelliğini aç"}. {"End User Session","Kullanıcı Oturumunu Kapat"}. @@ -85,7 +78,6 @@ {"Enter path to jabberd14 spool file","jabberd14 spool dosyası için yol giriniz"}. {"Enter path to text file","Metin dosyasının yolunu giriniz"}. {"Enter the text you see","Gördüğünüz metni giriniz"}. -{"Error","Hata"}. {"Exclude Jabber IDs from CAPTCHA challenge","CAPTCHA doğrulamasını şu Jabber ID'ler için yapma"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Sunucudaki tüm kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","Bir sunucudaki kullanıcıların verisini PIEFXIS dosyalarına (XEP-0227) dışa aktar:"}. @@ -93,21 +85,17 @@ {"Family Name","Soyisim"}. {"February","Şubat"}. {"Friday","Cuma"}. -{"From","Kimden"}. {"Full Name","Tam İsim"}. {"Get Number of Online Users","Bağlı Kullanıcı Sayısını Al"}. {"Get Number of Registered Users","Kayıtlı Kullanıcı Sayısını Al"}. {"Get User Last Login Time","Kullanıcı Son Giriş Zamanınlarını Al"}. -{"Get User Password","Kullanıcı Parolasını Al"}. {"Get User Statistics","Kullanıcı İstatistiklerini Al"}. {"Grant voice to this person?","Bu kişiye ses verelim mi?"}. -{"Groups","Gruplar"}. {"has been banned","odaya girmesi yasaklandı"}. {"has been kicked because of a system shutdown","sistem kapandığından dolayı atıldı"}. {"has been kicked because of an affiliation change","ilişki değişikliğinden dolayı atıldı"}. {"has been kicked because the room has been changed to members-only","oda üyelere-özel hale getirildiğinden dolayı atıldı"}. {"has been kicked","odadan atıldı"}. -{"Host","Sunucu"}. {"If you don't see the CAPTCHA image here, visit the web page.","Eğer burada CAPTCHA resmini göremiyorsanız, web sayfasını ziyaret edin."}. {"Import Directory","Dizini İçe Aktar"}. {"Import File","Dosyayı İçe Aktar"}. @@ -133,7 +121,6 @@ {"Last month","Geçen ay"}. {"Last year","Geçen yıl"}. {"leaves the room","odadan ayrıldı"}. -{"Low level update script","Düşük seviye güncelleme betiği"}. {"Make participants list public","Katılımcı listesini herkese açık hale getir"}. {"Make room CAPTCHA protected","Odayı insan doğrulaması (captcha) korumalı hale getir"}. {"Make room members-only","Odayı sadece üyelere açık hale getir"}. @@ -146,16 +133,12 @@ {"Maximum Number of Occupants","Odada En Fazla Bulunabilecek Kişi Sayısı"}. {"May","Mayıs"}. {"Membership is required to enter this room","Bu odaya girmek için üyelik gerekiyor"}. -{"Members:","Üyeler:"}. -{"Memory","Bellek"}. {"Message body","Mesajın gövdesi"}. {"Middle Name","Ortanca İsim"}. {"Minimum interval between voice requests (in seconds)","Ses istekleri arasında olabilecek en az aralık (saniye olarak)"}. {"Moderator privileges required","Moderatör yetkileri gerekli"}. -{"Modified modules","Değişen modüller"}. {"Monday","Pazartesi"}. {"Name","İsim"}. -{"Name:","İsim:"}. {"Never","Asla"}. {"New Password:","Yeni Parola:"}. {"Nickname Registration at ","Takma İsim Kaydı : "}. @@ -177,12 +160,9 @@ {"Number of online users","Bağlı kullanıcı sayısı"}. {"Number of registered users","Kayıtlı kullanıcı sayısı"}. {"October","Ekim"}. -{"Offline Messages","Çevirim-dışı Mesajlar"}. -{"Offline Messages:","Çevirim-dışı Mesajlar:"}. {"OK","Tamam"}. {"Old Password:","Eski Parola:"}. {"Online Users","Bağlı Kullanıcılar"}. -{"Online Users:","Bağlı Kullanıcılar:"}. {"Online","Bağlı"}. {"Only deliver notifications to available users","Uyarıları sadece durumu uygun kullanıcılara ulaştır"}. {"Only moderators and participants are allowed to change the subject in this room","Sadece moderatörlerin ve katılımcıların bu odanın konusunu değiştirmesine izin veriliyor"}. @@ -194,16 +174,13 @@ {"Organization Name","Kurum İsmi"}. {"Organization Unit","Kurumun İlgili Birimi"}. {"Outgoing s2s Connections","Giden s2s Bağlantıları"}. -{"Outgoing s2s Connections:","Giden s2s Bağlantıları:"}. {"Owner privileges required","Sahip yetkileri gerekli"}. -{"Packet","Paket"}. {"Password Verification","Parola Doğrulaması"}. {"Password Verification:","Parola Doğrulaması:"}. {"Password","Parola"}. {"Password:","Parola:"}. {"Path to Dir","Dizinin Yolu"}. {"Path to File","Dosyanın Yolu"}. -{"Pending","Sıra Bekleyen"}. {"Period: ","Periyot:"}. {"Persist items to storage","Öğeleri depoda kalıcı hale getir"}. {"Ping","Ping"}. @@ -220,17 +197,12 @@ {"RAM copy","RAM kopyala"}. {"Really delete message of the day?","Günün mesajını silmek istediğinize emin misiniz?"}. {"Recipient is not in the conference room","Alıcı konferans odasında değil"}. -{"Registered Users","Kayıtlı Kullanıcılar"}. -{"Registered Users:","Kayıtlı Kullanıcılar:"}. {"Register","Kayıt Ol"}. {"Remote copy","Uzak kopyala"}. -{"Remove All Offline Messages","Tüm Çevirim-dışı Mesajları Kaldır"}. {"Remove User","Kullanıcıyı Kaldır"}. -{"Remove","Kaldır"}. {"Replaced by new connection","Eski bağlantı yenisi ile değiştirildi"}. {"Resources","Kaynaklar"}. {"Restart Service","Servisi Tekrar Başlat"}. -{"Restart","Tekrar Başlat"}. {"Restore Backup from File at ","Dosyadaki Yedekten Geri Al : "}. {"Restore binary backup after next ejabberd restart (requires less memory):","ejabberd'nin bir sonraki tekrar başlatılışında ikili yedekten geri al (daha az bellek gerektirir)"}. {"Restore binary backup immediately:","Hemen ikili yedekten geri al:"}. @@ -243,10 +215,8 @@ {"Room title","Oda başlığı"}. {"Roster groups allowed to subscribe","Üye olunmasına izin verilen kontak listesi grupları"}. {"Roster size","İsim listesi boyutu"}. -{"RPC Call Error","RPC Çağrı Hatası"}. {"Running Nodes","Çalışan Düğümler"}. {"Saturday","Cumartesi"}. -{"Script check","Betik kontrolü"}. {"Search Results for ","Arama sonuçları : "}. {"Search users in ","Kullanıcılarda arama yap : "}. {"Send announcement to all online users on all hosts","Duyuruyu tüm sunuculardaki tüm bağlı kullanıcılara yolla"}. @@ -264,18 +234,12 @@ {"Specify the access model","Erişim modelini belirtiniz"}. {"Specify the event message type","Olay mesaj tipini belirtiniz"}. {"Specify the publisher model","Yayıncı modelini belirtiniz"}. -{"Statistics of ~p","~p istatistikleri"}. -{"Statistics","İstatistikler"}. -{"Stop","Durdur"}. {"Stopped Nodes","Durdurulmuş Düğümler"}. -{"Storage Type","Depolama Tipi"}. {"Store binary backup:","İkili yedeği sakla:"}. {"Store plain text backup:","Düz metin yedeği sakla:"}. {"Subject","Konu"}. -{"Submit","Gönder"}. {"Submitted","Gönderilenler"}. {"Subscriber Address","Üye Olanın Adresi"}. -{"Subscription","Üyelik"}. {"Sunday","Pazar"}. {"That nickname is already in use by another occupant","Takma isim odanın başka bir sakini tarafından halihazırda kullanımda"}. {"That nickname is registered by another person","O takma isim başka biri tarafından kaydettirilmiş"}. @@ -289,24 +253,14 @@ {"This room is not anonymous","Bu oda anonim değil"}. {"Thursday","Perşembe"}. {"Time delay","Zaman gecikmesi"}. -{"Time","Zaman"}. -{"To","Kime"}. {"Too many CAPTCHA requests","Çok fazla CAPTCHA isteği"}. {"Traffic rate limit is exceeded","Trafik oran sınırı aşıldı"}. -{"Transactions Aborted:","İptal Edilen Hareketler (Transactions):"}. -{"Transactions Committed:","Tamamlanan Hareketler (Transactions Committed):"}. -{"Transactions Logged:","Kaydı Tutulan Hareketler (Transactions):"}. -{"Transactions Restarted:","Tekrar Başlatılan Hareketler (Transactions):"}. {"Tuesday","Salı"}. {"Unable to generate a CAPTCHA","İnsan doğrulaması (CAPTCHA) oluşturulamadı"}. {"Unauthorized","Yetkisiz"}. {"Unregister","Kaydı Sil"}. {"Update message of the day (don't send)","Günün mesajını güncelle (gönderme)"}. {"Update message of the day on all hosts (don't send)","Tüm sunuculardaki günün mesajını güncelle (gönderme)"}. -{"Update plan","Planı güncelle"}. -{"Update script","Betiği Güncelle"}. -{"Update","GÜncelle"}. -{"Uptime:","Hizmet Süresi:"}. {"User JID","Kullanıcı JID"}. {"User Management","Kullanıcı Yönetimi"}. {"User","Kullanıcı"}. @@ -314,7 +268,6 @@ {"Users are not allowed to register accounts so quickly","Kullanıcıların bu kadar hızlı hesap açmalarına izin verilmiyor"}. {"Users Last Activity","Kullanıcıların Son Aktiviteleri"}. {"Users","Kullanıcılar"}. -{"Validate","Geçerli"}. {"vCard User Search","vCard Kullanıcı Araması"}. {"Virtual Hosts","Sanal Sunucuları"}. {"Visitors are not allowed to change their nicknames in this room","Bu odada ziyaretçilerin takma isimlerini değiştirmesine izin verilmiyor"}. diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 47e0029bf..32e0746a6 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -16,8 +16,6 @@ {"Account doesn't exist","Обліковий запис не існує"}. {"Action on user","Дія над користувачем"}. {"Add a hat to a user","Додати капелюх користувачу"}. -{"Add Jabber ID","Додати Jabber ID"}. -{"Add New","Додати"}. {"Add User","Додати користувача"}. {"Administration of ","Адміністрування "}. {"Administration","Адміністрування"}. @@ -48,7 +46,6 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Будь-хто, хто має підписку на отримання інформації про присутність в обох випадках або може підписуватись та отримувати матеріали"}. {"Anyone with Voice","Будь-хто, хто має голос"}. {"Anyone","Будь-хто"}. -{"Apparently your account has no administration rights in this server. Please check how to grant admin rights in: https://docs.ejabberd.im/admin/installation/#administration-account","Очевидно, ваш обліковий запис не має прав адміністратора на цьому сервері. Будь ласка, перевірте, як отримати права адміністратора за посиланням: https://docs.ejabberd.im/admin/installation/#administration-account"}. {"April","Квітень"}. {"Attribute 'channel' is required for this request","Атрибут \"канал\" є обов'язковим для цього запиту"}. {"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' обов'язковий для MIX повідомлень"}. @@ -93,30 +90,20 @@ {"Conference room does not exist","Кімната для переговорів відсутня"}. {"Configuration of room ~s","Конфігурація кімнати ~s"}. {"Configuration","Конфігурація"}. -{"Connected Resources:","Підключені ресурси:"}. {"Contact Addresses (normally, room owner or owners)","Контактні адреси (зазвичай, власника або власників кімнати)"}. -{"Contrib Modules","Запуск модулів"}. {"Country","Країна"}. -{"CPU Time:","Час роботи процесора:"}. {"Current Discussion Topic","Поточна тема обговорення"}. {"Database failure","Збій бази даних"}. -{"Database Tables at ~p","Таблиці бази даних на ~p"}. {"Database Tables Configuration at ","Конфігурація таблиць бази даних на "}. {"Database","База даних"}. {"December","Грудень"}. {"Default users as participants","Користувачі за замовчуванням як учасники"}. -{"Delete content","Видалити вміст"}. {"Delete message of the day on all hosts","Видалити повідомлення дня на усіх хостах"}. {"Delete message of the day","Видалити повідомлення дня"}. -{"Delete Selected","Видалити вибране"}. -{"Delete table","Видалити таблицю"}. {"Delete User","Видалити користувача"}. {"Deliver event notifications","Доставляти сповіщення про події"}. {"Deliver payloads with event notifications","Доставляти разом з повідомленнями про публікації самі публікації"}. -{"Description:","Опис:"}. {"Disc only copy","Тільки диск"}. -{"'Displayed groups' not added (they do not exist!): ","\"Відображені групи\" не додано (вони не існують!): "}. -{"Displayed:","Відображено:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","Нікому не кажіть свій пароль, навіть адміністраторам XMPP-сервера."}. {"Dump Backup to Text File at ","Копіювання в текстовий файл на "}. {"Dump to Text File","Копіювання в текстовий файл"}. @@ -131,7 +118,6 @@ {"ejabberd vCard module","ejabberd vCard модуль"}. {"ejabberd Web Admin","Веб-інтерфейс Адміністрування ejabberd"}. {"ejabberd","ejabberd"}. -{"Elements","Елементи"}. {"Email Address","Адреса ел. пошти"}. {"Email","Електронна пошта"}. {"Enable hats","Увімкнути капелюхи"}. @@ -145,7 +131,6 @@ {"Enter path to text file","Введіть шлях до текстового файла"}. {"Enter the text you see","Введіть текст, що ви бачите"}. {"Erlang XMPP Server","Ерланґ XMPP Сервер"}. -{"Error","Помилка"}. {"Exclude Jabber IDs from CAPTCHA challenge","Пропускати ці Jabber ID без CAPTCHA-запиту"}. {"Export all tables as SQL queries to a file:","Експортувати всі таблиці у файл як SQL запити:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Експорт даних всіх користувачів сервера до файлу PIEFXIS (XEP-0227):"}. @@ -164,7 +149,6 @@ {"Fill in the form to search for any matching XMPP User","Заповніть форму для пошуку будь-якого відповідного користувача XMPP"}. {"Friday","П'ятниця"}. {"From ~ts","Від ~ts"}. -{"From","Від кого"}. {"Full List of Room Admins","Повний перелік адміністраторів кімнати"}. {"Full List of Room Owners","Повний перелік власників кімнати"}. {"Full Name","Повне ім'я"}. @@ -174,13 +158,9 @@ {"Get Number of Registered Users","Отримати Кількість Зареєстрованих Користувачів"}. {"Get Pending","Очікування"}. {"Get User Last Login Time","Отримати Час Останнього Підключення Користувача"}. -{"Get User Password","Отримати Пароль Користувача"}. {"Get User Statistics","Отримати Статистику по Користувачу"}. {"Given Name","По-батькові"}. {"Grant voice to this person?","Надати голос персоні?"}. -{"Groups that will be displayed to the members","Групи, які показуватимуться учасникам"}. -{"Groups","Групи"}. -{"Group","Група"}. {"has been banned","заборонили вхід в кімнату"}. {"has been kicked because of a system shutdown","вигнано з кімнати внаслідок зупинки системи"}. {"has been kicked because of an affiliation change","вигнано з кімнати внаслідок зміни рангу"}. @@ -190,7 +170,6 @@ {"Hat URI","Назва URI"}. {"Hats limit exceeded","Перевищено швидкість передачі інформації"}. {"Host unknown","Невідоме ім'я сервера"}. -{"Host","Хост"}. {"HTTP File Upload","Відвантаження файлів по HTTP"}. {"Idle connection","Неактивне підключення"}. {"If you don't see the CAPTCHA image here, visit the web page.","Якщо ви не бачите зображення CAPTCHA, перейдіть за адресою."}. @@ -204,7 +183,6 @@ {"Import Users From jabberd14 Spool Files","Імпорт користувачів з jabberd14 файлів \"Spool\""}. {"Improper domain part of 'from' attribute","Неправильна доменна частина атрибута \"from\""}. {"Improper message type","Неправильний тип повідомлення"}. -{"Incoming s2s Connections:","Вхідні s2s-з'єднання:"}. {"Incorrect CAPTCHA submit","Неправильний ввід CAPTCHA"}. {"Incorrect data form","Неправильна форма даних"}. {"Incorrect password","Неправильний пароль"}. @@ -232,7 +210,6 @@ {"July","липня"}. {"June","червня"}. {"Just created","Щойно створено"}. -{"Label:","Мітка:"}. {"Last Activity","Останнє підключення"}. {"Last login","Останнє підключення"}. {"Last message","Останнє повідомлення"}. @@ -240,11 +217,9 @@ {"Last year","За останній рік"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Найменш значущі біти хеш-функції SHA-256 від тексту мають відповідати шістнадцятковій мітці."}. {"leaves the room","вийшов(ла) з кімнати"}. -{"List of rooms","Перелік кімнат"}. {"List of users with hats","Список користувачів із капелюхами"}. {"List users with hats","Список користувачів із капелюхами"}. {"Logging","Журналювання"}. -{"Low level update script","Низькорівневий сценарій поновлення"}. {"Make participants list public","Зробити список учасників видимим всім"}. {"Make room CAPTCHA protected","Зробити кімнату захищеною капчею"}. {"Make room members-only","Кімната тільки для зареєтрованых учасників"}. @@ -262,11 +237,8 @@ {"Maximum number of items to persist","Максимальна кількість елементів для збереження"}. {"Maximum Number of Occupants","Максимальна кількість учасників"}. {"May","травня"}. -{"Members not added (inexistent vhost!): ","Учасників не додано (вірт. сервер не існує!): "}. {"Membership is required to enter this room","В цю конференцію можуть входити тільки її члени"}. -{"Members:","Члени:"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","Запам'ятайте свій пароль або запишіть його на папері та зберігайте у безпечному місці. У XMPP не існує автоматизованого способу відновлення паролю, якщо ви його забудете."}. -{"Memory","Пам'ять"}. {"Mere Availability in XMPP (No Show Value)","Проста доступність у XMPP (без показу статусу)"}. {"Message body","Тіло повідомлення"}. {"Message not found in forwarded payload","Повідомлення не знайдено в пересланому вмісті"}. @@ -278,15 +250,12 @@ {"Moderator privileges required","Необхідні права модератора"}. {"Moderators Only","Тільки модераторам"}. {"Moderator","Модератор"}. -{"Modified modules","Змінені модулі"}. {"Module failed to handle the query","Модулю не вдалося обробити запит"}. {"Monday","Понеділок"}. {"Multicast","Мультікаст"}. {"Multiple elements are not allowed by RFC6121","Кілька елементів не дозволені RFC6121"}. {"Multi-User Chat","Багато-користувальницький чат"}. -{"Name in the rosters where this group will be displayed","Назва у списку контактів, де ця група буде відображена"}. {"Name","Назва"}. -{"Name:","Назва:"}. {"Natural Language for Room Discussions","Розмовна мова для обговорень у кімнаті"}. {"Natural-Language Room Name","Назва кімнати розмовною мовою"}. {"Neither 'jid' nor 'nick' attribute found","Не знайдено ні атрибута \"jid\", ні \"nick\""}. @@ -351,14 +320,10 @@ {"Occupants are allowed to query others","Учасникам дозволено ставити запитання іншим"}. {"Occupants May Change the Subject","Учасникам дозволено змінювати тему"}. {"October","грудня"}. -{"Offline Messages","Офлайнові повідомлення"}. -{"Offline Messages:","Офлайнові повідомлення:"}. {"OK","Продовжити"}. {"Old Password:","Старий пароль:"}. {"Online Users","Підключені користувачі"}. -{"Online Users:","Підключені користувачі:"}. {"Online","Підключений"}. -{"Only admins can see this","Тільки адміністратори можуть це бачити"}. {"Only collection node owners may associate leaf nodes with the collection","Лише власники вузлів колекції можуть асоціювати листові вузли з колекцією"}. {"Only deliver notifications to available users","Доставляти повідомленнями тільки доступним користувачам"}. {"Only or tags are allowed","Дозволені лише теги або "}. @@ -378,10 +343,8 @@ {"Organization Unit","Відділ організації"}. {"Other Modules Available:","Інші доступні модулі:"}. {"Outgoing s2s Connections","Вихідні s2s-з'єднання"}. -{"Outgoing s2s Connections:","Вихідні s2s-з'єднання:"}. {"Owner privileges required","Необхідні права власника"}. {"Packet relay is denied by service policy","Пересилання пакетів заборонене політикою сервісу"}. -{"Packet","Пакет"}. {"Participant ID","ID учасника"}. {"Participant","Учасник"}. {"Password Verification","Перевірка Пароля"}. @@ -391,7 +354,6 @@ {"Path to Dir","Шлях до директорії"}. {"Path to File","Шлях до файла"}. {"Payload semantic type information","Інформація про тип вмісту даних"}. -{"Pending","Очікування"}. {"Period: ","Період: "}. {"Persist items to storage","Зберігати елементи в сховищі"}. {"Persistent","Тривалий"}. @@ -422,17 +384,12 @@ {"Receive notification from all descendent nodes","Отримувати сповіщення від усіх підпорядкованих вузлів"}. {"Recipient is not in the conference room","Адресата немає в конференції"}. {"Register an XMPP account","Зареєструвати XMPP-запис"}. -{"Registered Users","Зареєстровані користувачі"}. -{"Registered Users:","Зареєстровані користувачі:"}. {"Register","Реєстрація"}. {"Remote copy","не зберігаеться локально"}. -{"Remove All Offline Messages","Видалити всі офлайнові повідомлення"}. {"Remove User","Видалити користувача"}. -{"Remove","Видалити"}. {"Replaced by new connection","Замінено новим з'єднанням"}. {"Resources","Ресурси"}. {"Restart Service","Перезапустити Сервіс"}. -{"Restart","Перезапустити"}. {"Restore Backup from File at ","Відновлення з резервної копії на "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Відновити з бінарної резервної копії при наступному запуску (потребує менше пам'яті):"}. {"Restore binary backup immediately:","Відновити з бінарної резервної копії негайно:"}. @@ -447,14 +404,10 @@ {"Room terminates","Кімната припиняється"}. {"Room title","Назва кімнати"}. {"Roster groups allowed to subscribe","Дозволені для підписки групи ростера"}. -{"Roster of ~ts","Список контактів ~ts"}. {"Roster size","Кількість контактів"}. -{"Roster:","Список контактів:"}. -{"RPC Call Error","Помилка виклику RPC"}. {"Running Nodes","Працюючі вузли"}. {"~s invites you to the room ~s","~s запрошує вас до кімнати ~s"}. {"Saturday","Субота"}. -{"Script check","Перевірка сценарію"}. {"Search Results for ","Результати пошуку в "}. {"Search users in ","Пошук користувачів в "}. {"Send announcement to all online users on all hosts","Надіслати сповіщення всім підключеним користувачам на всіх віртуальних серверах"}. @@ -472,18 +425,12 @@ {"Specify the access model","Визначити модель доступу"}. {"Specify the event message type","Вкажіть тип повідомлень зі сповіщеннями про події"}. {"Specify the publisher model","Умови публікації"}. -{"Statistics of ~p","Статистика вузла ~p"}. -{"Statistics","Статистика"}. {"Stopped Nodes","Зупинені вузли"}. -{"Stop","Зупинити"}. -{"Storage Type","Тип таблиці"}. {"Store binary backup:","Зберегти бінарну резервну копію:"}. {"Store plain text backup:","Зберегти текстову резервну копію:"}. {"Subject","Тема"}. {"Submitted","Відправлено"}. -{"Submit","Надіслати"}. {"Subscriber Address","Адреса абонента"}. -{"Subscription","Підписка"}. {"Sunday","Неділя"}. {"That nickname is already in use by another occupant","Псевдонім зайнято кимось з присутніх"}. {"That nickname is registered by another person","Псевдонім зареєстровано кимось іншим"}. @@ -508,7 +455,6 @@ {"Thursday","Четвер"}. {"Time delay","Час затримки"}. {"Timed out waiting for stream resumption","Час очікування на відновлення потоку закінчився"}. -{"Time","Час"}. {"To register, visit ~s","Щоб зареєструватися, відвідайте ~s"}. {"To ~ts","До ~ts"}. {"Token TTL","Токен TTL"}. @@ -521,13 +467,7 @@ {"Too many receiver fields were specified","Вказано забагато одержувачів"}. {"Too many unacked stanzas","Занадто багато пакетів без відповідей"}. {"Too many users in this conference","Надто багато користувачів у цій конференції"}. -{"Total rooms","Всього кімнат"}. -{"To","Кому"}. {"Traffic rate limit is exceeded","Швидкість передачі інформації було перевищено"}. -{"Transactions Aborted:","Транзакції відмінені:"}. -{"Transactions Committed:","Транзакції завершені:"}. -{"Transactions Logged:","Транзакції запротокольовані:"}. -{"Transactions Restarted:","Транзакції перезапущені:"}. {"~ts's Offline Messages Queue","Черга автономних повідомлень ~ts"}. {"Tuesday","Вівторок"}. {"Unable to generate a CAPTCHA","Нема можливості згенерувати капчу"}. @@ -537,16 +477,10 @@ {"Unexpected error condition: ~p","Умова несподіваної помилки: ~p"}. {"Unregister an XMPP account","Видалити обліковий запис XMPP"}. {"Unregister","Видалити"}. -{"Unselect All","Скасувати виділення з усіх"}. {"Unsupported element","Непідтримуваний елемент "}. {"Unsupported version","Непідтримувана версія"}. {"Update message of the day (don't send)","Оновити повідомлення дня (не надсилати)"}. {"Update message of the day on all hosts (don't send)","Оновити повідомлення дня на всіх хостах (не надсилати)"}. -{"Update plan","План оновлення"}. -{"Update ~p","Оновлення ~p"}. -{"Update script","Сценарій поновлення"}. -{"Update","Обновити"}. -{"Uptime:","Час роботи:"}. {"URL for Archived Discussion Logs","URL-адреса для журналів архівних обговорень"}. {"User already exists","Користувач уже існує"}. {"User JID","JID Користувача"}. @@ -561,12 +495,10 @@ {"Users Last Activity","Статистика останнього підключення користувачів"}. {"Users","Користувачі"}. {"User","Користувач"}. -{"Validate","Затвердити"}. {"Value of '~s' should be boolean","Значення \"~s\" має бути логічним"}. {"Value of '~s' should be datetime string","Значення \"~s\" має бути рядком дати і часу"}. {"Value of '~s' should be integer","Значення \"~s\" має бути цілим числом"}. {"vCard User Search","Пошук користувачів по vCard"}. -{"View Queue","Переглянути чергу"}. {"Virtual Hosts","віртуальні хости"}. {"Visitors are not allowed to change their nicknames in this room","Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті"}. {"Visitors are not allowed to send messages to all occupants","Відвідувачам не дозволяється надсилати повідомлення всім присутнім"}. diff --git a/priv/msgs/vi.msg b/priv/msgs/vi.msg index 600ef6422..186eb20e7 100644 --- a/priv/msgs/vi.msg +++ b/priv/msgs/vi.msg @@ -6,8 +6,6 @@ {" has set the subject to: "," đã đặt chủ đề thành: "}. {"Access denied by service policy","Sự truy cập bị chặn theo chính sách phục vụ"}. {"Action on user","Hành động đối với người sử dụng"}. -{"Add Jabber ID","Thêm Jabber ID"}. -{"Add New","Thêm Mới"}. {"Add User","Thêm Người Sử Dụng"}. {"Administration of ","Quản trị về "}. {"Administration","Quản trị"}. @@ -37,20 +35,16 @@ {"Commands","Lệnh"}. {"Conference room does not exist","Phòng họp không tồn tại"}. {"Configuration","Cấu hình"}. -{"Connected Resources:","Tài Nguyên Được Kết Nối:"}. {"Country","Quốc gia"}. -{"CPU Time:","Thời Gian CPU:"}. {"Database Tables Configuration at ","Cấu Hình Bảng Cơ Sở Dữ Liệu tại"}. {"Database","Cơ sở dữ liệu"}. {"December","Tháng Mười Hai"}. {"Default users as participants","Người sử dụng mặc định là người tham dự"}. {"Delete message of the day on all hosts","Xóa thư trong ngày trên tất cả các máy chủ"}. {"Delete message of the day","Xóa thư trong ngày"}. -{"Delete Selected","Tùy chọn Xóa được Chọn"}. {"Delete User","Xóa Người Sử Dụng"}. {"Deliver event notifications","Đưa ra các thông báo sự kiện"}. {"Deliver payloads with event notifications","Đưa ra thông tin dung lượng với các thông báo sự kiện"}. -{"Description:","Miêu tả:"}. {"Disc only copy","Chỉ sao chép vào đĩa"}. {"Dump Backup to Text File at ","Kết Xuất Sao Lưu ra Tập Tin Văn Bản tại"}. {"Dump to Text File","Kết xuất ra Tập Tin Văn Bản"}. @@ -70,18 +64,13 @@ {"Family Name","Họ"}. {"February","Tháng Hai"}. {"Friday","Thứ Sáu"}. -{"From","Từ"}. {"Full Name","Tên Đầy Đủ"}. {"Get Number of Online Users","Nhận Số Người Sử Dụng Trực Tuyến"}. {"Get Number of Registered Users","Nhận Số Người Sử Dụng Đã Đăng Ký"}. {"Get User Last Login Time","Nhận Thời Gian Đăng Nhập Cuối Cùng Của Người Sử Dụng"}. -{"Get User Password","Nhận Mật Khẩu Người Sử Dụng"}. {"Get User Statistics","Nhận Thông Tin Thống Kê Người Sử Dụng"}. -{"Group","Nhóm"}. -{"Groups","Nhóm"}. {"has been banned","đã bị cấm"}. {"has been kicked","đã bị đẩy ra khỏi"}. -{"Host","Máy chủ"}. {"Import Directory","Nhập Thư Mục"}. {"Import File","Nhập Tập Tin"}. {"Import User from File at ","Nhập Người Sử Dụng từ Tập Tin tại"}. @@ -103,7 +92,6 @@ {"Last month","Tháng trước"}. {"Last year","Năm trước"}. {"leaves the room","rời khỏi phòng này"}. -{"Low level update script","Lệnh cập nhật mức độ thấp"}. {"Make participants list public","Tạo danh sách người tham dự công khai"}. {"Make room members-only","Tạo phòng chỉ cho phép tư cách thành viên tham gia"}. {"Make room password protected","Tạo phòng được bảo vệ bằng mật khẩu"}. @@ -113,14 +101,11 @@ {"Max payload size in bytes","Kích thước dung lượng byte tối đa"}. {"Maximum Number of Occupants","Số Lượng Người Tham Dự Tối Đa"}. {"May","Tháng Năm"}. -{"Members:","Thành viên:"}. -{"Memory","Bộ Nhớ"}. {"Message body","Thân thư"}. {"Middle Name","Họ Đệm"}. {"Moderator privileges required","Yêu cầu đặc quyền của nhà điều phối"}. {"Monday","Thứ Hai"}. {"Name","Tên"}. -{"Name:","Tên:"}. {"Never","Không bao giờ"}. {"Nickname Registration at ","Đăng Ký Bí Danh tại"}. {"Nickname ~s does not exist in the room","Bí danh ~s không tồn tại trong phòng này"}. @@ -140,11 +125,8 @@ {"Number of online users","Số người sử dụng trực tuyến"}. {"Number of registered users","Số người sử dụng đã đăng ký"}. {"October","Tháng Mười"}. -{"Offline Messages","Thư Ngoại Tuyến"}. -{"Offline Messages:","Thư Ngoại Tuyến:"}. {"OK","OK"}. {"Online Users","Người Sử Dụng Trực Tuyến"}. -{"Online Users:","Người Sử Dụng Trực Tuyến:"}. {"Online","Trực tuyến"}. {"Only deliver notifications to available users","Chỉ gửi thông báo đến những người sử dụng hiện có"}. {"Only occupants are allowed to send messages to the conference","Chỉ có những đối tượng tham gia mới được phép gửi thư đến phòng họp"}. @@ -153,15 +135,12 @@ {"Organization Name","Tên Tổ Chức"}. {"Organization Unit","Bộ Phận"}. {"Outgoing s2s Connections","Kết Nối Bên Ngoài s2s"}. -{"Outgoing s2s Connections:","Kết Nối Bên Ngoài s2s:"}. {"Owner privileges required","Yêu cầu đặc quyền của người sở hữu"}. -{"Packet","Gói thông tin"}. {"Password Verification","Kiểm Tra Mật Khẩu"}. {"Password","Mật Khẩu"}. {"Password:","Mật Khẩu:"}. {"Path to Dir","Đường Dẫn đến Thư Mục"}. {"Path to File","Đường dẫn đến Tập Tin"}. -{"Pending","Chờ"}. {"Period: ","Giai đoạn: "}. {"Persist items to storage","Những mục cần để lưu trữ"}. {"Ping","Ping"}. @@ -175,15 +154,11 @@ {"RAM copy","Sao chép vào RAM"}. {"Really delete message of the day?","Có thực sự xóa thư trong ngày này không?"}. {"Recipient is not in the conference room","Người nhận không có trong phòng họp"}. -{"Registered Users","Người Sử Dụng Đã Đăng Ký"}. -{"Registered Users:","Người Sử Dụng Đã Đăng Ký:"}. {"Remote copy","Sao chép từ xa"}. {"Remove User","Gỡ Bỏ Người Sử Dụng"}. -{"Remove","Gỡ bỏ"}. {"Replaced by new connection","Được thay thế bởi kết nối mới"}. {"Resources","Nguồn tài nguyên"}. {"Restart Service","Khởi Động Lại Dịch Vụ"}. -{"Restart","Khởi động lại"}. {"Restore Backup from File at ","Phục hồi Sao Lưu từ Tập Tin tại "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Khôi phục bản sao lưu dự phòng dạng nhị phân sau lần khởi động ejabberd kế tiếp (yêu cầu ít bộ nhớ hơn):"}. {"Restore binary backup immediately:","Khôi phục bản sao lưu dự phòng dạng nhị phận ngay lập tức:"}. @@ -193,10 +168,8 @@ {"Room creation is denied by service policy","Việc tạo phòng bị ngăn lại theo chính sách dịch vụ"}. {"Room title","Tên phòng"}. {"Roster size","Kích thước bảng phân công"}. -{"RPC Call Error","Lỗi Gọi RPC"}. {"Running Nodes","Nút Hoạt Động"}. {"Saturday","Thứ Bảy"}. -{"Script check","Lệnh kiểm tra"}. {"Search Results for ","Kết Quả Tìm Kiếm cho "}. {"Search users in ","Tìm kiếm người sử dụng trong"}. {"Send announcement to all online users on all hosts","Gửi thông báo đến tất cả người sử dụng trực tuyến trên tất cả các máy chủ"}. @@ -212,42 +185,25 @@ {"Shut Down Service","Tắt Dịch Vụ"}. {"Specify the access model","Xác định mô hình truy cập"}. {"Specify the publisher model","Xác định mô hình nhà xuất bản"}. -{"Statistics of ~p","Thống kê về ~p"}. -{"Statistics","Số liệu thống kê"}. -{"Stop","Dừng"}. {"Stopped Nodes","Nút Dừng"}. -{"Storage Type","Loại Lưu Trữ"}. {"Store binary backup:","Lưu dữ liệu sao lưu dạng nhị phân:"}. {"Store plain text backup:","Khôi phục bản sao lưu dự phòng thuần văn bản"}. {"Subject","Tiêu đề"}. -{"Submit","Gửi"}. {"Submitted","Đã gửi"}. {"Subscriber Address","Địa Chỉ Người Đăng Ký"}. -{"Subscription","Đăng ký"}. {"Sunday","Chủ Nhật"}. {"the password is","mật khẩu là"}. {"This room is not anonymous","Phòng này không nặc danh"}. {"Thursday","Thứ Năm"}. {"Time delay","Thời gian trì hoãn"}. -{"Time","Thời Gian"}. -{"To","Đến"}. {"Traffic rate limit is exceeded","Quá giới hạn tỷ lệ lưu lượng truyền tải"}. -{"Transactions Aborted:","Giao Dịch Hủy Bỏ:"}. -{"Transactions Committed:","Giao Dịch Được Cam Kết:"}. -{"Transactions Logged:","Giao Dịch Được Ghi Nhận:"}. -{"Transactions Restarted:","Giao Dịch Khởi Động Lại:"}. {"Tuesday","Thứ Ba"}. {"Update message of the day (don't send)","Cập nhật thư trong ngày (không gửi)"}. {"Update message of the day on all hosts (don't send)","Cập nhật thư trong ngày trên tất cả các máy chủ (không gửi)"}. -{"Update plan","Kế hoạch cập nhật"}. -{"Update script","Cập nhận lệnh"}. -{"Update","Cập Nhật"}. -{"Uptime:","Thời gian tải lên:"}. {"User Management","Quản Lý Người Sử Dụng"}. {"User","Người sử dụng"}. {"Users Last Activity","Hoạt Động Cuối Cùng Của Người Sử Dụng"}. {"Users","Người sử dụng"}. -{"Validate","Xác nhận hợp lệ"}. {"vCard User Search","Tìm Kiếm Người Sử Dụng vCard"}. {"Virtual Hosts","Máy Chủ Ảo"}. {"Visitors are not allowed to send messages to all occupants","Người ghé thăm không được phép gửi thư đến tất cả các người tham dự"}. diff --git a/priv/msgs/wa.msg b/priv/msgs/wa.msg index ef375745c..23fcf49f5 100644 --- a/priv/msgs/wa.msg +++ b/priv/msgs/wa.msg @@ -9,8 +9,6 @@ {"Accept","Accepter"}. {"Access denied by service policy","L' accès a stî rfuzé pal politike do siervice"}. {"Action on user","Accion so l' uzeu"}. -{"Add Jabber ID","Radjouter èn ID Jabber"}. -{"Add New","Radjouter"}. {"Add User","Radjouter èn uzeu"}. {"Administration of ","Manaedjaedje di "}. {"Administration","Manaedjaedje"}. @@ -53,21 +51,16 @@ {"Conference room does not exist","Li såle di conferince n' egzistêye nén"}. {"Configuration of room ~s","Apontiaedje del såle ~s"}. {"Configuration","Apontiaedjes"}. -{"Connected Resources:","Raloyî avou les rsoûces:"}. {"Country","Payis"}. -{"CPU Time:","Tins CPU:"}. -{"Database Tables at ~p","Tåves del båze di dnêyes so ~p"}. {"Database Tables Configuration at ","Apontiaedje des tåves del båze di dnêyes so "}. {"Database","Båze di dnêyes"}. {"December","decimbe"}. {"Default users as participants","Les uzeus sont des pårticipants come prémetowe dujhance"}. {"Delete message of the day on all hosts","Disfacer l' messaedje do djoû so tos les lodjoes"}. {"Delete message of the day","Disfacer l' messaedje do djoû"}. -{"Delete Selected","Disfacer les elemints tchoezis"}. {"Delete User","Disfacer èn uzeu"}. {"Deliver event notifications","Evoyî des notifiaedjes d' evenmints"}. {"Deliver payloads with event notifications","Evoyî l' contnou avou les notifiaedjes d' evenmints"}. -{"Description:","Discrijhaedje:"}. {"Disc only copy","Copeye seulmint sol deure plake"}. {"Dump Backup to Text File at ","Copeye di såvritè viè on fitchî tecse so "}. {"Dump to Text File","Schaper en on fitchî tecse"}. @@ -79,7 +72,6 @@ {"ejabberd SOCKS5 Bytestreams module","Module SOCKS5 Bytestreams po ejabberd"}. {"ejabberd vCard module","Module vCard ejabberd"}. {"ejabberd Web Admin","Manaedjeu waibe ejabberd"}. -{"Elements","Elemints"}. {"Email","Emile"}. {"Enable logging","Mete en alaedje li djournå"}. {"Enable message archiving","Mete en alaedje l' årtchivaedje des messaedjes"}. @@ -90,7 +82,6 @@ {"Enter path to jabberd14 spool file","Dinez l' tchimin viè l' fitchî di spool jabberd14"}. {"Enter path to text file","Dinez l' tchimin viè l' fitchî tecse"}. {"Enter the text you see","Tapez l' tecse ki vos voeyoz"}. -{"Error","Aroke"}. {"Exclude Jabber IDs from CAPTCHA challenge","Esclure les IDs Jabber des kesses CAPTCHA"}. {"Export all tables as SQL queries to a file:","Espoirter totes les tåves, come des cmandes SQL, viè on fitchî"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","Espoirter les dnêyes di tos les uzeus do sierveu viè des fitchîs PIEFXIS (XEP-0227):"}. @@ -99,22 +90,17 @@ {"Family Name","No d' famile"}. {"February","fevrî"}. {"Friday","vénrdi"}. -{"From","Di"}. {"Full Name","No etir"}. {"Get Number of Online Users","Riçure li nombe d' uzeus raloyîs"}. {"Get Number of Registered Users","Riçure li nombe d' uzeus edjîstrés"}. {"Get User Last Login Time","Riçure li date/eure do dierin elodjaedje di l' uzeu"}. -{"Get User Password","Riçure sicret d' l' uzeu"}. {"Get User Statistics","Riçure les statistikes di l' uzeu"}. {"Grant voice to this person?","Permete li vwès po cisse djin ci?"}. -{"Group","Groupe"}. -{"Groups","Groupes"}. {"has been banned","a stî bani"}. {"has been kicked because of a system shutdown","a stî pité evoye cåze d' èn arestaedje do sistinme"}. {"has been kicked because of an affiliation change","a stî pité evoye cåze d' on candjmint d' afiyaedje"}. {"has been kicked because the room has been changed to members-only","a stî pité evoye cåze ki l' såle a stî ristrindowe åzès mimbes seulmint"}. {"has been kicked","a stî pité evoye"}. -{"Host","Sierveu"}. {"If you don't see the CAPTCHA image here, visit the web page.","Si vos n' voeyoz nole imådje CAPTCHA chal, vizitez l' pådje waibe."}. {"Import Directory","Sititchî d' on ridant"}. {"Import File","Sititchî d' on fitchî"}. @@ -125,7 +111,6 @@ {"Import Users from Dir at ","Sitichî des uzeus d' on ridant so "}. {"Import Users From jabberd14 Spool Files","Sititchî des uzeus Jabberd 1.4"}. {"Improper message type","Sôre di messaedje nén valide"}. -{"Incoming s2s Connections:","Raloyaedjes s2s en intrêye:"}. {"Incorrect password","Sicret nén corek"}. {"IP addresses","Adresses IP"}. {"is now known as","est asteure kinoxhou come"}. @@ -142,8 +127,6 @@ {"Last month","Dierin moes"}. {"Last year","Dierinne anêye"}. {"leaves the room","cwite li såle"}. -{"List of rooms","Djivêye des såles"}. -{"Low level update script","Sicripe di metaedje a djoû d' bas livea"}. {"Make participants list public","Rinde publike li djivêye des pårticipants"}. {"Make room CAPTCHA protected","Rinde li såle di berdelaedje protedjeye pa CAPTCHA"}. {"Make room members-only","Rinde li såle di berdelaedje ristrindowe ås mimbes seulmint"}. @@ -156,19 +139,15 @@ {"Maximum Number of Occupants","Nombe macsimom di prezints"}. {"May","may"}. {"Membership is required to enter this room","I fåt esse mimbe po poleur intrer dins cisse såle ci"}. -{"Members:","Mimbes:"}. -{"Memory","Memwere"}. {"Message body","Coir do messaedje"}. {"Middle Name","No do mitan"}. {"Minimum interval between voice requests (in seconds)","Tins minimom etur deus dmandes di vwès (e segondes)"}. {"Moderator privileges required","I fåt des priviledjes di moderateu"}. {"Moderator","Moderateu"}. -{"Modified modules","Modules di candjîs"}. {"Monday","londi"}. {"Multicast","Multicast"}. {"Multi-User Chat","Berdelaedje a sacwants"}. {"Name","No"}. -{"Name:","Pitit no:"}. {"Never","Måy"}. {"New Password:","Novea scret:"}. {"Nickname Registration at ","Edjîstraedje di metou no amon "}. @@ -191,12 +170,9 @@ {"Number of online users","Nombe d' uzeus raloyîs"}. {"Number of registered users","Nombe d' uzeus edjîstrés"}. {"October","octôbe"}. -{"Offline Messages","Messaedjes ki ratindèt"}. -{"Offline Messages:","Messaedjes ki ratindèt:"}. {"OK","'l est bon"}. {"Old Password:","Vî scret:"}. {"Online Users","Uzeus raloyîs"}. -{"Online Users:","Uzeus raloyîs:"}. {"Online","Raloyî"}. {"Only deliver notifications to available users","Seulmint evoyî des notifiaedje åzès uzeus disponibes"}. {"Only members may query archives of this room","Seulmint les mimbes polèt cweri les årtchives dins cisse såle ci"}. @@ -209,9 +185,7 @@ {"Organization Name","No d' l' organizåcion"}. {"Organization Unit","Unité d' l' organizåcion"}. {"Outgoing s2s Connections","Raloyaedjes s2s e rexhowe"}. -{"Outgoing s2s Connections:","Raloyaedjes s2s e rexhowe:"}. {"Owner privileges required","I fåt des priviledjes di prôpietaire"}. -{"Packet","Paket"}. {"Participant","Pårticipant"}. {"Password Verification","Acertinaedje do scret"}. {"Password Verification:","Acertinaedje do scret:"}. @@ -219,7 +193,6 @@ {"Password:","Sicret:"}. {"Path to Dir","Tchimin viè l' ridant"}. {"Path to File","Tchimin viè l' fitchî"}. -{"Pending","Ratindant"}. {"Period: ","Termene:"}. {"Persist items to storage","Cayets permanints a wårder"}. {"Ping","Ping"}. @@ -236,17 +209,12 @@ {"RAM copy","Copeye e memwere (RAM)"}. {"Really delete message of the day?","Voloz vs vormint disfacer l' messaedje do djoû?"}. {"Recipient is not in the conference room","Li riçuveu n' est nén dins l' såle di conferince"}. -{"Registered Users","Uzeus edjistrés"}. -{"Registered Users:","Uzeus edjistrés:"}. {"Register","Edjîstrer"}. {"Remote copy","Copeye å lon"}. -{"Remove All Offline Messages","Oister tos les messaedjes ki ratindèt"}. {"Remove User","Disfacer l' uzeu"}. -{"Remove","Oister"}. {"Replaced by new connection","Replaecî pa on novea raloyaedje"}. {"Resources","Rissoûces"}. {"Restart Service","Renonder siervice"}. -{"Restart","Renonder"}. {"Restore Backup from File at ","Rapexhî dispoy li fitchî copeye di såvrité so "}. {"Restore binary backup after next ejabberd restart (requires less memory):","Rapexhî l' copeye di såvrité binaire après l' renondaedje ki vént d' ejabberd (çoula prind moens d' memwere del fé insi):"}. {"Restore binary backup immediately:","Rapexhî do côp foû d' ene copeye di såvrité binaire:"}. @@ -260,10 +228,8 @@ {"Room title","Tite del såle"}. {"Roster groups allowed to subscribe","Pårtaedjîs groupes di soçons k' on s' î pout abouner"}. {"Roster size","Grandeu del djivêye des soçons"}. -{"RPC Call Error","Aroke di houcaedje RPC"}. {"Running Nodes","Nuks en alaedje"}. {"Saturday","semdi"}. -{"Script check","Acertinaedje do scripe"}. {"Search Results for ","Rizultats do cweraedje po "}. {"Search users in ","Cweri des uzeus dins "}. {"Send announcement to all online users on all hosts","Evoyî l' anonce a tos les uzeus raloyîs so tos les lodjoes"}. @@ -281,18 +247,12 @@ {"Specify the access model","Sipecifyî l' modele d' accès"}. {"Specify the event message type","Sipecifyî l' sôre do messaedje d' evenmint"}. {"Specify the publisher model","Dinez l' modele d' eplaideu"}. -{"Statistics of ~p","Sitatistikes di ~p"}. -{"Statistics","Sitatistikes"}. -{"Stop","Arester"}. {"Stopped Nodes","Nuks essoctés"}. -{"Storage Type","Sôre di wårdaedje"}. {"Store binary backup:","Copeye di såvrité binaire:"}. {"Store plain text backup:","Copeye di såvrité tecse:"}. {"Subject","Sudjet"}. -{"Submit","Evoyî"}. {"Submitted","Candjmints evoyîs"}. {"Subscriber Address","Adresse di l' abouné"}. -{"Subscription","Abounmimnt"}. {"Sunday","dimegne"}. {"That nickname is already in use by another occupant","Li metou no est ddja eployî pa ene ôte sakî sol såle"}. {"That nickname is registered by another person","Li metou no est ddja edjîstré pa ene ôte sakî"}. @@ -306,28 +266,16 @@ {"This room is not anonymous","Cisse såle ci n' est nén anonime"}. {"Thursday","djudi"}. {"Time delay","Tårdjaedje"}. -{"Time","Date"}. {"Too many CAPTCHA requests","Pår trop di dmandes CAPTCHA"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","I gn a-st avou pår trop (~p) d' otintifiaedjes k' ont fwait berwete vinant di ciste adresse IP la (~s). L' adresse serè disblokêye a ~s UTC"}. {"Too many unacked stanzas","Pår trop di messaedjes sins acertinaedje di rçuvaedje"}. -{"To","Po"}. -{"Total rooms","Totå di såles"}. {"Traffic rate limit is exceeded","Li limite pol volume di trafik a stî passêye"}. -{"Transactions Aborted:","Transaccions arestêyes:"}. -{"Transactions Committed:","Transaccions evoyeyes:"}. -{"Transactions Logged:","Transaccions wårdêyes e djournå:"}. -{"Transactions Restarted:","Transaccions renondêyes:"}. {"Tuesday","mårdi"}. {"Unable to generate a CAPTCHA","Nén moyén di djenerer on CAPTCHA"}. {"Unauthorized","Nén otorijhî"}. {"Unregister","Disdjîstrer"}. {"Update message of the day (don't send)","Mete a djoû l' messaedje do djoû (nén l' evoyî)"}. {"Update message of the day on all hosts (don't send)","Mete a djoû l' messaedje do djoû so tos les lodjoes (nén l' evoyî)"}. -{"Update plan","Plan d' metaedje a djoû"}. -{"Update ~p","Metaedje a djoû di ~p"}. -{"Update script","Sicripe di metaedje a djoû"}. -{"Update","Mete a djoû"}. -{"Uptime:","Tins dispoy l' enondaedje:"}. {"User JID","JID d' l' uzeu"}. {"User Management","Manaedjaedje des uzeus"}. {"Username:","No d' uzeu:"}. @@ -335,7 +283,6 @@ {"Users Last Activity","Dierinne activité des uzeus"}. {"Users","Uzeus"}. {"User","Uzeu"}. -{"Validate","Valider"}. {"vCard User Search","Calpin des uzeus"}. {"Virtual Hosts","Forveyous sierveus"}. {"Visitors are not allowed to change their nicknames in this room","Les viziteus èn polèt nén candjî leus metous no po ç' såle ci"}. From 394ba26d1d7acd395eb5075c211fd8aed8489a12 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 26 Jun 2024 17:59:04 +0200 Subject: [PATCH 0645/1302] ext_mod: Support to compile nested *.erl, and include other deps *.hrl This is useful to compile as a dependency https://github.com/deadtrickster/prometheus.erl --- src/ext_mod.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index fb7d5d438..fcf6fb172 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -594,15 +594,15 @@ compile_deps(LibDir) -> compile(LibDir) -> Bin = filename:join(LibDir, "ebin"), - Inc = filename:join(LibDir, "include"), Lib = filename:join(LibDir, "lib"), Src = filename:join(LibDir, "src"), - Options = [{outdir, Bin}, {i, Inc} | compile_options()], + Includes = [{i, Inc} || Inc <- filelib:wildcard(LibDir++"/../../**/include")], + Options = [{outdir, Bin}, {i, LibDir++"/.."} | Includes ++ compile_options()], filelib:ensure_dir(filename:join(Bin, ".")), [copy(App, Bin) || App <- filelib:wildcard(Src++"/*.app")], compile_c_files(LibDir), Er = [compile_erlang_file(Bin, File, Options) - || File <- filelib:wildcard(Src++"/*.erl")], + || File <- filelib:wildcard(Src++"/**/*.erl")], Ex = [compile_elixir_file(Bin, File) || File <- filelib:wildcard(Lib ++ "/**/*.ex")], compile_result(lists:flatten([Er, Ex])). From f3ad49ebc48ce2836177edd98d4c86c817b0489a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 26 Jun 2024 18:26:37 +0200 Subject: [PATCH 0646/1302] Fix copy deps *.app file, and strip *.src if present --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index fcf6fb172..5da3b34e7 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -599,7 +599,7 @@ compile(LibDir) -> Includes = [{i, Inc} || Inc <- filelib:wildcard(LibDir++"/../../**/include")], Options = [{outdir, Bin}, {i, LibDir++"/.."} | Includes ++ compile_options()], filelib:ensure_dir(filename:join(Bin, ".")), - [copy(App, Bin) || App <- filelib:wildcard(Src++"/*.app")], + [copy(App, filename:join(Bin, filename:basename(App, ".src"))) || App <- filelib:wildcard(Src++"/*.app*")], compile_c_files(LibDir), Er = [compile_erlang_file(Bin, File, Options) || File <- filelib:wildcard(Src++"/**/*.erl")], From 9b9eb409b2e9571b01872d76b14978cc82c4ee40 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 25 Jun 2024 16:50:03 +0200 Subject: [PATCH 0647/1302] join_cluster: Handle also atom arguments, useful for mod_libcluster --- src/ejabberd_admin.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index a1e8d52e3..eb33d2e0f 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -834,8 +834,9 @@ convert_to_yaml(In, Out) -> %%% Cluster management %%% -join_cluster(NodeBin) -> - Node = list_to_atom(binary_to_list(NodeBin)), +join_cluster(NodeBin) when is_binary(NodeBin) -> + join_cluster(list_to_atom(binary_to_list(NodeBin))); +join_cluster(Node) when is_atom(Node) -> IsNodes = lists:member(Node, ejabberd_cluster:get_nodes()), IsKnownNodes = lists:member(Node, ejabberd_cluster:get_known_nodes()), Ping = net_adm:ping(Node), @@ -876,8 +877,10 @@ join_cluster_here(Node, false, false, pong) -> {error, io_lib:format("Can't join node to this cluster: ~p", [Error])} end. -leave_cluster(NodeBin) -> - ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))). +leave_cluster(NodeBin) when is_binary(NodeBin) -> + leave_cluster(list_to_atom(binary_to_list(NodeBin))); +leave_cluster(Node) -> + ejabberd_cluster:leave(Node). list_cluster() -> ejabberd_cluster:get_nodes(). From d19bac70cedacfb1710964993597a53f4b645c20 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 10:54:09 +0200 Subject: [PATCH 0648/1302] Update version number of new commands, options and protocols to 24.06 --- ejabberd.doap | 2 +- src/ejabberd_admin.erl | 8 ++++---- src/ejabberd_options_doc.erl | 6 ++++-- src/ejabberd_sm.erl | 2 +- src/mod_admin_extra.erl | 18 +++++++++--------- src/mod_mam.erl | 2 +- src/mod_muc_admin.erl | 2 +- 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index da8678d5e..9644af2cc 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -759,7 +759,7 @@ 0.3.0 - 24.xx + 24.06 mod_mam diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index eb33d2e0f..37d5e622f 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -251,7 +251,7 @@ get_commands_spec() -> #ejabberd_commands{name = join_cluster, tags = [cluster], desc = "Join our local node into the cluster handled by Node", - note = "improved in 24.xx", + note = "improved in 24.06", module = ?MODULE, function = join_cluster, args_desc = ["Nodename of the node to join"], args_example = [<<"ejabberd1@machine7">>], @@ -259,7 +259,7 @@ get_commands_spec() -> result = {res, restuple}}, #ejabberd_commands{name = join_cluster_here, tags = [cluster], desc = "Join a remote Node here, into our cluster", - note = "added in 24.xx", + note = "added in 24.06", module = ?MODULE, function = join_cluster_here, args_desc = ["Nodename of the node to join here"], args_example = [<<"ejabberd1@machine7">>], @@ -286,7 +286,7 @@ get_commands_spec() -> result = {nodes, {list, {node, atom}}}}, #ejabberd_commands{name = list_cluster_detailed, tags = [cluster], desc = "List nodes (both running and known) and some stats", - note = "added in 24.xx", + note = "added in 24.06", module = ?MODULE, function = list_cluster_detailed, args = [], result_example = [{'ejabberd@localhost', "true", @@ -427,7 +427,7 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = get_master, tags = [cluster], desc = "Get master node of the clustered Mnesia tables", - note = "added in 24.xx", + note = "added in 24.06", longdesc = "If there is no master, returns `none`.", module = ?MODULE, function = get_master, result = {nodename, atom}}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index bd80461c0..061b1dccb 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -923,9 +923,11 @@ doc() -> [binary:part(ejabberd_config:version(), {0,5})]}}}, {update_sql_schema, #{value => "true | false", - note => "added in 23.10", + note => "updated in 24.06", desc => ?T("Allow ejabberd to update SQL schema. " + "This option was added in ejabberd 23.10, " + "and enabled by default since 24.06. " "The default value is 'true'.")}}, {oauth_access, #{value => ?T("AccessName"), @@ -1379,7 +1381,7 @@ doc() -> "or 'ram' if the latter is not set.")}}, {sql_server, #{value => "Host | IP Address | ODBC Connection String | Unix Socket Path", - note => "improved in 24.xx", + note => "improved in 24.06", desc => ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " "'mssql' or 'odbc' this can also be an ODBC connection string. " diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 93973dba1..91fcc5896 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -1054,7 +1054,7 @@ get_commands_spec() -> desc = "Disconnect user's active sessions", module = ?MODULE, function = kick_user_restuple, version = 2, - note = "modified in 24.xx", + note = "modified in 24.06", args = [{user, binary}, {host, binary}], args_desc = ["User name", "Server name"], args_example = [<<"user1">>, <<"example.com">>], diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 1bfbeb4d1..7a592462c 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -279,7 +279,7 @@ get_commands_spec() -> "and `_unban_account`_ API.", module = ?MODULE, function = ban_account_v2, version = 2, - note = "improved in 24.xx", + note = "improved in 24.06", args = [{user, binary}, {host, binary}, {reason, binary}], args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>], args_desc = ["User name to ban", "Server name", @@ -291,7 +291,7 @@ get_commands_spec() -> longdesc = "Check _`ban_account`_ API.", module = ?MODULE, function = get_ban_details, version = 2, - note = "added in 24.xx", + note = "added in 24.06", args = [{user, binary}, {host, binary}], args_example = [<<"attacker">>, <<"myserver.com">>], args_desc = ["User name to unban", "Server name"], @@ -309,7 +309,7 @@ get_commands_spec() -> longdesc = "Check _`ban_account`_ API.", module = ?MODULE, function = unban_account, version = 2, - note = "added in 24.xx", + note = "added in 24.06", args = [{user, binary}, {host, binary}], args_example = [<<"gooduser">>, <<"myserver.com">>], args_desc = ["User name to unban", "Server name"], @@ -692,7 +692,7 @@ get_commands_spec() -> ]}}}}}, #ejabberd_commands{name = get_roster_count, tags = [roster], desc = "Get number of contacts in a local user roster", - note = "added in 24.xx", + note = "added in 24.06", policy = user, module = ?MODULE, function = get_roster_count, args = [], @@ -810,7 +810,7 @@ get_commands_spec() -> #ejabberd_commands{name = srg_add, tags = [shared_roster_group], desc = "Add/Create a Shared Roster Group (without details)", module = ?MODULE, function = srg_add, - note = "added in 24.xx", + note = "added in 24.06", args = [{group, binary}, {host, binary}], args_example = [<<"group3">>, <<"myserver.com">>], args_desc = ["Group identifier", "Group server name"], @@ -843,7 +843,7 @@ get_commands_spec() -> #ejabberd_commands{name = srg_set_info, tags = [shared_roster_group], desc = "Set info of a Shared Roster Group", module = ?MODULE, function = srg_set_info, - note = "added in 24.xx", + note = "added in 24.06", args = [{group, binary}, {host, binary}, {key, binary}, {value, binary}], args_example = [<<"group3">>, <<"myserver.com">>, <<"label">>, <<"Family">>], args_desc = ["Group identifier", "Group server name", @@ -854,7 +854,7 @@ get_commands_spec() -> #ejabberd_commands{name = srg_get_displayed, tags = [shared_roster_group], desc = "Get displayed groups of a Shared Roster Group", module = ?MODULE, function = srg_get_displayed, - note = "added in 24.xx", + note = "added in 24.06", args = [{group, binary}, {host, binary}], args_example = [<<"group3">>, <<"myserver.com">>], args_desc = ["Group identifier", "Group server name"], @@ -864,7 +864,7 @@ get_commands_spec() -> #ejabberd_commands{name = srg_add_displayed, tags = [shared_roster_group], desc = "Add a group to displayed_groups of a Shared Roster Group", module = ?MODULE, function = srg_add_displayed, - note = "added in 24.xx", + note = "added in 24.06", args = [{group, binary}, {host, binary}, {add, binary}], args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>], @@ -874,7 +874,7 @@ get_commands_spec() -> #ejabberd_commands{name = srg_del_displayed, tags = [shared_roster_group], desc = "Delete a group from displayed_groups of a Shared Roster Group", module = ?MODULE, function = srg_del_displayed, - note = "added in 24.xx", + note = "added in 24.06", args = [{group, binary}, {host, binary}, {del, binary}], args_example = [<<"group3">>, <<"myserver.com">>, <<"group1">>], diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 57a6d94c9..77baecac3 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -28,7 +28,7 @@ -protocol({xep, 313, '0.6.1', '15.06', "", ""}). -protocol({xep, 334, '0.2'}). -protocol({xep, 359, '0.5.0'}). --protocol({xep, 425, '0.3.0', '24.xx', "", ""}). +-protocol({xep, 425, '0.3.0', '24.06', "", ""}). -protocol({xep, 441, '0.2.0'}). -behaviour(gen_mod). diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index b9e840022..be397fbd8 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -247,7 +247,7 @@ get_commands_spec() -> longdesc = "The MUC service argument can be `global` to get all hosts.", module = ?MODULE, function = rooms_empty_destroy_restuple, version = 2, - note = "modified in 24.xx", + note = "modified in 24.06", args_desc = ["MUC service, or `global` for all"], args_example = ["conference.example.com"], result_desc = "List of empty rooms that have been destroyed", From 410c81a51d83a6a98ba4934e60bbadb6fcddb65d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 11:01:52 +0200 Subject: [PATCH 0649/1302] Update man page to 24.06 --- man/ejabberd.yml.5 | 493 ++++++++++++++++++--------------------------- 1 file changed, 192 insertions(+), 301 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index a055935eb..a4df32226 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 02/24/2024 +.\" Date: 06/27/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "02/24/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "06/27/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,17 +82,17 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.02/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.06/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd\&. +This section describes top level options of ejabberd 24\&.06\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR .RS 4 This option defines -Access Rules\&. Each access rule is assigned a name that can be referenced from other parts of the configuration file (mostly from +\fIbasic\&.md#access\-rules|Access Rules\fR\&. Each access rule is assigned a name that can be referenced from other parts of the configuration file (mostly from \fIaccess\fR options of ejabberd modules)\&. Each rule definition may contain arbitrary number of \fIallow\fR @@ -247,7 +247,7 @@ is in the form of "regexp", the rule matches any JID with node part matching reg .PP \fBacme\fR: \fIOptions\fR .RS 4 -ACME +\fIbasic\&.md#acme|ACME\fR configuration, to automatically obtain SSL certificates for the domains served by ejabberd, which means that certificate requests and renewals are performed to some CA server (aka "ACME server") in a fully automated mode\&. The \fIOptions\fR are: @@ -304,7 +304,7 @@ acme: \fBallow_contrib_modules\fR: \fItrue | false\fR .RS 4 Whether to allow installation of third\-party modules or not\&. See -ejabberd\-contrib +\fI\&.\&./\&.\&./developer/extending\-ejabberd/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR documentation section\&. The default value is \fItrue\fR\&. .RE @@ -363,7 +363,7 @@ The default value is \fIsasl_anon\fR\&. \fBapi_permissions\fR: \fI[Permission, \&.\&.\&.]\fR .RS 4 Define the permissions for API access\&. Please consult the ejabberd Docs web → For Developers → ejabberd ReST API → -API Permissions\&. +\fI\&.\&./\&.\&./developer/ejabberd\-api/permissions\&.md|API Permissions\fR\&. .RE .PP \fBappend_host_config\fR: \fI{Host: Options}\fR @@ -398,12 +398,11 @@ Same as \fIcache_size\fR will be used\&. .RE -.sp -\fINote\fR about the next option: added in 23\&.10: .PP \fBauth_external_user_exists_check\fR: \fItrue | false\fR .RS 4 -Supplement check for user existence based on +\fINote\fR +about this option: added in 23\&.10\&. Supplement check for user existence based on \fImod_last\fR data, for authentication methods that don\(cqt have a way to reliably tell if a user exists (like is the case for \fIjwt\fR @@ -425,13 +424,12 @@ that can be installed from the ejabberd\-contrib Git repository\&. Please refer to that module\(cqs README file for details\&. .RE -.sp -\fINote\fR about the next option: improved in 20\&.01: .PP \fBauth_password_format\fR: \fIplain | scram\fR .RS 4 -The option defines in what format the users passwords are stored, plain text or in -SCRAM +\fINote\fR +about this option: improved in 20\&.01\&. The option defines in what format the users passwords are stored, plain text or in +\fIauthentication\&.md#scram|SCRAM\fR format: .sp .RS 4 @@ -464,7 +462,7 @@ The default value is \fIplain\fR\&. \fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR .RS 4 Hash algorithm that should be used to store password in -SCRAM +\fIauthentication\&.md#scram|SCRAM\fR format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. The default value is \fIsha\fR\&. .RE @@ -484,9 +482,9 @@ Full path to a file containing one or more CA certificates in PEM format\&. All field\&. There is no default value\&. .RE .sp -You can use host_config to specify this option per\-vhost\&. +You can use \fIhost_config\fR to specify this option per\-vhost\&. .sp -To set a specific file per listener, use the listener\(cqs cafile option\&. Please notice that \fIc2s_cafile\fR overrides the listener\(cqs \fIcafile\fR option\&. +To set a specific file per listener, use the listener\(cqs \fIlisten\-options\&.md#cafile|cafile\fR option\&. Please notice that \fIc2s_cafile\fR overrides the listener\(cqs \fIcafile\fR option\&. .PP \fBc2s_ciphers\fR: \fI[Cipher, \&.\&.\&.]\fR .RS 4 @@ -547,7 +545,7 @@ Whether to enable or disable TLS compression for c2s connections\&. The default Path to a file of CA root certificates\&. The default is to use system defined file if possible\&. .RE .sp -For server connections, this \fIca_file\fR option is overridden by the s2s_cafile option\&. +For server connections, this \fIca_file\fR option is overridden by the \fIs2s_cafile\fR option\&. .PP \fBcache_life_time\fR: \fItimeout()\fR .RS 4 @@ -580,13 +578,12 @@ A maximum number of items (not memory!) in cache\&. The rule of thumb, for all t \fIrouter_cache_size\fR, and \fIsm_cache_size\fR\&. .RE -.sp -\fINote\fR about the next option: improved in 23\&.01: .PP \fBcaptcha_cmd\fR: \fIPath | ModuleName\fR .RS 4 -Full path to a script that generates -CAPTCHA +\fINote\fR +about this option: improved in 23\&.01\&. Full path to a script that generates +\fIbasic\&.md#captcha|CAPTCHA\fR images\&. \fI@VERSION@\fR is replaced with ejabberd version number in @@ -595,6 +592,8 @@ format\&. \fI@SEMVER@\fR is replaced with ejabberd version number in semver format when compiled with Elixir\(cqs mix, or XX\&.YY format otherwise\&. Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. .sp +\fBExamples\fR: +.sp When using the ejabberd installers or container image, the example captcha scripts can be used like this: .sp .if n \{\ @@ -618,17 +617,16 @@ instead\&. \fBcaptcha_limit\fR: \fIpos_integer() | infinity\fR .RS 4 Maximum number of -CAPTCHA +\fIbasic\&.md#captcha|CAPTCHA\fR generated images per minute for any given JID\&. The option is intended to protect the server from CAPTCHA DoS\&. The default value is \fIinfinity\fR\&. .RE -.sp -\fINote\fR about the next option: improved in 23\&.04: .PP \fBcaptcha_url\fR: \fIURL | auto | undefined\fR .RS 4 -An URL where -CAPTCHA +\fINote\fR +about this option: improved in 23\&.04\&. An URL where +\fIbasic\&.md#captcha|CAPTCHA\fR requests should be sent\&. NOTE: you need to configure \fIrequest_handlers\fR for @@ -652,6 +650,8 @@ and so on\&. NOTE: if you modify the certificate files or change the value of th \fIejabberdctl reload\-config\fR in order to rebuild and reload the certificate chains\&. .sp +\fBExamples\fR: +.sp If you use Let\(cqs Encrypt certificates for your domain "domain\&.tld", the configuration will look like this: @@ -910,13 +910,12 @@ Disallows the usage of those options in the included file \fIFilename\fR\&. The options that match this criteria are not accepted\&. The default value is an empty list\&. .RE .RE -.sp -\fINote\fR about the next option: added in 23\&.10: .PP \fBinstall_contrib_modules\fR: \fI[Module, \&.\&.\&.]\fR .RS 4 -Modules to install from -ejabberd\-contrib +\fINote\fR +about this option: added in 23\&.10\&. Modules to install from +\fI\&.\&./\&.\&./developer/extending\-ejabberd/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR at start time\&. The default value is an empty list of modules: \fI[]\fR\&. .RE @@ -1071,31 +1070,28 @@ is assumed to be "%u"\&. \fBlisten\fR: \fI[Options, \&.\&.\&.]\fR .RS 4 The option for listeners configuration\&. See the -Listen Modules +\fIlisten\&.md|Listen Modules\fR section for details\&. .RE -.sp -\fINote\fR about the next option: added in 22\&.10: .PP \fBlog_burst_limit_count\fR: \fINumber\fR .RS 4 -The number of messages to accept in +\fINote\fR +about this option: added in 22\&.10\&. The number of messages to accept in log_burst_limit_window_time period before starting to drop them\&. Default 500 .RE -.sp -\fINote\fR about the next option: added in 22\&.10: .PP \fBlog_burst_limit_window_time\fR: \fINumber\fR .RS 4 -The time period to rate\-limit log messages by\&. Defaults to 1 second\&. +\fINote\fR +about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to 1 second\&. .RE -.sp -\fINote\fR about the next option: added in 23\&.01: .PP \fBlog_modules_fully\fR: \fI[Module, \&.\&.\&.]\fR .RS 4 -List of modules that will log everything independently from the general loglevel option\&. +\fINote\fR +about this option: added in 23\&.01\&. List of modules that will log everything independently from the general loglevel option\&. .RE .PP \fBlog_rotate_count\fR: \fINumber\fR @@ -1131,7 +1127,7 @@ This option specifies the maximum number of elements in the queue of the FSM (Fi \fBmodules\fR: \fI{Module: Options}\fR .RS 4 The option for modules configuration\&. See -Modules +\fImodules\&.md|Modules\fR section for details\&. .RE .PP @@ -1154,7 +1150,7 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/24\&.02/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/24\&.06/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1187,12 +1183,11 @@ Same as \fIcache_missed\fR will be used\&. .RE -.sp -\fINote\fR about the next option: added in 21\&.01: .PP \fBoauth_cache_rest_failure_life_time\fR: \fItimeout()\fR .RS 4 -The time that a failure in OAuth ReST is cached\&. The default value is +\fINote\fR +about this option: added in 21\&.01\&. The time that a failure in OAuth ReST is cached\&. The default value is \fIinfinity\fR\&. .RE .PP @@ -1263,29 +1258,26 @@ option)\&. Later, when memory drops below this \fI80\fR percents\&. .RE -.sp -\fINote\fR about the next option: changed in 23\&.01: .PP \fBoutgoing_s2s_families\fR: \fI[ipv6 | ipv4, \&.\&.\&.]\fR .RS 4 -Specify which address families to try, in what order\&. The default is +\fINote\fR +about this option: changed in 23\&.01\&. Specify which address families to try, in what order\&. The default is \fI[ipv6, ipv4]\fR which means it first tries connecting with IPv6, if that fails it tries using IPv4\&. This option is obsolete and irrelevant when using ejabberd 23\&.01 and Erlang/OTP 22, or newer versions of them\&. .RE -.sp -\fINote\fR about the next option: added in 20\&.12: .PP \fBoutgoing_s2s_ipv4_address\fR: \fIAddress\fR .RS 4 -Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example "127\&.0\&.0\&.1"\&. The default value is +\fINote\fR +about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example "127\&.0\&.0\&.1"\&. The default value is \fIundefined\fR\&. .RE -.sp -\fINote\fR about the next option: added in 20\&.12: .PP \fBoutgoing_s2s_ipv6_address\fR: \fIAddress\fR .RS 4 -Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example "::FFFF:127\&.0\&.0\&.1"\&. The default value is +\fINote\fR +about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example "::FFFF:127\&.0\&.0\&.1"\&. The default value is \fIundefined\fR\&. .RE .PP @@ -1455,7 +1447,7 @@ seconds\&. \fBs2s_access\fR: \fIAccess\fR .RS 4 This -Access Rule +\fIbasic\&.md#access\-rules|Access Rule\fR defines to what remote servers can s2s connections be established\&. The default value is \fIall\fR; no restrictions are applied, it is allowed to connect s2s to/from all remote servers\&. .RE @@ -1463,11 +1455,11 @@ defines to what remote servers can s2s connections be established\&. The default \fBs2s_cafile\fR: \fIPath\fR .RS 4 A path to a file with CA root certificates that will be used to authenticate s2s connections\&. If not set, the value of -ca_file +\fIca_file\fR will be used\&. .RE .sp -You can use host_config to specify this option per\-vhost\&. +You can use \fIhost_config\fR to specify this option per\-vhost\&. .PP \fBs2s_ciphers\fR: \fI[Cipher, \&.\&.\&.]\fR .RS 4 @@ -1704,12 +1696,11 @@ seconds\&. An SQL database name\&. For SQLite this must be a full path to a database file\&. The default value is \fIejabberd\fR\&. .RE -.sp -\fINote\fR about the next option: added in 24\&.02: .PP \fBsql_flags\fR: \fI[mysql_alternative_upsert]\fR .RS 4 -This option accepts a list of SQL flags, and is empty by default\&. +\fINote\fR +about this option: added in 24\&.02\&. This option accepts a list of SQL flags, and is empty by default\&. \fImysql_alternative_upsert\fR forces the alternative upsert implementation in MySQL\&. .RE @@ -1718,12 +1709,11 @@ forces the alternative upsert implementation in MySQL\&. .RS 4 An interval to make a dummy SQL request to keep alive the connections to the database\&. There is no default value, so no keepalive requests are made\&. .RE -.sp -\fINote\fR about the next option: added in 20\&.12: .PP \fBsql_odbc_driver\fR: \fIPath\fR .RS 4 -Path to the ODBC driver to use to connect to a Microsoft SQL Server database\&. This option only applies if the +\fINote\fR +about this option: added in 20\&.12\&. Path to the ODBC driver to use to connect to a Microsoft SQL Server database\&. This option only applies if the \fIsql_type\fR option is set to \fImssql\fR @@ -1755,12 +1745,11 @@ for PostgreSQL and \fI1433\fR for MS SQL\&. The option has no effect for SQLite\&. .RE -.sp -\fINote\fR about the next option: added in 20\&.01: .PP \fBsql_prepared_statements\fR: \fItrue | false\fR .RS 4 -This option is +\fINote\fR +about this option: added in 20\&.01\&. This option is \fItrue\fR by default, and is useful to disable prepared statements\&. The option is valid for PostgreSQL and MySQL\&. .RE @@ -1783,22 +1772,27 @@ or if the latter is not set\&. .RE .PP -\fBsql_server\fR: \fIHost\fR +\fBsql_server 🟤\fR: \fIHost | IP Address | ODBC Connection String | Unix Socket Path\fR .RS 4 -The hostname or IP address of the SQL server\&. For +\fINote\fR +about this option: improved in 24\&.06\&. The hostname or IP address of the SQL server\&. For \fIsql_type\fR \fImssql\fR or \fIodbc\fR -this can also be an ODBC connection string\&. The default value is +this can also be an ODBC connection string\&. When +\fIsql_type\fR +is +\fImysql\fR +or +\fIpgsql\fR, this can be the path to a unix domain socket expressed like: "unix:/path/to/socket"\&.The default value is \fIlocalhost\fR\&. .RE -.sp -\fINote\fR about the next option: improved in 20\&.03: .PP \fBsql_ssl\fR: \fItrue | false\fR .RS 4 -Whether to use SSL encrypted connections to the SQL server\&. The option is only available for MySQL, MS SQL and PostgreSQL\&. The default value is +\fINote\fR +about this option: improved in 20\&.03\&. Whether to use SSL encrypted connections to the SQL server\&. The option is only available for MySQL, MS SQL and PostgreSQL\&. The default value is \fIfalse\fR\&. .RE .PP @@ -1861,9 +1855,10 @@ to allow all proxies, or specify a list of IPs, possibly with masks\&. The defau header if you enable this option as, otherwise, the client can set it itself and as a result the IP value cannot be trusted for security rules in ejabberd\&. .RE .PP -\fBupdate_sql_schema\fR: \fItrue | false\fR +\fBupdate_sql_schema 🟤\fR: \fItrue | false\fR .RS 4 -Allow ejabberd to update SQL schema\&. The default value is +\fINote\fR +about this option: updated in 24\&.06\&. Allow ejabberd to update SQL schema\&. This option was added in ejabberd 23\&.10, and enabled by default since 24\&.06\&. The default value is \fItrue\fR\&. .RE .PP @@ -1918,7 +1913,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes options of all ejabberd modules\&. +This section describes modules options of ejabberd 24\&.06\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -1963,7 +1958,9 @@ Details for some commands: .IP \(bu 2.3 .\} \fIpushroster\fR: (and -\fIpushroster\-all\fR) The roster file must be placed, if using Windows, on the directory where you installed ejabberd: C:/Program Files/ejabberd or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&. +\fIpushroster\-all\fR) The roster file must be placed, if using Windows, on the directory where you installed ejabberd: +C:/Program Files/ejabberd +or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&. .RE .sp .RS 4 @@ -2048,7 +2045,7 @@ ejabberdctl srg\-create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g .RE .SS "mod_admin_update_sql" .sp -This module can be used to update existing SQL database from the default to the new schema\&. Check the section Default and New Schemas for details\&. Please note that only MS SQL, MySQL, and PostgreSQL are supported\&. When the module is loaded use \fIupdate_sql\fR API\&. +This module can be used to update existing SQL database from the default to the new schema\&. Check the section \fIdatabase\&.md#default\-and\-new\-schemas|Default and New Schemas\fR for details\&. Please note that only MS SQL, MySQL, and PostgreSQL are supported\&. When the module is loaded use \fIupdate_sql\fR API\&. .sp The module has no options\&. .SS "mod_announce" @@ -2257,7 +2254,7 @@ and some server\(cqs JID is in user\(cqs roster, then messages from any user of \fBcaptcha\fR: \fItrue | false\fR .RS 4 Whether to generate CAPTCHA or not in response to messages from strangers\&. See also section -CAPTCHA +\fIbasic\&.md#captcha|CAPTCHA\fR of the Configuration Guide\&. The default value is \fIfalse\fR\&. .RE @@ -2482,18 +2479,18 @@ While a client is inactive, queue presence stanzas that indicate (un)availabilit .RE .SS "mod_configure" .sp -The module provides server configuration functionality via XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR to be loaded\&. +The module provides server configuration functionality via XEP\-0050: Ad\-Hoc Commands\&. Implements many commands as defined in XEP\-0133: Service Administration\&. This module requires \fImod_adhoc\fR to be loaded\&. .sp The module has no options\&. .SS "mod_conversejs" .sp +\fINote\fR about this option: added in 21\&.12 and improved in 22\&.05\&. +.sp This module serves a simple page for the Converse XMPP web browser client\&. .sp -This module is available since ejabberd 21\&.12\&. Several options were improved in ejabberd 22\&.05\&. +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp -To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. -.sp -Make sure either \fImod_bosh\fR or \fIejabberd_http_ws\fR request_handlers are enabled\&. +Make sure either \fImod_bosh\fR or \fIejabberd_http_ws\fR \fIlisten\-options\&.md#request_handlers|request_handlers\fR are enabled\&. .sp When \fIconversejs_css\fR and \fIconversejs_script\fR are \fIauto\fR, by default they point to the public Converse client\&. .sp @@ -2521,20 +2518,18 @@ Converse CSS URL\&. The keyword is replaced with the hostname\&. The default value is \fIauto\fR\&. .RE -.sp -\fINote\fR about the next option: added in 22\&.05: .PP \fBconversejs_options\fR: \fI{Name: Value}\fR .RS 4 -Specify additional options to be passed to Converse\&. See +\fINote\fR +about this option: added in 22\&.05\&. Specify additional options to be passed to Converse\&. See Converse configuration\&. Only boolean, integer and string values are supported; lists are not supported\&. .RE -.sp -\fINote\fR about the next option: added in 22\&.05: .PP \fBconversejs_resources\fR: \fIPath\fR .RS 4 -Local path to the Converse files\&. If not set, the public Converse client will be used instead\&. +\fINote\fR +about this option: added in 22\&.05\&. Local path to the Converse files\&. If not set, the public Converse client will be used instead\&. .RE .PP \fBconversejs_script\fR: \fIauto | URL\fR @@ -2727,7 +2722,6 @@ acl: server: sat\-pubsub\&.example\&.org modules: - \&.\&.\&. mod_delegation: namespaces: urn:xmpp:mam:1: @@ -2879,11 +2873,11 @@ The number of C2S authentication failures to trigger the IP ban\&. The default v .RE .SS "mod_host_meta" .sp +\fINote\fR about this option: added in 22\&.05\&. +.sp This module serves small \fIhost\-meta\fR files as described in XEP\-0156: Discovering Alternative XMPP Connection Methods\&. .sp -This module is available since ejabberd 22\&.05\&. -.sp -To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp Notice it only works if ejabberd_http has tls enabled\&. .sp @@ -2949,9 +2943,9 @@ modules: .RE .SS "mod_http_api" .sp -This module provides a ReST interface to call ejabberd API commands using JSON data\&. +This module provides a ReST interface to call \fI\&.\&./\&.\&./developer/ejabberd\-api/index\&.md|ejabberd API\fR commands using JSON data\&. .sp -To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp To use a specific API version N, when defining the URL path in the request_handlers, add a \fIvN\fR\&. For example: \fI/api/v2: mod_http_api\fR .sp @@ -3077,18 +3071,13 @@ This example configuration will serve the files from the local directory \fI/var .\} .nf listen: - \&.\&.\&. \- port: 5280 module: ejabberd_http request_handlers: - \&.\&.\&. /pub/content: mod_http_fileserver - \&.\&.\&. - \&.\&.\&. modules: - \&.\&.\&. mod_http_fileserver: docroot: /var/www accesslog: /var/log/ejabberd/access\&.log @@ -3102,7 +3091,6 @@ modules: \&.ogg: audio/ogg \&.png: image/png default_content_type: text/html - \&.\&.\&. .fi .if n \{\ .RE @@ -3112,7 +3100,7 @@ modules: .sp This module allows for requesting permissions to upload a file via HTTP as described in XEP\-0363: HTTP File Upload\&. If the request is accepted, the client receives a URL for uploading the file and another URL from which that file can later be downloaded\&. .sp -In order to use this module, it must be enabled in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. +In order to use this module, it must be enabled in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3245,30 +3233,22 @@ A custom vCard of the service that will be displayed by some XMPP clients in Ser \fIvCard\fR is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. .sp -For example, the following XML representation of vCard: -.sp -.if n \{\ -.RS 4 -.\} -.nf - - Conferences - - - Elm Street - - -.fi -.if n \{\ -.RE -.\} -.sp -will be translated to: +\fBExample\fR: .sp .if n \{\ .RS 4 .\} .nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: vcard: fn: Conferences adr: @@ -3295,23 +3275,17 @@ vcard: .\} .nf listen: - \&.\&.\&. \- port: 5443 module: ejabberd_http tls: true request_handlers: - \&.\&.\&. /upload: mod_http_upload - \&.\&.\&. - \&.\&.\&. modules: - \&.\&.\&. mod_http_upload: docroot: /ejabberd/upload put_url: "https://@HOST@:5443/upload" - \&.\&.\&. .fi .if n \{\ .RE @@ -3370,19 +3344,15 @@ Please note that it\(cqs not necessary to specify the \fIaccess_hard_quota\fR an .\} .nf shaper_rules: - \&.\&.\&. soft_upload_quota: 1000: all # MiB hard_upload_quota: 1100: all # MiB - \&.\&.\&. modules: - \&.\&.\&. mod_http_upload: {} mod_http_upload_quota: max_days: 100 - \&.\&.\&. .fi .if n \{\ .RE @@ -3476,7 +3446,7 @@ This type of authentication was obsoleted in 2008 and you unlikely need this mod The module has no options\&. .SS "mod_mam" .sp -This module implements XEP\-0313: Message Archive Management\&. Compatible XMPP clients can use it to store their chat history on the server\&. +This module implements XEP\-0313: Message Archive Management and XEP\-0441: Message Archive Management Preferences\&. Compatible XMPP clients can use it to store their chat history on the server\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3577,9 +3547,9 @@ When this option is disabled, for each individual subscriber a separa mucsub mes .RE .SS "mod_matrix_gw" .sp -Matrix gateway\&. +\fINote\fR about this option: added in 24\&.02\&. .sp -This module is available since ejabberd 24\&.02\&. +Matrix gateway\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3792,9 +3762,11 @@ An internet port number at which the backend is listening for incoming connectio .RE .SS "mod_mix" .sp -This module is an experimental implementation of XEP\-0369: Mediated Information eXchange (MIX)\&. MIX support was added in ejabberd 16\&.03 as an experimental feature, updated in 19\&.02, and is not yet ready to use in production\&. It\(cqs asserted that the MIX protocol is going to replace the MUC protocol in the future (see \fImod_muc\fR)\&. +\fINote\fR about this option: added in 16\&.03 and improved in 19\&.02\&. .sp -To learn more about how to use that feature, you can refer to our tutorial: Getting started with XEP\-0369: Mediated Information eXchange (MIX) v0\&.1\&. +This module is an experimental implementation of XEP\-0369: Mediated Information eXchange (MIX)\&. It\(cqs asserted that the MIX protocol is going to replace the MUC protocol in the future (see \fImod_muc\fR)\&. +.sp +To learn more about how to use that feature, you can refer to our tutorial: \fI\&.\&./\&.\&./tutorials/mix\-010\&.md|Getting started with MIX\fR .sp The module depends on \fImod_mam\fR\&. .sp @@ -3906,7 +3878,7 @@ option, but applied to this module only\&. .RE .SS "mod_mqtt" .sp -This module adds support for the MQTT protocol version \fI3\&.1\&.1\fR and \fI5\&.0\fR\&. Remember to configure \fImod_mqtt\fR in \fImodules\fR and \fIlisten\fR sections\&. +This module adds \fI\&.\&./guide/mqtt/index\&.md|support for the MQTT\fR protocol version \fI3\&.1\&.1\fR and \fI5\&.0\fR\&. Remember to configure \fImod_mqtt\fR in \fImodules\fR and \fIlisten\fR sections\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -4050,7 +4022,6 @@ section with username/password field or certfile pointing to client certificate\ .\} .nf modules: - \&.\&.\&. mod_mqtt_bridge: servers: "mqtt://server\&.com": @@ -4062,7 +4033,6 @@ modules: authentication: certfile: "/etc/ejabberd/mqtt_server\&.pem" replication_user: "mqtt@xmpp\&.server\&.com" - \&.\&.\&. .fi .if n \{\ .RE @@ -4118,21 +4088,19 @@ To configure who is allowed to modify the room option\&. The default value is \fIall\fR, which means everyone is allowed to modify that option\&. .RE -.sp -\fINote\fR about the next option: improved in 23\&.10: .PP \fBaccess_register\fR: \fIAccessName\fR .RS 4 -This option specifies who is allowed to register nickname within the Multi\-User Chat service and rooms\&. The default is +\fINote\fR +about this option: improved in 23\&.10\&. This option specifies who is allowed to register nickname within the Multi\-User Chat service and rooms\&. The default is \fIall\fR for backward compatibility, which means that any user is allowed to register any free nick in the MUC service and in the rooms\&. .RE -.sp -\fINote\fR about the next option: added in 22\&.05: .PP \fBcleanup_affiliations_on_start\fR: \fItrue | false\fR .RS 4 -Remove affiliations for non\-existing local users on startup\&. The default value is +\fINote\fR +about this option: added in 22\&.05\&. Remove affiliations for non\-existing local users on startup\&. The default value is \fIfalse\fR\&. .RE .PP @@ -4142,8 +4110,6 @@ Same as top\-level \fIdefault_db\fR option, but applied to this module only\&. .RE -.sp -\fINote\fR about the next option: improved in 22\&.05: .PP \fBdefault_room_options\fR: \fIOptions\fR .RS 4 @@ -4173,7 +4139,7 @@ Occupants can send IQ queries to other occupants\&. The default value is \fBallow_subscription\fR: \fItrue | false\fR .RS 4 Allow users to subscribe to room events as described in -Multi\-User Chat Subscriptions\&. The default value is +\fI\&.\&./\&.\&./developer/xmpp\-clients\-bots/extensions/muc\-sub\&.md|Multi\-User Chat Subscriptions\fR\&. The default value is \fIfalse\fR\&. .RE .PP @@ -4216,7 +4182,7 @@ The room is anonymous: occupants don\(cqt see the real JIDs of other occupants\& \fBcaptcha_protected\fR: \fItrue | false\fR .RS 4 When a user tries to join a room where they have no affiliation (not owner, admin or member), the room requires them to fill a CAPTCHA challenge (see section -CAPTCHA +\fIbasic\&.md#captcha|CAPTCHA\fR in order to accept their join in the room\&. The default value is \fIfalse\fR\&. .RE @@ -4379,20 +4345,18 @@ option is not specified, the only Jabber ID will be the hostname of the virtual \fI@HOST@\fR is replaced with the real virtual host name\&. .RE -.sp -\fINote\fR about the next option: added in 21\&.01: .PP \fBmax_captcha_whitelist\fR: \fINumber\fR .RS 4 -This option defines the maximum number of characters that Captcha Whitelist can have when configuring the room\&. The default value is +\fINote\fR +about this option: added in 21\&.01\&. This option defines the maximum number of characters that Captcha Whitelist can have when configuring the room\&. The default value is \fIinfinity\fR\&. .RE -.sp -\fINote\fR about the next option: added in 21\&.01: .PP \fBmax_password\fR: \fINumber\fR .RS 4 -This option defines the maximum number of characters that Password can have when configuring the room\&. The default value is +\fINote\fR +about this option: added in 21\&.01\&. This option defines the maximum number of characters that Password can have when configuring the room\&. The default value is \fIinfinity\fR\&. .RE .PP @@ -4511,30 +4475,22 @@ A custom vCard of the service that will be displayed by some XMPP clients in Ser \fIvCard\fR is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. .sp -For example, the following XML representation of vCard: -.sp -.if n \{\ -.RS 4 -.\} -.nf - - Conferences - - - Elm Street - - -.fi -.if n \{\ -.RE -.\} -.sp -will be translated to: +\fBExample\fR: .sp .if n \{\ .RS 4 .\} .nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: vcard: fn: Conferences adr: @@ -4560,12 +4516,11 @@ This module depends on \fImod_muc\fR\&. .ps +1 \fBAvailable options:\fR .RS 4 -.sp -\fINote\fR about the next option: added in 22\&.05: .PP \fBsubscribe_room_many_max_users\fR: \fINumber\fR .RS 4 -How many users can be subscribed to a room at once using the +\fINote\fR +about this option: added in 22\&.05\&. How many users can be subscribed to a room at once using the \fIsubscribe_room_many\fR command\&. The default value is \fI50\fR\&. @@ -4825,21 +4780,21 @@ otherwise\&. There is no default value\&. .RE .SS "mod_muc_occupantid" .sp +\fINote\fR about this option: added in 23\&.10\&. +.sp This module implements XEP\-0421: Anonymous unique occupant identifiers for MUCs\&. .sp When the module is enabled, the feature is enabled in all semi\-anonymous rooms\&. .sp -This module is available since ejabberd 23\&.10\&. -.sp The module has no options\&. .SS "mod_muc_rtbl" .sp +\fINote\fR about this option: added in 23\&.04\&. +.sp This module implement Real\-time blocklists for MUC rooms\&. .sp It works by observing remote pubsub node conforming with specification described in https://xmppbl\&.org/\&. .sp -This module is available since ejabberd 23\&.04\&. -.sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 @@ -5038,7 +4993,7 @@ This module implements XEP\-0160: Best Practices for Handling Offline Messages a .ps -1 .br .sp -\fIejabberdctl\fR has a command to delete expired messages (see chapter Managing an ejabberd server in online documentation\&. +\fIejabberdctl\fR has a command to delete expired messages (see chapter \fI\&.\&./guide/managing\&.md|Managing an ejabberd server\fR in online documentation\&. .sp .5v .RE .sp @@ -5225,12 +5180,10 @@ is loaded and stream management is enabled by a client, killing the client conne .\} .nf modules: - \&.\&.\&. mod_ping: send_pings: true ping_interval: 4 min timeout_action: kill - \&.\&.\&. .fi .if n \{\ .RE @@ -5279,11 +5232,9 @@ minute\&. .\} .nf modules: - \&.\&.\&. mod_pres_counter: count: 5 interval: 30 secs - \&.\&.\&. .fi .if n \{\ .RE @@ -5358,7 +5309,7 @@ This module adds support for XEP\-0049: Private XML Storage\&. .sp Using this method, XMPP entities can store private data on the server, retrieve it whenever necessary and share it between multiple connected clients of the same user\&. The data stored might be anything, as long as it is a valid XML\&. One typical usage is storing a bookmark of all user\(cqs conferences (XEP\-0048: Bookmarks)\&. .sp -It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see the command bookmarks_to_pep\&. +It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see the command \fIbookmarks_to_pep\fR API\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -5522,7 +5473,6 @@ Sets write access to a user\(cqs roster\&. The default value is .\} .nf modules: - \&.\&.\&. mod_privilege: roster: get: all @@ -5530,7 +5480,6 @@ modules: managed_entity: all message: outgoing: all - \&.\&.\&. .fi .if n \{\ .RE @@ -5644,41 +5593,6 @@ bytes\&. A custom vCard of the service that will be displayed by some XMPP clients in Service Discovery\&. The value of \fIvCard\fR is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. -.sp -For example, the following XML representation of vCard: -.sp -.if n \{\ -.RS 4 -.\} -.nf - - Conferences - - - Elm Street - - -.fi -.if n \{\ -.RE -.\} -.sp -will be translated to: -.sp -.if n \{\ -.RS 4 -.\} -.nf -vcard: - fn: Conferences - adr: - \- - work: true - street: Elm Street -.fi -.if n \{\ -.RE -.\} .RE .RE .sp @@ -5713,7 +5627,6 @@ shaper: proxyrate: 10240 modules: - \&.\&.\&. mod_proxy65: host: proxy1\&.example\&.org name: "File Transfer Proxy" @@ -5724,7 +5637,6 @@ modules: shaper: proxy65_shaper recbuf: 10240 sndbuf: 10240 - \&.\&.\&. .fi .if n \{\ .RE @@ -5815,12 +5727,11 @@ To specify whether or not pubsub should cache last items\&. Value is or \fIfalse\fR\&. If not defined, pubsub does not cache last items\&. On systems with not so many nodes, caching last items speeds up pubsub and allows you to raise the user connection rate\&. The cost is memory usage, as every item is stored in memory\&. .RE -.sp -\fINote\fR about the next option: added in 21\&.12: .PP \fBmax_item_expire_node\fR: \fItimeout() | infinity\fR .RS 4 -Specify the maximum item epiry time\&. Default value is: +\fINote\fR +about this option: added in 21\&.12\&. Specify the maximum item epiry time\&. Default value is: \fIinfinity\fR\&. .RE .PP @@ -5957,32 +5868,24 @@ A custom vCard of the server that will be displayed by some XMPP clients in Serv \fIvCard\fR is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. .sp -The following XML representation of vCard: -.sp -.if n \{\ -.RS 4 -.\} -.nf - - PubSub Service - - - Elm Street - - -.fi -.if n \{\ -.RE -.\} -.sp -will be translated to: +\fBExample\fR: .sp .if n \{\ .RS 4 .\} .nf +# This XML representation of vCard: +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: vcard: - fn: PubSub Service + fn: Conferences adr: \- work: true @@ -6009,7 +5912,6 @@ Example of configuration that uses flat nodes as default, and allows use of flat .\} .nf modules: - \&.\&.\&. mod_pubsub: access_createnode: pubsub_createnode max_subscriptions_node: 100 @@ -6020,7 +5922,6 @@ modules: plugins: \- flat \- pep - \&.\&.\&. .fi .if n \{\ .RE @@ -6033,7 +5934,6 @@ Using relational database requires using mod_pubsub with db_type \fIsql\fR\&. On .\} .nf modules: - \&.\&.\&. mod_pubsub: db_type: sql access_createnode: pubsub_createnode @@ -6042,7 +5942,6 @@ modules: plugins: \- flat \- pep - \&.\&.\&. .fi .if n \{\ .RE @@ -6101,12 +6000,11 @@ If this option is set to \fItrue\fR, the sender\(cqs JID is included with push notifications generated for incoming messages with a body\&. The default value is \fIfalse\fR\&. .RE -.sp -\fINote\fR about the next option: added in 23\&.10: .PP \fBnotify_on\fR: \fImessages | all\fR .RS 4 -If this option is set to +\fINote\fR +about this option: added in 23\&.10\&. If this option is set to \fImessages\fR, notifications are generated only for actual chat messages with a body text (or some encrypted payload)\&. If it\(cqs set to \fIall\fR, any kind of XMPP stanza will trigger a notification\&. If unsure, it\(cqs strongly recommended to stick to \fIall\fR, which is the default value\&. @@ -6225,12 +6123,11 @@ doesn\(cqt allow the client to register new accounts from s2s or existing c2s se .RS 4 Specify rules to restrict access for user unregistration\&. By default any user is able to unregister their account\&. .RE -.sp -\fINote\fR about the next option: added in 21\&.12: .PP \fBallow_modules\fR: \fIall | [Module, \&.\&.\&.]\fR .RS 4 -List of modules that can register accounts, or +\fINote\fR +about this option: added in 21\&.12\&. List of modules that can register accounts, or \fIall\fR\&. The default value is \fIall\fR, which is equivalent to something like \fI[mod_register, mod_register_web]\fR\&. @@ -6239,7 +6136,7 @@ List of modules that can register accounts, or \fBcaptcha_protected\fR: \fItrue | false\fR .RS 4 Protect registrations with -CAPTCHA\&. The default is +\fIbasic\&.md#captcha|CAPTCHA\fR\&. The default is \fIfalse\fR\&. .RE .PP @@ -6318,11 +6215,11 @@ Change the password from an existing account on the server\&. Unregister an existing account on the server\&. .RE .sp -This module supports CAPTCHA to register a new account\&. To enable this feature, configure the top\-level \fIcaptcha_cmd\fR and top\-level \fIcaptcha_url\fR options\&. +This module supports \fIbasic\&.md#captcha|CAPTCHA\fR to register a new account\&. To enable this feature, configure the top\-level \fIcaptcha_cmd\fR and top\-level \fIcaptcha_url\fR options\&. .sp As an example usage, the users of the host \fIlocalhost\fR can visit the page: \fIhttps://localhost:5280/register/\fR It is important to include the last / character in the URL, otherwise the subpages URL will be incorrect\&. .sp -This module is enabled in \fIlisten\fR → \fIejabberd_http\fR → request_handlers, no need to enable in \fImodules\fR\&. The module depends on \fImod_register\fR where all the configuration is performed\&. +This module is enabled in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR, no need to enable in \fImodules\fR\&. The module depends on \fImod_register\fR where all the configuration is performed\&. .sp The module has no options\&. .sp @@ -6442,11 +6339,9 @@ Enables/disables Roster Versioning\&. The default value is .\} .nf modules: - \&.\&.\&. mod_roster: versioning: true store_current_id: false - \&.\&.\&. .fi .if n \{\ .RE @@ -6500,14 +6395,12 @@ An access rule that can be used to restrict dialback for some servers\&. The def .\} .nf modules: - \&.\&.\&. mod_s2s_dialback: access: allow: server: legacy\&.domain\&.tld server: invalid\-cert\&.example\&.org deny: all - \&.\&.\&. .fi .if n \{\ .RE @@ -6544,12 +6437,10 @@ A list of servers or connected components to which stanzas will be forwarded\&. .\} .nf modules: - \&.\&.\&. mod_service_log: loggers: \- xmpp\-server\&.tld \- component\&.domain\&.tld - \&.\&.\&. .fi .if n \{\ .RE @@ -6791,11 +6682,11 @@ Control parameters: .IP \(bu 2.3 .\} Connection parameters: The module also accepts the connection parameters, all of which default to the top\-level parameter of the same name, if unspecified\&. See -LDAP Connection +\fIldap\&.md#ldap\-connection|LDAP Connection\fR section for more information about them\&. .RE .sp -Check also the Configuration examples section to get details about retrieving the roster, and configuration examples including Flat DIT and Deep DIT\&. +Check also the \fIldap\&.md#ldap\-examples|Configuration examples\fR section to get details about retrieving the roster, and configuration examples including Flat DIT and Deep DIT\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -6865,7 +6756,7 @@ option, but applied to this module only\&. \fBldap_filter\fR .RS 4 Additional filter which is AND\-ed together with "User Filter" and "Group Filter"\&. For more information check the LDAP -Filters +\fIldap\&.md#filters|Filters\fR section\&. .RE .PP @@ -6917,7 +6808,7 @@ A globbing format for extracting user ID from the value of the attribute named b .RS 4 A regex for extracting user ID from the value of the attribute named by \fIldap_memberattr\fR\&. Check the LDAP -Control Parameters +\fIldap\&.md#control\-parameters|Control Parameters\fR section\&. .RE .PP @@ -6990,7 +6881,7 @@ option, but applied to this module only\&. \fIldap_userdesc\fR and \fIldap_useruid\fR\&. For more information check the LDAP -Filters +\fIldap\&.md#filters|Filters\fR section\&. .RE .PP @@ -7064,7 +6955,7 @@ This module adds SIP proxy/registrar support for the corresponding virtual host\ .ps -1 .br .sp -It is not enough to just load this module\&. You should also configure listeners and DNS records properly\&. For details see the section about the ejabberd_sip listen module in the ejabberd Documentation\&. +It is not enough to just load this module\&. You should also configure listeners and DNS records properly\&. For details see the section about the \fIlisten\&.md#ejabberd_sip|ejabberd_sip\fR listen module in the ejabberd Documentation\&. .sp .5v .RE .sp @@ -7144,7 +7035,6 @@ are mandatory (e\&.g\&. you cannot omit "port" or "scheme")\&. .\} .nf modules: - \&.\&.\&. mod_sip: always_record_route: false record_route: "sip:example\&.com;lr" @@ -7157,7 +7047,6 @@ modules: \- tls://sip\-tls\&.example\&.com:5061 \- tcp://sip\-tcp\&.example\&.com:5060 \- udp://sip\-udp\&.example\&.com:5060 - \&.\&.\&. .fi .if n \{\ .RE @@ -7308,7 +7197,9 @@ minutes\&. .RE .SS "mod_stun_disco" .sp -This module allows XMPP clients to discover STUN/TURN services and to obtain temporary credentials for using them as per XEP\-0215: External Service Discovery\&. This module is included in ejabberd since version 20\&.04\&. +\fINote\fR about this option: added in 20\&.04\&. +.sp +This module allows XMPP clients to discover STUN/TURN services and to obtain temporary credentials for using them as per XEP\-0215: External Service Discovery\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -7549,30 +7440,24 @@ A custom vCard of the server that will be displayed by some XMPP clients in Serv \fIvCard\fR is a YAML map constructed from an XML representation of vCard\&. Since the representation has no attributes, the mapping is straightforward\&. .sp -For example, the following XML representation of vCard: -.sp -.if n \{\ -.RS 4 -.\} -.nf - - Conferences - - - Elm Street - - -.fi -.if n \{\ -.RE -.\} -.sp -will be translated to: +\fBExample\fR: .sp .if n \{\ .RS 4 .\} .nf +# This XML representation of vCard: +# +# +# Conferences +# +# +# Elm Street +# +# +# +# is translated to: +# vcard: fn: Conferences adr: @@ -7661,6 +7546,8 @@ for available words)\&. is the LDAP attribute or the pattern \fI%u\fR\&. .sp +\fBExamples\fR: +.sp The default is: .sp .if n \{\ @@ -7697,6 +7584,8 @@ is the vCard field name defined in the \fIldap_vcard_map\fR option\&. .sp +\fBExamples\fR: +.sp The default is: .sp .if n \{\ @@ -7784,6 +7673,8 @@ will be replaced with the user part of a JID, and \fI%d\fR will be replaced with the domain part of a JID\&. .sp +\fBExamples\fR: +.sp The default is: .sp .if n \{\ @@ -7913,7 +7804,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes options of all ejabberd listeners\&. +This section describes listeners options of ejabberd 24\&.06\&. .sp TODO .SH "AUTHOR" @@ -7921,13 +7812,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 24\&.02\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 24\&.06\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.02/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.06/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 56458582624327c9e2e4c0575f5d844ddb09b926 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:07:48 +0000 Subject: [PATCH 0650/1302] Bump docker/build-push-action from 5 to 6 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 8d6056f17..33ae16960 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -108,7 +108,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: build-args: | METHOD=package From 686938045bc24310ebcc0088b01006b9a20675f4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 15:12:29 +0200 Subject: [PATCH 0651/1302] Update changelog --- CHANGELOG.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5083cb36..42ee0c767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,111 @@ +## Version 24.06 + +#### Core + +- `econf`: Add ability to use additional custom errors when parsing options +- `ejabberd_logger`: Reloading configuration will update logger settings +- `gen_mod`: Add support to specify a hook global, not vhost-specific +- [`mod_configure`](https://docs.ejabberd.im/admin/configuration/modules/#mod_configure): Retract `Get User Password` command to update [XEP-0133](https://xmpp.org/extensions/xep-0133.html) 1.3.0 +- [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod_conversejs): Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/issues/4167)) +- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Document that [XEP-0441](https://xmpp.org/extensions/xep-0441.html) is implemented as well +- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Update support for [XEP-0425](https://xmpp.org/extensions/xep-0425.html) version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/issues/4193)) +- [`mod_matrix_gw`](https://docs.ejabberd.im/admin/configuration/modules/#mod_matrix_gw): Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/issues/4167)) +- [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Hide join/leave lines, add method to show them +- [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Support `allowpm` introduced in 2bd61ab +- [`mod_muc_room`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_room): Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/issues/4191)) +- [`mod_private`](https://docs.ejabberd.im/admin/configuration/modules/#mod_private): Cope with bookmark decoding errors +- [`mod_vcard_xupdate`](https://docs.ejabberd.im/admin/configuration/modules/#mod_vcard_xupdate): Send hash after avatar get set for first time +- `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/issues/4188)) + +#### SQL + +- [`update_sql_schema`](https://docs.ejabberd.im/admin/configuration/toplevel/#update_sql_schema): Enable this option by default +- CI: Don't load database schema files for mysql and pgsql +- Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/issues/3716)) +- Fix handling of `mqtt_pub` table definition from `mysql.sql` and fix `should_update_schema/1` in `ejabberd_sql_schema.erl` +- Don't start sql connection pools for unknown hosts +- Add `update_primary_key` command to sql schema updater +- Fix crash running [`export2sql`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#export2sql) when MAM enabled but MUC disabled +- Improve detection of types in odbc + +#### Commands API + +- New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/issues/4201)) +- [`join_cluster_here`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join_cluster_here): New command to join a remote node into our local cluster +- Don't name integer and string results in API examples ([#4198](https://github.com/processone/issues/4198)) +- [`get_user_subscriptions`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#get_user_subscriptions): Fix validation of user field in that command +- [`mod_admin_extra`](https://docs.ejabberd.im/admin/configuration/modules/#mod_admin_extra): Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/issues/4201)) +- [`mod_muc_admin`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_admin): Improve validation of arguments in several commands + +#### Compile + +- `ejabberdctl`: Comment ERTS_VSN variable when not used ([#4194](https://github.com/processone/issues/4194)) +- `ejabberdctl`: Fix iexlive after `make prod` when using Elixir +- `ejabberdctl`: If `INET_DIST_INTERFACE` is IPv6, set required option ([#4189](https://github.com/processone/issues/4189)) +- `ejabberdctl`: Make native dynamic node names work when using fully qualified domain names +- `rebar.config.script`: Support relaxed dependency version ([#4192](https://github.com/processone/issues/4192)) +- `rebar.config`: Update deps version to rebar3's relaxed versioning +- `rebar.lock`: Track file, now that rebar3 uses loose dependency versioning +- `configure.ac`: When using rebar3, unlock dependencies that are disabled ([#4212](https://github.com/processone/issues/4212)) +- `configure.ac`: When using rebar3 with old Erlang, unlock some dependencies ([#4213](https://github.com/processone/issues/4213)) +- `mix:exs`: Move `xmpp` from `included_applications` to `applications` + +#### Dependencies + +- Base64url: Use only when using rebar2 and Erlang lower than 24 +- Idna: Bump from 6.0.0 to 6.1.1 +- Jiffy: Use Json module when Erlang/OTP 27, jiffy with older ones +- Jose: Update to the new 1.11.10 for Erlang/OTP higher than 23 +- Luerl: Update to 1.2.0 when OTP same or higher than 20, simplifies commit a09f222 +- P1_acme: Update to support Jose 1.11.10 and Ipv6 support ([#4170](https://github.com/processone/issues/4170)) +- P1_acme: Update to use Erlang's json library instead of jiffy when OTP 27 +- Port_compiler: Update to 1.15.0 that supports Erlang/OTP 27.0 + +#### Development Help + +- `.gitignore`: Ignore ctags/etags files +- `make dialyzer`: Add support to run Dialyzer with Mix +- `make format|indent`: New targets to format and indent source code +- `make relive`: Add Sync tool with Rebar3, ExSync with Mix +- `hook_deps`: Use precise name: hooks are added and later deleted, not removed +- `hook_deps`: Fix to handle FileNo as tuple `{FileNumber, CharacterPosition}` +- Add support to test also EUnit suite +- Fix `code:lib_dir` call to work with Erlang/OTP 27.0-rc2 +- Set process flags when Erlang/OTP 27 to help debugging +- Test retractions in mam_tests + +#### Documentation + +- Add some XEPs support that was forgotten +- Fix documentation links to new URLs generated by MkDocs +- Remove `...` in example configuration: it is assumed and reduces verbosity +- Support for version note in modules too +- Mark toplevel options, commands and modules that changed in latest version +- Now modules themselves can have version annotations in `note` + +#### Installers and Container + +- make-binaries: Bump Erlang/OTP to 26.2.5 and Elixir 1.16.3 +- make-binaries: Bump OpenSSL to 3.3.1 +- make-binaries: Bump Linux-PAM to 1.6.1 +- make-binaries: Bump Expat to 2.6.2 +- make-binaries: Revert temporarily an OTP commit that breaks MSSQL ([#4178](https://github.com/processone/issues/4178)) +- CONTAINER.md: Invalid `CTL_ON_CREATE` usage in docker-compose example + +#### WebAdmin + +- ejabberd_ctl: Improve parsing of commas in arguments +- ejabberd_ctl: Fix output of UTF-8-encoded binaries +- WebAdmin: Remove webadmin_view for now, as commands allow more fine-grained permissions +- WebAdmin: Unauthorized response: include some text to direct to the logs +- WebAdmin: Improve home page +- WebAdmin: Sort alphabetically the menu items, except the most used ones +- WebAdmin: New login box in the left menu bar +- WebAdmin: Add make_command functions to produce HTML command element +- Document 'any' argument and result type, useful for internal commands +- Commands with 'internal' tag: don't list and block execution by frontends +- WebAdmin: Move content to commands; new pages; hook changes; new commands + ## Version 24.02 #### Core: From 27ba09debf38cd0a87928cbe3a907dcaea24017a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 15:43:26 +0200 Subject: [PATCH 0652/1302] Set version to 24.06 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4d3ea7643..b21d0998e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.02` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.06` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From 3124644315d028909552873d28c58cec2cfa9a8d Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 28 Jun 2024 05:31:16 +0300 Subject: [PATCH 0653/1302] Fix matrix_id_as_jid option documentation --- src/mod_matrix_gw.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 37943ae02..a6470a06e 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -924,10 +924,10 @@ mod_doc() -> {matrix_id_as_jid, #{value => "true | false", desc => - ?T("If set to 'false', all packets failing to be delivered via an XMPP " + ?T("If set to 'true', all packets failing to be delivered via an XMPP " "server-to-server connection will then be routed to the Matrix gateway " "by translating a Jabber ID 'user@matrixdomain.tld' to a Matrix user " - "identifier '@user:matrixdomain.tld'. When set to 'true', messages " + "identifier '@user:matrixdomain.tld'. When set to 'false', messages " "must be explicitly sent to the matrix gateway service Jabber ID to be " "routed to a remote Matrix server. In this case, to send a message to " "Matrix user '@user:matrixdomain.tld', the client must send a message " From 82d95ac81d471d4c53729d3fd3c04a5381364ea3 Mon Sep 17 00:00:00 2001 From: Pouriya Date: Mon, 1 Jul 2024 21:29:41 +0330 Subject: [PATCH 0654/1302] feat: Hook subscribers --- src/ejabberd_hooks.erl | 222 +++++++++++++++++++++++++++++++++++------ 1 file changed, 194 insertions(+), 28 deletions(-) diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 3102c91a0..11b5190f2 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -34,6 +34,10 @@ delete/3, delete/4, delete/5, + subscribe/4, + subscribe/5, + unsubscribe/4, + unsubscribe/5, run/2, run/3, run_fold/3, @@ -59,6 +63,8 @@ -include("ejabberd_stacktrace.hrl"). -record(state, {}). +-type subscriber() :: {Module :: atom(), Function :: atom(), InitArg :: any()}. +-type subscriber_event() :: before | 'after' | before_callback | after_callback. -type hook() :: {Seq :: integer(), Module :: atom(), Function :: atom() | fun()}. -define(TRACE_HOOK_KEY, '$trace_hook'). @@ -87,6 +93,34 @@ add(Hook, Module, Function, Seq) -> add(Hook, Host, Module, Function, Seq) -> gen_server:call(?MODULE, {add, Hook, Host, Module, Function, Seq}). +-spec subscribe(atom(), atom(), atom(), any()) -> ok. +%% @doc Add a subscriber to this hook. +%% +%% Before running any hook callback, the subscriber will be called in form of +%% Module:Function(InitArg, 'before', Host :: binary() | global, Hook, HookArgs) +%% Above function should return new state. +%% +%% Before running each callback, the subscriber will be called in form of +%% Module:Function(State, 'before_callback', Host :: binary() | global, Hook, {CallbackMod, CallbackArg, Seq, HookArgs}) +%% Above function should return new state. +%% +%% After running each callback, the subscriber will be called in form of +%% Module:Function(State, 'after_callback', Host :: binary() | global, Hook, {CallbackMod, CallbackArg, Seq, HookArgs}) +%% Above function should return new state. +%% +%% After running any hook callback, the subscriber will be called in form of +%% Module:Function(State, 'after', Host :: binary() | global, Hook, HookArgs) +%% Return value of this function call will be dropped. +%% +%% For every ejabberd_hooks:[run|run_fold] for every subscriber above functions will be called and the hook runner +%% maintains State in above four calls. +subscribe(Hook, Module, Function, InitArg) -> + subscribe(Hook, global, Module, Function, InitArg). + +-spec subscribe(atom(), binary() | global, atom(), atom(), any()) -> ok. +subscribe(Hook, Host, Module, Function, InitArg) -> + gen_server:call(?MODULE, {subscribe, Hook, Host, Module, Function, InitArg}). + -spec delete(atom(), fun(), integer()) -> ok. %% @doc See del/4. delete(Hook, Function, Seq) when is_function(Function) -> @@ -105,8 +139,20 @@ delete(Hook, Module, Function, Seq) -> delete(Hook, Host, Module, Function, Seq) -> gen_server:call(?MODULE, {delete, Hook, Host, Module, Function, Seq}). + + +-spec unsubscribe(atom(), atom(), atom(), any()) -> ok. +%% @doc Removes a subscriber from this hook. +unsubscribe(Hook, Module, Function, InitArg) -> + unsubscribe(Hook, global, Module, Function, InitArg). + +-spec unsubscribe(atom(), binary() | global, atom(), atom(), any()) -> ok. +unsubscribe(Hook, Host, Module, Function, InitArg) -> + gen_server:call(?MODULE, {unsubscribe, Hook, Host, Module, Function, InitArg}). + + -spec run(atom(), list()) -> ok. -%% @doc Run the calls of this hook in order, don't care about function results. +%% @doc Run the calls (and subscibers) of this hook in order, don't care about function results. %% If a call returns stop, no more calls are performed. run(Hook, Args) -> run(Hook, global, Args). @@ -114,17 +160,28 @@ run(Hook, Args) -> -spec run(atom(), binary() | global, list()) -> ok. run(Hook, Host, Args) -> try ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> case erlang:get(?TRACE_HOOK_KEY) of - undefined -> + undefined when Subs == [] -> run1(Ls, Hook, Args); + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run1(Ls, Hook, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok; TracingHooksOpts -> case do_get_tracing_options(Hook, Host, TracingHooksOpts) of undefined -> - run1(Ls, Hook, Args); + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run1(Ls, Hook, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok; TracingOpts -> foreach_start_hook_tracing(TracingOpts, Hook, Host, Args), - run2(Ls, Hook, Args, Host, TracingOpts) + Subs2 = call_subscriber_list(Subs, Host, Hook, Args, before, []), + Subs3 = run2(Ls, Hook, Args, Host, TracingOpts, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, Args, 'after', []), + ok end end; [] -> @@ -134,7 +191,7 @@ run(Hook, Host, Args) -> end. -spec run_fold(atom(), T, list()) -> T. -%% @doc Run the calls of this hook in order. +%% @doc Run the calls (and subscribers) of this hook in order. %% The arguments passed to the function are: [Val | Args]. %% The result of a call is used as Val for the next call. %% If a call returns 'stop', no more calls are performed. @@ -145,17 +202,28 @@ run_fold(Hook, Val, Args) -> -spec run_fold(atom(), binary() | global, T, list()) -> T. run_fold(Hook, Host, Val, Args) -> try ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> case erlang:get(?TRACE_HOOK_KEY) of - undefined -> + undefined when Subs == [] -> run_fold1(Ls, Hook, Val, Args); + undefined -> + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold1(Ls, Hook, Val, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2; TracingHooksOpts -> case do_get_tracing_options(Hook, Host, TracingHooksOpts) of undefined -> - run_fold1(Ls, Hook, Val, Args); + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold1(Ls, Hook, Val, Args, Host, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2; TracingOpts -> fold_start_hook_tracing(TracingOpts, Hook, Host, [Val | Args]), - run_fold2(Ls, Hook, Val, Args, Host, TracingOpts) + Subs2 = call_subscriber_list(Subs, Host, Hook, [Val | Args], before, []), + {Val2, Subs3} = run_fold2(Ls, Hook, Val, Args, Host, TracingOpts, Subs2), + _Subs4 = call_subscriber_list(Subs3, Host, Hook, [Val2 | Args], 'after', []), + Val2 end end; [] -> @@ -230,6 +298,14 @@ handle_call({delete, Hook, Host, Module, Function, Seq}, _From, State) -> HookFormat = {Seq, Module, Function}, Reply = handle_delete(Hook, Host, HookFormat), {reply, Reply, State}; +handle_call({subscribe, Hook, Host, Module, Function, InitArg}, _From, State) -> + SubscriberFormat = {Module, Function, InitArg}, + Reply = handle_subscribe(Hook, Host, SubscriberFormat), + {reply, Reply, State}; +handle_call({unsubscribe, Hook, Host, Module, Function, InitArg}, _From, State) -> + SubscriberFormat = {Module, Function, InitArg}, + Reply = handle_unsubscribe(Hook, Host, SubscriberFormat), + {reply, Reply, State}; handle_call(Request, From, State) -> ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), {noreply, State}. @@ -237,27 +313,53 @@ handle_call(Request, From, State) -> -spec handle_add(atom(), atom(), hook()) -> ok. handle_add(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> case lists:member(El, Ls) of true -> ok; false -> NewLs = lists:merge(Ls, [El]), - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, Subs}), ok end; [] -> NewLs = [El], - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, []}), ok end. -spec handle_delete(atom(), atom(), hook()) -> ok. handle_delete(Hook, Host, El) -> case ets:lookup(hooks, {Hook, Host}) of - [{_, Ls}] -> + [{_, Ls, Subs}] -> NewLs = lists:delete(El, Ls), - ets:insert(hooks, {{Hook, Host}, NewLs}), + ets:insert(hooks, {{Hook, Host}, NewLs, Subs}), + ok; + [] -> + ok + end. + +-spec handle_subscribe(atom(), atom(), subscriber()) -> ok. +handle_subscribe(Hook, Host, El) -> + case ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + case lists:member(El, Subs) of + true -> + ok; + false -> + ets:insert(hooks, {{Hook, Host}, Ls, Subs ++ [El]}), + ok + end; + [] -> + ets:insert(hooks, {{Hook, Host}, [], [El]}), + ok + end. + +-spec handle_unsubscribe(atom(), atom(), subscriber()) -> ok. +handle_unsubscribe(Hook, Host, El) -> + case ets:lookup(hooks, {Hook, Host}) of + [{_, Ls, Subs}] -> + ets:insert(hooks, {{Hook, Host}, Ls, lists:delete(El, Subs)}), ok; [] -> ok @@ -310,6 +412,40 @@ run_fold1([{_Seq, Module, Function} | Ls], Hook, Val, Args) -> run_fold1(Ls, Hook, NewVal, Args) end. +-spec run1([hook()], atom(), list(), binary() | global, [subscriber()]) -> [subscriber()]. +run1([], _Hook, _Args, _Host, SubscriberList) -> + SubscriberList; +run1([{Seq, Module, Function} | Ls], Hook, Args, Host, SubscriberList) -> + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, Args}, before_callback, []), + Res = safe_apply(Hook, Module, Function, Args), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, Args}, after_callback, []), + case Res of + 'EXIT' -> + run1(Ls, Hook, Args, Host, SubscriberList3); + stop -> + SubscriberList3; + _ -> + run1(Ls, Hook, Args, Host, SubscriberList3) + end. + +-spec run_fold1([hook()], atom(), T, list(), binary() | global, [subscriber()]) -> {T, [subscriber()]}. +run_fold1([], _Hook, Val, _Args, _Host, SubscriberList) -> + {Val, SubscriberList}; +run_fold1([{Seq, Module, Function} | Ls], Hook, Val, Args, Host, SubscriberList) -> + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, [Val | Args]}, before_callback, []), + Res = safe_apply(Hook, Module, Function, [Val | Args]), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, [Val | Args]}, after_callback, []), + case Res of + 'EXIT' -> + run_fold1(Ls, Hook, Val, Args, Host, SubscriberList3); + stop -> + {Val, SubscriberList3}; + {stop, NewVal} -> + {NewVal, SubscriberList3}; + NewVal -> + run_fold1(Ls, Hook, NewVal, Args, Host, SubscriberList3) + end. + -spec safe_apply(atom(), atom(), atom() | fun(), list()) -> any(). safe_apply(Hook, Module, Function, Args) -> ?DEBUG("Running hook ~p: ~p:~p/~B", @@ -332,6 +468,32 @@ safe_apply(Hook, Module, Function, Args) -> 'EXIT' end. +-spec call_subscriber_list([subscriber()], binary() | global, atom(), {atom(), atom(), integer(), list()} | list(), subscriber_event(), [subscriber()]) -> any(). +call_subscriber_list([], _Host, _Hook, _CallbackOrArgs, _Event, []) -> + []; +call_subscriber_list([], _Host, _Hook, _CallbackOrArgs, _Event, Result) -> + lists:reverse(Result); +call_subscriber_list([{Mod, Func, InitArg} | SubscriberList], Host, Hook, CallbackOrArgs, Event, Result) -> + SubscriberArgs = [InitArg, Event, Host, Hook, CallbackOrArgs], + ?DEBUG("Running hook subsciber ~p: ~p:~p/~B with event ~p", + [Hook, Mod, Func, length(SubscriberArgs), Event]), + try apply(Mod, Func, SubscriberArgs) of + State -> + call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, [{Mod, Func, State} | Result]) + catch ?EX_RULE(E, R, St) when E /= exit; R /= normal -> + Stack = ?EX_STACK(St), + ?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++ + string:join( + ["** ~ts"| + ["** Arg " ++ integer_to_list(I) ++ " = ~p" + || I <- lists:seq(1, length(SubscriberArgs))]], + "~n"), + [Hook, Mod, Func, length(SubscriberArgs), + misc:format_exception(2, E, R, Stack)|SubscriberArgs]), + %% Do not append subscriber for next calls: + call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result) + end. + %%%---------------------------------------------------------------------- %%% Internal tracing functions %%%---------------------------------------------------------------------- @@ -453,41 +615,45 @@ do_get_tracing_options(Hook, Host, MaybeMap) -> end end. -run2([], Hook, Args, Host, Opts) -> +run2([], Hook, Args, Host, Opts, SubscriberList) -> foreach_stop_hook_tracing(Opts, Hook, Host, Args, undefined), - ok; -run2([{Seq, Module, Function} | Ls], Hook, Args, Host, TracingOpts) -> + SubscriberList; +run2([{Seq, Module, Function} | Ls], Hook, Args, Host, TracingOpts, SubscriberList) -> foreach_start_callback_tracing(TracingOpts, Hook, Host, Module, Function, Args, Seq), + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, Args}, before_callback, []), Res = safe_apply(Hook, Module, Function, Args), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, Args}, after_callback, []), foreach_stop_callback_tracing(TracingOpts, Hook, Host, Module, Function, Args, Seq, Res), case Res of 'EXIT' -> - run2(Ls, Hook, Args, Host, TracingOpts); + run2(Ls, Hook, Args, Host, TracingOpts, SubscriberList3); stop -> foreach_stop_hook_tracing(TracingOpts, Hook, Host, Args, {Module, Function, Seq, Ls}), - ok; + SubscriberList3; _ -> - run2(Ls, Hook, Args, Host, TracingOpts) + run2(Ls, Hook, Args, Host, TracingOpts, SubscriberList3) end. -run_fold2([], Hook, Val, Args, Host, Opts) -> +run_fold2([], Hook, Val, Args, Host, Opts, SubscriberList) -> fold_stop_hook_tracing(Opts, Hook, Host, [Val | Args], undefined), - Val; -run_fold2([{Seq, Module, Function} | Ls], Hook, Val, Args, Host, TracingOpts) -> + {Val, SubscriberList}; +run_fold2([{Seq, Module, Function} | Ls], Hook, Val, Args, Host, TracingOpts, SubscriberList) -> fold_start_callback_tracing(TracingOpts, Hook, Host, Module, Function, [Val | Args], Seq), + SubscriberList2 = call_subscriber_list(SubscriberList, Host, Hook, {Module, Function, Seq, [Val | Args]}, before_callback, []), Res = safe_apply(Hook, Module, Function, [Val | Args]), + SubscriberList3 = call_subscriber_list(SubscriberList2, Host, Hook, {Module, Function, Seq, [Val | Args]}, after_callback, []), fold_stop_callback_tracing(TracingOpts, Hook, Host, Module, Function, [Val | Args], Seq, Res), case Res of 'EXIT' -> - run_fold2(Ls, Hook, Val, Args, Host, TracingOpts); + run_fold2(Ls, Hook, Val, Args, Host, TracingOpts, SubscriberList3); stop -> fold_stop_hook_tracing(TracingOpts, Hook, Host, [Val | Args], {Module, Function, Seq, {old, Val}, Ls}), - Val; + {Val, SubscriberList3}; {stop, NewVal} -> fold_stop_hook_tracing(TracingOpts, Hook, Host, [Val | Args], {Module, Function, Seq, {new, NewVal}, Ls}), - NewVal; + {NewVal, SubscriberList3}; NewVal -> - run_fold2(Ls, Hook, NewVal, Args, Host, TracingOpts) + run_fold2(Ls, Hook, NewVal, Args, Host, TracingOpts, SubscriberList3) end. foreach_start_hook_tracing(TracingOpts, Hook, Host, Args) -> From 949649e3a9e699f61feef195ef7290705c31c310 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Jun 2024 17:20:50 +0200 Subject: [PATCH 0655/1302] Fix typos (thanks to Jerome Sautret) --- src/mod_admin_extra.erl | 12 ++++++------ src/mod_mam.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_offline.erl | 4 ++-- src/mod_push.erl | 2 +- src/mod_stream_mgmt.erl | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 7a592462c..ca99b120e 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2389,7 +2389,7 @@ mod_doc() -> #{desc => [?T("This module provides additional administrative commands."), "", ?T("Details for some commands:"), "", - ?T("- 'ban-acount':"), + ?T("- 'ban_account':"), ?T("This command kicks all the connected sessions of the account " "from the server. It also changes their password to a randomly " "generated one, so they can't login anymore unless a server " @@ -2402,8 +2402,8 @@ mod_doc() -> "`C:/Program Files/ejabberd` or similar. If you use other " "Operating System, place the file on the same directory where " "the .beam files are installed. See below an example roster file."), - ?T("- 'srg-create':"), - ?T("If you want to put a group Name with blankspaces, use the " + ?T("- 'srg_create':"), + ?T("If you want to put a group Name with blank spaces, use the " "characters \"\' and \'\" to define when the Name starts and " "ends. See an example below.")], example => @@ -2427,6 +2427,6 @@ mod_doc() -> "boby@example.org will be kicked, and its password will be set " "to something like " "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"), - ["ejabberdctl vhost example.org ban-account boby \"Spammed rooms\""]}, - {?T("Call to srg-create using double-quotes and single-quotes:"), - ["ejabberdctl srg-create g1 example.org \"\'Group number 1\'\" this_is_g1 g1"]}]}. + ["ejabberdctl vhost example.org ban_account boby \"Spammed rooms\""]}, + {?T("Call to srg_create using double-quotes and single-quotes:"), + ["ejabberdctl srg_create g1 example.org \"\'Group number 1\'\" this_is_g1 g1"]}]}. diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 77baecac3..ea53dfc85 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1678,7 +1678,7 @@ mod_doc() -> #{value => "true | false", desc => ?T("When this option is disabled, for each individual " - "subscriber a separa mucsub message is stored. With this " + "subscriber a separate mucsub message is stored. With this " "option enabled, when a user fetches archive virtual " "mucsub, messages are generated from muc archives. " "The default value is 'false'.")}}]}. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index e297d866f..adc80921e 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1582,7 +1582,7 @@ mod_doc() -> desc => ?T("This option defines after how many users in the room, " "it is considered overcrowded. When a MUC room is considered " - "overcrowed, presence broadcasts are limited to reduce load, " + "overcrowded, presence broadcasts are limited to reduce load, " "traffic and excessive presence \"storm\" received by participants. " "The default value is '1000'.")}}, {min_message_interval, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index e8c8c52bf..516b3ba4a 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1225,7 +1225,7 @@ mod_doc() -> {bounce_groupchat, #{value => "true | false", desc => - ?T("This option is use the disable an optimisation that " + ?T("This option is use the disable an optimization that " "avoids bouncing error messages when groupchat messages " "could not be stored as offline. It will reduce chat " "room load, without any drawback in standard use cases. " @@ -1235,7 +1235,7 @@ mod_doc() -> "but the bounce is much more likely to happen in the context " "of MucSub, so it is even more important to have it on " "large MucSub services. The default value is 'false', meaning " - "the optimisation is enabled.")}}, + "the optimization is enabled.")}}, {db_type, #{value => "mnesia | sql", desc => diff --git a/src/mod_push.erl b/src/mod_push.erl index 815539803..f893ed777 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -176,7 +176,7 @@ mod_doc() -> "\"app servers\" operated by third-party vendors of " "mobile apps. Those app servers will usually trigger " "notification delivery to the user's mobile device using " - "platform-dependant backend services such as FCM or APNS."), + "platform-dependent backend services such as FCM or APNS."), opts => [{notify_on, #{value => "messages | all", diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 339eec55b..b125b9e4d 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -950,7 +950,7 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0198.html" "[XEP-0198: Stream Management]. This protocol allows " "active management of an XML stream between two XMPP " - "entities, including features for stanza acknowledgements " + "entities, including features for stanza acknowledgments " "and stream resumption."), opts => [{max_ack_queue, @@ -992,7 +992,7 @@ mod_doc() -> {ack_timeout, #{value => "timeout()", desc => - ?T("A time to wait for stanza acknowledgements. " + ?T("A time to wait for stanza acknowledgments. " "Setting it to 'infinity' effectively disables the timeout. " "The default value is '1' minute.")}}, {resend_on_timeout, From 4258d3dc24aad7d740ed901b081eb4018dd6421b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 1 Jul 2024 18:44:11 +0200 Subject: [PATCH 0656/1302] Update rebar.lock --- rebar.lock | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/rebar.lock b/rebar.lock index ca05ecd90..3ea42655d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -14,32 +14,17 @@ {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.16">>},0}, - {<<"p1_acme">>, - {git,"https://github.com/processone/p1_acme", - {ref,"176b4a8c67627c3229fbea3c05d82a5d567e6e49"}}, - 0}, - {<<"p1_mysql">>, - {git,"https://github.com/processone/p1_mysql", - {ref,"0dccb755a60cda61d41e7b092b344d38d1ebc802"}}, - 0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.23">>},0}, + {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.24">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>, - {git,"https://github.com/processone/p1_pgsql", - {ref,"fe0bb2a2a2ae21cd9fc48b81520cfce508520a56"}}, - 0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.26">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, - {<<"pkix">>, - {git,"https://github.com/processone/pkix", - {ref,"03be27c7168449bdfeb8c5b6cc0f800f71f8cfe9"}}, - 0}, + {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.14">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.12">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"a1fb778bd385d832f913e564558152ea507dac37"}}, - 0}, + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.2">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. [ {pkg_hash,[ @@ -58,12 +43,17 @@ {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, {<<"mqtree">>, <<"F8F8B4971E4CA94313BA9BCAAA1AA1077DAABA5E3FD3468FFB491420A4CC3593">>}, + {<<"p1_acme">>, <<"791AEF0F79DC7F768B228808250C349FA9CE585CD8779DA50CA93106EB3394D0">>}, + {<<"p1_mysql">>, <<"0ED1E098C5A4525032448C65A2715F30980AAE725615A4D255FD25F26BB22507">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, + {<<"p1_pgsql">>, <<"D3C3748C3638A1D7DB5644E4FC63A6DA7614B3009E172EF92A01D0217C3BEC65">>}, {<<"p1_utils">>, <<"2D39B5015A567BBD2CC7033EEB93A7C60D8C84EFE1EF69A3473FAA07FA268187">>}, + {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"F9EA0CFF8540865FDFDB7E24EEF34DC46677364B1C070896E99B5BF08C8A7FD7">>}, {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, {<<"stun">>, <<"A65DF67A8AAAECB6A94D687977B2E9F161820819910CB97BBE26A3525356525B">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"7C26FAE7CA83B307BAB99624595DCE706D427A17EED9C6305980550F8120A3A3">>}, {<<"yconf">>, <<"E22998B3D7728270BDD06162A9515BD142B14FAE8927CBDBD3EF639C32AA6F7A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -81,11 +71,16 @@ {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, {<<"mqtree">>, <<"C87D1C95575DB65AF29B795C9DAA3BED43F5C1BF84072A74469659BCF53594EB">>}, + {<<"p1_acme">>, <<"8CE196F26E3D22EA10B7809122950465878C127F80767E325207AED7E8D0DD59">>}, + {<<"p1_mysql">>, <<"F058865F64257F507A2C6A5AFF369B1375DBCB30B3D4258DAD4F1B3EAFFB655F">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, + {<<"p1_pgsql">>, <<"85F230DB530333106B8A1F9E5D5AF032E6C3DD23B432E03D68E9D29013A6DCFC">>}, {<<"p1_utils">>, <<"9219214428F2C6E5D3187FF8EB9A8783695C2427420BE9A259840E07ADA32847">>}, + {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"85054B6CA297343C159ED6794A473FF2C8EEABD854B6FE02F711C0BFD373CE86">>}, {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, {<<"stun">>, <<"A2055032B6D338D0454142004BCB12FAFB0C64AB1F273F1D0C6923EBBC8EDE40">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"53A9F85AD44103A358DC173225BC96D08076D4E78506F87D838337B2FA3B381F">>}, {<<"yconf">>, <<"7FF2AB24D3C9833842716B9AAAA01A8F96641A7695CBB701B03445C4DEF01117">>}]} ]. From 543b874a103975e9b1b94fc362d4d71f0f7a39b4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 2 Jul 2024 13:43:01 +0200 Subject: [PATCH 0657/1302] ext_mod: Handle case when contrib module has no *.ex and no *.erl --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 5da3b34e7..7eea46a93 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -1195,7 +1195,7 @@ is_elixir_module(Module) -> filelib:wildcard(Src++"/*.{erl}")} of {[_ | _], []} -> true; - {[], [_ | _]} -> + {[], _} -> false end. From 6c2dfd3d3199027ceb6cd5ee593baf252c856a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20R=C3=A9mond?= Date: Tue, 2 Jul 2024 15:42:41 +0200 Subject: [PATCH 0658/1302] Update CODE_OF_CONDUCT.md Try to limit the number of ping comments in the community. This can get into the way of efficient work and be considered a bit offensive for some users or maintainers. --- CODE_OF_CONDUCT.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a9145ba2f..099c1be0b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -22,6 +22,21 @@ Examples of unacceptable behavior by participants include: * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting +## Guidelines for Respectful and Efficient Communication on Issues, Discussions, and PRs + +To ensure that our maintainers can efficiently manage issues and provide timely updates, we kindly ask that all comments on GitHub tickets remain relevant to the topic of the issue. Please avoid posting comments solely to ping maintainers or ask for updates. If you need information on the status of an issue, consider the following: + +- **Check the Issue Timeline:** Review the existing comments and updates on the issue before posting. +- **Use Reactions:** If you want to show that you are interested in an issue, use GitHub's reaction feature (e.g., thumbs up) instead of commenting. +- **Be Patient:** Understand that maintainers may be working on multiple tasks and will provide updates as soon as possible. + +Additionally, please be aware that: + +- **User Responses:** Users who report issues may no longer be using the software, may have switched to other projects, or may simply be busy. It is their right not to respond to follow-up questions or comments. +- **Maintainer Priorities:** Maintainers have the right to define their own priorities and schedule. They will address issues based on their availability and the project's needs. + +By following these guidelines, you help us maintain a productive and respectful environment for everyone involved. + ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. From 35042ebc6a22fe8618f778c30d86ca5573102873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 12:38:27 +0200 Subject: [PATCH 0659/1302] Make sql query in testsuite compatible with pg9.1 --- test/ejabberd_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 7fe6c4542..3ab0a8d45 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -186,7 +186,7 @@ end_per_group(mssql, Config) -> end, ok; end_per_group(pgsql, Config) -> - Query = "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'mqtt_pub');", + Query = "SELECT EXISTS (SELECT 0 FROM information_schema.tables WHERE table_name = 'mqtt_pub');", case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [Query]) of {selected, [t]} -> clear_sql_tables(pgsql, Config); From 54f5db851defb5f69e75830081c6aad4a991a20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 13:15:33 +0200 Subject: [PATCH 0660/1302] Don't use host from url in webadmin, prefer host used for authentication --- src/ejabberd_web_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 7db93bf6a..0331b1329 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1691,7 +1691,7 @@ make_command2(Name, Request, BaseArguments, Options) -> CallerInfo = #{usr => {RUser, RServer, <<"">>}, ip => RIp, - caller_host => RHost, + caller_host => RServer, caller_module => ?MODULE}, try {ejabberd_commands:get_command_definition(Name), ejabberd_access_permissions:can_access(Name, CallerInfo)} From 8d4c1e3617422397de947b6642928632cfb3652e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 13:44:48 +0200 Subject: [PATCH 0661/1302] One more fix for pg91 in test suite --- test/ejabberd_SUITE.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 3ab0a8d45..9b153fd48 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -190,6 +190,8 @@ end_per_group(pgsql, Config) -> case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [Query]) of {selected, [t]} -> clear_sql_tables(pgsql, Config); + {selected, _, [[<<"t">>]]} -> + clear_sql_tables(pgsql, Config); Other -> ct:fail({failed_to_check_table_existence, pgsql, Other}) end, From a4fd756eae7768a1f2884fed0e4eb2972b8bb7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 15:20:48 +0200 Subject: [PATCH 0662/1302] Add misc:json_encode_With_kv_lists and use it in matrix sign function R27 json module doesn't recognize list of tuples as object specification, so this creates wrapper that offer this functionality. --- src/misc.erl | 11 ++++++++++- src/mod_matrix_gw.erl | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/misc.erl b/src/misc.erl index d53dc3c7b..096b7de42 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -42,7 +42,7 @@ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4, get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, - json_encode/1, json_decode/1, + json_encode/1, json_decode/1, json_encode_with_kv_lists/1, set_proc_label/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). @@ -132,11 +132,20 @@ crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL). -endif. -ifdef(OTP_BELOW_27). +json_encode_with_kv_lists(Term) -> + jiffy:encode(Term). json_encode(Term) -> jiffy:encode(Term). json_decode(Bin) -> jiffy:decode(Bin, [return_maps]). -else. +json_encode_with_kv_lists(Term) -> + iolist_to_binary(json:encode(Term), + fun([{_, _} | _] = Val, Encoder) -> + json:encode_key_value_list(Val, Encoder); + (Val, Encoder) -> + json:encode_value(Val, Encoder) + end). json_encode(Term) -> iolist_to_binary(json:encode(Term)). json_decode(Bin) -> diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index a6470a06e..80142ff49 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -673,7 +673,7 @@ get_pruned_event_id(PrunedEvent) -> encode_canonical_json(JSON) -> JSON2 = sort_json(JSON), - misc:json_encode(JSON2). + misc:json_encode_with_kv_lists(JSON2). sort_json(#{} = Map) -> Map2 = maps:map(fun(_K, V) -> From b44b1304b804c27319a3898337be55aea675e038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 15:55:29 +0200 Subject: [PATCH 0663/1302] Fix typo in last commit --- src/misc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.erl b/src/misc.erl index 096b7de42..c73221732 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -140,12 +140,12 @@ json_decode(Bin) -> jiffy:decode(Bin, [return_maps]). -else. json_encode_with_kv_lists(Term) -> - iolist_to_binary(json:encode(Term), + iolist_to_binary(json:encode(Term, fun([{_, _} | _] = Val, Encoder) -> json:encode_key_value_list(Val, Encoder); (Val, Encoder) -> json:encode_value(Val, Encoder) - end). + end)). json_encode(Term) -> iolist_to_binary(json:encode(Term)). json_decode(Bin) -> From b978a479256916d44d7240803fe896ddbeece8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 3 Jul 2024 15:55:48 +0200 Subject: [PATCH 0664/1302] Fix unused variable warning --- src/ejabberd_web_admin.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 0331b1329..018314898 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1685,8 +1685,7 @@ make_command2(Name, Request, BaseArguments, Options) -> TO = proplists:get_value(table_options, Options, {999999, []}), Style = proplists:get_value(style, Options, normal), #request{us = {RUser, RServer}, - ip = RIp, - host = RHost} = + ip = RIp} = Request, CallerInfo = #{usr => {RUser, RServer, <<"">>}, From 25b78b73d08d761a448fa1c8ecd67fb01721bf64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 4 Jul 2024 11:21:26 +0200 Subject: [PATCH 0665/1302] Add ability to specify custom timeout for sql operations --- src/ejabberd_sql.erl | 53 +++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 5e5549c45..a803b4139 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -32,9 +32,12 @@ %% External exports -export([start_link/2, sql_query/2, + sql_query/3, sql_query_t/1, sql_transaction/2, + sql_transaction/3, sql_bloc/2, + sql_bloc/3, abort/1, restart/1, use_new_schema/0, @@ -84,7 +87,8 @@ reconnect_count = 0 :: non_neg_integer(), host :: binary(), pending_requests :: p1_queue:queue(), - overload_reported :: undefined | integer()}). + overload_reported :: undefined | integer(), + timeout :: pos_integer()}). -define(STATE_KEY, ejabberd_sql_state). -define(NESTING_KEY, ejabberd_sql_nesting_level). @@ -120,35 +124,48 @@ start_link(Host, I) -> p1_fsm:start_link({local, Proc}, ?MODULE, [Host], fsm_limit_opts() ++ ?FSMOPTS). +-spec sql_query(binary(), sql_query(T), pos_integer()) -> sql_query_result(T). +sql_query(Host, Query, Timeout) -> + sql_call(Host, {sql_query, Query}, Timeout). + -spec sql_query(binary(), sql_query(T)) -> sql_query_result(T). sql_query(Host, Query) -> - sql_call(Host, {sql_query, Query}). + sql_query(Host, Query, query_timeout(Host)). %% SQL transaction based on a list of queries %% This function automatically --spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T)) -> +-spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T), pos_integer()) -> {atomic, T} | {aborted, any()}. -sql_transaction(Host, Queries) +sql_transaction(Host, Queries, Timeout) when is_list(Queries) -> F = fun () -> lists:foreach(fun (Query) -> sql_query_t(Query) end, Queries) end, - sql_transaction(Host, F); + sql_transaction(Host, F, Timeout); %% SQL transaction, based on a erlang anonymous function (F = fun) -sql_transaction(Host, F) when is_function(F) -> - case sql_call(Host, {sql_transaction, F}) of +sql_transaction(Host, F, Timeout) when is_function(F) -> + case sql_call(Host, {sql_transaction, F}, Timeout) of {atomic, _} = Ret -> Ret; {aborted, _} = Ret -> Ret; Err -> {aborted, Err} end. -%% SQL bloc, based on a erlang anonymous function (F = fun) -sql_bloc(Host, F) -> sql_call(Host, {sql_bloc, F}). +-spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T)) -> + {atomic, T} | + {aborted, any()}. +sql_transaction(Host, Queries) -> + sql_transaction(Host, Queries, query_timeout(Host)). -sql_call(Host, Msg) -> - Timeout = query_timeout(Host), +%% SQL bloc, based on a erlang anonymous function (F = fun) +sql_bloc(Host, F, Timeout) -> + sql_call(Host, {sql_bloc, F}, Timeout). + +sql_bloc(Host, F) -> + sql_bloc(Host, F, query_timeout(Host)). + +sql_call(Host, Msg, Timeout) -> case get(?STATE_KEY) of undefined -> sync_send_event(Host, @@ -355,7 +372,8 @@ init([Host]) -> QueueType = ejabberd_option:sql_queue_type(Host), {ok, connecting, #state{db_type = DBType, host = Host, - pending_requests = p1_queue:new(QueueType, max_fsm_queue())}}. + pending_requests = p1_queue:new(QueueType, max_fsm_queue()), + timeout = query_timeout(Host)}}. connecting(connect, #state{host = Host} = State) -> ConnectRes = case db_opts(Host) of @@ -496,10 +514,12 @@ handle_reconnect(Reason, #state{host = Host, reconnect_count = RC} = State) -> _ -> ok end, p1_fsm:send_event_after(StartInterval, connect), - {next_state, connecting, State#state{reconnect_count = RC + 1}}. + {next_state, connecting, State#state{reconnect_count = RC + 1, + timeout = query_timeout(Host)}}. run_sql_cmd(Command, From, State, Timestamp) -> - case current_time() >= Timestamp of + CT = current_time(), + case CT >= Timestamp of true -> State1 = report_overload(State), {next_state, session_established, State1}; @@ -510,8 +530,9 @@ run_sql_cmd(Command, From, State, Timestamp) -> State#state.pending_requests), handle_reconnect(Reason, State#state{pending_requests = PR}) after 0 -> + Timeout = min(query_timeout(State#state.host), Timestamp - CT), put(?NESTING_KEY, ?TOP_LEVEL_TXN), - put(?STATE_KEY, State), + put(?STATE_KEY, State#state{timeout = Timeout}), abort_on_driver_error(outer_op(Command), From, Timestamp) end end. @@ -726,7 +747,7 @@ sql_query_internal(F) when is_function(F) -> sql_query_internal(Query) -> State = get(?STATE_KEY), ?DEBUG("SQL: \"~ts\"", [Query]), - QueryTimeout = query_timeout(State#state.host), + QueryTimeout = State#state.timeout, Res = case State#state.db_type of odbc -> to_odbc(odbc:sql_query(State#state.db_ref, [Query], From fe472a63a0d9d57ed9a17e5473ab7e9cc4accb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 4 Jul 2024 13:49:29 +0200 Subject: [PATCH 0666/1302] Improve cross version handling of muc retractions --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 85 ++++++++++++++++++++++---------------------- src/mod_muc_room.erl | 43 +++++++++------------- 4 files changed, 62 insertions(+), 70 deletions(-) diff --git a/mix.exs b/mix.exs index 002f8628c..52fd90f93 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.8.2"}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ce76eda23d8c7654a815f50de6c7cfcaa6c46850", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index bbb02af56..9999574e7 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.13"}}}}, - {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", {tag, "1.8.2"}}}, + {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", "ce76eda23d8c7654a815f50de6c7cfcaa6c46850"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 3ea42655d..750fb5f49 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,86 +1,87 @@ {"1.2.0", [{<<"base64url">>,{pkg,<<"base64url">>,<<"1.0.1">>},1}, - {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.30">>},0}, - {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.22">>},0}, + {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.31">>},0}, + {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.23">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.52">>},0}, - {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.12">>},0}, - {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.19">>},0}, - {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.51">>},0}, - {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.36">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.53">>},0}, + {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.20">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.52">>},0}, + {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.1">>},0}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, - {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.16">>},0}, + {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.23">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.24">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.26">>},0}, - {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.25">>},0}, + {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, - {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.14">>},0}, - {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.29">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.12">>},0}, + {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.13">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.2">>},0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.15">>},0}]}. + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"ce76eda23d8c7654a815f50de6c7cfcaa6c46850"}}, + 0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, - {<<"cache_tab">>, <<"6D35EECFB65FBE5FC85988503A27338D32DE01243F3FC8EA3EE7161AF08725A4">>}, - {<<"eimp">>, <<"FA9B376EF0B50E8455DB15C7C11DEA4522C6902E04412288AAB436D26335F6EB">>}, + {<<"cache_tab">>, <<"E4097B50A6F373AB1E0A5F01BAB0BEF6626771A4CD6C93404ED6D54810E11FBC">>}, + {<<"eimp">>, <<"AAF32EFAB061143403DADBAA63E699EF8E81702FBFA96FD436D5E9BE4D6A8D3A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, - {<<"esip">>, <<"A2840287C493A4280E6FBA57A257706843B025C315875E38B03FD07190E22DBA">>}, - {<<"ezlib">>, <<"FFE906BA10D03AAEE7977E1E0E81D9FFC3BB8B47FB9CD8E2E453507A2E56221F">>}, - {<<"fast_tls">>, <<"F52731A4B35259FA06CF23E2A0732920AD9EFCE7C3D68377F129A474998747BB">>}, - {<<"fast_xml">>, <<"A7F8C6942591632309099386D5C339C89997AC2BBDD1216F6C196DEE6D7828A9">>}, - {<<"fast_yaml">>, <<"65413A34A570FD4E205A460BA602E4EE7A682F35C22D2E1C839025DBF515105C">>}, + {<<"esip">>, <<"482786A79D8F5B0AEFC60CA68B8C6C7F074F6EDE2653705E4206C6779EDC5A56">>}, + {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, + {<<"fast_tls">>, <<"D6F12D9AE4FE57E880B144B912E735AF89343A8463D39B7EB4BE3F6CA6163879">>}, + {<<"fast_xml">>, <<"0289DAAFBF1190B0E53B444D4885CCCF41E4B05768D4B3ACC76DD8D143668E10">>}, + {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"ACA10F47AA91697BF24AB9582C74E00E8E95474C7EF9F76D4F1A338D0F5DE21B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, - {<<"mqtree">>, <<"F8F8B4971E4CA94313BA9BCAAA1AA1077DAABA5E3FD3468FFB491420A4CC3593">>}, + {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, {<<"p1_acme">>, <<"791AEF0F79DC7F768B228808250C349FA9CE585CD8779DA50CA93106EB3394D0">>}, {<<"p1_mysql">>, <<"0ED1E098C5A4525032448C65A2715F30980AAE725615A4D255FD25F26BB22507">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_pgsql">>, <<"D3C3748C3638A1D7DB5644E4FC63A6DA7614B3009E172EF92A01D0217C3BEC65">>}, - {<<"p1_utils">>, <<"2D39B5015A567BBD2CC7033EEB93A7C60D8C84EFE1EF69A3473FAA07FA268187">>}, + {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, - {<<"sqlite3">>, <<"F9EA0CFF8540865FDFDB7E24EEF34DC46677364B1C070896E99B5BF08C8A7FD7">>}, - {<<"stringprep">>, <<"02F23E8C3A219A3DFE40A22E908BECE3A2F68AF0FF599EA8A7B714ECB21E62EE">>}, - {<<"stun">>, <<"A65DF67A8AAAECB6A94D687977B2E9F161820819910CB97BBE26A3525356525B">>}, + {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, + {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, + {<<"stun">>, <<"C3E855F10F6B0C3AC150BCE3D6C96C04A85207A3A5C7A7207876D8B36DB2B0A4">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"7C26FAE7CA83B307BAB99624595DCE706D427A17EED9C6305980550F8120A3A3">>}, - {<<"yconf">>, <<"E22998B3D7728270BDD06162A9515BD142B14FAE8927CBDBD3EF639C32AA6F7A">>}]}, + {<<"yconf">>, <<"D59521D66FF89F219411B6E9277CD6FEEC7CC6FCE11554E67DE02A8D0A470479">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, - {<<"cache_tab">>, <<"6D8A5E00D8F84C42627706A6DBEDB02E34D58495F3ED61935C8475CA0531CDA0">>}, - {<<"eimp">>, <<"B3B9FFB1D9A5F4A2BA88AC418A819164932D9A9D3A2FC3D32CA338CE855C4392">>}, + {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, + {<<"eimp">>, <<"907C780023CB2893E4FC4BDBE6A4F02C355913862AC67F0ECC26605E816B628A">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, - {<<"esip">>, <<"6F00165395900500AA262CE0297162D93931C78C1464D89FD0EDC6E3D6BC011F">>}, - {<<"ezlib">>, <<"30E94355FB42260AAB6E12582CB0C56BF233515E655C8AEAF48760E7561E4EBB">>}, - {<<"fast_tls">>, <<"DB34322C8782D4C5139CCB80709D8EC8C38089B44262EDD0C2F660AC495BD389">>}, - {<<"fast_xml">>, <<"7FCE41B7D1A4BA438A2D7A088DABE74A3CA0739F1AF2ABCB77E62DAF43E0409A">>}, - {<<"fast_yaml">>, <<"1ABE8F758FC2A86B08EDFF80BBC687CFD41EBC1412CFEC0EF4A0ACFCD032052F">>}, + {<<"esip">>, <<"A9A1BD5EA52B0E2D1B1D1FEC5FB7F0301E90610DB1ECFEB205A18CC4E1CB3FC7">>}, + {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, + {<<"fast_tls">>, <<"D09A12472A56A34C5EECAAED33EA283F00FCDF9DC2E8282ECBAAE827F13FC21B">>}, + {<<"fast_xml">>, <<"795192390E06D2B65016A6990BBFA5727F4A26D2914808B1C3C9A32EEDCD1BFD">>}, + {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"62E1F0581C3C19C33A725C781DFA88410D8BFF1BBAFC3885A2552286B4785C4C">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, - {<<"mqtree">>, <<"C87D1C95575DB65AF29B795C9DAA3BED43F5C1BF84072A74469659BCF53594EB">>}, + {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, {<<"p1_acme">>, <<"8CE196F26E3D22EA10B7809122950465878C127F80767E325207AED7E8D0DD59">>}, {<<"p1_mysql">>, <<"F058865F64257F507A2C6A5AFF369B1375DBCB30B3D4258DAD4F1B3EAFFB655F">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_pgsql">>, <<"85F230DB530333106B8A1F9E5D5AF032E6C3DD23B432E03D68E9D29013A6DCFC">>}, - {<<"p1_utils">>, <<"9219214428F2C6E5D3187FF8EB9A8783695C2427420BE9A259840E07ADA32847">>}, + {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, - {<<"sqlite3">>, <<"85054B6CA297343C159ED6794A473FF2C8EEABD854B6FE02F711C0BFD373CE86">>}, - {<<"stringprep">>, <<"928EBA304C3006EB1512110EBD7B87DB163B00859A09375A1E4466152C6C462A">>}, - {<<"stun">>, <<"A2055032B6D338D0454142004BCB12FAFB0C64AB1F273F1D0C6923EBBC8EDE40">>}, + {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, + {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, + {<<"stun">>, <<"9CF4191491A60573ED6197E636530AF1D25C9B064845AABED0C02F780D33EA3F">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"53A9F85AD44103A358DC173225BC96D08076D4E78506F87D838337B2FA3B381F">>}, - {<<"yconf">>, <<"7FF2AB24D3C9833842716B9AAAA01A8F96641A7695CBB701B03445C4DEF01117">>}]} + {<<"yconf">>, <<"E947813273F38711C7B2E5A8E4ACC9A51C7BBE854F744A345F60300B38586C89">>}]} ]. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index e97d73f81..c96114e6f 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -505,12 +505,12 @@ normal_state({route, <<"">>, process_iq_adhoc(From, IQ, StateData); #register{} -> mod_muc:process_iq_register(IQ); - #message_moderate{} = Moderate -> % moderate:1 - process_iq_moderate(From, IQ, Moderate, StateData); + #message_moderate{id = Id, reason = Reason} -> % moderate:1 + process_iq_moderate(From, IQ, Id, Reason, StateData); #fasten_apply_to{id = ModerateId} = ApplyTo -> - case xmpp:get_subtag(ApplyTo, #message_moderate{}) of - #message_moderate{} = Moderate -> % moderate:0 - process_iq_moderate(From, IQ, Moderate#message_moderate{id = ModerateId}, StateData); + case xmpp:get_subtag(ApplyTo, #message_moderate_21{}) of + #message_moderate_21{reason = Reason} -> % moderate:0 + process_iq_moderate(From, IQ, ModerateId, Reason, StateData); _ -> Txt = ?T("The feature requested is not " "supported by the conference"), @@ -5162,13 +5162,12 @@ add_presence_hats(JID, Pres, StateData) -> Pres end. --spec process_iq_moderate(jid(), iq(), message_moderate(), state()) -> +-spec process_iq_moderate(jid(), iq(), binary(), binary() | undefined, state()) -> {result, undefined, state()} | {error, stanza_error()}. -process_iq_moderate(_From, #iq{type = get}, _Moderate, _StateData) -> +process_iq_moderate(_From, #iq{type = get}, _Id, _Reason, _StateData) -> {error, xmpp:err_bad_request()}; -process_iq_moderate(From, #iq{type = set, lang = Lang}, - #message_moderate{id = Id, reason = Reason, xmlns = Xmlns}, +process_iq_moderate(From, #iq{type = set, lang = Lang}, Id, Reason, #state{config = Config, room = Room, host = Host, jid = JID, server_host = Server} = StateData) -> FAffiliation = get_affiliation(From, StateData), @@ -5189,25 +5188,17 @@ process_iq_moderate(From, #iq{type = set, lang = Lang}, ok end, By = jid:replace_resource(JID, find_nick_by_jid(From, StateData)), - SubEl = case Xmlns of - ?NS_MESSAGE_MODERATE_0 -> - SubEls = [#xmlel{name = <<"reason">>, - attrs = [], - children = [{xmlcdata, Reason}]}, - #message_retract{id = Id}], - ModeratedEl = #message_moderated{by = By, - sub_els = SubEls}, - #fasten_apply_to{id = Id, - sub_els = [ModeratedEl]}; - ?NS_MESSAGE_MODERATE_1 -> - ModeratedEl = #message_moderated{by = By}, - #message_retract{id = Id, - reason = Reason, - moderated = ModeratedEl} - end, + Mod21 = #message_moderated_21{by = By, + reason = Reason, + sub_els = [#message_retract_30{}]}, + SubEl = [#fasten_apply_to{id = Id, + sub_els = [Mod21]}, + #message_retract{id = Id, + reason = Reason, + moderated = #message_moderated{by = By}}], Packet0 = #message{type = groupchat, from = From, - sub_els = [SubEl]}, + sub_els = SubEl}, {FromNick, _Role} = get_participant_data(From, StateData), Packet = ejabberd_hooks:run_fold(muc_filter_message, StateData#state.server_host, From a733ba311c197b78db590443371bd7fedb551a90 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 4 Jul 2024 15:29:33 +0200 Subject: [PATCH 0667/1302] New tests for API commands Only mod_http_api is tested right now; support to test also ejabberdctl, ejabberd_xmlrpc and web_admin would be great. This uses mod_example from ejabberd-contrib How to run only those tests: CT_BACKENDS=no_db rebar3 ct --suite=test/ejabberd_SUITE --group=commands_single --- test/commands_tests.erl | 171 ++++++++++++++++++++++++++++++++++++++++ test/ejabberd_SUITE.erl | 1 + 2 files changed, 172 insertions(+) create mode 100644 test/commands_tests.erl diff --git a/test/commands_tests.erl b/test/commands_tests.erl new file mode 100644 index 000000000..4081a9e07 --- /dev/null +++ b/test/commands_tests.erl @@ -0,0 +1,171 @@ +%%%------------------------------------------------------------------- +%%% Author : Badlop +%%% Created : 2 Jul 2024 by Badlop +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- + +%%%% definitions + +%% @format-begin + +-module(commands_tests). + +-compile(export_all). + +-include("suite.hrl"). + +%%%================================== +%%%% setup + +single_cases() -> + {commands_single, + [sequence], + [single_test(setup), + single_test(ejabberdctl), + single_test(http_integer), + single_test(http_string), + single_test(http_binary), + single_test(http_atom), + single_test(http_rescode), + single_test(http_restuple), + single_test(http_list), + single_test(http_tuple), + single_test(http_list_tuple)]}. + +setup(_Config) -> + M = <<"mod_example">>, + execute(module_uninstall, [M]), + case execute(module_install, [M]) of + ok -> + ok; + {error, not_available} -> + ?match(ok, execute(modules_update_specs, [])), + ?match(ok, execute(module_install, [M])) + end, + Installed = execute(modules_installed, []), + ?match(true, lists:keymember(mod_example, 1, Installed)). + +%%%================================== +%%%% ejabberdctl + +%% TODO: find a way to read and check the output printed in the command line +ejabberdctl(_Config) -> + R = ejabberd_ctl:process(["modules_installed"]), + ct:pal("ejabberdctl: R ~p", [R]), + ct:pal("ejabberdctl: Q ~p", [os:cmd("ejabberdctl modules_installed")]), + Installed = execute(modules_installed, []), + ?match(true, lists:keymember(mod_example, 1, Installed)). + +%%%================================== +%%%% mod_http_api + +http_integer(Config) -> + Integer = 123456789, + ?match(Integer, query(Config, "command_test_integer", #{arg_integer => Integer})). + +http_string(Config) -> + S = "This is a string.", + B = iolist_to_binary(S), + ?match(B, query(Config, "command_test_string", #{arg_string => S})), + ?match(B, query(Config, "command_test_string", #{arg_string => B})). + +http_binary(Config) -> + B = <<"This is a binary.">>, + ?match(B, query(Config, "command_test_binary", #{arg_binary => B})). + +%% mod_http_api doesn't handle 'atom' result format +%% and formats the result as a binary by default +http_atom(Config) -> + S = "Some string.", + B = <<"Some string.">>, + ?match(B, query(Config, "command_test_atom", #{arg_string => S})), + ?match(B, query(Config, "command_test_atom", #{arg_string => B})). + +http_rescode(Config) -> + ?match(0, query(Config, "command_test_rescode", #{code => "true"})), + ?match(0, query(Config, "command_test_rescode", #{code => "ok"})), + ?match(1, query(Config, "command_test_rescode", #{code => "problem"})), + ?match(1, query(Config, "command_test_rescode", #{code => "error"})). + +http_restuple(Config) -> + ?match(<<"Deleted 0 users: []">>, query(Config, "delete_old_users", #{days => 99})), + ?match(<<"Good">>, + query(Config, "command_test_restuple", #{code => "true", text => "Good"})), + ?match(<<"OK!!">>, + query(Config, "command_test_restuple", #{code => "ok", text => "OK!!"})). + +http_list(Config) -> + ListS = ["one", "first", "primary"], + ListB = [<<"one">>, <<"first">>, <<"primary">>], + ?match(ListB, query(Config, "command_test_list", #{arg_list => ListS})), + ?match(ListB, query(Config, "command_test_list", #{arg_list => ListB})). + +http_tuple(Config) -> + MapA = + #{element1 => "one", + element2 => "first", + element3 => "primary"}, + MapB = + #{<<"element1">> => <<"one">>, + <<"element2">> => <<"first">>, + <<"element3">> => <<"primary">>}, + ?match(MapB, query(Config, "command_test_tuple", #{arg_tuple => MapA})), + ?match(MapB, query(Config, "command_test_tuple", #{arg_tuple => MapB})). + +http_list_tuple(Config) -> + LTA = [#{element1 => "one", element2 => "uno"}, + #{element1 => "dos", element2 => "two"}, + #{element1 => "three", element2 => "tres"}], + LTB = [#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, + #{<<"element1">> => <<"dos">>, <<"element2">> => <<"two">>}, + #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}], + ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTA})), + ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTB})). + +%%%================================== +%%%% internal functions + +single_test(T) -> + list_to_atom("commands_" ++ atom_to_list(T)). + +execute(Name, Args) -> + ejabberd_commands:execute_command2(Name, Args, #{caller_module => ejabberd_ctl}, 1000000). + +page(Config, Tail) -> + Server = ?config(server_host, Config), + Port = ct:get_config(web_port, 5280), + "http://" ++ Server ++ ":" ++ integer_to_list(Port) ++ "/api/" ++ Tail. + +query(Config, Tail, Map) -> + BodyQ = misc:json_encode(Map), + Body = make_query(Config, Tail, BodyQ), + misc:json_decode(Body). + +make_query(Config, Tail, BodyQ) -> + ?match({ok, {{"HTTP/1.1", 200, _}, _, Body}}, + httpc:request(post, + {page(Config, Tail), [], "application/json", BodyQ}, + [], + [{body_format, binary}]), + Body). + +%%%================================== + +%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 9b153fd48..b3f2bf980 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -390,6 +390,7 @@ no_db_tests() -> auth_external_wrong_jid, auth_external_wrong_server, auth_external_invalid_cert, + commands_tests:single_cases(), jidprep_tests:single_cases(), sm_tests:single_cases(), sm_tests:master_slave_cases(), From 4192190a963b38941e39f78cc24f588435d26e5c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Nov 2023 16:33:36 +0100 Subject: [PATCH 0668/1302] Fix problem parsing tuples when using OTP 27 json library (#4242) --- src/mod_http_api.erl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 359f00c24..1e7e44a33 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -337,16 +337,27 @@ format_arg(Elements, when is_list(Elements) -> [{format_arg(Element, ElementDefFormat)} || Element <- Elements]; + +%% Covered by command_test_list and command_test_list_tuple format_arg(Elements, {list, {_ElementDefName, ElementDefFormat}}) when is_list(Elements) -> [format_arg(Element, ElementDefFormat) || Element <- Elements]; + format_arg({[{Name, Value}]}, {tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]}) when Tuple1S == binary; Tuple1S == string -> {format_arg(Name, Tuple1S), format_arg(Value, Tuple2S)}; + +%% Covered by command_test_tuple and command_test_list_tuple +format_arg(Elements, + {tuple, ElementsDef}) + when is_map(Elements) -> + list_to_tuple([element(2, maps:find(atom_to_binary(Name, latin1), Elements)) + || {Name, _Format} <- ElementsDef]); + format_arg({Elements}, {tuple, ElementsDef}) when is_list(Elements) -> @@ -363,10 +374,12 @@ format_arg({Elements}, end end, ElementsDef), list_to_tuple(F); + format_arg(Elements, {list, ElementsDef}) when is_list(Elements) and is_atom(ElementsDef) -> [format_arg(Element, ElementsDef) || Element <- Elements]; + format_arg(Arg, integer) when is_integer(Arg) -> Arg; format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg); format_arg(Arg, binary) when is_binary(Arg) -> Arg; @@ -452,6 +465,7 @@ format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) -> format_result(Els, {Name, {list, {_, {tuple, [{name, string}, {value, _}]}} = Fmt}}) -> {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; +%% Covered by command_test_list and command_test_list_tuple format_result(Els, {Name, {list, Def}}) -> {misc:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]}; @@ -465,9 +479,11 @@ format_result(Tuple, {_Name, {tuple, [{name, string}, {value, _} = ValFmt]}}) -> {_, Val2} = format_result(Val, ValFmt), {iolist_to_binary(Name2), Val2}; +%% Covered by command_test_tuple and command_test_list_tuple format_result(Tuple, {Name, {tuple, Def}}) -> Els = lists:zip(tuple_to_list(Tuple), Def), - {misc:atom_to_binary(Name), {[format_result(El, ElDef) || {El, ElDef} <- Els]}}; + Els2 = [format_result(El, ElDef) || {El, ElDef} <- Els], + {misc:atom_to_binary(Name), maps:from_list(Els2)}; format_result(404, {_Name, _}) -> "not_found". From 7c63cd1000f4f0b8c507baab34fb0d38512ad16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 5 Jul 2024 09:57:07 +0200 Subject: [PATCH 0669/1302] Print message when starting ejabberd application fails --- src/ejabberd.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 69f02522c..0fd237497 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -53,7 +53,10 @@ -include("logger.hrl"). start() -> - application:ensure_all_started(ejabberd). + case application:ensure_all_started(ejabberd) of + {error, Err} -> io:format("Failed to start ejabberd application: ~p", [Err]); + Ok -> Ok + end. stop() -> application:stop(ejabberd). From e25bdba16d83ad3d3ff080ac94848a292414f37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 5 Jul 2024 10:25:59 +0200 Subject: [PATCH 0670/1302] Use error_logger when printing startup failure message --- src/ejabberd.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 0fd237497..a83ab0738 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -54,7 +54,7 @@ start() -> case application:ensure_all_started(ejabberd) of - {error, Err} -> io:format("Failed to start ejabberd application: ~p", [Err]); + {error, Err} -> error_logger:error_msg("Failed to start ejabberd application: ~p", [Err]); Ok -> Ok end. From 1add1de23b1ea2ae1542da0c752a8859100e133a Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 5 Jul 2024 22:41:18 +0200 Subject: [PATCH 0671/1302] ejabberd_options: Add trailing @ to @VERSION@ Thanks to Marc Schink for reporting the issue. --- src/ejabberd_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 6aca9c80f..b6bb18d5a 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -117,7 +117,7 @@ opt_type(captcha_cmd) -> fun(V) -> V2 = misc:expand_keyword(<<"@SEMVER@">>, V, ejabberd_option:version()), - misc:expand_keyword(<<"@VERSION">>, V2, + misc:expand_keyword(<<"@VERSION@">>, V2, misc:semver_to_xxyy(ejabberd_option:version())) end); opt_type(captcha_host) -> From f1739ce34dd6fa2be07f2b5d29b307142b0764ad Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Jul 2024 16:03:45 +0200 Subject: [PATCH 0672/1302] mod_register: Add example configuration of welcome_message option --- src/mod_register.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 0e595991f..7da8ca74a 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -702,4 +702,13 @@ mod_doc() -> #{value => "{subject: Subject, body: Body}", desc => ?T("Set a welcome message that is sent to each newly registered account. " - "The message will have subject 'Subject' and text 'Body'.")}}]}. + "The message will have subject 'Subject' and text 'Body'."), + example => + ["modules:", + " mod_register:", + " welcome_message:", + " subject: \"Welcome!\"", + " body: |-", + " Hi!", + " Welcome to this XMPP server"]}} + ]}. From ce95f1f25a8043ba2e5d591e0564ad4a9ea3b0f9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jul 2024 10:53:50 +0200 Subject: [PATCH 0673/1302] WebAdmin: Fix crash when viewing SRG created using ejabberd 24.02 (#4245) --- src/mod_admin_extra.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index ca99b120e..ca7edb486 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1934,7 +1934,7 @@ srg_get_displayed(Group, Host) -> error -> [] end, - proplists:get_value(displayed_groups, Opts). + proplists:get_value(displayed_groups, Opts, []). srg_add_displayed(Group, Host, NewGroup) -> Opts = From 8a3344e78acfde1447439f9f6be630ed09c85ea3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Jul 2024 12:50:43 +0200 Subject: [PATCH 0674/1302] ext_mod: Compile all Elixir files in a library with one function call Some Elixir libraries have files with dependencies between them. Compiler can detect and solve those internal dependency if we call Compile with a list of all the files https://hexdocs.pm/elixir/1.17.2/Kernel.ParallelCompiler.html#compile/2 This is useful to compile libcluster --- src/ext_mod.erl | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 7eea46a93..f718b9611 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -36,7 +36,7 @@ install_contrib_modules/2, config_dir/0, get_commands_spec/0]). -export([modules_configs/0, module_ebin_dir/1]). --export([compile_erlang_file/2, compile_elixir_file/2]). +-export([compile_erlang_file/2, compile_elixir_files/2]). -export([web_menu_node/3, web_page_node/3, webadmin_node_contrib/3]). %% gen_server callbacks @@ -603,8 +603,7 @@ compile(LibDir) -> compile_c_files(LibDir), Er = [compile_erlang_file(Bin, File, Options) || File <- filelib:wildcard(Src++"/**/*.erl")], - Ex = [compile_elixir_file(Bin, File) - || File <- filelib:wildcard(Lib ++ "/**/*.ex")], + Ex = compile_elixir_files(Bin, filelib:wildcard(Lib ++ "/**/*.ex")), compile_result(lists:flatten([Er, Ex])). compile_c_files(LibDir) -> @@ -669,18 +668,23 @@ compile_erlang_file(Dest, File, ErlOptions) -> end. -ifdef(ELIXIR_ENABLED). -compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) -> - compile_elixir_file(list_to_binary(Dest), list_to_binary(File)); +compile_elixir_files(Dest, [File | _] = Files) when is_list(Dest) and is_list(File) -> + BinFiles = [list_to_binary(F) || F <- Files], + compile_elixir_files(list_to_binary(Dest), BinFiles); -compile_elixir_file(Dest, File) -> - try 'Elixir.Kernel.ParallelCompiler':files_to_path([File], Dest, []) of +compile_elixir_files(Dest, Files) -> + try 'Elixir.Kernel.ParallelCompiler':files_to_path(Files, Dest, []) of Modules when is_list(Modules) -> {ok, Modules} catch - _ -> {error, {compilation_failed, File}} + A:B -> + ?ERROR_MSG("Problem ~p compiling Elixir files: ~p~nFiles: ~p", [A, B, Files]), + {error, {compilation_failed, Files}} end. -else. -compile_elixir_file(_, File) -> - {error, {compilation_failed, File}}. +compile_elixir_files(_, []) -> + ok; +compile_elixir_files(_, Files) -> + {error, {compilation_failed, Files}}. -endif. install(Module, Spec, SrcDir, LibDir, Config) -> From 28e37bcaad2ce27ed768ccb846120d3fb71bb8a2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Jul 2024 12:08:29 +0200 Subject: [PATCH 0675/1302] ext_mod: files_to_path is deprecated, use compile_to_path As recommended in https://github.com/elixir-lang/elixir/blob/d9cf285d7104db5a61607afe2ae4322220681a51/lib/elixir/lib/kernel/parallel_compiler.ex#L240 --- src/ext_mod.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index f718b9611..dc3a1ae09 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -673,8 +673,12 @@ compile_elixir_files(Dest, [File | _] = Files) when is_list(Dest) and is_list(Fi compile_elixir_files(list_to_binary(Dest), BinFiles); compile_elixir_files(Dest, Files) -> - try 'Elixir.Kernel.ParallelCompiler':files_to_path(Files, Dest, []) of - Modules when is_list(Modules) -> {ok, Modules} + try 'Elixir.Kernel.ParallelCompiler':compile_to_path(Files, Dest, [{return_diagnostics, true}]) of + {ok, Modules, []} when is_list(Modules) -> + {ok, Modules}; + {ok, Modules, Warnings} when is_list(Modules) -> + ?WARNING_MSG("Warnings compiling module: ~n~p", [Warnings]), + {ok, Modules} catch A:B -> ?ERROR_MSG("Problem ~p compiling Elixir files: ~p~nFiles: ~p", [A, B, Files]), From 7a8c0331c1b7613d2d6f74410d8744d1b6f0eacb Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Jul 2024 12:04:54 +0200 Subject: [PATCH 0676/1302] mix.exs: Include Elixir's Logger in the OTP release, useful for libcluster --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 52fd90f93..5e52e7469 100644 --- a/mix.exs +++ b/mix.exs @@ -43,7 +43,7 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, - applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, + applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :logger, :fast_tls, :fast_xml, :fast_yaml, :jose, :p1_utils, :stringprep, :syntax_tools, :yconf, :xmpp] ++ cond_apps(), From df5291e4bdfef881c030f30d8a6a332328d5e1d5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 11 Jul 2024 11:19:43 +0200 Subject: [PATCH 0677/1302] ext_mod: Improve error result when problem compiling elixir file --- src/ext_mod.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index dc3a1ae09..dc4e96f2a 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -688,7 +688,12 @@ compile_elixir_files(Dest, Files) -> compile_elixir_files(_, []) -> ok; compile_elixir_files(_, Files) -> - {error, {compilation_failed, Files}}. + ErrorString = "Attempted to compile Elixir files, but Elixir support is " + "not available in ejabberd. Try compiling ejabberd using " + "'./configure --enable-elixir' or './configure --with-rebar=mix'", + ?ERROR_MSG(ErrorString, []), + io:format("Error: " ++ ErrorString ++ "~n", []), + {error, {elixir_not_available, Files}}. -endif. install(Module, Spec, SrcDir, LibDir, Config) -> From ef933c07cc2fc464c0ee2e819d7408a629a3d30c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Jul 2024 12:53:38 +0200 Subject: [PATCH 0678/1302] WebAdmin: Support groupid with spaces when making shared roster result (#4245) --- src/ejabberd_web_admin.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 018314898..d884382bf 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -2225,8 +2225,11 @@ make_result(Binary, when (ElementName == ResultName) or (ElementName == unknown_element_name) -> First = proplists:get_value(first, ArgumentsUsed), Second = proplists:get_value(second, ArgumentsUsed), + FirstUrlencoded = + hd(string:replace( + misc:url_encode(First), "%40", "@")), {GroupId, Host} = - case jid:decode(First) of + case jid:decode(FirstUrlencoded) of #jid{luser = <<"">>, lserver = G} -> {G, Second}; #jid{luser = G, lserver = H} -> From 9a0ff13cc2c228e45e9def48f3e25122003c1e6f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 12 Jul 2024 11:21:55 +0200 Subject: [PATCH 0679/1302] mod_register: Send welcome message as 'chat' too (#4246) Apparently, some clients don't display 'normal' messages to the user. --- src/mod_register.erl | 6 ++++++ test/ejabberd_SUITE.erl | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/mod_register.erl b/src/mod_register.erl index 7da8ca74a..d656263bf 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -412,6 +412,12 @@ send_welcome_message(JID) -> case mod_register_opt:welcome_message(Host) of {<<"">>, <<"">>} -> ok; {Subj, Body} -> + ejabberd_router:route( + #message{from = jid:make(Host), + to = JID, + type = chat, + subject = xmpp:mk_text(Subj), + body = xmpp:mk_text(<>)}), ejabberd_router:route( #message{from = jid:make(Host), to = JID, diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index b3f2bf980..b7c72a02b 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -920,6 +920,8 @@ presence_broadcast(Config) -> IQ = #iq{type = get, from = JID, sub_els = [#disco_info{node = Node}]} = recv_iq(Config), + #message{type = chat, + subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config), #message{type = normal, subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config), #presence{from = JID, to = JID} = recv_presence(Config), From a935302a19686b0ffc30604dd1b924afceff1b3d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 11 Jul 2024 17:30:32 +0200 Subject: [PATCH 0680/1302] ejabberd.yml.example: Add api_permissions group for webadmin (#4249) --- ejabberd.yml.example | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index df52c85be..27cf8e4c5 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -111,10 +111,13 @@ access_rules: api_permissions: "console commands": - from: - - ejabberd_ctl + from: ejabberd_ctl who: all what: "*" + "webadmin commands": + from: ejabberd_web_admin + who: admin + what: "*" "admin access": who: access: From 101cce0c1ef699288a93282f808d49326f3bb22c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 9 Jul 2024 16:35:48 +0200 Subject: [PATCH 0681/1302] ext_mod: Fetch dependencies from hex.pm when mix is available This doesn't work when running an OTP release build using mix, which means it doesn't work in binary installers or containers; only when using relive, or compiled with rebar3. Set the desired hex package version in the module's rebar.config For example, to fetch hex package recon 2.5.5 when mix is available, and otherwise download using git: in the file ejabberd-contrib/ejabberd_observer_cli/rebar.config set both the hex version and git details: {deps, [ {recon, "2.5.5", {git, "https://github.com/ferd/recon"}} ]}. --- src/ext_mod.erl | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index dc4e96f2a..2228af99e 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -762,8 +762,10 @@ fetch_rebar_deps(SrcDir) -> {ok, CurDir} = file:get_cwd(), file:set_cwd(SrcDir), filelib:ensure_dir(filename:join("deps", ".")), - lists:foreach(fun({_App, Cmd}) -> - os:cmd("cd deps; "++Cmd++"; cd ..") + lists:foreach(fun({App, Cmd}) -> + io:format("Fetching dependency ~s: ", [App]), + Result = os:cmd("cd deps; "++Cmd++"; cd .."), + io:format("~s", [Result]) end, Deps), file:set_cwd(CurDir) end. @@ -777,6 +779,19 @@ rebar_deps(Script) -> _ -> [] end. + +rebar_dep({App, Version, Git}) when Version /= ".*" -> + AppS = atom_to_list(App), + Help = os:cmd("mix hex.package"), + case string:find(Help, "mix hex.package fetch") /= nomatch of + true -> + {App, "mix hex.package fetch "++AppS++" "++Version++" --unpack"}; + false -> + io:format("I'll download ~p using git because I can't use Mix " + "to fetch from hex.pm:~n~s", [AppS, help]), + rebar_dep({App, ".*", Git}) + end; + rebar_dep({App, _, {git, Url}}) -> {App, "git clone "++Url++" "++filename:basename(App)}; rebar_dep({App, _, {git, Url, {branch, Ref}}}) -> From 090a8e3c957933912f0d7ec8fdb433194fcfe3eb Mon Sep 17 00:00:00 2001 From: Michael Slezak Date: Fri, 12 Jul 2024 14:39:42 -0600 Subject: [PATCH 0682/1302] Fix template error for elixir-enabled applications. --- src/ejabberd_logger.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index e9fb7ada7..f2fd7b3d7 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -370,7 +370,7 @@ console_template() -> andalso 'Elixir.System':version() >= <<"1.15">> of true -> - [date, " ", time, " [", level, "] ", message, "\n"]; + [date, " ", time, " [", level, "] ", msg, "\n"]; false -> [time, " [", level, "] " | msg()] end. From 8ac51e63b55d8980d999c29908becc001b823839 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 14 Jul 2024 17:42:40 +0200 Subject: [PATCH 0683/1302] node_pep: Add missing feature Publishers may specify item identifiers for PEP nodes as well. (Greetings to Thilo!) --- src/node_pep.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node_pep.erl b/src/node_pep.erl index 3a79a7848..2a2e044be 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -86,6 +86,7 @@ features() -> <<"delete-nodes">>, <<"delete-items">>, <<"filtered-notifications">>, + <<"item-ids">>, <<"modify-affiliations">>, <<"multi-items">>, <<"outcast-affiliation">>, From 936460212f744880279b50954010459d12a0188f Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 14 Jul 2024 13:35:38 +0200 Subject: [PATCH 0684/1302] Fix bug in 8a3344e when Elixir enabled but no need to compile elixir files --- src/ext_mod.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 2228af99e..cb24d2289 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -668,6 +668,8 @@ compile_erlang_file(Dest, File, ErlOptions) -> end. -ifdef(ELIXIR_ENABLED). +compile_elixir_files(_, []) -> + ok; compile_elixir_files(Dest, [File | _] = Files) when is_list(Dest) and is_list(File) -> BinFiles = [list_to_binary(F) || F <- Files], compile_elixir_files(list_to_binary(Dest), BinFiles); From a70bdc07760d00ba9e78abd4d4096c6eecd2b601 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 14 Jul 2024 15:46:29 +0200 Subject: [PATCH 0685/1302] WebAdmin: Fix number of accounts shown in the online-users page --- src/ejabberd_web_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index d884382bf..dba415792 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -587,7 +587,7 @@ process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = Res = [make_command(connected_users_vhost, R, [{<<"host">>, Host}], - [{table_options, {2, RPath}}, + [{table_options, {100, RPath}}, {result_links, [{sessions, user, Level, <<"">>}]}])], make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, Host, R, AJID, Level); process_admin(Host, From aa02c4de1eda9b168e373a0ab13f63f115814b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 15 Jul 2024 21:26:16 +0200 Subject: [PATCH 0686/1302] Move logger app to included_applications We don't particularly need this to start with ejabberd, and by having it always started we will change logger to always use elixir formating. --- mix.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 5e52e7469..89f73bda0 100644 --- a/mix.exs +++ b/mix.exs @@ -43,11 +43,11 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, - applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :logger, + applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, :fast_tls, :fast_xml, :fast_yaml, :jose, :p1_utils, :stringprep, :syntax_tools, :yconf, :xmpp] ++ cond_apps(), - included_applications: [:mnesia, :os_mon, + included_applications: [:mnesia, :os_mon, :logger, :cache_tab, :eimp, :mqtree, :p1_acme, :p1_oauth2, :pkix] ++ cond_included_apps()] From c013a59d166c4642a1578623276653514e5b4ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 15 Jul 2024 21:29:17 +0200 Subject: [PATCH 0687/1302] Restore args conversion of {"k":"v"} to tuple lists in mod_http_api Switching to json compatible output did broke this, which caused issues in for example create_rooms_with_opts command. --- src/mod_http_api.erl | 8 ++++++++ test/commands_tests.erl | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 1e7e44a33..ac122b5dc 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -332,6 +332,14 @@ format_arg({Elements}, ({Val}) when is_list(Val) -> format_arg({Val}, Tuple) end, Elements); +format_arg(Map, + {list, {_ElementDefName, {tuple, [{_Tuple1N, Tuple1S}, {_Tuple2N, Tuple2S}]}}}) + when is_map(Map) andalso + (Tuple1S == binary orelse Tuple1S == string) -> + maps:fold( + fun(K, V, Acc) -> + [{format_arg(K, Tuple1S), format_arg(V, Tuple2S)} | Acc] + end, [], Map); format_arg(Elements, {list, {_ElementDefName, {list, _} = ElementDefFormat}}) when is_list(Elements) -> diff --git a/test/commands_tests.erl b/test/commands_tests.erl index 4081a9e07..9cc23dee5 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -47,7 +47,8 @@ single_cases() -> single_test(http_restuple), single_test(http_list), single_test(http_tuple), - single_test(http_list_tuple)]}. + single_test(http_list_tuple), + single_test(http_list_tuple_map)]}. setup(_Config) -> M = <<"mod_example">>, @@ -139,6 +140,13 @@ http_list_tuple(Config) -> ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTA})), ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTB})). +http_list_tuple_map(Config) -> + LTA = #{<<"one">> => <<"uno">>, <<"dos">> => <<"two">>, <<"three">> => <<"tres">>}, + LTB = lists:sort([#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, + #{<<"element1">> => <<"dos">>, <<"element2">> => <<"two">>}, + #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}]), + ?match(LTB, lists:sort(query(Config, "command_test_list_tuple", #{arg_list => LTA}))). + %%%================================== %%%% internal functions From 9bd6b110077e98d8d6f82b2e14742b69b98135de Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 15 Jul 2024 13:42:44 +0200 Subject: [PATCH 0688/1302] Use proper format depending on the formatter (#4256) This fixes recent commmit 090a8e3 --- src/ejabberd_logger.erl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index f2fd7b3d7..3189860fc 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -370,7 +370,16 @@ console_template() -> andalso 'Elixir.System':version() >= <<"1.15">> of true -> - [date, " ", time, " [", level, "] ", msg, "\n"]; + {ok, DC} = logger:get_handler_config(default), + MessageFormat = case maps:get(formatter, DC) of + %% https://hexdocs.pm/logger/1.17.2/Logger.Formatter.html#module-formatting + {'Elixir.Logger.Formatter', _} -> + message; + %% https://www.erlang.org/doc/apps/kernel/logger_formatter#t:template/0 + {logger_formatter, _} -> + msg + end, + [date, " ", time, " [", level, "] ", MessageFormat, "\n"]; false -> [time, " [", level, "] " | msg()] end. From 0bfbe3e15441174ace60eea20c5cf070d1073e7d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 15 Jul 2024 13:42:34 +0200 Subject: [PATCH 0689/1302] Add ejabberd example config files to the hex package --- mix.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/mix.exs b/mix.exs index 89f73bda0..7253d1f39 100644 --- a/mix.exs +++ b/mix.exs @@ -209,6 +209,7 @@ defmodule Ejabberd.MixProject do [# These are the default files included in the package files: ["include", "lib", "priv", "sql", "src", "COPYING", "README.md", + "ejabberd.yml.example", "config/runtime.exs", "mix.exs", "rebar.config", "rebar.config.script", "vars.config"], maintainers: ["ProcessOne"], licenses: ["GPL-2.0-or-later"], From 4cc95dda4231831f55f922d213c3b03f1ca5652e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 06:44:17 +0000 Subject: [PATCH 0690/1302] Bump ex_doc from 0.34.1 to 0.34.2 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.34.1 to 0.34.2. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.34.1...v0.34.2) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.lock b/mix.lock index 4078a6bf3..12bf8f2ad 100644 --- a/mix.lock +++ b/mix.lock @@ -2,13 +2,13 @@ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.53", "482786a79d8f5b0aefc60ca68b8c6c7f074f6ede2653705e4206c6779edc5a56", [:rebar3], [{:fast_tls, "1.1.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.13", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a9a1bd5ea52b0e2d1b1d1fec5fb7f0301e90610db1ecfeb205a18cc4e1cb3fc7"}, - "ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"}, + "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.20", "d6f12d9ae4fe57e880b144b912e735af89343a8463d39b7eb4be3f6ca6163879", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d09a12472a56a34c5eecaaed33ea283f00fcdf9dc2e8282ecbaae827f13fc21b"}, From c5f2b389c3a1864238522d3b82150ebebf4052ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jul 2024 15:25:55 +0200 Subject: [PATCH 0691/1302] Allow to configure number of restart in sql_transaction() --- src/ejabberd_sql.erl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index a803b4139..8a4a0e7a4 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -35,7 +35,7 @@ sql_query/3, sql_query_t/1, sql_transaction/2, - sql_transaction/3, + sql_transaction/4, sql_bloc/2, sql_bloc/3, abort/1, @@ -134,19 +134,19 @@ sql_query(Host, Query) -> %% SQL transaction based on a list of queries %% This function automatically --spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T), pos_integer()) -> +-spec sql_transaction(binary(), [sql_query(T)] | fun(() -> T), pos_integer(), pos_integer()) -> {atomic, T} | {aborted, any()}. -sql_transaction(Host, Queries, Timeout) +sql_transaction(Host, Queries, Timeout, Restarts) when is_list(Queries) -> F = fun () -> lists:foreach(fun (Query) -> sql_query_t(Query) end, Queries) end, - sql_transaction(Host, F, Timeout); + sql_transaction(Host, F, Timeout, Restarts); %% SQL transaction, based on a erlang anonymous function (F = fun) -sql_transaction(Host, F, Timeout) when is_function(F) -> - case sql_call(Host, {sql_transaction, F}, Timeout) of +sql_transaction(Host, F, Timeout, Restarts) when is_function(F) -> + case sql_call(Host, {sql_transaction, F, Restarts}, Timeout) of {atomic, _} = Ret -> Ret; {aborted, _} = Ret -> Ret; Err -> {aborted, Err} @@ -156,7 +156,7 @@ sql_transaction(Host, F, Timeout) when is_function(F) -> {atomic, T} | {aborted, any()}. sql_transaction(Host, Queries) -> - sql_transaction(Host, Queries, query_timeout(Host)). + sql_transaction(Host, Queries, query_timeout(Host), ?MAX_TRANSACTION_RESTARTS). %% SQL bloc, based on a erlang anonymous function (F = fun) sql_bloc(Host, F, Timeout) -> @@ -538,12 +538,14 @@ run_sql_cmd(Command, From, State, Timestamp) -> end. %% @doc Only called by handle_call, only handles top level operations. --spec outer_op(Op::{atom(), binary()}) -> +-spec outer_op(Op::{atom(), binary()} | {sql_transaction, binary(), pos_integer()}) -> {error, Reason::binary()} | {aborted, Reason::binary()} | {atomic, Result::any()}. outer_op({sql_query, Query}) -> sql_query_internal(Query); outer_op({sql_transaction, F}) -> - outer_transaction(F, ?MAX_TRANSACTION_RESTARTS, <<"">>); + outer_op({sql_transaction, F, ?MAX_TRANSACTION_RESTARTS}); +outer_op({sql_transaction, F, Restarts}) -> + outer_transaction(F, Restarts, <<"">>); outer_op({sql_bloc, F}) -> execute_bloc(F). %% Called via sql_query/transaction/bloc from client code when inside a @@ -551,10 +553,12 @@ outer_op({sql_bloc, F}) -> execute_bloc(F). nested_op({sql_query, Query}) -> sql_query_internal(Query); nested_op({sql_transaction, F}) -> + nested_op({sql_transaction, F, ?MAX_TRANSACTION_RESTARTS}); +nested_op({sql_transaction, F, Restarts}) -> NestingLevel = get(?NESTING_KEY), if NestingLevel =:= (?TOP_LEVEL_TXN) -> - outer_transaction(F, ?MAX_TRANSACTION_RESTARTS, <<"">>); - true -> inner_transaction(F) + outer_transaction(F, Restarts, <<"">>); + true -> inner_transaction(F) end; nested_op({sql_bloc, F}) -> execute_bloc(F). From f56739fd9fa713557a3fcceb328ff6d1c0e176a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jul 2024 15:26:52 +0200 Subject: [PATCH 0692/1302] Make any uncatched exception inside transaction trigger rollback --- src/ejabberd_sql.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 8a4a0e7a4..c8734079b 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -620,7 +620,7 @@ outer_transaction(F, NRestarts, _Reason) -> [?MAX_TRANSACTION_RESTARTS, Reason, StackTrace, get(?STATE_KEY)]), maybe_restart_transaction(F, NRestarts, Reason, true); - ?EX_RULE(exit, Reason, _) -> + ?EX_RULE(_, Reason, _) -> maybe_restart_transaction(F, 0, Reason, true) end end. From ead87e3727e63041fe3d68fe2f9bf6c4295b4662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jul 2024 15:36:14 +0200 Subject: [PATCH 0693/1302] Add option update_sql_schema_timeout to allow schema update use longer timeouts This also makes batch of schema updates to single table use transaction, which should help in not leaving table in inconsistent state if some update steps fails (unless you use mysql where you can't rollback changes to table schemas). --- src/ejabberd_option.erl | 8 + src/ejabberd_options.erl | 3 + src/ejabberd_options_doc.erl | 6 + src/ejabberd_sql_schema.erl | 474 +++++++++++++++++------------------ 4 files changed, 254 insertions(+), 237 deletions(-) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index e7dc04da0..bc0571e81 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -164,6 +164,7 @@ -export([sql_username/0, sql_username/1]). -export([trusted_proxies/0]). -export([update_sql_schema/0]). +-export([update_sql_schema_timeout/0, update_sql_schema_timeout/1]). -export([use_cache/0, use_cache/1]). -export([validate_stream/0]). -export([version/0]). @@ -1109,6 +1110,13 @@ trusted_proxies() -> update_sql_schema() -> ejabberd_config:get_option({update_sql_schema, global}). +-spec update_sql_schema_timeout() -> 'infinity' | pos_integer(). +update_sql_schema_timeout() -> + update_sql_schema_timeout(global). +-spec update_sql_schema_timeout(global | binary()) -> 'infinity' | pos_integer(). +update_sql_schema_timeout(Host) -> + ejabberd_config:get_option({update_sql_schema_timeout, Host}). + -spec use_cache() -> boolean(). use_cache() -> use_cache(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index b6bb18d5a..c30cc072f 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -264,6 +264,8 @@ opt_type(new_sql_schema) -> econf:bool(); opt_type(update_sql_schema) -> econf:bool(); +opt_type(update_sql_schema_timeout) -> + econf:timeout(second, infinity); opt_type(oauth_access) -> econf:acl(); opt_type(oauth_cache_life_time) -> @@ -613,6 +615,7 @@ options() -> {net_ticktime, timer:seconds(60)}, {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT}, {update_sql_schema, true}, + {update_sql_schema_timeout, timer:minutes(5)}, {oauth_access, none}, {oauth_cache_life_time, fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 061b1dccb..2e2313c97 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -929,6 +929,12 @@ doc() -> "This option was added in ejabberd 23.10, " "and enabled by default since 24.06. " "The default value is 'true'.")}}, + {update_sql_schema_timeout, + #{value => "timeout()", + note => "added in 24.07", + desc => + ?T("Time allocated to SQL schema update queries. " + "The default value is set to 5 minutes.")}}, {oauth_access, #{value => ?T("AccessName"), desc => ?T("By default creating OAuth tokens is not allowed. " diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 91b8aab72..644844714 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -226,6 +226,13 @@ store_version(Host, Module, Version) -> ["!module=%(SModule)s", "version=%(Version)d"]). +store_version_t(Module, Version) -> + SModule = misc:atom_to_binary(Module), + ?SQL_UPSERT_T( + "schema_version", + ["!module=%(SModule)s", + "version=%(Version)d"]). + table_exists(Host, Table) -> ejabberd_sql:sql_query( Host, @@ -398,28 +405,25 @@ get_current_version(Host, Module, Schemas) -> Version end. -sqlite_table_copy(Host, SchemaInfo, Table) -> - ejabberd_sql:sql_transaction(Host, - fun() -> - TableName = Table#sql_table.name, - NewTableName = <<"new_", TableName/binary>>, - NewTable = Table#sql_table{name = NewTableName}, - create_table_t(SchemaInfo, NewTable), - SQL2 = <<"INSERT INTO ", NewTableName/binary, - " SELECT * FROM ", TableName/binary>>, - ?INFO_MSG("Copying table ~s to ~s:~n~s~n", - [TableName, NewTableName, SQL2]), - ejabberd_sql:sql_query_t(SQL2), - SQL3 = <<"DROP TABLE ", TableName/binary>>, - ?INFO_MSG("Droping old table ~s:~n~s~n", - [TableName, SQL2]), - ejabberd_sql:sql_query_t(SQL3), - SQL4 = <<"ALTER TABLE ", NewTableName/binary, - " RENAME TO ", TableName/binary>>, - ?INFO_MSG("Renameing table ~s to ~s:~n~s~n", - [NewTableName, TableName, SQL4]), - ejabberd_sql:sql_query_t(SQL4) - end). +sqlite_table_copy_t(SchemaInfo, Table) -> + TableName = Table#sql_table.name, + NewTableName = <<"new_", TableName/binary>>, + NewTable = Table#sql_table{name = NewTableName}, + create_table_t(SchemaInfo, NewTable), + SQL2 = <<"INSERT INTO ", NewTableName/binary, + " SELECT * FROM ", TableName/binary>>, + ?INFO_MSG("Copying table ~s to ~s:~n~s~n", + [TableName, NewTableName, SQL2]), + ejabberd_sql:sql_query_t(SQL2), + SQL3 = <<"DROP TABLE ", TableName/binary>>, + ?INFO_MSG("Droping old table ~s:~n~s~n", + [TableName, SQL2]), + ejabberd_sql:sql_query_t(SQL3), + SQL4 = <<"ALTER TABLE ", NewTableName/binary, + " RENAME TO ", TableName/binary>>, + ?INFO_MSG("Renameing table ~s to ~s:~n~s~n", + [NewTableName, TableName, SQL4]), + ejabberd_sql:sql_query_t(SQL4). format_type(#sql_schema_info{db_type = pgsql}, Column) -> case Column#sql_column.type of @@ -875,18 +879,18 @@ update_schema(Host, Module, RawSchemas) -> end. do_update_schema(Host, Module, SchemaInfo, Schema) -> - lists:foreach( - fun({add_column, TableName, ColumnName}) -> - {value, Table} = - lists:keysearch( - TableName, #sql_table.name, Schema#sql_schema.tables), - {value, Column} = - lists:keysearch( - ColumnName, #sql_column.name, Table#sql_table.columns), - Res = - ejabberd_sql:sql_query( - Host, - fun(DBType, _DBVersion) -> + F = fun() -> + lists:foreach( + fun({add_column, TableName, ColumnName}) -> + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Column} = + lists:keysearch( + ColumnName, #sql_column.name, Table#sql_table.columns), + Res = + ejabberd_sql:sql_query_t( + fun(DBType, _DBVersion) -> Def = format_column_def(SchemaInfo, Column), Default = format_default(SchemaInfo, Column), SQLs = @@ -911,209 +915,205 @@ do_update_schema(Host, Module, SchemaInfo, Schema) -> ColumnName, SQLs]), lists:foreach( - fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, - SQLs) - end), - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); - _ -> - ok - end; - ({drop_column, TableName, ColumnName}) -> - Res = - ejabberd_sql:sql_query( - Host, - fun(_DBType, _DBVersion) -> - SQL = [<<"ALTER TABLE ">>, - TableName, - <<" DROP COLUMN ">>, - ColumnName, - <<";">>], - ?INFO_MSG("Drop column ~s/~s:~n~s~n", - [TableName, - ColumnName, - SQL]), - ejabberd_sql:sql_query_t(SQL) - end), - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); - _ -> - ok - end; - ({create_index, TableName, Columns1}) -> - Columns = - case ejabberd_sql:use_new_schema() of - true -> - Columns1; - false -> - lists:delete( - <<"server_host">>, Columns1) - end, - {value, Table} = - lists:keysearch( - TableName, #sql_table.name, Schema#sql_schema.tables), - {value, Index} = - lists:keysearch( - Columns, #sql_index.columns, Table#sql_table.indices), - case Index#sql_index.meta of - #{ignore := true} -> ok; - _ -> - Res = - ejabberd_sql:sql_query( - Host, - fun() -> - case Index#sql_index.meta of - #{primary_key := true} -> - SQL1 = format_add_primary_key( - SchemaInfo, Table, Index), - SQL = iolist_to_binary(SQL1), - ?INFO_MSG("Add primary key ~s/~p:~n~s~n", - [Table#sql_table.name, - Index#sql_index.columns, - SQL]), - ejabberd_sql:sql_query_t(SQL); - _ -> - SQL1 = format_create_index( - SchemaInfo, Table, Index), - SQL = iolist_to_binary(SQL1), - ?INFO_MSG("Create index ~s/~p:~n~s~n", - [Table#sql_table.name, - Index#sql_index.columns, - SQL]), - ejabberd_sql:sql_query_t(SQL) - end - end), - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); - _ -> - ok - end - end; - ({update_primary_key, TableName, Columns1}) -> - Columns = - case ejabberd_sql:use_new_schema() of - true -> - Columns1; - false -> - lists:delete( - <<"server_host">>, Columns1) - end, - {value, Table} = - lists:keysearch( - TableName, #sql_table.name, Schema#sql_schema.tables), - {value, Index} = - lists:keysearch( - Columns, #sql_index.columns, Table#sql_table.indices), - Res = - case SchemaInfo#sql_schema_info.db_type of - sqlite -> - sqlite_table_copy(Host, SchemaInfo, Table); - pgsql -> - TableName = Table#sql_table.name, - SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP CONSTRAINT ", - TableName/binary,"_pkey, ", - "ADD PRIMARY KEY (">>, - lists:join( - <<", ">>, - Index#sql_index.columns), - <<");">>], - SQL = iolist_to_binary(SQL1), - ?INFO_MSG("Update primary key ~s/~p:~n~s~n", - [Table#sql_table.name, - Index#sql_index.columns, - SQL]), - ejabberd_sql:sql_query( - Host, - fun(_DBType, _DBVersion) -> - ejabberd_sql:sql_query_t(SQL) - end); - mysql -> - TableName = Table#sql_table.name, - SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP PRIMARY KEY, " - "ADD PRIMARY KEY (">>, - lists:join( - <<", ">>, - lists:map( - fun(Col) -> - format_mysql_index_column(Table, Col) - end, Index#sql_index.columns)), - <<");">>], - SQL = iolist_to_binary(SQL1), - ?INFO_MSG("Update primary key ~s/~p:~n~s~n", - [Table#sql_table.name, - Index#sql_index.columns, - SQL]), - ejabberd_sql:sql_query( - Host, - fun(_DBType, _DBVersion) -> - ejabberd_sql:sql_query_t(SQL) - end) - end, - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); - _ -> - ok - end; - ({drop_index, TableName, Columns1}) -> - Columns = - case ejabberd_sql:use_new_schema() of - true -> - Columns1; - false -> - lists:delete( - <<"server_host">>, Columns1) - end, - case find_index_name(Host, TableName, Columns) of - false -> - ?ERROR_MSG("Can't find an index to drop for ~s/~p", - [TableName, Columns]); - {ok, IndexName} -> - Res = - ejabberd_sql:sql_query( - Host, - fun(DBType, _DBVersion) -> - SQL = - case DBType of - mysql -> - [<<"DROP INDEX ">>, - IndexName, - <<" ON ">>, - TableName, - <<";">>]; - _ -> - [<<"DROP INDEX ">>, - IndexName, <<";">>] - end, - ?INFO_MSG("Drop index ~s/~p:~n~s~n", - [TableName, - Columns, - SQL]), - ejabberd_sql:sql_query_t(SQL) - end), - case Res of - {error, Error} -> - ?ERROR_MSG("Failed to update table ~s: ~p", - [TableName, Error]), - error(Error); - _ -> - ok - end - end - end, Schema#sql_schema.update), - store_version(Host, Module, Schema#sql_schema.version). - + fun(SQL) -> ejabberd_sql:sql_query_t(SQL) end, + SQLs) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({drop_column, TableName, ColumnName}) -> + Res = + ejabberd_sql:sql_query_t( + fun(_DBType, _DBVersion) -> + SQL = [<<"ALTER TABLE ">>, + TableName, + <<" DROP COLUMN ">>, + ColumnName, + <<";">>], + ?INFO_MSG("Drop column ~s/~s:~n~s~n", + [TableName, + ColumnName, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({create_index, TableName, Columns1}) -> + Columns = + case ejabberd_sql:use_new_schema() of + true -> + Columns1; + false -> + lists:delete( + <<"server_host">>, Columns1) + end, + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Index} = + lists:keysearch( + Columns, #sql_index.columns, Table#sql_table.indices), + case Index#sql_index.meta of + #{ignore := true} -> ok; + _ -> + Res = + ejabberd_sql:sql_query_t( + fun() -> + case Index#sql_index.meta of + #{primary_key := true} -> + SQL1 = format_add_primary_key( + SchemaInfo, Table, Index), + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Add primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t(SQL); + _ -> + SQL1 = format_create_index( + SchemaInfo, Table, Index), + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Create index ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end + end; + ({update_primary_key, TableName, Columns1}) -> + Columns = + case ejabberd_sql:use_new_schema() of + true -> + Columns1; + false -> + lists:delete( + <<"server_host">>, Columns1) + end, + {value, Table} = + lists:keysearch( + TableName, #sql_table.name, Schema#sql_schema.tables), + {value, Index} = + lists:keysearch( + Columns, #sql_index.columns, Table#sql_table.indices), + Res = + case SchemaInfo#sql_schema_info.db_type of + sqlite -> + sqlite_table_copy_t(SchemaInfo, Table); + pgsql -> + TableName = Table#sql_table.name, + SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP CONSTRAINT ", + TableName/binary, "_pkey, ", + "ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + Index#sql_index.columns), + <<");">>], + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Update primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t( + fun(_DBType, _DBVersion) -> + ejabberd_sql:sql_query_t(SQL) + end); + mysql -> + TableName = Table#sql_table.name, + SQL1 = [<<"ALTER TABLE ">>, TableName, <<" DROP PRIMARY KEY, " + "ADD PRIMARY KEY (">>, + lists:join( + <<", ">>, + lists:map( + fun(Col) -> + format_mysql_index_column(Table, Col) + end, Index#sql_index.columns)), + <<");">>], + SQL = iolist_to_binary(SQL1), + ?INFO_MSG("Update primary key ~s/~p:~n~s~n", + [Table#sql_table.name, + Index#sql_index.columns, + SQL]), + ejabberd_sql:sql_query_t( + fun(_DBType, _DBVersion) -> + ejabberd_sql:sql_query_t(SQL) + end) + end, + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end; + ({drop_index, TableName, Columns1}) -> + Columns = + case ejabberd_sql:use_new_schema() of + true -> + Columns1; + false -> + lists:delete( + <<"server_host">>, Columns1) + end, + case find_index_name(Host, TableName, Columns) of + false -> + ?ERROR_MSG("Can't find an index to drop for ~s/~p", + [TableName, Columns]); + {ok, IndexName} -> + Res = + ejabberd_sql:sql_query_t( + fun(DBType, _DBVersion) -> + SQL = + case DBType of + mysql -> + [<<"DROP INDEX ">>, + IndexName, + <<" ON ">>, + TableName, + <<";">>]; + _ -> + [<<"DROP INDEX ">>, + IndexName, <<";">>] + end, + ?INFO_MSG("Drop index ~s/~p:~n~s~n", + [TableName, + Columns, + SQL]), + ejabberd_sql:sql_query_t(SQL) + end), + case Res of + {error, Error} -> + ?ERROR_MSG("Failed to update table ~s: ~p", + [TableName, Error]), + error(Error); + _ -> + ok + end + end + end, Schema#sql_schema.update), + store_version_t(Module, Schema#sql_schema.version) + end, + ejabberd_sql:sql_transaction(Host, F, ejabberd_option:update_sql_schema_timeout(), 1). print_schema(SDBType, SDBVersion, SNewSchema) -> {DBType, DBVersion} = From 2016cf547ff38c114dd70ce94111ed6aecc0da04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jul 2024 15:56:41 +0200 Subject: [PATCH 0694/1302] Make dialyzer happy --- src/ejabberd_sql.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index c8734079b..53c83bc4a 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -542,8 +542,6 @@ run_sql_cmd(Command, From, State, Timestamp) -> {error, Reason::binary()} | {aborted, Reason::binary()} | {atomic, Result::any()}. outer_op({sql_query, Query}) -> sql_query_internal(Query); -outer_op({sql_transaction, F}) -> - outer_op({sql_transaction, F, ?MAX_TRANSACTION_RESTARTS}); outer_op({sql_transaction, F, Restarts}) -> outer_transaction(F, Restarts, <<"">>); outer_op({sql_bloc, F}) -> execute_bloc(F). @@ -552,8 +550,6 @@ outer_op({sql_bloc, F}) -> execute_bloc(F). %% nested operation nested_op({sql_query, Query}) -> sql_query_internal(Query); -nested_op({sql_transaction, F}) -> - nested_op({sql_transaction, F, ?MAX_TRANSACTION_RESTARTS}); nested_op({sql_transaction, F, Restarts}) -> NestingLevel = get(?NESTING_KEY), if NestingLevel =:= (?TOP_LEVEL_TXN) -> From 0e0fdb440c8a1b8aa7cb7771e2a8e559a9638e3a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jul 2024 13:18:12 +0200 Subject: [PATCH 0695/1302] CHANGELOG.md: Fix Issues URLs --- CHANGELOG.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42ee0c767..c1b563bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,22 +6,22 @@ - `ejabberd_logger`: Reloading configuration will update logger settings - `gen_mod`: Add support to specify a hook global, not vhost-specific - [`mod_configure`](https://docs.ejabberd.im/admin/configuration/modules/#mod_configure): Retract `Get User Password` command to update [XEP-0133](https://xmpp.org/extensions/xep-0133.html) 1.3.0 -- [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod_conversejs): Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/issues/4167)) +- [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod_conversejs): Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) - [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Document that [XEP-0441](https://xmpp.org/extensions/xep-0441.html) is implemented as well -- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Update support for [XEP-0425](https://xmpp.org/extensions/xep-0425.html) version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/issues/4193)) -- [`mod_matrix_gw`](https://docs.ejabberd.im/admin/configuration/modules/#mod_matrix_gw): Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/issues/4167)) +- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Update support for [XEP-0425](https://xmpp.org/extensions/xep-0425.html) version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/ejabberd/issues/4193)) +- [`mod_matrix_gw`](https://docs.ejabberd.im/admin/configuration/modules/#mod_matrix_gw): Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) - [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Hide join/leave lines, add method to show them - [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Support `allowpm` introduced in 2bd61ab -- [`mod_muc_room`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_room): Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/issues/4191)) +- [`mod_muc_room`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_room): Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/ejabberd/issues/4191)) - [`mod_private`](https://docs.ejabberd.im/admin/configuration/modules/#mod_private): Cope with bookmark decoding errors - [`mod_vcard_xupdate`](https://docs.ejabberd.im/admin/configuration/modules/#mod_vcard_xupdate): Send hash after avatar get set for first time -- `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/issues/4188)) +- `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/ejabberd/issues/4188)) #### SQL - [`update_sql_schema`](https://docs.ejabberd.im/admin/configuration/toplevel/#update_sql_schema): Enable this option by default - CI: Don't load database schema files for mysql and pgsql -- Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/issues/3716)) +- Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/ejabberd/issues/3716)) - Fix handling of `mqtt_pub` table definition from `mysql.sql` and fix `should_update_schema/1` in `ejabberd_sql_schema.erl` - Don't start sql connection pools for unknown hosts - Add `update_primary_key` command to sql schema updater @@ -30,24 +30,24 @@ #### Commands API -- New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/issues/4201)) +- New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/ejabberd/issues/4201)) - [`join_cluster_here`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join_cluster_here): New command to join a remote node into our local cluster -- Don't name integer and string results in API examples ([#4198](https://github.com/processone/issues/4198)) +- Don't name integer and string results in API examples ([#4198](https://github.com/processone/ejabberd/issues/4198)) - [`get_user_subscriptions`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#get_user_subscriptions): Fix validation of user field in that command -- [`mod_admin_extra`](https://docs.ejabberd.im/admin/configuration/modules/#mod_admin_extra): Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/issues/4201)) +- [`mod_admin_extra`](https://docs.ejabberd.im/admin/configuration/modules/#mod_admin_extra): Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/ejabberd/issues/4201)) - [`mod_muc_admin`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_admin): Improve validation of arguments in several commands #### Compile -- `ejabberdctl`: Comment ERTS_VSN variable when not used ([#4194](https://github.com/processone/issues/4194)) +- `ejabberdctl`: Comment ERTS_VSN variable when not used ([#4194](https://github.com/processone/ejabberd/issues/4194)) - `ejabberdctl`: Fix iexlive after `make prod` when using Elixir -- `ejabberdctl`: If `INET_DIST_INTERFACE` is IPv6, set required option ([#4189](https://github.com/processone/issues/4189)) +- `ejabberdctl`: If `INET_DIST_INTERFACE` is IPv6, set required option ([#4189](https://github.com/processone/ejabberd/issues/4189)) - `ejabberdctl`: Make native dynamic node names work when using fully qualified domain names -- `rebar.config.script`: Support relaxed dependency version ([#4192](https://github.com/processone/issues/4192)) +- `rebar.config.script`: Support relaxed dependency version ([#4192](https://github.com/processone/ejabberd/issues/4192)) - `rebar.config`: Update deps version to rebar3's relaxed versioning - `rebar.lock`: Track file, now that rebar3 uses loose dependency versioning -- `configure.ac`: When using rebar3, unlock dependencies that are disabled ([#4212](https://github.com/processone/issues/4212)) -- `configure.ac`: When using rebar3 with old Erlang, unlock some dependencies ([#4213](https://github.com/processone/issues/4213)) +- `configure.ac`: When using rebar3, unlock dependencies that are disabled ([#4212](https://github.com/processone/ejabberd/issues/4212)) +- `configure.ac`: When using rebar3 with old Erlang, unlock some dependencies ([#4213](https://github.com/processone/ejabberd/issues/4213)) - `mix:exs`: Move `xmpp` from `included_applications` to `applications` #### Dependencies @@ -57,7 +57,7 @@ - Jiffy: Use Json module when Erlang/OTP 27, jiffy with older ones - Jose: Update to the new 1.11.10 for Erlang/OTP higher than 23 - Luerl: Update to 1.2.0 when OTP same or higher than 20, simplifies commit a09f222 -- P1_acme: Update to support Jose 1.11.10 and Ipv6 support ([#4170](https://github.com/processone/issues/4170)) +- P1_acme: Update to support Jose 1.11.10 and Ipv6 support ([#4170](https://github.com/processone/ejabberd/issues/4170)) - P1_acme: Update to use Erlang's json library instead of jiffy when OTP 27 - Port_compiler: Update to 1.15.0 that supports Erlang/OTP 27.0 @@ -89,7 +89,7 @@ - make-binaries: Bump OpenSSL to 3.3.1 - make-binaries: Bump Linux-PAM to 1.6.1 - make-binaries: Bump Expat to 2.6.2 -- make-binaries: Revert temporarily an OTP commit that breaks MSSQL ([#4178](https://github.com/processone/issues/4178)) +- make-binaries: Revert temporarily an OTP commit that breaks MSSQL ([#4178](https://github.com/processone/ejabberd/issues/4178)) - CONTAINER.md: Invalid `CTL_ON_CREATE` usage in docker-compose example #### WebAdmin From 45007809f6ab49c42015a516c8972b35a75a041e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jul 2024 13:24:18 +0200 Subject: [PATCH 0696/1302] CHANGELOG.md: Only include URLs at the end of line for plaintext readability --- CHANGELOG.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b563bbb..97bf5a430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,37 +5,37 @@ - `econf`: Add ability to use additional custom errors when parsing options - `ejabberd_logger`: Reloading configuration will update logger settings - `gen_mod`: Add support to specify a hook global, not vhost-specific -- [`mod_configure`](https://docs.ejabberd.im/admin/configuration/modules/#mod_configure): Retract `Get User Password` command to update [XEP-0133](https://xmpp.org/extensions/xep-0133.html) 1.3.0 -- [`mod_conversejs`](https://docs.ejabberd.im/admin/configuration/modules/#mod_conversejs): Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) -- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Document that [XEP-0441](https://xmpp.org/extensions/xep-0441.html) is implemented as well -- [`mod_mam`](https://docs.ejabberd.im/admin/configuration/modules/#mod_mam): Update support for [XEP-0425](https://xmpp.org/extensions/xep-0425.html) version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/ejabberd/issues/4193)) -- [`mod_matrix_gw`](https://docs.ejabberd.im/admin/configuration/modules/#mod_matrix_gw): Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) -- [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Hide join/leave lines, add method to show them -- [`mod_muc_log`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_log): Support `allowpm` introduced in 2bd61ab -- [`mod_muc_room`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_room): Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/ejabberd/issues/4191)) -- [`mod_private`](https://docs.ejabberd.im/admin/configuration/modules/#mod_private): Cope with bookmark decoding errors -- [`mod_vcard_xupdate`](https://docs.ejabberd.im/admin/configuration/modules/#mod_vcard_xupdate): Send hash after avatar get set for first time +- `mod_configure`: Retract `Get User Password` command to update XEP-0133 1.3.0 +- `mod_conversejs`: Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) +- `mod_mam`: Document that XEP-0441 is implemented as well +- `mod_mam`: Update support for XEP-0425 version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/ejabberd/issues/4193)) +- `mod_matrix_gw`: Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167)) +- `mod_muc_log`: Hide join/leave lines, add method to show them +- `mod_muc_log`: Support `allowpm` introduced in 2bd61ab +- `mod_muc_room`: Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/ejabberd/issues/4191)) +- `mod_private`): Cope with bookmark decoding errors +- `mod_vcard_xupdate`: Send hash after avatar get set for first time - `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/ejabberd/issues/4188)) #### SQL -- [`update_sql_schema`](https://docs.ejabberd.im/admin/configuration/toplevel/#update_sql_schema): Enable this option by default +- `update_sql_schema`: Enable this option by default - CI: Don't load database schema files for mysql and pgsql - Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/ejabberd/issues/3716)) - Fix handling of `mqtt_pub` table definition from `mysql.sql` and fix `should_update_schema/1` in `ejabberd_sql_schema.erl` - Don't start sql connection pools for unknown hosts - Add `update_primary_key` command to sql schema updater -- Fix crash running [`export2sql`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#export2sql) when MAM enabled but MUC disabled +- Fix crash running `export2sql` when MAM enabled but MUC disabled - Improve detection of types in odbc #### Commands API - New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/ejabberd/issues/4201)) -- [`join_cluster_here`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join_cluster_here): New command to join a remote node into our local cluster +- `join_cluster_here`: New command to join a remote node into our local cluster - Don't name integer and string results in API examples ([#4198](https://github.com/processone/ejabberd/issues/4198)) -- [`get_user_subscriptions`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#get_user_subscriptions): Fix validation of user field in that command -- [`mod_admin_extra`](https://docs.ejabberd.im/admin/configuration/modules/#mod_admin_extra): Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/ejabberd/issues/4201)) -- [`mod_muc_admin`](https://docs.ejabberd.im/admin/configuration/modules/#mod_muc_admin): Improve validation of arguments in several commands +- `get_user_subscriptions`: Fix validation of user field in that command +- `mod_admin_extra`: Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/ejabberd/issues/4201)) +- `mod_muc_admin`: Improve validation of arguments in several commands #### Compile @@ -116,8 +116,8 @@ - Support tls-exporter channel binding - Support XEP-0474: SASL SCRAM Downgrade Protection - Fix presenting features and returning results of inline bind2 elements -- [`disable_sasl_scram_downgrade_protection`](https://docs.ejabberd.im/admin/configuration/toplevel/#disable-sasl-scram-downgrade-protection): New option to disable XEP-0474 -- [`negotiation_timeout`](https://docs.ejabberd.im/admin/configuration/toplevel/#negotiation-timeout): Increase default value from 30s to 2m +- `disable_sasl_scram_downgrade_protection`: New option to disable XEP-0474 +- `negotiation_timeout`: Increase default value from 30s to 2m - mod_carboncopy: Teach how to interact with bind2 inline requests #### Other: @@ -155,10 +155,10 @@ - ejabberdctl: Rework temporary node name generation - ejabberdctl: Print argument description, examples and note in help - ejabberdctl: Document exclusive ejabberdctl commands like all the others -- Commands: Add a new [`muc_sub`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#muc-sub) tag to all the relevant commands +- Commands: Add a new `muc_sub` tag to all the relevant commands - Commands: Improve syntax of many commands documentation - Commands: Use list arguments in many commands that used separators -- Commands: [`set_presence`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#set-presence): switch priority argument from string to integer +- Commands: `set_presence`: switch priority argument from string to integer - ejabberd_commands: Add the command API version as [a tag `vX`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#v1) - ejabberd_ctl: Add support for list and tuple arguments - ejabberd_xmlrpc: Fix support for restuple error response From bd0332c71651d82fa4457fc7f78b362f56dbf2f7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jul 2024 13:34:16 +0200 Subject: [PATCH 0697/1302] ext_mod: Fix typo in log message --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index cb24d2289..6373644b5 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -790,7 +790,7 @@ rebar_dep({App, Version, Git}) when Version /= ".*" -> {App, "mix hex.package fetch "++AppS++" "++Version++" --unpack"}; false -> io:format("I'll download ~p using git because I can't use Mix " - "to fetch from hex.pm:~n~s", [AppS, help]), + "to fetch from hex.pm:~n~s", [AppS, Help]), rebar_dep({App, ".*", Git}) end; From 0fdbb03f54b25c1cf17f65ebf3e199551d12654c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Jul 2024 16:26:15 +0200 Subject: [PATCH 0698/1302] Don't add iex to included_applications - iex is unnecessary for ejabberd in OTP release - adding iex breaks dialyzer when --enable-elixir --- src/ejabberd.app.src.script | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd.app.src.script b/src/ejabberd.app.src.script index 089295804..a4e245461 100644 --- a/src/ejabberd.app.src.script +++ b/src/ejabberd.app.src.script @@ -2,9 +2,9 @@ {ok, Terms} -> Backends = [mssql, mysql, odbc, pgsql, redis, sqlite], EBs = lists:filter(fun(Backend) -> lists:member({Backend, true}, Terms) end, Backends), - Elixirs = case lists:keyfind(elixir, 1, Terms) of - {elixir, true} -> [elixir, iex, logger, mix]; - _ -> [] + Elixirs = case proplists:get_bool(elixir, Terms) of + true -> [elixir, logger, mix]; + false -> [] end, ProfileEnvironmentVariable = os:getenv("REBAR_PROFILE"), From 926f60b2ed6ca4ce2796bcc83ccad62aba12fb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Jul 2024 18:54:48 +0200 Subject: [PATCH 0699/1302] Output muc#roominfo_avatarhash in room disco info as per updated xep-0486 This should fix issue #4234. --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 2 +- src/mod_muc_room.erl | 8 +++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 7253d1f39..789d6bfd5 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ce76eda23d8c7654a815f50de6c7cfcaa6c46850", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "9728433608e25d196a49ed0f609208e353621135", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 9999574e7..28b4327dd 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.13"}}}}, - {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", "ce76eda23d8c7654a815f50de6c7cfcaa6c46850"}}, + {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", "9728433608e25d196a49ed0f609208e353621135"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 750fb5f49..59652c096 100644 --- a/rebar.lock +++ b/rebar.lock @@ -26,7 +26,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"ce76eda23d8c7654a815f50de6c7cfcaa6c46850"}}, + {ref,"9728433608e25d196a49ed0f609208e353621135"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index c96114e6f..73b7a7386 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4576,8 +4576,14 @@ iq_disco_info_extras(Lang, StateData, Static) -> false -> Fs3 end, + Fs5 = case (StateData#state.config)#config.vcard_xupdate of + Hash when is_binary(Hash) -> + [{avatarhash, [Hash]} | Fs4]; + _ -> + Fs4 + end, #xdata{type = result, - fields = muc_roominfo:encode(Fs4, Lang)}. + fields = muc_roominfo:encode(Fs5, Lang)}. -spec process_iq_disco_items(jid(), iq(), state()) -> {error, stanza_error()} | {result, disco_items()}. From af99799e8a535da62fc3c4ff974e2473140e9eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 17 Jul 2024 11:11:00 +0200 Subject: [PATCH 0700/1302] Use tagged deps --- mix.exs | 2 +- mix.lock | 10 +++++----- rebar.config | 10 +++++----- rebar.lock | 37 ++++++++++++++++++------------------- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/mix.exs b/mix.exs index 789d6bfd5..a82addd7b 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "9728433608e25d196a49ed0f609208e353621135", override: true}, + {:xmpp, ">= 1.8.3"}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 12bf8f2ad..9c9a77393 100644 --- a/mix.lock +++ b/mix.lock @@ -7,11 +7,11 @@ "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "esip": {:hex, :esip, "1.0.53", "482786a79d8f5b0aefc60ca68b8c6c7f074f6ede2653705e4206c6779edc5a56", [:rebar3], [{:fast_tls, "1.1.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.13", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "a9a1bd5ea52b0e2d1b1d1fec5fb7f0301e90610db1ecfeb205a18cc4e1cb3fc7"}, + "esip": {:hex, :esip, "1.0.54", "dae8fb8278fd3b2c0d38c2e832c4b8d26700eb239b9a42c8ea574fee76f5e76a", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.14", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "8187af819d7259cdaddaf69726c239ef604c9b0b0298a5f2d3e687bf5e2237ee"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, - "fast_tls": {:hex, :fast_tls, "1.1.20", "d6f12d9ae4fe57e880b144b912e735af89343a8463d39b7eb4be3f6ca6163879", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d09a12472a56a34c5eecaaed33ea283f00fcdf9dc2e8282ecbaae827f13fc21b"}, + "fast_tls": {:hex, :fast_tls, "1.1.21", "65d7d547a09eefb37a1c0d04d8601fac4f3e6e2c1ede859a7787081670f9648d", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "131542913937025e48cd80aa81f00359686d5501b75621e72026a87b5229505b"}, "fast_xml": {:hex, :fast_xml, "1.1.52", "0289daafbf1190b0e53b444d4885cccf41e4b05768d4b3acc76dd8d143668e10", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "795192390e06d2b65016a6990bbfa5727f4a26d2914808b1c3c9a32eedcd1bfd"}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, @@ -27,13 +27,13 @@ "p1_acme": {:hex, :p1_acme, "1.0.23", "791aef0f79dc7f768b228808250c349fa9ce585cd8779da50ca93106eb3394d0", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "8ce196f26e3d22ea10b7809122950465878c127f80767e325207aed7e8d0dd59"}, "p1_mysql": {:hex, :p1_mysql, "1.0.24", "0ed1e098c5a4525032448c65a2715f30980aae725615a4d255fd25f26bb22507", [:rebar3], [], "hexpm", "f058865f64257f507a2c6a5aff369b1375dbcb30b3d4258dad4f1b3eaffb655f"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.26", "d3c3748c3638a1d7db5644e4fc63a6da7614b3009e172ef92a01d0217c3bec65", [:rebar3], [{:xmpp, "~> 1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "85f230db530333106b8a1f9e5d5af032e6c3dd23b432e03d68e9d29013a6dcfc"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.27", "883e335d82ac062de0bde7981f8250a2e632258bb7a47df839a4cbdcb3e971e6", [:rebar3], [{:xmpp, "~> 1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "8e4d1a7602cb68780e55d89dc5a9b2e1aaca3f4f1ee3d1a25f2f8c3d2364ffb9"}, "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, - "stun": {:hex, :stun, "1.2.13", "c3e855f10f6b0c3ac150bce3d6c96c04a85207a3a5c7a7207876d8b36db2b0a4", [:rebar3], [{:fast_tls, "1.1.20", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9cf4191491a60573ed6197e636530af1d25c9b064845aabed0c02f780d33ea3f"}, + "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.8.2", "7c26fae7ca83b307bab99624595dce706d427a17eed9c6305980550f8120a3a3", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "53a9f85ad44103a358dc173225bc96d08076d4e78506f87d838337b2fa3b381f"}, + "xmpp": {:hex, :xmpp, "1.8.3", "acf39a8b70b066bb8f10b0862e031e8abcf92fe89d1c41d06c1e39ae5caf89c4", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ed70065f9a89a818dcff43b74c080c9e7f4f1414e1051beddb7280db809af711"}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index 28b4327dd..af1519510 100644 --- a/rebar.config +++ b/rebar.config @@ -33,10 +33,10 @@ {if_var_true, redis, {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.53"}}}}, + {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.54"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.20"}}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.21"}}}, {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.52"}}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -61,15 +61,15 @@ {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.24"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.26"}}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.27"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, - {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.13"}}}}, - {xmpp, "~> 1.8.2", {git, "https://github.com/processone/xmpp", "9728433608e25d196a49ed0f609208e353621135"}}, + {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", {tag, "1.8.3"}}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 59652c096..6dc25b683 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,30 +4,27 @@ {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.23">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.53">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.54">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, - {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.20">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.21">>},0}, {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.52">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.1">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.23">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.24">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.26">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.27">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.13">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.14">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"9728433608e25d196a49ed0f609208e353621135"}}, - 0}, + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.3">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ {pkg_hash,[ @@ -36,26 +33,27 @@ {<<"eimp">>, <<"AAF32EFAB061143403DADBAA63E699EF8E81702FBFA96FD436D5E9BE4D6A8D3A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, - {<<"esip">>, <<"482786A79D8F5B0AEFC60CA68B8C6C7F074F6EDE2653705E4206C6779EDC5A56">>}, + {<<"esip">>, <<"DAE8FB8278FD3B2C0D38C2E832C4B8D26700EB239B9A42C8EA574FEE76F5E76A">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, - {<<"fast_tls">>, <<"D6F12D9AE4FE57E880B144B912E735AF89343A8463D39B7EB4BE3F6CA6163879">>}, + {<<"fast_tls">>, <<"65D7D547A09EEFB37A1C0D04D8601FAC4F3E6E2C1EDE859A7787081670F9648D">>}, {<<"fast_xml">>, <<"0289DAAFBF1190B0E53B444D4885CCCF41E4B05768D4B3ACC76DD8D143668E10">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, - {<<"jiffy">>, <<"ACA10F47AA91697BF24AB9582C74E00E8E95474C7EF9F76D4F1A338D0F5DE21B">>}, + {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, {<<"p1_acme">>, <<"791AEF0F79DC7F768B228808250C349FA9CE585CD8779DA50CA93106EB3394D0">>}, {<<"p1_mysql">>, <<"0ED1E098C5A4525032448C65A2715F30980AAE725615A4D255FD25F26BB22507">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"D3C3748C3638A1D7DB5644E4FC63A6DA7614B3009E172EF92A01D0217C3BEC65">>}, + {<<"p1_pgsql">>, <<"883E335D82AC062DE0BDE7981F8250A2E632258BB7A47DF839A4CBDCB3E971E6">>}, {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, - {<<"stun">>, <<"C3E855F10F6B0C3AC150BCE3D6C96C04A85207A3A5C7A7207876D8B36DB2B0A4">>}, + {<<"stun">>, <<"6F538AC80C842131DBD149055570D116BFABC9B5EBFF4BD6AF2E7888958C660C">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"ACF39A8B70B066BB8F10B0862E031E8ABCF92FE89D1C41D06C1E39AE5CAF89C4">>}, {<<"yconf">>, <<"D59521D66FF89F219411B6E9277CD6FEEC7CC6FCE11554E67DE02A8D0A470479">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -63,25 +61,26 @@ {<<"eimp">>, <<"907C780023CB2893E4FC4BDBE6A4F02C355913862AC67F0ECC26605E816B628A">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, - {<<"esip">>, <<"A9A1BD5EA52B0E2D1B1D1FEC5FB7F0301E90610DB1ECFEB205A18CC4E1CB3FC7">>}, + {<<"esip">>, <<"8187AF819D7259CDADDAF69726C239EF604C9B0B0298A5F2D3E687BF5E2237EE">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, - {<<"fast_tls">>, <<"D09A12472A56A34C5EECAAED33EA283F00FCDF9DC2E8282ECBAAE827F13FC21B">>}, + {<<"fast_tls">>, <<"131542913937025E48CD80AA81F00359686D5501B75621E72026A87B5229505B">>}, {<<"fast_xml">>, <<"795192390E06D2B65016A6990BBFA5727F4A26D2914808B1C3C9A32EEDCD1BFD">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, - {<<"jiffy">>, <<"62E1F0581C3C19C33A725C781DFA88410D8BFF1BBAFC3885A2552286B4785C4C">>}, + {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, {<<"p1_acme">>, <<"8CE196F26E3D22EA10B7809122950465878C127F80767E325207AED7E8D0DD59">>}, {<<"p1_mysql">>, <<"F058865F64257F507A2C6A5AFF369B1375DBCB30B3D4258DAD4F1B3EAFFB655F">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"85F230DB530333106B8A1F9E5D5AF032E6C3DD23B432E03D68E9D29013A6DCFC">>}, + {<<"p1_pgsql">>, <<"8E4D1A7602CB68780E55D89DC5A9B2E1AACA3F4F1EE3D1A25F2F8C3D2364FFB9">>}, {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, - {<<"stun">>, <<"9CF4191491A60573ED6197E636530AF1D25C9B064845AABED0C02F780D33EA3F">>}, + {<<"stun">>, <<"E134807B1B7A8DFFD94E64EEFEE00E65C7B4042F3D14E16F8F43566D20371583">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"ED70065F9A89A818DCFF43B74C080C9E7F4F1414E1051BEDDB7280DB809AF711">>}, {<<"yconf">>, <<"E947813273F38711C7B2E5A8E4ACC9A51C7BBE854F744A345F60300B38586C89">>}]} ]. From 11055f61a68e622eeabd11176ac67f027b27b1fa Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 17 Jul 2024 13:33:46 +0200 Subject: [PATCH 0701/1302] make-installers: Don't edit too much The init script and systemd units don't refer to "/opt/ejabberd", and ejabberdctl doesn't refer to "/opt/ejabberd-$version". Fixes #4258. --- tools/make-installers | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/make-installers b/tools/make-installers index e1c7c47eb..9439fc597 100755 --- a/tools/make-installers +++ b/tools/make-installers @@ -247,7 +247,6 @@ create_setup_script() if [ "\$code_dir" != '$default_code_dir' ] then sed -i "s|$default_code_dir|\$code_dir|g" \ - "\$code_dir/bin/${rel_name}ctl" \ "\$code_dir/bin/$rel_name.init" \ "\$code_dir/bin/$rel_name.service" fi @@ -255,8 +254,6 @@ create_setup_script() then sed -i "s|$default_data_dir|\$data_dir|g" \ "\$code_dir/bin/${rel_name}ctl" \ - "\$code_dir/bin/$rel_name.init" \ - "\$code_dir/bin/$rel_name.service" \ "\$data_dir/conf/$rel_name.yml" \ "\$data_dir/conf/${rel_name}ctl.cfg" fi From 93cdee80ed8e7f60e617c4089f69d31dcf7bfe3c Mon Sep 17 00:00:00 2001 From: haha Date: Thu, 18 Jul 2024 15:14:39 +0800 Subject: [PATCH 0702/1302] [fix] fix mysql.sql archive origin_id --- sql/mysql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql.sql b/sql/mysql.sql index 84ef27387..e7de710a9 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -110,7 +110,7 @@ CREATE INDEX i_timestamp USING BTREE ON archive(timestamp); CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); -- To update 'archive' from ejabberd <= 23.10: --- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ADD COLUMN origin_id origin_id(191) NOT NULL DEFAULT ''; -- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; -- CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); From 65e16dcac180a3722d5a0d27f336503efe0bf346 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 Jul 2024 19:04:10 +0200 Subject: [PATCH 0703/1302] make format --- src/ejabberd_web_admin.erl | 4 +--- test/commands_tests.erl | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index dba415792..5264d1d1e 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1684,9 +1684,7 @@ make_command2(Name, Request, BaseArguments, Options) -> ResultLinks = proplists:get_value(result_links, Options, []), TO = proplists:get_value(table_options, Options, {999999, []}), Style = proplists:get_value(style, Options, normal), - #request{us = {RUser, RServer}, - ip = RIp} = - Request, + #request{us = {RUser, RServer}, ip = RIp} = Request, CallerInfo = #{usr => {RUser, RServer, <<"">>}, ip => RIp, diff --git a/test/commands_tests.erl b/test/commands_tests.erl index 9cc23dee5..7e77136b9 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -141,7 +141,9 @@ http_list_tuple(Config) -> ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTB})). http_list_tuple_map(Config) -> - LTA = #{<<"one">> => <<"uno">>, <<"dos">> => <<"two">>, <<"three">> => <<"tres">>}, + LTA = #{<<"one">> => <<"uno">>, + <<"dos">> => <<"two">>, + <<"three">> => <<"tres">>}, LTB = lists:sort([#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, #{<<"element1">> => <<"dos">>, <<"element2">> => <<"two">>}, #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}]), From 9ee70548236e1a362198b95a05566c02cc06d548 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 10:33:32 +0200 Subject: [PATCH 0704/1302] Update Chinese translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 292 ++++++++++++++++++++++++----------------------- 1 file changed, 147 insertions(+), 145 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index b6817a1f6..34808fd90 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -33,17 +33,17 @@ {"Allow visitors to send private messages to","允许访客发送私信至"}. {"Allow visitors to send status text in presence updates","允许访客在在线状态更新中发送状态文本"}. {"Allow visitors to send voice requests","允许访客发送发言请求"}. -{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义群聊成员资格相关联的 LDAP 群组;按群组特定于实现或特定于部署的定义,应该是一个 LDAP 专有名称。"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义群聊成员资格相关联的 LDAP 组;根据组的特定实施或特定部署的定义,使用 LDAP 专有名称。"}. {"Announcements","公告"}. {"Answer associated with a picture","与图片相关的答案"}. {"Answer associated with a video","与视频相关的答案"}. {"Answer associated with speech","与讲话相关的答案"}. {"Answer to a question","问题的答案"}. -{"Anyone in the specified roster group(s) may subscribe and retrieve items","指定花名册群组中的人可以订阅并检索内容项"}. -{"Anyone may associate leaf nodes with the collection","任何人都可以将叶子节点与集合关联"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","指定花名册组中的任何人都可以订阅和检索项目"}. +{"Anyone may associate leaf nodes with the collection","任何人都可以将叶节点与集合关联"}. {"Anyone may publish","任何人都可以发布"}. {"Anyone may subscribe and retrieve items","任何人都可以订阅和检索内容项"}. -{"Anyone with a presence subscription of both or from may subscribe and retrieve items","对全部或来源进行了状态订阅的任何人均可订阅并检索内容项"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","任何拥有 both 或 from 的在线状态订阅的用户都可以订阅和检索项目"}. {"Anyone with Voice","任何有发言权的人"}. {"Anyone","任何人"}. {"April","四月"}. @@ -82,7 +82,7 @@ {"Chatroom is stopped","群聊已停止"}. {"Chatrooms","群聊"}. {"Choose a username and password to register with this server","请选择要在此服务器中注册的用户名和密码"}. -{"Choose storage type of tables","请选择表单的存储类型"}. +{"Choose storage type of tables","选择表的存储类型"}. {"Choose whether to approve this entity's subscription.","选择是否批准此实体的订阅。"}. {"City","城市"}. {"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. @@ -94,7 +94,7 @@ {"Country","国家/地区"}. {"Current Discussion Topic","当前讨论话题"}. {"Database failure","数据库失败"}. -{"Database Tables Configuration at ","数据库表格配置位于 "}. +{"Database Tables Configuration at ","数据库表配置在 "}. {"Database","数据库"}. {"December","十二月"}. {"Default users as participants","默认用户为参与者"}. @@ -103,21 +103,21 @@ {"Delete User","删除用户"}. {"Deliver event notifications","传递事件通知"}. {"Deliver payloads with event notifications","用事件通知传递有效负载"}. -{"Disc only copy","仅磁盘复制"}. +{"Disc only copy","仅磁盘副本"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人,甚至是 XMPP 服务的管理员。"}. {"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. -{"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许有重复的群组"}. -{"Dynamically specify a replyto of the item publisher","为项目发布者动态指定一个 replyto"}. +{"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许重复的组"}. +{"Dynamically specify a replyto of the item publisher","动态指定项目发布者的 replyto"}. {"Edit Properties","编辑属性"}. {"Either approve or decline the voice request.","批准或拒绝发言请求。"}. {"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. {"ejabberd Multicast service","ejabberd 多重映射服务"}. -{"ejabberd Publish-Subscribe module","ejabberd 发布—订阅模块"}. +{"ejabberd Publish-Subscribe module","ejabberd 发布–订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. {"ejabberd vCard module","ejabberd vCard 模块"}. -{"ejabberd Web Admin","ejabberd Web 管理员"}. +{"ejabberd Web Admin","ejabberd Web 管理"}. {"ejabberd","ejabberd"}. {"Email Address","电子邮件地址"}. {"Email","电子邮件"}. @@ -134,14 +134,14 @@ {"Enter the text you see","请输入您看到的文本"}. {"Erlang XMPP Server","Erlang XMPP 服务器"}. {"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除 Jabber ID"}. -{"Export all tables as SQL queries to a file:","将所有表以 SQL 查询语句导出到文件:"}. +{"Export all tables as SQL queries to a file:","将所有表以 SQL 查询导出到文件:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器中所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","将主机中用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. {"External component failure","外部组件故障"}. {"External component timeout","外部组件超时"}. {"Failed to activate bytestream","激活字节流失败"}. -{"Failed to extract JID from your voice request approval","无法从您的发言请求批准中提取 JID"}. -{"Failed to map delegated namespace to external component","未能将代理命名空间映射到外部组件"}. +{"Failed to extract JID from your voice request approval","无法从发言请求批准中提取 JID"}. +{"Failed to map delegated namespace to external component","未能将委托命名空间映射到外部组件"}. {"Failed to parse HTTP response","HTTP 响应解析失败"}. {"Failed to process option '~s'","无法处理选项“~s”"}. {"Family Name","姓氏"}. @@ -149,7 +149,7 @@ {"February","二月"}. {"File larger than ~w bytes","文件大于 ~w 字节"}. {"Fill in the form to search for any matching XMPP User","填写表单以搜索任何匹配的 XMPP 用户"}. -{"Friday","星期五"}. +{"Friday","周五"}. {"From ~ts","来自 ~ts"}. {"Full List of Room Admins","群聊管理员完整列表"}. {"Full List of Room Owners","群聊所有者完整列表"}. @@ -160,12 +160,12 @@ {"Get Number of Registered Users","获取注册用户数"}. {"Get Pending","获取待处理"}. {"Get User Last Login Time","获取用户上次登录时间"}. -{"Get User Statistics","获取用户统计"}. +{"Get User Statistics","获取用户统计数据"}. {"Given Name","中间名"}. {"Grant voice to this person?","授予此用户发言权?"}. {"has been banned","已被封禁"}. -{"has been kicked because of a system shutdown","因系统关机而被踢出"}. -{"has been kicked because of an affiliation change","由于从属关系的改变而踢出"}. +{"has been kicked because of a system shutdown","因系统关闭而被踢出"}. +{"has been kicked because of an affiliation change","由于从属关系的更改而被踢出"}. {"has been kicked because the room has been changed to members-only","被踢出,因为群聊已更改为仅成员进入"}. {"has been kicked","已被踢出"}. {"Hat title","头衔标题"}. @@ -180,13 +180,13 @@ {"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. {"Import User from File at ","从以下位置的文件导入用户 "}. {"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件(XEP-0227)导入用户数据:"}. -{"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. -{"Import Users from Dir at ","从以下位置目录导入用户 "}. +{"Import users data from jabberd14 spool directory:","从 jabberd14 spool 目录导入用户数据:"}. +{"Import Users from Dir at ","从以下位置的目录导入用户 "}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. {"Improper domain part of 'from' attribute","“from”属性域名部分不正确"}. {"Improper message type","消息类型不正确"}. {"Incorrect CAPTCHA submit","提交的验证码不正确"}. -{"Incorrect data form","数据形式不正确"}. +{"Incorrect data form","数据表单不正确"}. {"Incorrect password","密码不正确"}. {"Incorrect value of 'action' attribute","“action”属性的值不正确"}. {"Incorrect value of 'action' in data form","数据表单中“action”的值不正确"}. @@ -202,11 +202,11 @@ {"IP addresses","IP 地址"}. {"is now known as","现在昵称为"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此群聊发送错误消息。参与者(~s)发送了错误消息(~s),被踢出了群聊"}. -{"It is not allowed to send private messages of type \"groupchat\"","不允许发送“群聊”类型的私信"}. +{"It is not allowed to send private messages of type \"groupchat\"","不允许发送“groupchat”类型的私信"}. {"It is not allowed to send private messages to the conference","不允许向群聊发送私信"}. {"Jabber ID","Jabber ID"}. {"January","一月"}. -{"JID normalization denied by service policy","JID 规范化被服务策略拒绝"}. +{"JID normalization denied by service policy","服务策略拒绝 JID 规范化"}. {"JID normalization failed","JID 规范化失败"}. {"Joined MIX channels of ~ts","加入了 ~ts 的 MIX 频道"}. {"Joined MIX channels:","加入了 MIX 频道:"}. @@ -223,7 +223,8 @@ {"leaves the room","离开群聊"}. {"List of users with hats","有头衔用户的列表"}. {"List users with hats","有头衔用户列表"}. -{"Logging","正在记录"}. +{"Logged Out","已登出"}. +{"Logging","日志记录"}. {"Make participants list public","公开参与者列表"}. {"Make room CAPTCHA protected","让群聊受验证码保护"}. {"Make room members-only","让群聊仅成员进入"}. @@ -232,20 +233,20 @@ {"Make room persistent","让群聊持续存在"}. {"Make room public searchable","让群聊可公开搜索"}. {"Malformed username","用户名格式不正确"}. -{"MAM preference modification denied by service policy","MAM 偏好被服务策略拒绝"}. +{"MAM preference modification denied by service policy","服务策略拒绝修改 MAM 首选项"}. {"March","三月"}. {"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要持久化的最大项目数 #,或“max”表示除服务器施加的最大值之外没有特定限制"}. -{"Max payload size in bytes","最大有效负载字节数"}. +{"Max payload size in bytes","最大有效负载大小(字节)"}. {"Maximum file size","最大文件大小"}. {"Maximum Number of History Messages Returned by Room","群聊返回的聊天记录消息的最大值"}. {"Maximum number of items to persist","要持久化的最大项目数"}. -{"Maximum Number of Occupants","最大占用人数"}. +{"Maximum Number of Occupants","最大参与者人数"}. {"May","五月"}. {"Membership is required to enter this room","进入此群聊需要成员资格"}. {"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住密码,或将密码写在纸上,放在安全的地方。在 XMPP 中,如果您忘记密码,没有自动恢复密码的方法。"}. {"Mere Availability in XMPP (No Show Value)","XMPP 中的可用性(无显示值)"}. -{"Message body","消息主体"}. -{"Message not found in forwarded payload","转发的有效载荷中找不到消息"}. +{"Message body","消息正文"}. +{"Message not found in forwarded payload","在转发的有效负载中找不到消息"}. {"Messages from strangers are rejected","拒绝来自陌生人的消息"}. {"Messages of type headline","标题类型的消息"}. {"Messages of type normal","普通类型的消息"}. @@ -255,7 +256,7 @@ {"Moderators Only","仅主持人"}. {"Moderator","主持人"}. {"Module failed to handle the query","模块无法处理查询"}. -{"Monday","星期一"}. +{"Monday","周一"}. {"Multicast","多重映射"}. {"Multiple elements are not allowed by RFC6121","按照 RFC6121,多个 元素是不允许的"}. {"Multi-User Chat","多用户聊天"}. @@ -278,24 +279,24 @@ {"No child elements found","没有找到子元素"}. {"No data form found","没有找到数据表单"}. {"No Data","没有数据"}. -{"No features available","没有可用特征"}. +{"No features available","没有可用功能"}. {"No element found","未找到 元素"}. -{"No hook has processed this command","没有任何钩子已处理此命令"}. -{"No info about last activity found","未找到上次活动的信息"}. +{"No hook has processed this command","没有钩子处理此命令"}. +{"No info about last activity found","未找到有关上次活动的信息"}. {"No 'item' element found","未找到“item”元素"}. {"No items found in this query","在此查询中找不到任何项目"}. -{"No limit","不限"}. +{"No limit","没有限制"}. {"No module is handling this query","没有模块正在处理此查询"}. {"No node specified","未指定节点"}. {"No 'password' found in data form","在数据表单中找不到“password”"}. {"No 'password' found in this query","在此查询中找不到“password”"}. {"No 'path' found in data form","在数据表单中找不到“path”"}. {"No pending subscriptions found","未找到待处理的订阅"}. -{"No privacy list with this name found","未找到带此名称的隐私列表"}. -{"No private data found in this query","此查询中未发现私有数据"}. -{"No running node found","没有找到运行中的节点"}. +{"No privacy list with this name found","未找到具有此名称的隐私列表"}. +{"No private data found in this query","在此查询中找不到专用数据"}. +{"No running node found","找不到正在运行的节点"}. {"No services available","无可用服务"}. -{"No statistics found for this item","未找到此项的统计数据"}. +{"No statistics found for this item","未找到此项目的统计数据"}. {"No 'to' attribute found in the invitation","邀请中未找到“to”属性"}. {"Nobody","没有人"}. {"Node already exists","节点已存在"}. @@ -310,92 +311,92 @@ {"Not allowed","不允许"}. {"Not Found","没有找到"}. {"Not subscribed","未订阅"}. -{"Notify subscribers when items are removed from the node","当从节点删除内容条目时通知订阅者"}. -{"Notify subscribers when the node configuration changes","当节点设置改变时通知订阅者"}. -{"Notify subscribers when the node is deleted","当节点被删除时通知订阅者"}. +{"Notify subscribers when items are removed from the node","从节点中移除项目时通知订阅者"}. +{"Notify subscribers when the node configuration changes","节点配置更改时通知订阅者"}. +{"Notify subscribers when the node is deleted","删除节点时通知订阅者"}. {"November","十一月"}. {"Number of answers required","所需答案数量"}. -{"Number of occupants","占用人数"}. +{"Number of occupants","参与者人数"}. {"Number of Offline Messages","离线消息数量"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. {"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","等待多少秒后自动清除项目,“max”表示除服务器施加的最大值外没有特定限制"}. -{"Occupants are allowed to invite others","允许占用者邀请别人"}. -{"Occupants are allowed to query others","允许占用者查询别人"}. -{"Occupants May Change the Subject","占用者可以更改话题"}. +{"Occupants are allowed to invite others","允许参与者邀请别人"}. +{"Occupants are allowed to query others","允许参与者查询别人"}. +{"Occupants May Change the Subject","参与者可以更改话题"}. {"October","十月"}. {"OK","确定"}. {"Old Password:","旧密码:"}. {"Online Users","在线用户"}. {"Online","在线"}. -{"Only collection node owners may associate leaf nodes with the collection","只有集合节点所有者可以将叶子节点与集合关联"}. -{"Only deliver notifications to available users","仅将通知发送给可发送的用户"}. +{"Only collection node owners may associate leaf nodes with the collection","只有集合节点所有者可以将叶节点与集合关联"}. +{"Only deliver notifications to available users","仅向在线用户发送通知"}. {"Only or tags are allowed","仅允许 标签"}. {"Only element is allowed in this query","此查询中只允许 元素"}. {"Only members may query archives of this room","只有成员才能查询此群聊的存档"}. -{"Only moderators and participants are allowed to change the subject in this room","只有主持人和参与者才允许在此群聊更改话题"}. -{"Only moderators are allowed to change the subject in this room","只有主持人才允许在此群聊更改话题"}. -{"Only moderators are allowed to retract messages","只有主持人才允许撤回消息"}. +{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者在此群聊更改话题"}. +{"Only moderators are allowed to change the subject in this room","只允许主持人在此群聊更改话题"}. +{"Only moderators are allowed to retract messages","只允许主持人撤回消息"}. {"Only moderators can approve voice requests","只有主持人才能批准发言请求"}. -{"Only occupants are allowed to send messages to the conference","只有占用者才允许向群聊发送消息"}. -{"Only occupants are allowed to send queries to the conference","只有占用者才允许向群聊发送查询"}. -{"Only publishers may publish","只有发布人可以发布"}. -{"Only service administrators are allowed to send service messages","只有服务管理员才允许发送服务消息"}. -{"Only those on a whitelist may associate leaf nodes with the collection","仅白名单用户可以将叶节点与集合关联"}. -{"Only those on a whitelist may subscribe and retrieve items","仅白名单用户可以订阅和检索内容项"}. +{"Only occupants are allowed to send messages to the conference","只允许参与者向群聊发送消息"}. +{"Only occupants are allowed to send queries to the conference","只允许参与者向群聊发送查询"}. +{"Only publishers may publish","只有发布者才能发布"}. +{"Only service administrators are allowed to send service messages","只允许服务管理员发送服务消息"}. +{"Only those on a whitelist may associate leaf nodes with the collection","只有白名单上的那些可以将叶节点与集合关联"}. +{"Only those on a whitelist may subscribe and retrieve items","只有白名单上的那些才可以订阅和检索项目"}. {"Organization Name","组织名称"}. {"Organization Unit","组织单位"}. {"Other Modules Available:","其他可用模块:"}. -{"Outgoing s2s Connections","出站 s2s 连接"}. +{"Outgoing s2s Connections","传出 s2s 连接"}. {"Owner privileges required","需要所有者权限"}. -{"Packet relay is denied by service policy","包中继被服务策略拒绝"}. +{"Packet relay is denied by service policy","服务策略拒绝数据包中继"}. {"Participant ID","参与者 ID"}. {"Participant","参与者"}. {"Password Verification","密码验证"}. {"Password Verification:","密码验证:"}. {"Password","密码"}. {"Password:","密码:"}. -{"Path to Dir","目录的路径"}. +{"Path to Dir","目录路径"}. {"Path to File","文件路径"}. -{"Payload semantic type information","有效载荷语义类型信息"}. +{"Payload semantic type information","有效负载语义类型信息"}. {"Period: ","持续时间: "}. -{"Persist items to storage","持久化内容条目"}. +{"Persist items to storage","将项目持久化到存储"}. {"Persistent","持久"}. {"Ping query is incorrect","Ping 查询不正确"}. {"Ping","Ping"}. -{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项仅将备份内置的 Mnesia 数据库。如果您正在使用 ODBC 模块,您还需要分别备份您的数据库。"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项只会备份内置的 Mnesia 数据库。如果使用 ODBC 模块,还需要单独备份 SQL 数据库。"}. {"Please, wait for a while before sending new voice request","请稍候,然后再发送新的发言请求"}. {"Pong","Pong"}. {"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许有“ask”属性"}. {"Present real Jabber IDs to","将用户真实 JID 显示给"}. {"Previous session not found","上一个会话未找到"}. -{"Previous session PID has been killed","上一个会话的 PID 已被杀掉"}. -{"Previous session PID has exited","上一个会话的 PID 已退出"}. -{"Previous session PID is dead","上一个会话的 PID 已死"}. -{"Previous session timed out","上一个会话已超时"}. +{"Previous session PID has been killed","上一个会话 PID 已终止"}. +{"Previous session PID has exited","上一个会话 PID 已退出"}. +{"Previous session PID is dead","上一个会话 PID 已失效"}. +{"Previous session timed out","上一个会话超时"}. {"private, ","私人, "}. {"Public","公开"}. {"Publish model","发布模型"}. -{"Publish-Subscribe","发布—订阅"}. +{"Publish-Subscribe","发布–订阅"}. {"PubSub subscriber request","PubSub 订阅者请求"}. -{"Purge all items when the relevant publisher goes offline","相关发布人离线后清除所有选项"}. -{"Push record not found","没有找到推送记录"}. +{"Purge all items when the relevant publisher goes offline","相关发布者离线后清除所有项目"}. +{"Push record not found","推送记录未找到"}. {"Queries to the conference members are not allowed in this room","不允许在此群聊中查询群聊成员"}. {"Query to another users is forbidden","禁止查询其他用户"}. -{"RAM and disc copy","内存与磁盘复制"}. -{"RAM copy","内存(RAM)复制"}. -{"Really delete message of the day?","确实要删除每日消息吗?"}. +{"RAM and disc copy","RAM 和磁盘副本"}. +{"RAM copy","RAM 副本"}. +{"Really delete message of the day?","是否确定删除每日消息?"}. {"Receive notification from all descendent nodes","接收所有后代节点的通知"}. -{"Receive notification from direct child nodes only","仅接收所有直接子节点的通知"}. -{"Receive notification of new items only","仅接收新内容项的通知"}. +{"Receive notification from direct child nodes only","仅接收直接子节点的通知"}. +{"Receive notification of new items only","仅接收新项目的通知"}. {"Receive notification of new nodes only","仅接收新节点的通知"}. {"Recipient is not in the conference room","接收者不在群聊"}. {"Register an XMPP account","注册 XMPP 账号"}. {"Register","注册"}. -{"Remote copy","远程复制"}. +{"Remote copy","远程副本"}. {"Remove a hat from a user","移除用户头衔"}. {"Remove User","移除用户"}. -{"Replaced by new connection","被新的连接替换"}. +{"Replaced by new connection","替换为新连接"}. {"Request has timed out","请求已超时"}. {"Request is ignored","请求被忽略"}. {"Requested role","请求的角色"}. @@ -410,47 +411,48 @@ {"Roles for which Presence is Broadcasted","广播在线状态的角色"}. {"Roles that May Send Private Messages","可以发送私信的角色"}. {"Room Configuration","群聊配置"}. -{"Room creation is denied by service policy","群聊创建被服务策略拒绝"}. +{"Room creation is denied by service policy","服务策略拒绝群聊创建"}. {"Room description","群聊描述"}. -{"Room Occupants","群聊占用者"}. +{"Room Occupants","群聊参与者"}. {"Room terminates","群聊终止"}. {"Room title","群聊标题"}. {"Roster groups allowed to subscribe","允许订阅的花名册组"}. {"Roster size","花名册大小"}. -{"Running Nodes","运行中的节点"}. +{"Running Nodes","正在运行的节点"}. {"~s invites you to the room ~s","~s 邀请您加入群聊 ~s"}. -{"Saturday","星期六"}. +{"Saturday","周六"}. {"Search from the date","从日期搜索"}. {"Search Results for ","搜索结果 "}. {"Search the text","搜索文本"}. {"Search until the date","搜索截至日期"}. {"Search users in ","在以下位置搜索用户 "}. -{"Send announcement to all online users on all hosts","发送通知给所有主机的在线用户"}. -{"Send announcement to all online users","发送通知给所有在线用户"}. -{"Send announcement to all users on all hosts","发送通知给所有主机上的所有用户"}. -{"Send announcement to all users","发送通知给所有用户"}. +{"Send announcement to all online users on all hosts","向所有主机上的所有在线用户发送公告"}. +{"Send announcement to all online users","向所有在线用户发送公告"}. +{"Send announcement to all users on all hosts","向所有主机上的所有用户发送公告"}. +{"Send announcement to all users","向所有用户发送公告"}. {"September","九月"}. {"Server:","服务器:"}. {"Service list retrieval timed out","服务列表检索超时"}. {"Session state copying timed out","会话状态复制超时"}. {"Set message of the day and send to online users","设置每日消息并发送给在线用户"}. {"Set message of the day on all hosts and send to online users","在所有主机上设置每日消息并发送给在线用户"}. -{"Shared Roster Groups","共享的花名册组群"}. -{"Show Integral Table","显示完整列表"}. -{"Show Ordinary Table","显示普通列表"}. +{"Shared Roster Groups","共享的花名册组"}. +{"Show Integral Table","显示完整表"}. +{"Show Occupants Join/Leave","显示参与者加入/离开"}. +{"Show Ordinary Table","显示普通表"}. {"Shut Down Service","关闭服务"}. {"SOCKS5 Bytestreams","SOCKS5 字节流"}. {"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","某些 XMPP 客户端可以将您的密码存储在计算机中,但出于安全考虑,您应该仅在个人计算机中存储密码。"}. -{"Sources Specs:","源参数:"}. -{"Specify the access model","指定访问范例"}. +{"Sources Specs:","源规格:"}. +{"Specify the access model","指定访问模型"}. {"Specify the event message type","指定事件消息类型"}. -{"Specify the publisher model","指定发布人范例"}. -{"Stanza id is not valid","节 id 无效"}. +{"Specify the publisher model","指定发布者模型"}. +{"Stanza id is not valid","节 ID 无效"}. {"Stanza ID","节 ID"}. -{"Statically specify a replyto of the node owner(s)","静态指定节点所有者的回复"}. -{"Stopped Nodes","已经停止的节点"}. -{"Store binary backup:","存储为二进制备份:"}. -{"Store plain text backup:","存储为普通文本备份:"}. +{"Statically specify a replyto of the node owner(s)","静态指定节点所有者的 replyto"}. +{"Stopped Nodes","已停止的节点"}. +{"Store binary backup:","存储二进制备份:"}. +{"Store plain text backup:","存储明文备份:"}. {"Stream management is already enabled","流管理已启用"}. {"Stream management is not enabled","流管理未启用"}. {"Subject","话题"}. @@ -459,12 +461,12 @@ {"Subscribers may publish","订阅者可以发布"}. {"Subscription requests must be approved and only subscribers may retrieve items","订阅请求必须得到批准,只有订阅者才能检索项目"}. {"Subscriptions are not allowed","不允许订阅"}. -{"Sunday","星期天"}. +{"Sunday","周日"}. {"Text associated with a picture","与图片相关的文字"}. {"Text associated with a sound","与声音相关的文字"}. {"Text associated with a video","与视频相关的文字"}. {"Text associated with speech","与语音相关的文字"}. -{"That nickname is already in use by another occupant","该昵称已被另一占用者使用了"}. +{"That nickname is already in use by another occupant","该昵称已被另一参与者使用了"}. {"That nickname is registered by another person","该昵称已被另一用户注册了"}. {"The account already exists","此账号已存在"}. {"The account was not unregistered","此账号未注销"}. @@ -472,35 +474,35 @@ {"The CAPTCHA is valid.","验证码有效。"}. {"The CAPTCHA verification has failed","验证码检查失败"}. {"The captcha you entered is wrong","您输入的验证码错误"}. -{"The child nodes (leaf or collection) associated with a collection","关联集合的字节点(叶子或集合)"}. -{"The collections with which a node is affiliated","加入结点的集合"}. +{"The child nodes (leaf or collection) associated with a collection","与集合关联的子节点(叶或集合)"}. +{"The collections with which a node is affiliated","节点所属的集合"}. {"The DateTime at which a leased subscription will end or has ended","租赁订阅将结束或已结束的日期时间"}. -{"The datetime when the node was created","节点创建的日期时间"}. -{"The default language of the node","该节点的默认语言"}. +{"The datetime when the node was created","创建节点的日期时间"}. +{"The default language of the node","节点的默认语言"}. {"The feature requested is not supported by the conference","群聊不支持请求的功能"}. {"The JID of the node creator","节点创建者的 JID"}. -{"The JIDs of those to contact with questions","问题联系人的 JID"}. -{"The JIDs of those with an affiliation of owner","拥有所有者从属关系的用户的 JID"}. +{"The JIDs of those to contact with questions","有问题要联系的人的 JID"}. +{"The JIDs of those with an affiliation of owner","与所有者的从属关系有关的用户 JID"}. {"The JIDs of those with an affiliation of publisher","与发布者的从属关系有关的用户 JID"}. -{"The list of all online users","所有在线用户列表"}. -{"The list of all users","所有用户列表"}. +{"The list of all online users","所有在线用户的列表"}. +{"The list of all users","所有用户的列表"}. {"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的 JID 列表"}. -{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合相关联的最大子节点数,“max”表示除服务器施加的最大值外没有特定限制"}. -{"The minimum number of milliseconds between sending any two notification digests","发送任何两个通知摘要之间的最小毫秒数"}. -{"The name of the node","该节点的名称"}. -{"The node is a collection node","该节点是集合节点"}. -{"The node is a leaf node (default)","该节点是叶子节点(默认)"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合关联的子节点的最大数量,或“max”表示除服务器施加的最大值外没有特定限制"}. +{"The minimum number of milliseconds between sending any two notification digests","发送任意两个通知摘要之间的最小毫秒数"}. +{"The name of the node","节点的名称"}. +{"The node is a collection node","节点是集合节点"}. +{"The node is a leaf node (default)","节点是叶节点(默认)"}. {"The NodeID of the relevant node","相关节点的 NodeID"}. -{"The number of pending incoming presence subscription requests","待处理的传入状态订阅请求数"}. -{"The number of subscribers to the node","该节点的订阅用户数"}. -{"The number of unread or undelivered messages","未读或未发送的消息数"}. +{"The number of pending incoming presence subscription requests","待处理的传入在线状态订阅请求数"}. +{"The number of subscribers to the node","节点的订阅者数量"}. +{"The number of unread or undelivered messages","未读或未传递的消息数"}. {"The password contains unacceptable characters","密码包含不可接受的字符"}. -{"The password is too weak","密码强度太弱"}. +{"The password is too weak","密码太弱"}. {"the password is","密码是"}. {"The password of your XMPP account was successfully changed.","您的 XMPP 账号密码已成功更改。"}. {"The password was not changed","密码未更改"}. -{"The passwords are different","密码不一致"}. -{"The presence states for which an entity wants to receive notifications","实体要为其接收通知的状态"}. +{"The passwords are different","密码不同"}. +{"The presence states for which an entity wants to receive notifications","实体要接收通知的在线状态"}. {"The query is only allowed from local users","仅允许本地用户查询"}. {"The query must not contain elements","查询不能包含 元素"}. {"The room subject can be modified by participants","群聊话题可由参与者修改"}. @@ -516,30 +518,30 @@ {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:MacBeth 和 Macbeth 都是 macbeth。"}. {"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器中注册 XMPP 账号,您的 JID(Jabber ID)的格式为:用户名@服务器。请仔细阅读说明以正确填写字段。"}. {"This page allows to unregister an XMPP account in this XMPP server.","本页面允许在此 XMPP 服务器中注销 XMPP 账号。"}. -{"This room is not anonymous","此群聊不是匿名的"}. +{"This room is not anonymous","此群聊是非匿名的"}. {"This service can not process the address: ~s","此服务无法处理地址:~s"}. -{"Thursday","星期四"}. +{"Thursday","周四"}. {"Time delay","时间延迟"}. {"Timed out waiting for stream resumption","等待流恢复超时"}. {"To register, visit ~s","要注册,请访问 ~s"}. -{"To ~ts","发送到~ts"}. -{"Token TTL","TTL 令牌"}. -{"Too many active bytestreams","活跃的字节流太多"}. +{"To ~ts","到 ~ts"}. +{"Token TTL","令牌 TTL"}. +{"Too many active bytestreams","活动字节流太多"}. {"Too many CAPTCHA requests","验证码请求太多"}. {"Too many child elements","太多子元素"}. {"Too many elements","太多 元素"}. {"Too many elements","太多 元素"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自 IP 地址(~p)的(~s)失败认证太多。将在 UTC 时间 ~s 解除对该地址的封锁"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","有太多 (~p) 失败的身份验证来自此 IP 地址 (~s),将在 UTC 时间 ~s 解除对该地址的屏蔽"}. {"Too many receiver fields were specified","指定的接收者字段太多"}. -{"Too many unacked stanzas","未被确认的节太多"}. +{"Too many unacked stanzas","太多未确认的节"}. {"Too many users in this conference","此群聊中的用户太多"}. -{"Traffic rate limit is exceeded","已经超过传输率限制"}. +{"Traffic rate limit is exceeded","超过流量速率限制"}. {"~ts's Offline Messages Queue","~ts 的离线消息队列"}. -{"Tuesday","星期二"}. +{"Tuesday","周二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. -{"Unable to register route on existing local domain","在已存在的本地域上无法注册路由"}. -{"Unauthorized","未认证的"}. -{"Unexpected action","意外行为"}. +{"Unable to register route on existing local domain","无法在现有本地域上注册路由"}. +{"Unauthorized","未经授权"}. +{"Unexpected action","意外的操作"}. {"Unexpected error condition: ~p","意外错误条件:~p"}. {"Uninstall","卸载"}. {"Unregister an XMPP account","注销 XMPP 账号"}. @@ -548,8 +550,8 @@ {"Unsupported version","不支持的版本"}. {"Update message of the day (don't send)","更新每日消息(不发送)"}. {"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. -{"Update specs to get modules source, then install desired ones.","更新参数获取模块源,然后安装所需的模块。"}. -{"Update Specs","更新参数"}. +{"Update specs to get modules source, then install desired ones.","更新规格以获取模块源,然后安装所需的模块。"}. +{"Update Specs","更新规格"}. {"Upgrade","升级"}. {"URL for Archived Discussion Logs","存档讨论日志的 URL"}. {"User already exists","用户已存在"}. @@ -566,33 +568,33 @@ {"Users","用户"}. {"User","用户"}. {"Value 'get' of 'type' attribute is not allowed","不允许“type”属性的“get”值"}. -{"Value of '~s' should be boolean","'~s' 的值应为布尔型"}. -{"Value of '~s' should be datetime string","'~s' 的值应为日期时间字符串"}. -{"Value of '~s' should be integer","'~s' 的值应为整数"}. +{"Value of '~s' should be boolean","“~s”的值应为布尔值"}. +{"Value of '~s' should be datetime string","“~s”的值应为日期时间字符串"}. +{"Value of '~s' should be integer","“~s”的值应为整数"}. {"Value 'set' of 'type' attribute is not allowed","不允许“type”属性的“set”值"}. {"vCard User Search","vCard 用户搜索"}. {"View joined MIX channels","查看已加入的 MIX 频道"}. {"Virtual Hosts","虚拟主机"}. {"Visitors are not allowed to change their nicknames in this room","不允许访客在此群聊中更改其昵称"}. -{"Visitors are not allowed to send messages to all occupants","不允许访客向所有占用者发送信息"}. +{"Visitors are not allowed to send messages to all occupants","不允许访客向所有参与者发送消息"}. {"Visitor","访客"}. {"Voice requests are disabled in this conference","此群聊中禁用发言请求"}. {"Voice request","发言请求"}. -{"Wednesday","星期三"}. -{"When a new subscription is processed and whenever a subscriber comes online","当新的订阅被处理和当订阅者上线"}. -{"When a new subscription is processed","当新的订阅被处理"}. -{"When to send the last published item","何时发送最新发布的内容条目"}. -{"Whether an entity wants to receive an XMPP message body in addition to the payload format","除有效载荷格式外,实体是否还希望接收 XMPP 消息正文"}. -{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","实体是否要接收通知的摘要(汇总)或单独接收所有通知"}. +{"Wednesday","周三"}. +{"When a new subscription is processed and whenever a subscriber comes online","处理新订阅时和订阅者上线时"}. +{"When a new subscription is processed","处理新订阅时"}. +{"When to send the last published item","何时发送最后发布的项目"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","除有效负载格式外,实体是否还希望接收 XMPP 消息正文"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","实体是要接收通知摘要(汇总) 还是要单独接收所有通知"}. {"Whether an entity wants to receive or disable notifications","实体是否要接收或禁用通知"}. {"Whether owners or publisher should receive replies to items","所有者或发布者是否应收到对项目的回复"}. -{"Whether the node is a leaf (default) or a collection","节点是叶子(默认)还是集合"}. +{"Whether the node is a leaf (default) or a collection","节点是叶(默认)还是集合"}. {"Whether to allow subscriptions","是否允许订阅"}. -{"Whether to make all subscriptions temporary, based on subscriber presence","是否根据订阅者的存在将所有订阅设为临时"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","是否根据订阅者的在线状态将所有订阅设为临时订阅"}. {"Whether to notify owners about new subscribers and unsubscribes","是否通知所有者新的订阅者和退订者"}. {"Who can send private messages","谁可以发送私信"}. -{"Who may associate leaf nodes with a collection","谁可以将叶子节点与集合关联"}. -{"Wrong parameters in the web formulary","网络配方中的参数错误"}. +{"Who may associate leaf nodes with a collection","谁可以将叶节点与集合关联"}. +{"Wrong parameters in the web formulary","web 公式中的参数错误"}. {"Wrong xmlns","错误的 xmlns"}. {"XMPP Account Registration","XMPP 账号注册"}. {"XMPP Domains","XMPP 域"}. @@ -600,7 +602,7 @@ {"XMPP Show Value of Chat","XMPP 的聊天显示值"}. {"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND(请勿打扰)显示值"}. {"XMPP Show Value of XA (Extended Away)","XMPP 的 XA(延长离开)显示值"}. -{"XMPP URI of Associated Publish-Subscribe Node","关联发布—订阅节点的 XMPP URI"}. +{"XMPP URI of Associated Publish-Subscribe Node","关联发布–订阅节点的 XMPP URI"}. {"You are being removed from the room because of a system shutdown","由于系统关闭,您将会从群聊中移除"}. {"You are not allowed to send private messages","不允许您发送私信"}. {"You are not joined to the channel","您未加入频道"}. From c42cb2bbac9096ea680b1cd93d70db47175a0878 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 10:34:26 +0200 Subject: [PATCH 0705/1302] Update Italian translation (thanks to Ermete Melchiorre) --- priv/msgs/it.msg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index b50e82688..79674666c 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -223,6 +223,7 @@ {"leaves the room","esce dalla stanza"}. {"List of users with hats","Elenco degli utenti con cappelli"}. {"List users with hats","Elenca gli utenti con cappelli"}. +{"Logged Out","Disconnesso"}. {"Logging","Registrazione"}. {"Make participants list public","Rendere pubblica la lista dei partecipanti"}. {"Make room CAPTCHA protected","Rendere la stanza protetta da CAPTCHA"}. @@ -437,6 +438,7 @@ {"Set message of the day on all hosts and send to online users","Impostare il messaggio del giorno (MOTD) su tutti gli host e inviarlo agli utenti online"}. {"Shared Roster Groups","Gruppi di liste di contatti comuni"}. {"Show Integral Table","Mostrare la tabella integrale"}. +{"Show Occupants Join/Leave","Mostra gli occupanti che si uniscono/escono"}. {"Show Ordinary Table","Mostrare la tabella normale"}. {"Shut Down Service","Terminare il servizio"}. {"SOCKS5 Bytestreams","SOCKS5 flussi di byte"}. @@ -531,6 +533,7 @@ {"Too many elements","Troppi elementi "}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Troppe (~p) autenticazioni non riuscite da questo indirizzo IP (~s). L'indirizzo verrà sbloccato alle ~s UTC"}. {"Too many receiver fields were specified","Sono stati specificati troppi campi del ricevitore"}. +{"Too many unacked stanzas","Troppe stanze non riconosciute"}. {"Too many users in this conference","Troppi utenti in questa conferenza"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. {"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}. From e274bcc87d0c787100775469b6664f231b80608c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 10:34:52 +0200 Subject: [PATCH 0706/1302] Update Portuguese (Brazil) translation (thanks to Wellington Uemura) --- priv/msgs/pt-br.msg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 76275ac63..d23f55ac0 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -223,6 +223,7 @@ {"leaves the room","Sair da sala"}. {"List of users with hats","Lista dos usuários com chapéus"}. {"List users with hats","Lista os usuários com chapéus"}. +{"Logged Out","Desconectado"}. {"Logging","Registrando no log"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a senha da sala"}. @@ -437,6 +438,7 @@ {"Set message of the day on all hosts and send to online users","Definir mensagem do dia em todos os hosts e enviar para os usuários online"}. {"Shared Roster Groups","Grupos Shared Roster"}. {"Show Integral Table","Mostrar Tabela Integral"}. +{"Show Occupants Join/Leave","Mostrar a entrada e a saída de ocupantes"}. {"Show Ordinary Table","Mostrar Tabela Ordinária"}. {"Shut Down Service","Parar Serviço"}. {"SOCKS5 Bytestreams","Bytestreams SOCKS5"}. From 7a333eecbe3e0b931c70c64c3055fee29478e2dd Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 10:35:53 +0200 Subject: [PATCH 0707/1302] Update more translations translation --- mix.lock | 1 + priv/msgs/ca.msg | 2 ++ priv/msgs/es.msg | 2 ++ priv/msgs/uk.msg | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 9c9a77393..9238811a6 100644 --- a/mix.lock +++ b/mix.lock @@ -4,6 +4,7 @@ "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, + "ejabberd_po": {:git, "https://github.com/processone/ejabberd-po.git", "6ef974cbc28e1a52d7a35fd4d9081d175d0ac10d", []}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index b90cbbd56..4663f95f0 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -223,6 +223,7 @@ {"leaves the room","surt de la sala"}. {"List of users with hats","Llista d'usuaris amb barrets"}. {"List users with hats","Llista d'usuaris amb barrets"}. +{"Logged Out","Desconectat"}. {"Logging","Registre"}. {"Make participants list public","Crear una llista de participants pública"}. {"Make room CAPTCHA protected","Crear una sala protegida per CAPTCHA"}. @@ -437,6 +438,7 @@ {"Set message of the day on all hosts and send to online users","Escriure missatge del dia en tots els hosts i enviar-ho als usuaris connectats"}. {"Shared Roster Groups","Grups de contactes compartits"}. {"Show Integral Table","Mostrar Taula Integral"}. +{"Show Occupants Join/Leave","Mostrar Entrades/Eixides dels Ocupants"}. {"Show Ordinary Table","Mostrar Taula Ordinaria"}. {"Shut Down Service","Apager el Servei"}. {"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index be06874f6..e51c3facc 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -223,6 +223,7 @@ {"leaves the room","sale de la sala"}. {"List of users with hats","Lista de usuarios con sombreros"}. {"List users with hats","Listar usuarios con sombreros"}. +{"Logged Out","Desconectad@"}. {"Logging","Histórico de mensajes"}. {"Make participants list public","La lista de participantes es pública"}. {"Make room CAPTCHA protected","Proteger la sala con CAPTCHA"}. @@ -437,6 +438,7 @@ {"Set message of the day on all hosts and send to online users","Poner mensaje del día en todos los dominios y enviar a los usuarios conectados"}. {"Shared Roster Groups","Grupos Compartidos"}. {"Show Integral Table","Mostrar Tabla Integral"}. +{"Show Occupants Join/Leave","Mostrar personas activas Entrar/Salir"}. {"Show Ordinary Table","Mostrar Tabla Ordinaria"}. {"Shut Down Service","Detener el servicio"}. {"SOCKS5 Bytestreams","SOCKS5 Bytestreams"}. diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 32e0746a6..5992bfdf1 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -222,7 +222,7 @@ {"Logging","Журналювання"}. {"Make participants list public","Зробити список учасників видимим всім"}. {"Make room CAPTCHA protected","Зробити кімнату захищеною капчею"}. -{"Make room members-only","Кімната тільки для зареєтрованых учасників"}. +{"Make room members-only","Кімната тільки для зареєтрованих учасників"}. {"Make room moderated","Зробити кімнату модерованою"}. {"Make room password protected","Зробити кімнату захищеною паролем"}. {"Make room persistent","Зробити кімнату постійною"}. From 68e69debffb4d30216ebe360111dfef6ba6dd4be Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 11:00:09 +0200 Subject: [PATCH 0708/1302] Update man page to 24.07 --- man/ejabberd.yml.5 | 79 +++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index a4df32226..d34a84918 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 06/27/2024 +.\" Date: 07/18/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "06/27/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "07/18/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.06/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.07/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 24\&.06\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 24\&.07\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR .RS 4 @@ -1150,7 +1150,7 @@ This option can be used to tune tick time parameter of Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/24\&.06/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/24\&.07/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1772,7 +1772,7 @@ or if the latter is not set\&. .RE .PP -\fBsql_server 🟤\fR: \fIHost | IP Address | ODBC Connection String | Unix Socket Path\fR +\fBsql_server\fR: \fIHost | IP Address | ODBC Connection String | Unix Socket Path\fR .RS 4 \fINote\fR about this option: improved in 24\&.06\&. The hostname or IP address of the SQL server\&. For @@ -1855,13 +1855,19 @@ to allow all proxies, or specify a list of IPs, possibly with masks\&. The defau header if you enable this option as, otherwise, the client can set it itself and as a result the IP value cannot be trusted for security rules in ejabberd\&. .RE .PP -\fBupdate_sql_schema 🟤\fR: \fItrue | false\fR +\fBupdate_sql_schema\fR: \fItrue | false\fR .RS 4 \fINote\fR about this option: updated in 24\&.06\&. Allow ejabberd to update SQL schema\&. This option was added in ejabberd 23\&.10, and enabled by default since 24\&.06\&. The default value is \fItrue\fR\&. .RE .PP +\fBupdate_sql_schema_timeout 🟤\fR: \fItimeout()\fR +.RS 4 +\fINote\fR +about this option: added in 24\&.07\&. Time allocated to SQL schema update queries\&. The default value is set to 5 minutes\&. +.RE +.PP \fBuse_cache\fR: \fItrue | false\fR .RS 4 Enable or disable cache\&. The default is @@ -1913,7 +1919,12 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 24\&.06\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 24\&.07\&. The modules that changed in this version are marked with 🟤\&. +.SS "Elixir\&.ModPresenceDemo" +.sp +This is just a demonstration\&. +.sp +The module has no options\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -1946,7 +1957,7 @@ Details for some commands: .sp -1 .IP \(bu 2.3 .\} -\fIban\-acount\fR: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&. +\fIban_account\fR: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&. .RE .sp .RS 4 @@ -1971,7 +1982,7 @@ or similar\&. If you use other Operating System, place the file on the same dire .sp -1 .IP \(bu 2.3 .\} -\fIsrg\-create\fR: If you want to put a group Name with blankspaces, use the characters "\*(Aq and \*(Aq" to define when the Name starts and ends\&. See an example below\&. +\fIsrg_create\fR: If you want to put a group Name with blank spaces, use the characters "\*(Aq and \*(Aq" to define when the Name starts and ends\&. See an example below\&. .RE .sp The module has no options\&. @@ -2025,19 +2036,19 @@ With this call, the sessions of the local account which JID is boby@example\&.or .RS 4 .\} .nf -ejabberdctl vhost example\&.org ban\-account boby "Spammed rooms" +ejabberdctl vhost example\&.org ban_account boby "Spammed rooms" .fi .if n \{\ .RE .\} .sp -Call to srg\-create using double\-quotes and single\-quotes: +Call to srg_create using double\-quotes and single\-quotes: .sp .if n \{\ .RS 4 .\} .nf -ejabberdctl srg\-create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g1 +ejabberdctl srg_create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g1 .fi .if n \{\ .RE @@ -3541,7 +3552,7 @@ option, but applied to this module only\&. .PP \fBuser_mucsub_from_muc_archive\fR: \fItrue | false\fR .RS 4 -When this option is disabled, for each individual subscriber a separa mucsub message is stored\&. With this option enabled, when a user fetches archive virtual mucsub, messages are generated from muc archives\&. The default value is +When this option is disabled, for each individual subscriber a separate mucsub message is stored\&. With this option enabled, when a user fetches archive virtual mucsub, messages are generated from muc archives\&. The default value is \fIfalse\fR\&. .RE .RE @@ -3590,11 +3601,11 @@ is replaced with the hostname\&. The default value is \fBmatrix_id_as_jid\fR: \fItrue | false\fR .RS 4 If set to -\fIfalse\fR, all packets failing to be delivered via an XMPP server\-to\-server connection will then be routed to the Matrix gateway by translating a Jabber ID +\fItrue\fR, all packets failing to be delivered via an XMPP server\-to\-server connection will then be routed to the Matrix gateway by translating a Jabber ID \fIuser@matrixdomain\&.tld\fR to a Matrix user identifier \fI@user:matrixdomain\&.tld\fR\&. When set to -\fItrue\fR, messages must be explicitly sent to the matrix gateway service Jabber ID to be routed to a remote Matrix server\&. In this case, to send a message to Matrix user +\fIfalse\fR, messages must be explicitly sent to the matrix gateway service Jabber ID to be routed to a remote Matrix server\&. In this case, to send a message to Matrix user \fI@user:matrixdomain\&.tld\fR, the client must send a message to the JID \fIuser%\fR\fImatrixdomain\&.tld@matrix\&.myxmppdomain\fR\fI\&.tld\fR, where \fImatrix\&.myxmppdomain\&.tld\fR @@ -4405,7 +4416,7 @@ This option defines the number of service admins or room owners allowed to enter .PP \fBmax_users_presence\fR: \fINumber\fR .RS 4 -This option defines after how many users in the room, it is considered overcrowded\&. When a MUC room is considered overcrowed, presence broadcasts are limited to reduce load, traffic and excessive presence "storm" received by participants\&. The default value is +This option defines after how many users in the room, it is considered overcrowded\&. When a MUC room is considered overcrowded, presence broadcasts are limited to reduce load, traffic and excessive presence "storm" received by participants\&. The default value is \fI1000\fR\&. .RE .PP @@ -5013,9 +5024,9 @@ This option defines which access rule will be enforced to limit the maximum numb .PP \fBbounce_groupchat\fR: \fItrue | false\fR .RS 4 -This option is use the disable an optimisation that avoids bouncing error messages when groupchat messages could not be stored as offline\&. It will reduce chat room load, without any drawback in standard use cases\&. You may change default value only if you have a custom module which uses offline hook after +This option is use the disable an optimization that avoids bouncing error messages when groupchat messages could not be stored as offline\&. It will reduce chat room load, without any drawback in standard use cases\&. You may change default value only if you have a custom module which uses offline hook after \fImod_offline\fR\&. This option can be useful for both standard MUC and MucSub, but the bounce is much more likely to happen in the context of MucSub, so it is even more important to have it on large MucSub services\&. The default value is -\fIfalse\fR, meaning the optimisation is enabled\&. +\fIfalse\fR, meaning the optimization is enabled\&. .RE .PP \fBcache_life_time\fR: \fItimeout()\fR @@ -5949,7 +5960,7 @@ modules: .RE .SS "mod_push" .sp -This module implements the XMPP server\(cqs part of the push notification solution specified in XEP\-0357: Push Notifications\&. It does not generate, for example, APNS or FCM notifications directly\&. Instead, it\(cqs designed to work with so\-called "app servers" operated by third\-party vendors of mobile apps\&. Those app servers will usually trigger notification delivery to the user\(cqs mobile device using platform\-dependant backend services such as FCM or APNS\&. +This module implements the XMPP server\(cqs part of the push notification solution specified in XEP\-0357: Push Notifications\&. It does not generate, for example, APNS or FCM notifications directly\&. Instead, it\(cqs designed to work with so\-called "app servers" operated by third\-party vendors of mobile apps\&. Those app servers will usually trigger notification delivery to the user\(cqs mobile device using platform\-dependent backend services such as FCM or APNS\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -6176,6 +6187,24 @@ Set a welcome message that is sent to each newly registered account\&. The messa \fISubject\fR and text \fIBody\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_register: + welcome_message: + subject: "Welcome!" + body: |\- + Hi! + Welcome to this XMPP server +.fi +.if n \{\ +.RE +.\} .RE .RE .SS "mod_register_web" @@ -7119,7 +7148,7 @@ The protocol extension is deferred and seems like even a few clients that were s The module has no options\&. .SS "mod_stream_mgmt" .sp -This module adds support for XEP\-0198: Stream Management\&. This protocol allows active management of an XML stream between two XMPP entities, including features for stanza acknowledgements and stream resumption\&. +This module adds support for XEP\-0198: Stream Management\&. This protocol allows active management of an XML stream between two XMPP entities, including features for stanza acknowledgments and stream resumption\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -7131,7 +7160,7 @@ This module adds support for XEP\-0198: Stream Management\&. This protocol allow .PP \fBack_timeout\fR: \fItimeout()\fR .RS 4 -A time to wait for stanza acknowledgements\&. Setting it to +A time to wait for stanza acknowledgments\&. Setting it to \fIinfinity\fR effectively disables the timeout\&. The default value is \fI1\fR @@ -7804,7 +7833,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 24\&.06\&. +This section describes listeners options of ejabberd 24\&.07\&. .sp TODO .SH "AUTHOR" @@ -7812,13 +7841,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 24\&.06\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 24\&.07\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.06/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.07/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 20a01a25e43e6ede936d0ad5a2bbe3f0526585d3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 11:09:39 +0200 Subject: [PATCH 0709/1302] Update changelog --- CHANGELOG.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97bf5a430..16dbd77ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,58 @@ +## Version 24.07 + +#### Core + +- `ejabberd_options`: Add trailing `@` to `@VERSION@` parsing +- `mod_http_api`: Fix problem parsing tuples when using OTP 27 json library ([#4242](https://github.com/processone/ejabberd/issues/4242)) +- `mod_http_api`: Restore args conversion of `{"k":"v"}` to tuple lists +- `mod_matrix_gw`: Add misc:json_encode_With_kv_lists and use it in matrix sign function +- `mod_muc`: Output `muc#roominfo_avatarhash` in room disco info as per updated XEP-0486 ([#4234](https://github.com/processone/ejabberd/issues/4234)) +- `mod_muc`: Improve cross version handling of muc retractions +- `node_pep`: Add missing feature `item-ids` to node_pep +- `mod_register`: Send welcome message as `chat` too ([#4246](https://github.com/processone/ejabberd/issues/4246)) +- `ejabberd_hooks`: Support for ejabberd hook subscribers, useful for [mod_prometheus](https://github.com/processone/ejabberd-contrib/tree/master/mod_prometheus) +- `ejabberd.app`: Don't add `iex` to included_applications +- `make-installers`: Fix path in scripts in regular user install ([#4258](https://github.com/processone/ejabberd/issues/4258)) +- Test: New tests for API commands + +#### Documentation + +- `mod_matrix_gw`: Fix `matrix_id_as_jid` option documentation +- `mod_register`: Add example configuration of `welcome_message` option +- `mix.exs`: Add ejabberd example config files to the hex package +- Update `CODE_OF_CONDUCT.md` + +#### ext_mod + +- Fetch dependencies from hex.pm when mix is available +- files_to_path is deprecated, use compile_to_path +- Compile all Elixir files in a library with one function call +- Improve error result when problem compiling elixir file +- Handle case when contrib module has no `*.ex` and no `*.erl` +- `mix.exs`: Include Elixir's Logger in the OTP release, useful for [mod_libcluster](https://github.com/processone/ejabberd-contrib/tree/master/mod_libcluster) + +#### Logs + +- Print message when starting ejabberd application fails +- Use error_logger when printing startup failure message +- Use proper format depending on the formatter ([#4256](https://github.com/processone/ejabberd/issues/4256)) + +#### SQL + +- Add option `update_sql_schema_timeout` to allow schema update use longer timeouts +- Add ability to specify custom timeout for sql operations +- Allow to configure number of restart in `sql_transaction()` +- Make sql query in testsuite compatible with pg9.1 +- In `mysql.sql`, fix update instructions for the `archive` table, `origin_id` column ([#4259](https://github.com/processone/ejabberd/issues/4259)) + +#### WebAdmin + +- `ejabberd.yml.example`: Add `api_permissions` group for webadmin ([#4249](https://github.com/processone/ejabberd/issues/4249)) +- Don't use host from url in webadmin, prefer host used for authentication +- Fix number of accounts shown in the online-users page +- Fix crash when viewing old shared roster groups ([#4245](https://github.com/processone/ejabberd/issues/4245)) +- Support groupid with spaces when making shared roster result ([#4245](https://github.com/processone/ejabberd/issues/4245)) + ## Version 24.06 #### Core From 9bd1b39f9f083bc4b8bbbefed73a3ea0f8d9508b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 18 Jul 2024 11:11:20 +0200 Subject: [PATCH 0710/1302] Set version to 24.07 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b21d0998e..838e596f4 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.06` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.07` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From d2a3fe3ed2956d392fdba9d0d9e713c1bd8169ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 18 Jul 2024 17:13:45 +0200 Subject: [PATCH 0711/1302] Fix column type in comment with schema update --- sql/mysql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql.sql b/sql/mysql.sql index e7de710a9..d013d782c 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -110,7 +110,7 @@ CREATE INDEX i_timestamp USING BTREE ON archive(timestamp); CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); -- To update 'archive' from ejabberd <= 23.10: --- ALTER TABLE archive ADD COLUMN origin_id origin_id(191) NOT NULL DEFAULT ''; +-- ALTER TABLE archive ADD COLUMN origin_id varchar(191) NOT NULL DEFAULT ''; -- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; -- CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(username(191), origin_id(191)); From 4e35515a8caefc59f2228f5b062b8c4434e50eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 18 Jul 2024 17:55:37 +0200 Subject: [PATCH 0712/1302] Also change mysql.new.sql in similar way --- sql/mysql.new.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index fc19a71e2..afd9c6a07 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -122,7 +122,7 @@ CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(191), tim CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); -- To update 'archive' from ejabberd <= 23.10: --- ALTER TABLE archive ADD COLUMN origin_id text NOT NULL DEFAULT ''; +-- ALTER TABLE archive ADD COLUMN origin_id varchar(191) NOT NULL DEFAULT ''; -- ALTER TABLE archive ALTER COLUMN origin_id DROP DEFAULT; -- CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(server_host(191), username(191), origin_id(191)); From bc7c8e3952874e7ae22d1ace8bfde13936ef47fc Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 19 Jul 2024 17:57:30 +0200 Subject: [PATCH 0713/1302] Handle case when elixir support is enabled but not available This happens when: ./configure --with-rebar=rebar3 --enable-elixir make rebar3 ct --- src/ejabberd_app.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index b65b0380e..3fcfd65eb 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -175,7 +175,11 @@ file_queue_init() -> -ifdef(ELIXIR_ENABLED). is_using_elixir_config() -> Config = ejabberd_config:path(), - 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config). + try 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config) of + B when is_boolean(B) -> B + catch + _:_ -> false + end. setup_if_elixir_conf_used() -> case is_using_elixir_config() of From 10d17048990c3c6b40cc72654817d1e5c4d41790 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Jul 2024 13:01:49 +0200 Subject: [PATCH 0714/1302] mod_muc_rtbl: Fix call to gen_server:stop (#4260) --- src/mod_muc_rtbl.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index e6ff9356b..4a63e05df 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -46,7 +46,7 @@ start(Host, _Opts) -> gen_server:start({local, gen_mod:get_module_proc(Host, ?MODULE)}, ?MODULE, [Host], []). stop(Host) -> - gen_server:stop({local, gen_mod:get_module_proc(Host, ?MODULE)}). + gen_server:stop(gen_mod:get_module_proc(Host, ?MODULE)). init([Host]) -> ejabberd_mnesia:create(?MODULE, muc_rtbl, From a565b0e4c111f9dd48499658420da37eeee63f45 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 25 Jul 2024 16:19:45 +0200 Subject: [PATCH 0715/1302] ext_mod: Handle info message when contrib module transfers table ownership --- src/ext_mod.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 6373644b5..7416848da 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -79,6 +79,11 @@ handle_cast(Msg, State) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {noreply, State}. +handle_info({'ETS-TRANSFER', Table, Process, Module}, State) -> + ?DEBUG("ejabberd now controls ETS table ~p from process ~p for module ~p", + [Table, Process, Module]), + {noreply, State}; + handle_info(Info, State) -> ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. From 47f1beca0cca12b9e8182973aca2a349b008e7f6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Aug 2024 11:01:52 +0200 Subject: [PATCH 0716/1302] CI: Update path to sqlcmd command in the mssql container As mentioned in https://learn.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker Starting with SQL Server 2022 (16.x) CU 14 and SQL Server 2019 (15.x) CU 28, the container images include the new mssql-tools18 package. The previous directory /opt/mssql-tools/bin is being phased out. The new directory for Microsoft ODBC 18 tools is /opt/mssql-tools18/bin, aligning with the latest tools offering. --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ab74d3c7..7b7eebc44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,8 +68,8 @@ jobs: - name: Prepare databases run: | - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql sudo systemctl start mysql.service sudo systemctl start postgresql.service mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" @@ -256,12 +256,12 @@ jobs: run: | [[ -d logs ]] && rm -rf logs [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" mysql -u root -proot -e "DROP DATABASE ejabberd_test;" sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql - docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" From d5e030a638c939a5ea04089b1031bcc8c652dbcf Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 Aug 2024 11:33:30 +0200 Subject: [PATCH 0717/1302] CI: Tell sqlcmd to trust server self-signed certificate As explained in https://techcommunity.microsoft.com/t5/sql-server-blog/odbc-driver-18-0-for-sql-server-released/ba-p/3169228 Version 18.0 of the Microsoft ODBC Driver 18 for SQL Server has been released. BREAKING CHANGE - Default Encrypt to Yes/Mandatory. --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b7eebc44..8f5e4fe57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,8 +68,8 @@ jobs: - name: Prepare databases run: | - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql sudo systemctl start mysql.service sudo systemctl start postgresql.service mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" @@ -256,12 +256,12 @@ jobs: run: | [[ -d logs ]] && rm -rf logs [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop database [ejabberd_test];" + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -Q "drop login [ejabberd_test];" mysql -u root -proot -e "DROP DATABASE ejabberd_test;" sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql - docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql + docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.new.sql mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* TO 'ejabberd_test'@'localhost';" From 918806006ca3535e19b801d1aaa97277d6aebe8d Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 19 Jul 2024 11:13:21 +0200 Subject: [PATCH 0718/1302] Update fast_xml to use use_maps and remove obsolete elixir files --- mix.exs | 2 +- mix.lock | 5 ++--- rebar.config | 2 +- rebar.lock | 9 +++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mix.exs b/mix.exs index a82addd7b..9093fef9f 100644 --- a/mix.exs +++ b/mix.exs @@ -136,7 +136,7 @@ defmodule Ejabberd.MixProject do {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, ">= 1.1.18"}, - {:fast_xml, ">= 1.1.51"}, + {:fast_xml, git: "https://github.com/processone/fast_xml.git", ref: "e7dc91310046831f436a03abf029587f0c2764f4", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index 9238811a6..263d84ec3 100644 --- a/mix.lock +++ b/mix.lock @@ -4,7 +4,6 @@ "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, - "ejabberd_po": {:git, "https://github.com/processone/ejabberd-po.git", "6ef974cbc28e1a52d7a35fd4d9081d175d0ac10d", []}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, @@ -13,7 +12,7 @@ "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.21", "65d7d547a09eefb37a1c0d04d8601fac4f3e6e2c1ede859a7787081670f9648d", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "131542913937025e48cd80aa81f00359686d5501b75621e72026a87b5229505b"}, - "fast_xml": {:hex, :fast_xml, "1.1.52", "0289daafbf1190b0e53b444d4885cccf41e4b05768d4b3acc76dd8d143668e10", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "795192390e06d2b65016a6990bbfa5727f4a26d2914808b1c3c9a32eedcd1bfd"}, + "fast_xml": {:git, "https://github.com/processone/fast_xml.git", "e7dc91310046831f436a03abf029587f0c2764f4", [ref: "e7dc91310046831f436a03abf029587f0c2764f4"]}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, @@ -22,7 +21,7 @@ "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "p1_acme": {:hex, :p1_acme, "1.0.23", "791aef0f79dc7f768b228808250c349fa9ce585cd8779da50ca93106eb3394d0", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "8ce196f26e3d22ea10b7809122950465878c127f80767e325207aed7e8d0dd59"}, diff --git a/rebar.config b/rebar.config index af1519510..51738394f 100644 --- a/rebar.config +++ b/rebar.config @@ -37,7 +37,7 @@ {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.21"}}}, - {fast_xml, "~> 1.1.51", {git, "https://github.com/processone/fast_xml", {tag, "1.1.52"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "e7dc91310046831f436a03abf029587f0c2764f4"}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", diff --git a/rebar.lock b/rebar.lock index 6dc25b683..d0cbb0aee 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,10 +7,13 @@ {<<"esip">>,{pkg,<<"esip">>,<<"1.0.54">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.21">>},0}, - {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.52">>},0}, + {<<"fast_xml">>, + {git,"https://github.com/processone/fast_xml", + {ref,"e7dc91310046831f436a03abf029587f0c2764f4"}}, + 0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, @@ -36,7 +39,6 @@ {<<"esip">>, <<"DAE8FB8278FD3B2C0D38C2E832C4B8D26700EB239B9A42C8EA574FEE76F5E76A">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, {<<"fast_tls">>, <<"65D7D547A09EEFB37A1C0D04D8601FAC4F3E6E2C1EDE859A7787081670F9648D">>}, - {<<"fast_xml">>, <<"0289DAAFBF1190B0E53B444D4885CCCF41E4B05768D4B3ACC76DD8D143668E10">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, @@ -64,7 +66,6 @@ {<<"esip">>, <<"8187AF819D7259CDADDAF69726C239EF604C9B0B0298A5F2D3E687BF5E2237EE">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, {<<"fast_tls">>, <<"131542913937025E48CD80AA81F00359686D5501B75621E72026A87B5229505B">>}, - {<<"fast_xml">>, <<"795192390E06D2B65016A6990BBFA5727F4A26D2914808B1C3C9A32EEDCD1BFD">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, From 15569d0b135445db709aca9215cb8f503dbdac53 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 12 Aug 2024 12:43:30 +0200 Subject: [PATCH 0719/1302] Add links in top-level options documentation to their Docs website sections --- src/ejabberd_options_doc.erl | 118 +++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 40 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 2e2313c97..1f66cafe9 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -30,8 +30,9 @@ doc() -> [{hosts, #{value => ?T("[Domain1, Domain2, ...]"), desc => - ?T("The option defines a list containing one or more " - "domains that 'ejabberd' will serve. This is a " + ?T("List of one or more " + "_`../configuration/basic.md#host-names|host names`_ " + "(or domains) that 'ejabberd' will serve. This is a " "**mandatory** option.")}}, {listen, #{value => "[Options, ...]", @@ -42,15 +43,15 @@ doc() -> {modules, #{value => "{Module: Options}", desc => - ?T("The option for modules configuration. See " - "_`modules.md|Modules`_ section " - "for details.")}}, + ?T("Set all the " + "_`modules.md|modules`_ configuration options.")}}, {loglevel, #{value => "none | emergency | alert | critical | " "error | warning | notice | info | debug", desc => - ?T("Verbosity of log files generated by ejabberd. " + ?T("Verbosity of ejabberd " + "_`../configuration/basic.md#logging|logging`_. " "The default value is 'info'. " "NOTE: previous versions of ejabberd had log levels " "defined in numeric format ('0..5'). The numeric values " @@ -106,7 +107,8 @@ doc() -> {default_db, #{value => "mnesia | sql", desc => - ?T("Default persistent storage for ejabberd. " + ?T("_`database.md#default-database|Default database`_ " + "to store persistent data in ejabberd. " "Modules and other components (e.g. authentication) " "may have its own value. The default value is 'mnesia'.")}}, {default_ram_db, @@ -135,7 +137,9 @@ doc() -> {acl, #{value => "{ACLName: {ACLType: ACLValue}}", desc => - ?T("The option defines access control lists: named sets " + ?T("This option defines " + "_`../configuration/basic.md#acl|access control lists`_: " + "named sets " "of rules which are used to match against different targets " "(such as a JID or an IP address). Every set of rules " "has name 'ACLName': it can be any string except 'all' or 'none' " @@ -317,7 +321,9 @@ doc() -> {anonymous_protocol, #{value => "login_anon | sasl_anon | both", desc => - [?T("Define what anonymous protocol will be used: "), "", + [?T("Define what " + "_`authentication.md#anonymous-login-and-sasl-anonymous|anonymous`_ " + "protocol will be used: "), "", ?T("* 'login_anon' means that the anonymous login method will be used. "), "", ?T("* 'sasl_anon' means that the SASL Anonymous method will be used. "), "", ?T("* 'both' means that SASL Anonymous and login anonymous are both " @@ -332,10 +338,8 @@ doc() -> {append_host_config, #{value => "{Host: Options}", desc => - ?T("To define specific ejabberd modules in a virtual host, " - "you can define the global 'modules' option with the common modules, " - "and later add specific modules to certain virtual hosts. " - "To accomplish that, 'append_host_config' option can be used.")}}, + ?T("Add a few specific options to a certain " + "_`../configuration/basic.md#virtual-hosting|virtual host`_.")}}, {auth_cache_life_time, #{value => "timeout()", desc => @@ -354,7 +358,7 @@ doc() -> {auth_method, #{value => "[mnesia | sql | anonymous | external | jwt | ldap | pam, ...]", desc => - ?T("A list of authentication methods to use. " + ?T("A list of _`authentication.md|authentication`_ methods to use. " "If several methods are defined, authentication is " "considered successful as long as authentication of " "at least one of the methods succeeds. " @@ -533,7 +537,9 @@ doc() -> {define_macro, #{value => "{MacroName: MacroValue}", desc => - ?T("Defines a macro. The value can be any valid arbitrary " + ?T("Defines a " + "_`../configuration/file-format.md#macros-in-configuration-file|macro`_. " + "The value can be any valid arbitrary " "YAML value. For convenience, it's recommended to define " "a 'MacroName' in capital letters. Duplicated macros are not allowed. " "Macros are processed after additional configuration files have " @@ -572,7 +578,9 @@ doc() -> {domain_balancing, #{value => "{Domain: Options}", desc => - ?T("An algorithm to load balance the components that are plugged " + ?T("An algorithm to " + "_`../guide/clustering.md#service-load-balancing|load-balance`_ " + "the components that are plugged " "on an ejabberd cluster. It means that you can plug one or several " "instances of the same component on each ejabberd node and that " "the traffic will be automatically distributed. The algorithm " @@ -605,19 +613,23 @@ doc() -> {extauth_pool_name, #{value => ?T("Name"), desc => - ?T("Define the pool name appendix, so the full pool name will be " + ?T("Define the pool name appendix in " + "_`authentication.md#external-script|external auth`_, " + "so the full pool name will be " "'extauth_pool_Name'. The default value is the hostname.")}}, {extauth_pool_size, #{value => ?T("Size"), desc => ?T("The option defines the number of instances of the same " - "external program to start for better load balancing. " + "_`authentication.md#external-script|external auth`_ " + "program to start for better load balancing. " "The default is the number of available CPU cores.")}}, {extauth_program, #{value => ?T("Path"), desc => - ?T("Indicate in this option the full path to the external " - "authentication script. The script must be executable by ejabberd.")}}, + ?T("Indicate in this option the full path to the " + "_`authentication.md#external-script|external authentication script`_. " + "The script must be executable by ejabberd.")}}, {ext_api_headers, #{value => "Headers", desc => @@ -655,8 +667,10 @@ doc() -> {host_config, #{value => "{Host: Options}", desc => - ?T("The option is used to redefine 'Options' for virtual host " - "'Host'. In the example below LDAP authentication method " + ?T("The option is used to redefine 'Options' for " + "_`../configuration/basic.md#virtual-hosting|virtual host`_ " + "'Host'. " + "In the example below LDAP authentication method " "will be used on virtual host 'domain.tld' and SQL method " "will be used on virtual host 'example.org'."), example => @@ -674,7 +688,9 @@ doc() -> {include_config_file, #{value => "[Filename, ...\\] | {Filename: Options}", desc => - ?T("Read additional configuration from 'Filename'. If the " + ?T("Read and " + "_`../configuration/file-format.md#include-additional-files|include additional file`_ " + "from 'Filename'. If the " "value is provided in 'pass:[{Filename: Options}]' format, the " "'Options' must be one of the following:")}, [{disallow, @@ -701,7 +717,8 @@ doc() -> {jwt_auth_only_rule, #{value => ?T("AccessName"), desc => - ?T("This ACL rule defines accounts that can use only this auth " + ?T("This ACL rule defines accounts that can use only the " + "_`authentication.md#jwt-authentication|JWT`_ auth " "method, even if others are also defined in the ejabberd " "configuration file. In other words: if there are several auth " "methods enabled for this host (JWT, SQL, ...), users that " @@ -711,20 +728,25 @@ doc() -> #{value => ?T("FieldName"), desc => ?T("By default, the JID is defined in the '\"jid\"' JWT field. " - "In this option you can specify other JWT field name " + "In this option you can specify other " + "_`authentication.md#jwt-authentication|JWT`_ " + "field name " "where the JID is defined.")}}, {jwt_key, #{value => ?T("FilePath"), desc => - ?T("Path to the file that contains the JWK Key. " + ?T("Path to the file that contains the " + "_`authentication.md#jwt-authentication|JWT`_ key. " "The default value is 'undefined'.")}}, {language, #{value => ?T("Language"), desc => - ?T("The option defines the default language of server strings " + ?T("Define the " + "_`../configuration/basic.md#default-language|default language`_ " + "of server strings " "that can be seen by XMPP clients. If an XMPP client does not " "possess 'xml:lang' attribute, the specified language is used. " - "The default value is '\"en\"'.")}}, + "The default value is '\"en\"'. ")}}, {ldap_servers, #{value => "[Host, ...]", desc => @@ -909,7 +931,9 @@ doc() -> {new_sql_schema, #{value => "true | false", desc => - {?T("Whether to use 'new' SQL schema. All schemas are located " + {?T("Whether to use the " + "_`database.md#default-and-new-schemas|new SQL schema`_. " + "All schemas are located " "at . " "There are two schemas available. The default legacy schema " "stores one XMPP domain into one ejabberd database. " @@ -1049,14 +1073,18 @@ doc() -> {pam_service, #{value => ?T("Name"), desc => - ?T("This option defines the PAM service name. Refer to the PAM " + ?T("This option defines the " + "_`authentication.md#pam-authentication|PAM`_ " + "service name. Refer to the PAM " "documentation of your operation system for more information. " "The default value is 'ejabberd'.")}}, {pam_userinfotype, #{value => "username | jid", desc => ?T("This option defines what type of information about the " - "user ejabberd provides to the PAM service: only the username, " + "user ejabberd provides to the " + "_`authentication.md#pam-authentication|PAM`_ " + "service: only the username, " "or the user's JID. Default is 'username'.")}}, {pgsql_users_number_estimate, #{value => "true | false", @@ -1073,36 +1101,42 @@ doc() -> #{value => "timeout()", desc => ?T("A timeout to wait for the connection to be re-established " - "to the Redis server. The default is '1 second'.")}}, + "to the _`database.md#redis|Redis`_ " + "server. The default is '1 second'.")}}, {redis_db, #{value => ?T("Number"), - desc => ?T("Redis database number. The default is '0'.")}}, + desc => ?T("_`database.md#redis|Redis`_ " + "database number. The default is '0'.")}}, {redis_password, #{value => ?T("Password"), desc => - ?T("The password to the Redis server. " + ?T("The password to the _`database.md#redis|Redis`_ server. " "The default is an empty string, i.e. no password.")}}, {redis_pool_size, #{value => ?T("Number"), desc => - ?T("The number of simultaneous connections to the Redis server. " + ?T("The number of simultaneous connections to the " + "_`database.md#redis|Redis`_ server. " "The default value is '10'.")}}, {redis_port, #{value => "1..65535", desc => - ?T("The port where the Redis server is accepting connections. " + ?T("The port where the _`database.md#redis|Redis`_ " + " server is accepting connections. " "The default is '6379'.")}}, {redis_queue_type, #{value => "ram | file", desc => - ?T("The type of request queue for the Redis server. " + ?T("The type of request queue for the " + "_`database.md#redis|Redis`_ server. " "See description of _`queue_type`_ option for the explanation. " "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {redis_server, #{value => ?T("Hostname"), desc => - ?T("A hostname or an IP address of the Redis server. " + ?T("A hostname or an IP address of the " + "_`database.md#redis|Redis`_ server." "The default is 'localhost'.")}}, {registration_timeout, #{value => "timeout()", @@ -1257,7 +1291,9 @@ doc() -> {shaper, #{value => "{ShaperName: Rate}", desc => - ?T("The option defines a set of shapers. Every shaper is assigned " + ?T("The option defines a set of " + "_`../configuration/basic.md#shapers|shapers`_. " + "Every shaper is assigned " "a name 'ShaperName' that can be used in other parts of the " "configuration file, such as _`shaper_rules`_ option. The shaper " "itself is defined by its 'Rate', where 'Rate' stands for the " @@ -1274,7 +1310,9 @@ doc() -> {shaper_rules, #{value => "{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}", desc => - ?T("An entry allowing to declaring shaper to use for matching user/hosts. " + ?T("This option defines " + "_`../configuration/basic.md#shaper-rules|shaper rules`_ " + "to use for matching user/hosts. " "Semantics is similar to _`access_rules`_ option, the only difference is " "that instead using 'allow' or 'deny', a name of a shaper (defined in " "_`shaper`_ option) or a positive number should be used."), From 01141e5f69a98787526fd6b00afca1bfa5c4e1b4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Aug 2024 13:33:32 +0200 Subject: [PATCH 0720/1302] Relax password complexity in test database This password is not strong enough in MSSQL 2022, relax this restriction https://learn.microsoft.com/en-us/sql/relational-databases/security/password-policy?view=sql-server-ver16 https://learn.microsoft.com/en-us/sql/t-sql/statements/create-login-transact-sql?view=sql-server-ver16#check_policy---on--off- --- test/docker/db/mssql/initdb/initdb_mssql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/docker/db/mssql/initdb/initdb_mssql.sql b/test/docker/db/mssql/initdb/initdb_mssql.sql index b8208ad70..8c7acc708 100644 --- a/test/docker/db/mssql/initdb/initdb_mssql.sql +++ b/test/docker/db/mssql/initdb/initdb_mssql.sql @@ -18,7 +18,7 @@ GO USE ejabberd_test; GO -CREATE LOGIN ejabberd_test WITH PASSWORD = 'ejabberd_Test1'; +CREATE LOGIN ejabberd_test WITH PASSWORD = 'ejabberd_Test1', CHECK_POLICY = OFF; GO CREATE USER ejabberd_test FOR LOGIN ejabberd_test; From 937d5fe4957f3367d8fe267ea3e745af97d240cb Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Aug 2024 19:59:33 +0200 Subject: [PATCH 0721/1302] Document which SQL servers can really use update_sql_schema --- src/ejabberd_options_doc.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 1f66cafe9..add6371e6 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -949,7 +949,8 @@ doc() -> #{value => "true | false", note => "updated in 24.06", desc => - ?T("Allow ejabberd to update SQL schema. " + ?T("Allow ejabberd to update SQL schema in " + "MySQL, PostgreSQL and SQLite databases. " "This option was added in ejabberd 23.10, " "and enabled by default since 24.06. " "The default value is 'true'.")}}, From b07e28be2c7373f04060afe8fdfcb8c6db42376d Mon Sep 17 00:00:00 2001 From: Michael Slezak Date: Wed, 14 Aug 2024 16:10:46 -0600 Subject: [PATCH 0722/1302] Fix 'mix release' error: logger being regular and included application (#4265) --- mix.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9093fef9f..405b95c25 100644 --- a/mix.exs +++ b/mix.exs @@ -43,11 +43,11 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, - applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, - :fast_tls, :fast_xml, :fast_yaml, :jose, + extra_applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, + :fast_tls, :fast_xml, :fast_yaml, :jose, :logger, :p1_utils, :stringprep, :syntax_tools, :yconf, :xmpp] ++ cond_apps(), - included_applications: [:mnesia, :os_mon, :logger, + included_applications: [:mnesia, :os_mon, :cache_tab, :eimp, :mqtree, :p1_acme, :p1_oauth2, :pkix] ++ cond_included_apps()] From e99fe98db48921bb377c3079bea6e2a35e152435 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 10:43:52 +0200 Subject: [PATCH 0723/1302] Remove from extra_applications the apps already defined in deps (#4265) --- mix.exs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 405b95c25..16496771e 100644 --- a/mix.exs +++ b/mix.exs @@ -43,9 +43,8 @@ defmodule Ejabberd.MixProject do def application do [mod: {:ejabberd_app, []}, - extra_applications: [:idna, :inets, :kernel, :sasl, :ssl, :stdlib, :mix, - :fast_tls, :fast_xml, :fast_yaml, :jose, :logger, - :p1_utils, :stringprep, :syntax_tools, :yconf, :xmpp] + extra_applications: [:inets, :kernel, :sasl, :ssl, :stdlib, :syntax_tools, + :logger, :mix] ++ cond_apps(), included_applications: [:mnesia, :os_mon, :cache_tab, :eimp, :mqtree, :p1_acme, From 6110f213dec260a30cf0c171dce1a41b1d5668f0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 16 Aug 2024 10:55:55 +0200 Subject: [PATCH 0724/1302] Return error stanza when storage doesn't support vcard update (#4266) --- src/mod_vcard.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 9a6a42b23..15f0cd590 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -398,13 +398,17 @@ vcard_iq_set(#iq{from = From, lang = Lang, sub_els = [VCard]} = IQ) -> %% Should not be here? Txt = ?T("Nodeprep has failed"), {stop, xmpp:err_internal_server_error(Txt, Lang)}; + {error, not_implemented} -> + Txt = ?T("Updating the vCard is not supported by the vCard storage backend"), + {stop, xmpp:err_feature_not_implemented(Txt, Lang)}; ok -> IQ end; vcard_iq_set(Acc) -> Acc. --spec set_vcard(binary(), binary(), xmlel() | vcard_temp()) -> {error, badarg|binary()} | ok. +-spec set_vcard(binary(), binary(), xmlel() | vcard_temp()) -> + {error, badarg | not_implemented | binary()} | ok. set_vcard(User, LServer, VCARD) -> case jid:nodeprep(User) of error -> From db3a5d89150d69b5f595e60261ac798a786b48af Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Aug 2024 19:50:55 +0200 Subject: [PATCH 0725/1302] Start ExSync manually to ensure it's started if (and only if) Relive --- mix.exs | 3 +-- src/ejabberd_app.erl | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 16496771e..3714a8fed 100644 --- a/mix.exs +++ b/mix.exs @@ -165,7 +165,7 @@ defmodule Ejabberd.MixProject do {Mix.env() == :translations, {:ejabberd_po, git: "https://github.com/processone/ejabberd-po.git"}}, {Mix.env() == :dev, - {:exsync, "~> 0.2"}}, + {:exsync, "~> 0.2", optional: true, runtime: false}}, {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, @@ -183,7 +183,6 @@ defmodule Ejabberd.MixProject do defp cond_apps do for {:true, app} <- [{config(:stun), :stun}, - {Map.has_key?(System.get_env(), "RELIVE"), :exsync}, {if_version_below(~c"27", true), :jiffy}, {config(:tools), :observer}], do: app diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 3fcfd65eb..a0065a8fa 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -59,6 +59,7 @@ start(normal, _Args) -> ejabberd_hooks:run(ejabberd_started, []), ejabberd:check_apps(), ejabberd_systemd:ready(), + maybe_start_exsync(), {T2, _} = statistics(wall_clock), ?INFO_MSG("ejabberd ~ts is started in the node ~p in ~.2fs", [ejabberd_option:version(), @@ -198,8 +199,15 @@ start_elixir_application() -> ok -> ok; {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", []) end. + +maybe_start_exsync() -> + case os:getenv("RELIVE") of + "true" -> rpc:call(node(), 'Elixir.ExSync.Application', start, []); + _ -> ok + end. -else. setup_if_elixir_conf_used() -> ok. register_elixir_config_hooks() -> ok. start_elixir_application() -> ok. +maybe_start_exsync() -> ok. -endif. From df5202a2f0edbb79f56ac5e3001e8979dfb4dead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 23 Aug 2024 20:42:34 +0200 Subject: [PATCH 0726/1302] Update deps to bring improved s2s fallback for invalid direct tls connections This allows connections to server that have both xmpps-server and xmpp-server srv entries, for which xmpps version doesn't work correctly. Before this change we would stop on non-working xmpps server, now we will also attempt to connect further servers on list. --- mix.exs | 3 ++- rebar.config | 4 ++-- rebar.lock | 14 ++++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/mix.exs b/mix.exs index 3714a8fed..debbb3d7f 100644 --- a/mix.exs +++ b/mix.exs @@ -135,6 +135,7 @@ defmodule Ejabberd.MixProject do {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, ">= 1.1.18"}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f", override: true}, {:fast_xml, git: "https://github.com/processone/fast_xml.git", ref: "e7dc91310046831f436a03abf029587f0c2764f4", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, @@ -144,7 +145,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.8.3"}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "dd70dd4a684e429ec17a4382d5888213d2c6bcf2", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 51738394f..faa6543ee 100644 --- a/rebar.config +++ b/rebar.config @@ -36,7 +36,7 @@ {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.54"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.21"}}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {ref, "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "e7dc91310046831f436a03abf029587f0c2764f4"}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", {tag, "1.8.3"}}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", {ref, "dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index d0cbb0aee..eaa44d3cf 100644 --- a/rebar.lock +++ b/rebar.lock @@ -6,7 +6,10 @@ {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, {<<"esip">>,{pkg,<<"esip">>,<<"1.0.54">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, - {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.21">>},0}, + {<<"fast_tls">>, + {git,"https://github.com/processone/fast_tls", + {ref,"75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}, + 0}, {<<"fast_xml">>, {git,"https://github.com/processone/fast_xml", {ref,"e7dc91310046831f436a03abf029587f0c2764f4"}}, @@ -27,7 +30,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.14">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.8.3">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}, + 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ {pkg_hash,[ @@ -38,7 +44,6 @@ {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, {<<"esip">>, <<"DAE8FB8278FD3B2C0D38C2E832C4B8D26700EB239B9A42C8EA574FEE76F5E76A">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, - {<<"fast_tls">>, <<"65D7D547A09EEFB37A1C0D04D8601FAC4F3E6E2C1EDE859A7787081670F9648D">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, @@ -55,7 +60,6 @@ {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"6F538AC80C842131DBD149055570D116BFABC9B5EBFF4BD6AF2E7888958C660C">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"ACF39A8B70B066BB8F10B0862E031E8ABCF92FE89D1C41D06C1E39AE5CAF89C4">>}, {<<"yconf">>, <<"D59521D66FF89F219411B6E9277CD6FEEC7CC6FCE11554E67DE02A8D0A470479">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -65,7 +69,6 @@ {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, {<<"esip">>, <<"8187AF819D7259CDADDAF69726C239EF604C9B0B0298A5F2D3E687BF5E2237EE">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, - {<<"fast_tls">>, <<"131542913937025E48CD80AA81F00359686D5501B75621E72026A87B5229505B">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, @@ -82,6 +85,5 @@ {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"E134807B1B7A8DFFD94E64EEFEE00E65C7B4042F3D14E16F8F43566D20371583">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"ED70065F9A89A818DCFF43B74C080C9E7F4F1414E1051BEDDB7280DB809AF711">>}, {<<"yconf">>, <<"E947813273F38711C7B2E5A8E4ACC9A51C7BBE854F744A345F60300B38586C89">>}]} ]. From 8e7489c2be4deb03e9d9f8f099cb2015bd1be6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 23 Aug 2024 20:52:36 +0200 Subject: [PATCH 0727/1302] Remove duplicate dep from mix.exs --- mix.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/mix.exs b/mix.exs index debbb3d7f..1ba0f6686 100644 --- a/mix.exs +++ b/mix.exs @@ -134,7 +134,6 @@ defmodule Ejabberd.MixProject do {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, - {:fast_tls, ">= 1.1.18"}, {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f", override: true}, {:fast_xml, git: "https://github.com/processone/fast_xml.git", ref: "e7dc91310046831f436a03abf029587f0c2764f4", override: true}, {:fast_yaml, "~> 1.0"}, From 3237a955e538536c6ce65fc3add033d4ef4ffabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 23 Aug 2024 20:56:52 +0200 Subject: [PATCH 0728/1302] Fix dep spec for rebar2 --- rebar.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index faa6543ee..9ab633de8 100644 --- a/rebar.config +++ b/rebar.config @@ -36,7 +36,7 @@ {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.54"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {ref, "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "e7dc91310046831f436a03abf029587f0c2764f4"}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", {ref, "dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. From 34a58863e3c3b77fc081fca194d4ab7ab16647fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Sat, 24 Aug 2024 13:16:03 +0200 Subject: [PATCH 0729/1302] Update xmpp dep --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 1ba0f6686..5efd4e117 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "dd70dd4a684e429ec17a4382d5888213d2c6bcf2", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "1745bc253e9da0dd0662c218ba5faeea9b3147ef", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 9ab633de8..e314b97a5 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "1745bc253e9da0dd0662c218ba5faeea9b3147ef"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index eaa44d3cf..4c63f9840 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"dd70dd4a684e429ec17a4382d5888213d2c6bcf2"}}, + {ref,"1745bc253e9da0dd0662c218ba5faeea9b3147ef"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ From ec7fd059875ce697916e822b31105091971f3886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Sat, 24 Aug 2024 15:27:58 +0200 Subject: [PATCH 0730/1302] Update xmpp once more --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 5efd4e117..171e8762b 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "1745bc253e9da0dd0662c218ba5faeea9b3147ef", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "2a54443436dc8a942969f2ef7c5654d5acab7533", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index e314b97a5..017546f02 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "1745bc253e9da0dd0662c218ba5faeea9b3147ef"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "2a54443436dc8a942969f2ef7c5654d5acab7533"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 4c63f9840..f2f50b144 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"1745bc253e9da0dd0662c218ba5faeea9b3147ef"}}, + {ref,"2a54443436dc8a942969f2ef7c5654d5acab7533"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ From 7cd34d3709819c290f1dc240af4781d8d27060dc Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Aug 2024 12:25:19 +0200 Subject: [PATCH 0731/1302] CONTAINER.md: Use same general badges in both container images readme files --- CONTAINER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 6fbb12534..c62e597d7 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1,7 +1,7 @@ [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&label=&color=49c0c4)](https://github.com/processone/ejabberd/tags) -[![GitHub Container](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=docker)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) - +[![ejabberd Container on GitHub](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=docker)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) +[![ecs Container on Docker](https://img.shields.io/docker/v/ejabberd/ecs?label=ecs&sort=semver&logo=docker)](https://hub.docker.com/r/ejabberd/ecs/) `ejabberd` Container Image ========================== From da1673e264f504628dcc34a21c0d8ca36f34b46b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 Aug 2024 17:24:44 +0200 Subject: [PATCH 0732/1302] mix.lock: Don't mention Relive deps, otherwise they are always downloaded --- mix.lock | 2 -- 1 file changed, 2 deletions(-) diff --git a/mix.lock b/mix.lock index 263d84ec3..9b98d333e 100644 --- a/mix.lock +++ b/mix.lock @@ -9,12 +9,10 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.54", "dae8fb8278fd3b2c0d38c2e832c4b8d26700eb239b9a42c8ea574fee76f5e76a", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.14", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "8187af819d7259cdaddaf69726c239ef604c9b0b0298a5f2d3e687bf5e2237ee"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, - "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.21", "65d7d547a09eefb37a1c0d04d8601fac4f3e6e2c1ede859a7787081670f9648d", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "131542913937025e48cd80aa81f00359686d5501b75621e72026a87b5229505b"}, "fast_xml": {:git, "https://github.com/processone/fast_xml.git", "e7dc91310046831f436a03abf029587f0c2764f4", [ref: "e7dc91310046831f436a03abf029587f0c2764f4"]}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, - "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, From e726ba9a8bbba400e520317643a89acd26c0984a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 21:11:13 +0200 Subject: [PATCH 0733/1302] .vscode/relive.sh: Update to benefit from ejabberd 24.02 behaviour --- .vscode/relive.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/relive.sh b/.vscode/relive.sh index 6a0394955..c7db2f74e 100755 --- a/.vscode/relive.sh +++ b/.vscode/relive.sh @@ -1,6 +1,6 @@ [ ! -f Makefile ] \ && ./autogen.sh \ - && ./configure --with-rebar=./rebar3 \ + && ./configure --with-rebar=rebar3 \ && make deps make relive From fddacd51a43e7176775d1c45be18c3d569f8b632 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 22:27:31 +0200 Subject: [PATCH 0734/1302] .vscode/launch.json: Experimental support for debugging with Neovim --- .vscode/launch.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6e9fd45ae..6725568c3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,23 @@ { "version": "0.2.0", "configurations": [ + { + "name": "Relive (Vim)", + "type": "erlang", + "request": "launch", + "runinterminal": [ + "./rebar3", "shell", + "--apps", "ejabberd", + "--config", "rel/relive.config", + "--script", "rel/relive.escript", + "--name", "ejabberd@localhost", + "--setcookie", "COOKIE" + ], + "projectnode": "ejabberd@localhost", + "cookie": "COOKIE", + "timeout": 900, + "cwd": "." + }, { "name": "Relive", "type": "erlang", From 8f4179050b840f43ef4aebdd154b77d62d9685c6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 22:45:17 +0200 Subject: [PATCH 0735/1302] erlang_ls.config: Let it find paths, update to Erlang 26, enable crossref --- erlang_ls.config | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erlang_ls.config b/erlang_ls.config index 74c892a8d..83e3e52a5 100644 --- a/erlang_ls.config +++ b/erlang_ls.config @@ -1,5 +1,5 @@ -otp_path: "/usr/lib/erlang" -plt_path: "_build/default/rebar3_24.3.3_plt" +#otp_path: "/usr/lib/erlang" +#plt_path: "_build/default/rebar3_24.3.3_plt" #code_reload: # node: ejabberd@localhost apps_dirs: @@ -14,12 +14,12 @@ macros: - name: DEPRECATED_GET_STACKTRACE - name: HAVE_ERL_ERROR - name: HAVE_URI_STRING - - name: OTP_BELOW_25 + - name: OTP_BELOW_27 - name: SIP - name: STUN diagnostics: -# enabled: -# - crossref + enabled: + - crossref disabled: # - dialyzer - unused_includes # Otherwise it complains about unused logger.hrl From 5f4d17621ff859fc003049a0f9fa823bb916d9a6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 13:24:30 +0200 Subject: [PATCH 0736/1302] Fix Erlang LS warning about unused macro definitions Macro name + commit when it was added - commit when usage was removed: * BATCH_SIZE + f6db8428 - 71c44bff8 * INVALID_SETTING_MSG - 6b126171d - 381065397 * POLICY_ACCESS + 7c1e7e5b - 56d273477 * PROCNAME + 068db1a2 - 6876a37e6 * SALT_LENGTH + e575c87e - 633b68db1 * SERVER + 068db1a2 - but was never used! * SERVER + f44e23b8c - but was never used! * STORAGE_TYPES + 92db9ff1 - 9a93acc62 * TCP_SEND_TIMEOUT + f0af10e6 - 6e900d6a8 * TDTD + c3280e9 - 5a34020 * TVFIELD + dcc05ac8 - da310a517 --- src/ejabberd_auth_sql.erl | 4 ---- src/ejabberd_commands.erl | 2 -- src/ejabberd_listener.erl | 2 -- src/ejabberd_mnesia.erl | 1 - src/ejabberd_redis.erl | 2 -- src/mod_announce.erl | 3 --- src/mod_matrix_gw_room.erl | 2 -- src/mod_matrix_gw_s2s.erl | 2 -- src/mod_muc_admin.erl | 3 --- src/mod_shared_roster_ldap.erl | 1 - 10 files changed, 22 deletions(-) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index dd58f0af5..49da353f7 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -41,8 +41,6 @@ -include("ejabberd_sql_pt.hrl"). -include("ejabberd_auth.hrl"). --define(SALT_LENGTH, 16). - %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- @@ -161,8 +159,6 @@ remove_user(User, Server) -> {error, db_failure} end. --define(BATCH_SIZE, 1000). - scram_hash_encode(Hash, StoreKey) -> case Hash of sha -> StoreKey; diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 509f3622d..a2d4bf525 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -56,8 +56,6 @@ -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). --define(POLICY_ACCESS, '$policy'). - -type auth() :: {binary(), binary(), binary() | {oauth, binary()}, boolean()} | map(). -record(state, {}). diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index a5c589f99..e749d23e9 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -60,8 +60,6 @@ -optional_callbacks([listen_opt_type/1, tcp_init/2, udp_init/2]). --define(TCP_SEND_TIMEOUT, 15000). - start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index c835f95ac..da817f27f 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -39,7 +39,6 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --define(STORAGE_TYPES, [disc_copies, disc_only_copies, ram_copies]). -define(NEED_RESET, [local_content, type]). -include("logger.hrl"). diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 12a44949c..45a689549 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -42,8 +42,6 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --define(SERVER, ?MODULE). --define(PROCNAME, 'ejabberd_redis_client'). -define(TR_STACK, redis_transaction_stack). -define(DEFAULT_MAX_QUEUE, 10000). -define(MAX_RETRIES, 1). diff --git a/src/mod_announce.erl b/src/mod_announce.erl index ba8b71d4b..d7f983716 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -494,9 +494,6 @@ announce_commands(From, To, {error, xmpp:err_bad_request(Txt, Lang)} end. --define(TVFIELD(Type, Var, Val), - #xdata_field{type = Type, var = Var, values = vvaluel(Val)}). - vvaluel(Val) -> case Val of <<>> -> []; diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 77d323f59..aa054eb85 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -41,8 +41,6 @@ -export([init/1, terminate/3, code_change/4, callback_mode/0]). -export([handle_event/4]). --define(SERVER, ?MODULE). - -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index e516ec744..6caceb032 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -35,8 +35,6 @@ -export([init/1, terminate/3, code_change/4, callback_mode/0]). -export([handle_event/4]). --define(SERVER, ?MODULE). - -include("logger.hrl"). -include("ejabberd_http.hrl"). -include_lib("kernel/include/inet.hrl"). diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index be397fbd8..6d035a548 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -620,9 +620,6 @@ web_menu_host(Acc, _Host, Lang) -> %%--------------- %% Web Admin Page --define(TDTD(L, N), - ?XE(<<"tr">>, [?XCT(<<"td">>, L), ?XC(<<"td">>, integer_to_binary(N))])). - web_page_main(_, #request{path = [<<"muc">>], lang = Lang} = R) -> PageTitle = translate:translate(Lang, ?T("Multi-User Chat")), Title = ?H1GL(PageTitle, <<"modules/#mod_muc">>, <<"mod_muc">>), diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index bad252e9f..ee3ee5823 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -52,7 +52,6 @@ -define(GROUP_CACHE, shared_roster_ldap_group_cache). -define(DISPLAYED_CACHE, shared_roster_ldap_displayed_cache). -define(LDAP_SEARCH_TIMEOUT, 5). %% Timeout for LDAP search queries in seconds --define(INVALID_SETTING_MSG, "~ts is not properly set! ~ts will not function."). -record(state, {host = <<"">> :: binary(), From 70ee294079758a3e4de897d758414399a39a39e4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 22:57:36 +0200 Subject: [PATCH 0737/1302] elvis.config: Fix file syntax, set vim mode, disable many tests Let's disable the tests that would require major changes in existing ejabberd source code, and fixing them would produce a curtain of changes that would difficult using git blame and git log, but provide minimal benefits. Don't check erlang header files by now. Don't check rebar.config because it has customizations that must be parsed by rebar.config.script to have suitable syntax. --- elvis.config | 58 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/elvis.config b/elvis.config index c679b7e2f..074e13010 100644 --- a/elvis.config +++ b/elvis.config @@ -5,38 +5,54 @@ {config, [#{dirs => ["src"], filter => "*.erl", + ignore => ['ELDAPv3', eldap_filter_yecc], ruleset => erl_files, - rules => [{elvis_style, line_length, #{limit => 100, - skip_comments => false}}, + rules => [{elvis_text_style, line_length, #{limit => 1000, skip_comments => false}}, {elvis_text_style, no_tabs, disable}, - {elvis_style, no_debug_call, disable}, - {elvis_style, operator_spaces, disable}, + {elvis_style, atom_naming_convention, disable}, + {elvis_style, consistent_variable_casing, disable}, + {elvis_style, dont_repeat_yourself, #{min_complexity => 70}}, + {elvis_style, export_used_types, disable}, + {elvis_style, function_naming_convention, disable}, + {elvis_style, god_modules, #{limit => 300}}, {elvis_style, invalid_dynamic_call, disable}, - {elvis_style, variable_naming_convention, #{ regex => ".*" }}, - {elvis_style, dont_repeat_yourself, #{min_complexity => 20}} - ] + {elvis_style, macro_module_names, disable}, + {elvis_style, macro_names, disable}, + {elvis_style, max_function_arity, disable}, % #{max_arity => 15}}, + {elvis_style, nesting_level, disable}, + {elvis_style, no_author, disable}, + {elvis_style, no_catch_expressions, disable}, + {elvis_style, no_debug_call, disable}, + {elvis_style, no_if_expression, disable}, + {elvis_style, no_import, disable}, + {elvis_style, no_match_in_condition, disable}, + {elvis_style, no_nested_try_catch, disable}, + {elvis_style, no_single_clause_case, disable}, + {elvis_style, no_spec_with_records, disable}, + {elvis_style, no_throw, disable}, + {elvis_style, operator_spaces, disable}, + {elvis_style, param_pattern_matching, disable}, + {elvis_style, private_data_types, disable}, + {elvis_style, variable_naming_convention, disable} + ] }, + + %#{dirs => ["include"], + % filter => "*.hrl", + % ruleset => hrl_files}, + #{dirs => ["."], filter => "Makefile.in", ruleset => makefiles, - rules => [{elvis_style, line_length, #{limit => 100, + rules => [{elvis_text_style, line_length, #{limit => 400, skip_comments => false}}, - {elvis_style, no_tabs, disable}, - {elvis_style, dont_repeat_yourself, #{min_complexity => 20}} - ] - }, - #{dirs => ["."], - filter => "rebar.config", - ruleset => rebar_config, - rules => [{elvis_style, line_length, #{limit => 100, - skip_comments => false}}, - {elvis_style, no_tabs, disable}, - {elvis_style, dont_repeat_yourself, #{min_complexity => 20}} - ] + {elvis_style, dont_repeat_yourself, #{min_complexity => 20}} + ] } - } ] } ] } ]. + +%% vim: set filetype=erlang tabstop=8: From d4d9771a71c0075410b93ea093d7670bb139710d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 11:54:36 +0200 Subject: [PATCH 0738/1302] Makefile: Add support for "make elvis" when using rebar3 --- Makefile.in | 8 ++++++++ rebar.config | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 5adf241d5..82073c367 100644 --- a/Makefile.in +++ b/Makefile.in @@ -642,6 +642,13 @@ dialyzer: erlang_plt deps_plt ejabberd_plt endif endif +#. +#' elvis +# + +elvis: + $(REBAR) lint + #. #' test # @@ -699,6 +706,7 @@ help: @echo " indent Indent source code using erlang-mode [emacs]" @echo "" @echo " dialyzer Run Dialyzer static analyzer" + @echo " elvis Run Elvis source code style reviewer [rebar3]" @echo " hooks Run hooks validator" @echo " test Run Common Tests suite [rebar3]" @echo " test-eunit Run EUnit suite [rebar3]" diff --git a/rebar.config b/rebar.config index 017546f02..5fcc1e5bb 100644 --- a/rebar.config +++ b/rebar.config @@ -156,7 +156,8 @@ {branch, "consolidation_fix"}}} }]}}. {if_rebar3, {project_plugins, [configure_deps, - {if_var_true, tools, rebar3_format} + {if_var_true, tools, rebar3_format}, + {if_var_true, tools, rebar3_lint} ]}}. {if_not_rebar3, {plugins, [ deps_erl_opts, override_deps_versions2, override_opts, configure_deps From 90ce65e4dc37686d09717aa826ba634686fb2b5c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 21 Aug 2024 23:30:00 +0200 Subject: [PATCH 0739/1302] Fix Elvis report: Remove trailing whitespace characters https://github.com/inaka/elvis_core/blob/main/doc_rules/elvis_text_style/no_trailing_whitespace.md --- src/ejabberd_auth_jwt.erl | 6 +++--- src/ejabberd_web.erl | 1 - src/mod_bosh_sql.erl | 2 +- src/mod_push_sql.erl | 2 +- src/mod_vcard_sql.erl | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl index bf3abe221..6f8c16727 100644 --- a/src/ejabberd_auth_jwt.erl +++ b/src/ejabberd_auth_jwt.erl @@ -44,9 +44,9 @@ %%%---------------------------------------------------------------------- start(Host) -> %% We add our default JWT verifier with hook priority 100. - %% So if you need to check or verify your custom JWT before the - %% default verifier, It's better to use this hook with priority - %% little than 100 and return bool() or {stop, bool()} in your own + %% So if you need to check or verify your custom JWT before the + %% default verifier, It's better to use this hook with priority + %% little than 100 and return bool() or {stop, bool()} in your own %% callback function. ejabberd_hooks:add(check_decoded_jwt, Host, ?MODULE, check_decoded_jwt, 100), case ejabberd_option:jwt_key(Host) of diff --git a/src/ejabberd_web.erl b/src/ejabberd_web.erl index d7867d90d..b9dcd7943 100644 --- a/src/ejabberd_web.erl +++ b/src/ejabberd_web.erl @@ -1,7 +1,6 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_web.erl %%% Author : Alexey Shchepin -%%% Purpose : %%% Purpose : %%% Created : 28 Feb 2004 by Alexey Shchepin %%% diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index 0e54ee56c..643928fe1 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : mod_bosh_sql.erl %%% Author : Evgeny Khramtsov -%%% Purpose : +%%% Purpose : %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index 8e4cc1f1c..0b4d84afd 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : mod_push_sql.erl %%% Author : Evgeniy Khramtsov -%%% Purpose : +%%% Purpose : %%% Created : 26 Oct 2017 by Evgeny Khramtsov %%% %%% diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index c67338f51..64e411ab8 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -263,7 +263,7 @@ remove_user(LUser, LServer) -> " where lusername=%(LUser)s and %(LServer)H")) end). -export(_Server) -> +export(_Server) -> [{vcard, fun(Host, #vcard{us = {LUser, LServer}, vcard = VCARD}) when LServer == Host -> From 74b80bfe083a9034207124e2d65fb01cc018bb73 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 11:05:29 +0200 Subject: [PATCH 0740/1302] Fix Elvis report: Don't use ignored variables https://github.com/inaka/elvis_core/blob/main/doc_rules/elvis_style/used_ignored_variable.md --- src/ejabberd_bosh.erl | 28 ++++++++++++++-------------- src/ejabberd_sql.erl | 4 ++-- src/ejabberd_websocket.erl | 4 ++-- src/mod_bosh.erl | 4 ++-- src/mod_fail2ban.erl | 8 ++++---- src/mod_matrix_gw.erl | 4 ++-- src/mod_offline_sql.erl | 4 ++-- src/mod_roster_sql.erl | 8 ++++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index f6ccb4887..c196c78ce 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -308,9 +308,9 @@ init([#body{attrs = Attrs}, IP, SID]) -> ignore end. -wait_for_session(_Event, State) -> +wait_for_session(Event, State) -> ?ERROR_MSG("Unexpected event in 'wait_for_session': ~p", - [_Event]), + [Event]), {next_state, wait_for_session, State}. wait_for_session(#body{attrs = Attrs} = Req, From, @@ -367,16 +367,16 @@ wait_for_session(#body{attrs = Attrs} = Req, From, reply_next_state(State4, Resp#body{els = RespEls}, RID, From) end; -wait_for_session(_Event, _From, State) -> +wait_for_session(Event, _From, State) -> ?ERROR_MSG("Unexpected sync event in 'wait_for_session': ~p", - [_Event]), + [Event]), {reply, {error, badarg}, wait_for_session, State}. active({#body{} = Body, From}, State) -> active1(Body, From, State); -active(_Event, State) -> +active(Event, State) -> ?ERROR_MSG("Unexpected event in 'active': ~p", - [_Event]), + [Event]), {next_state, active, State}. active(#body{attrs = Attrs, size = Size} = Req, From, @@ -408,9 +408,9 @@ active(#body{attrs = Attrs, size = Size} = Req, From, end; true -> active1(Req, From, State1) end; -active(_Event, _From, State) -> +active(Event, _From, State) -> ?ERROR_MSG("Unexpected sync event in 'active': ~p", - [_Event]), + [Event]), {reply, {error, badarg}, active, State}. active1(#body{attrs = Attrs} = Req, From, State) -> @@ -517,9 +517,9 @@ handle_event({activate, C2SPid}, StateName, handle_event({change_shaper, Shaper}, StateName, State) -> {next_state, StateName, State#state{shaper_state = Shaper}}; -handle_event(_Event, StateName, State) -> +handle_event(Event, StateName, State) -> ?ERROR_MSG("Unexpected event in '~ts': ~p", - [StateName, _Event]), + [StateName, Event]), {next_state, StateName, State}. handle_sync_event({send_xml, @@ -557,9 +557,9 @@ handle_sync_event(deactivate_socket, _From, StateName, StateData) -> {reply, ok, StateName, StateData#state{c2s_pid = undefined}}; -handle_sync_event(_Event, _From, StateName, State) -> +handle_sync_event(Event, _From, StateName, State) -> ?ERROR_MSG("Unexpected sync event in '~ts': ~p", - [StateName, _Event]), + [StateName, Event]), {reply, {error, badarg}, StateName, State}. handle_info({timeout, TRef, wait_timeout}, StateName, @@ -583,9 +583,9 @@ handle_info({timeout, TRef, shaper_timeout}, StateName, {stop, normal, State}; _ -> {next_state, StateName, State} end; -handle_info(_Info, StateName, State) -> +handle_info(Info, StateName, State) -> ?ERROR_MSG("Unexpected info:~n** Msg: ~p~n** StateName: ~p", - [_Info, StateName]), + [Info, StateName]), {next_state, StateName, State}. terminate(_Reason, _StateName, State) -> diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 53c83bc4a..117609182 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -183,8 +183,8 @@ keep_alive(Host, Proc) -> Timeout) of {selected,_,[[<<"1">>]]} -> ok; - _Err -> - ?ERROR_MSG("Keep alive query failed, closing connection: ~p", [_Err]), + Err -> + ?ERROR_MSG("Keep alive query failed, closing connection: ~p", [Err]), sync_send_event(Proc, force_timeout, Timeout) end. diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 57a3e9606..474a64c51 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -256,9 +256,9 @@ ws_loop(Codec, Socket, WsHandleLoopPid, SockMod, Shaper) -> "with pid ~p", [self()]), websocket_close(Codec, Socket, WsHandleLoopPid, SockMod, 1001); % going away - _Ignored -> + Ignored -> ?WARNING_MSG("Received unexpected message, ignoring: ~p", - [_Ignored]), + [Ignored]), ws_loop(Codec, Socket, WsHandleLoopPid, SockMod, Shaper) end. diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index 9b6e0eeaf..cd2fea99b 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -74,8 +74,8 @@ process([], #request{method = 'GET', data = <<>>}) -> {200, ?HEADER(?CT_XML), get_human_html_xmlel()}; process([], #request{method = 'OPTIONS', data = <<>>}) -> {200, ?OPTIONS_HEADER, []}; -process(_Path, _Request) -> - ?DEBUG("Bad Request: ~p", [_Request]), +process(_Path, Request) -> + ?DEBUG("Bad Request: ~p", [Request]), {400, ?HEADER(?CT_XML), #xmlel{name = <<"h1">>, attrs = [], children = [{xmlcdata, <<"400 Bad Request">>}]}}. diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index 3c15a0a59..3ed3a9810 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -139,8 +139,8 @@ handle_call(Request, From, State) -> ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), {noreply, State}. -handle_cast(_Msg, State) -> - ?WARNING_MSG("Unexpected cast = ~p", [_Msg]), +handle_cast(Msg, State) -> + ?WARNING_MSG("Unexpected cast = ~p", [Msg]), {noreply, State}. handle_info(clean, State) -> @@ -151,8 +151,8 @@ handle_info(clean, State) -> ets:fun2ms(fun({_, _, UnbanTS, _}) -> UnbanTS =< Now end)), erlang:send_after(?CLEAN_INTERVAL, self(), clean), {noreply, State}; -handle_info(_Info, State) -> - ?WARNING_MSG("Unexpected info = ~p", [_Info]), +handle_info(Info, State) -> + ?WARNING_MSG("Unexpected info = ~p", [Info]), {noreply, State}. terminate(_Reason, #state{host = Host}) -> diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 80142ff49..28f1ccac1 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -386,8 +386,8 @@ process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], {result, HTTPResult} -> HTTPResult end; -process(_Path, _Request) -> - ?DEBUG("matrix 404: ~p~n~p~n", [_Path, _Request]), +process(Path, Request) -> + ?DEBUG("matrix 404: ~p~n~p~n", [Path, Request]), ejabberd_web:error(not_found). preprocess_federation_request(Request) -> diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index 1f48b7ea9..ea4901539 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -127,8 +127,8 @@ remove_old_messages(Days, LServer) -> of {updated, N} -> ?INFO_MSG("~p message(s) deleted from offline spool", [N]); - _Error -> - ?ERROR_MSG("Cannot delete message in offline spool: ~p", [_Error]) + Error -> + ?ERROR_MSG("Cannot delete message in offline spool: ~p", [Error]) end, {atomic, ok}. diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 565861751..2f159d681 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -340,16 +340,16 @@ update_roster_sql({LUser, LServer, SJID, Name, SSubscription, SAsk, AskMessage}, raw_to_record(LServer, [User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType]) -> + SServer, SSubscribe, SType]) -> raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}); + SServer, SSubscribe, SType}); raw_to_record(LServer, {User, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}) -> + SServer, SSubscribe, SType}) -> raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, - _SServer, _SSubscribe, _SType}); + SServer, SSubscribe, SType}); raw_to_record(LServer, {User, LServer, SJID, Nick, SSubscription, SAsk, SAskMessage, _SServer, _SSubscribe, _SType}) -> From 0304428d958067b075b045e1c99e98f8d2a362e0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 11:28:54 +0200 Subject: [PATCH 0741/1302] Fix Elvis report: Remove spaces in weird places https://github.com/inaka/elvis_core/blob/main/doc_rules/elvis_style/no_space.md --- src/ejabberd_hooks.erl | 2 +- src/ejabberd_http_ws.erl | 2 +- src/ejabberd_old_config.erl | 2 +- src/mod_http_api.erl | 2 +- src/mod_mqtt_bridge.erl | 4 ++-- src/mod_muc_admin.erl | 2 +- src/mod_offline_mnesia.erl | 2 +- src/mod_pubsub.erl | 2 +- src/win32_dns.erl | 5 ++--- 9 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 11b5190f2..4ef95d0ff 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -80,7 +80,7 @@ start_link() -> add(Hook, Function, Seq) when is_function(Function) -> add(Hook, global, undefined, Function, Seq). --spec add(atom(), HostOrModule :: binary() | atom(), fun() | atom() , integer()) -> ok. +-spec add(atom(), HostOrModule :: binary() | atom(), fun() | atom(), integer()) -> ok. add(Hook, Host, Function, Seq) when is_function(Function) -> add(Hook, Host, undefined, Function, Seq); diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index e8bb42cd8..c4939008d 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -189,7 +189,7 @@ handle_sync_event({send_xml, Packet}, _From, StateName, El2 end end, - {xmlstreamelement , El3}; + {xmlstreamelement, El3}; _ -> Packet end, diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl index 832ac1517..cee8f6194 100644 --- a/src/ejabberd_old_config.erl +++ b/src/ejabberd_old_config.erl @@ -619,7 +619,7 @@ strings_to_binary(L) when is_list(L) -> end; strings_to_binary({A, B, C, D}) when is_integer(A), is_integer(B), is_integer(C), is_integer(D) -> - {A, B, C ,D}; + {A, B, C, D}; strings_to_binary(T) when is_tuple(T) -> list_to_tuple(strings_to_binary1(tuple_to_list(T))); strings_to_binary(X) -> diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index ac122b5dc..0ad9cfbe0 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -542,7 +542,7 @@ log(Call, Args, IP) -> ?INFO_MSG("API call ~ts ~p (~p)", [Call, hide_sensitive_args(Args), IP]). hide_sensitive_args(Args=[_H|_T]) -> - lists:map( fun({<<"password">>, Password}) -> {<<"password">>, ejabberd_config:may_hide_data(Password)}; + lists:map(fun({<<"password">>, Password}) -> {<<"password">>, ejabberd_config:may_hide_data(Password)}; ({<<"newpass">>,NewPassword}) -> {<<"newpass">>, ejabberd_config:may_hide_data(NewPassword)}; (E) -> E end, Args); diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 8ba69288d..94930049c 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -142,8 +142,8 @@ mod_opt_type(servers) -> econf:options( #{ certfile => econf:pem() - }, [{return, map}]) - )}, [{return, map}]), + }, [{return, map}]))}, + [{return, map}]), [{return, map}]), fun(Servers) -> maps:fold( diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 6d035a548..3fb112c2f 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1471,7 +1471,7 @@ get_room_occupants(Pid) -> get_room_occupants_number(Room, Host) -> case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of - {Pid, _, _} when is_pid(Pid )-> + {Pid, _, _} when is_pid(Pid)-> {ok, #{occupants_number := N}} = mod_muc_room:get_info(Pid), N; _ -> diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl index df9a96651..77839c30c 100644 --- a/src/mod_offline_mnesia.erl +++ b/src/mod_offline_mnesia.erl @@ -217,7 +217,7 @@ need_transform(_) -> transform({offline_msg, {U, S}, Timestamp, Expire, From, To, _, Packet}) -> #offline_msg{us = {U, S}, timestamp = Timestamp, expire = Expire, - from = From ,to = To, packet = Packet}; + from = From, to = To, packet = Packet}; transform(#offline_msg{us = {U, S}, from = From, to = To, packet = El} = R) -> R#offline_msg{us = {iolist_to_binary(U), iolist_to_binary(S)}, diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 0fd73cf39..f2ffade95 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4200,7 +4200,7 @@ delete_old_items(N) -> fun(#pubsub_node{id = Nidx, type = Type}) -> case node_action(Host, Type, remove_extra_items, - [Nidx , N]) of + [Nidx, N]) of {result, _} -> ok; {error, _} -> diff --git a/src/win32_dns.erl b/src/win32_dns.erl index cdbc6f4dd..3569e0217 100644 --- a/src/win32_dns.erl +++ b/src/win32_dns.erl @@ -39,9 +39,8 @@ get_nameservers() -> is_good_ns(Addr) -> element(1, inet_res:nnslookup("a.root-servers.net", in, a, [{Addr,53}], - timer:seconds(5) - ) - ) =:= ok. + timer:seconds(5))) + =:= ok. reg() -> {ok, R} = win32reg:open([read]), From db6d3e63f20fd85553db8bdf022c219a23ca0616 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 11:29:05 +0200 Subject: [PATCH 0742/1302] Fix Elvis report: Fix dollar space syntax https://github.com/inaka/elvis_core/blob/main/doc_rules/elvis_style/no_dollar_space.md --- src/ejabberd_logger.erl | 2 +- src/ejabberd_web_admin.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 3189860fc..ced5378fc 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -283,7 +283,7 @@ start(Level) -> burst_limit_window_time => LogBurstLimitWindowTime, burst_limit_max_count => LogBurstLimitCount}, FmtConfig = #{legacy_header => false, - time_designator => $ , + time_designator => $\s, max_size => 100*1024, single_line => false}, FileFmtConfig = FmtConfig#{template => file_template()}, diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 5264d1d1e..cc54915cf 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1180,7 +1180,7 @@ pretty_print_xml({xmlcdata, CData}, Prefix) -> ($\n) -> true; ($\t) -> true; ($\v) -> true; - ($ ) -> true; + ($\s) -> true; (_) -> false end, binary_to_list(CData)), if IsBlankCData -> From aa1717ee77d0a6ba022d1366e2a8e5f52893ce46 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 13:19:30 +0200 Subject: [PATCH 0743/1302] CI: Add Elvis tests --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f5e4fe57..582c3f8c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,6 +133,8 @@ jobs: - run: make xref - run: make dialyzer - run: make test-eunit + - run: make elvis + if: matrix.otp >= 23 - name: Check Production Release run: | From 795498fa455d4527ceedbc543e04d5e832068dff Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 Aug 2024 13:40:19 +0200 Subject: [PATCH 0744/1302] Runtime: Cache hex.pm archive from rebar3 and mix --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 582c3f8c0..21faf598e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: - name: Remove syntax_tools from release run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script - - name: Cache rebar + - name: Cache Hex.pm uses: actions/cache@v4 with: path: | diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 87f9328e6..f3d4a1ffd 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -61,6 +61,13 @@ jobs: apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ libsqlite3-dev libwebp-dev libyaml-dev + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.cache/rebar3/ + key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + - name: Compile run: | ./autogen.sh @@ -204,6 +211,13 @@ jobs: sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config cat rebar.config + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.cache/rebar3/ + key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + - name: Compile run: | ./autogen.sh @@ -347,6 +361,13 @@ jobs: run: | mix deps.unlock jose + - name: Cache Hex.pm + uses: actions/cache@v4 + with: + path: | + ~/.hex/ + key: ${{matrix.otp}}-${{hashFiles('mix.exs')}} + - name: Compile run: | ./autogen.sh From 5f47860ee17d0c0aee7e36404beedef10504e3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Sep 2024 11:55:46 +0200 Subject: [PATCH 0745/1302] Remove support for old websocket connection protocol This removes handling of pre-rfc7395 encapsulation of xmpp in websocket (where data send in websocket was using just raw data as send in regular socket). This didn't work correctly for last 5 years, and as we didn't see complains about this, we can assume it's not used anymore. --- src/ejabberd_http_ws.erl | 83 +++++++++++----------------------------- 1 file changed, 22 insertions(+), 61 deletions(-) diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index c4939008d..a807408dd 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -50,8 +50,7 @@ input = [] :: list(), active = false :: boolean(), c2s_pid :: pid(), - ws :: {#ws{}, pid()}, - rfc_compliant = undefined :: boolean() | undefined}). + ws :: {#ws{}, pid()}}). %-define(DBGFSM, true). @@ -166,18 +165,18 @@ handle_event({new_shaper, Shaper}, StateName, #state{ws = {_, WsPid}} = StateDat {next_state, StateName, StateData}. handle_sync_event({send_xml, Packet}, _From, StateName, - #state{ws = {_, WsPid}, rfc_compliant = R} = StateData) -> - Packet2 = case {case R of undefined -> true; V -> V end, Packet} of - {true, {xmlstreamstart, _, Attrs}} -> + #state{ws = {_, WsPid}} = StateData) -> + Packet2 = case Packet of + {xmlstreamstart, _, Attrs} -> Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], {xmlstreamelement, #xmlel{name = <<"open">>, attrs = Attrs2}}; - {true, {xmlstreamend, _}} -> + {xmlstreamend, _} -> {xmlstreamelement, #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}}; - {true, {xmlstreamraw, <<"\r\n\r\n">>}} -> % cdata ping + {xmlstreamraw, <<"\r\n\r\n">>} -> % cdata ping skip; - {true, {xmlstreamelement, #xmlel{name=Name2} = El2}} -> + {xmlstreamelement, #xmlel{name=Name2} = El2} -> El3 = case Name2 of <<"stream:", _/binary>> -> fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); @@ -189,9 +188,7 @@ handle_sync_event({send_xml, Packet}, _From, StateName, El2 end end, - {xmlstreamelement, El3}; - _ -> - Packet + {xmlstreamelement, El3} end, case Packet2 of {xmlstreamstart, Name, Attrs3} -> @@ -215,13 +212,11 @@ handle_sync_event({send_xml, Packet}, _From, StateName, StateName end, {reply, ok, SN2, StateData}; -handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compliant = true} = StateData) +handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}} = StateData) when StateName /= stream_end_sent -> Close = #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}, route_text(WsPid, fxml:element_to_binary(Close)), - {stop, normal, StateData}; -handle_sync_event(close, _From, _StateName, StateData) -> {stop, normal, StateData}. handle_info(closed, _StateName, StateData) -> @@ -313,53 +308,19 @@ get_human_html_xmlel() -> "client that supports it.">>}]}]}]}. -parse(#state{rfc_compliant = C} = State, Data) -> - case C of - undefined -> - P = fxml_stream:new(self()), - P2 = fxml_stream:parse(P, Data), - fxml_stream:close(P2), - case parsed_items([]) of - error -> - {State#state{rfc_compliant = true}, <<"parse error">>}; - [] -> - {State#state{rfc_compliant = true}, <<"parse error">>}; - [{xmlstreamstart, <<"open">>, _} | _] -> - parse(State#state{rfc_compliant = true}, Data); - _ -> - parse(State#state{rfc_compliant = false}, Data) - end; - true -> - El = fxml_stream:parse_element(Data), - case El of - #xmlel{name = <<"open">>, attrs = Attrs} -> - Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} | - lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], - {State, [{xmlstreamstart, <<"stream:stream">>, Attrs2}]}; - #xmlel{name = <<"close">>} -> - {State, [{xmlstreamend, <<"stream:stream">>}]}; - {error, _} -> - {State, <<"parse error">>}; - _ -> - {State, [El]} - end; - false -> - {State, Data} - end. - -parsed_items(List) -> - receive - {'$gen_event', El} - when element(1, El) == xmlel; - element(1, El) == xmlstreamstart; - element(1, El) == xmlstreamelement; - element(1, El) == xmlstreamcdata; - element(1, El) == xmlstreamend -> - parsed_items([El | List]); - {'$gen_event', {xmlstreamerror, _}} -> - error - after 0 -> - lists:reverse(List) +parse(State, Data) -> + El = fxml_stream:parse_element(Data), + case El of + #xmlel{name = <<"open">>, attrs = Attrs} -> + Attrs2 = [{<<"xmlns:stream">>, ?NS_STREAM}, {<<"xmlns">>, <<"jabber:client">>} | + lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], + {State, [{xmlstreamstart, <<"stream:stream">>, Attrs2}]}; + #xmlel{name = <<"close">>} -> + {State, [{xmlstreamend, <<"stream:stream">>}]}; + {error, _} -> + {State, [{xmlstreamerror, {4, <<"not well-formed">>}}]}; + _ -> + {State, [El]} end. -spec route_text(pid(), binary()) -> ok. From 017b2feac195e67775135b9342cab5794c830317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Sep 2024 12:01:56 +0200 Subject: [PATCH 0746/1302] Make `set_presence` command return error when session not found Should fix issue #4274 --- src/mod_admin_extra.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index ca7edb486..0c761cc8d 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1425,8 +1425,10 @@ set_presence(User, Host, Resource, Type, Show, Status, Priority) -> show = misc:binary_to_atom(Show), priority = Priority, sub_els = []}, - Ref = ejabberd_sm:get_session_pid(User, Host, Resource), - ejabberd_c2s:set_presence(Ref, Pres). + case ejabberd_sm:get_session_pid(User, Host, Resource) of + none -> throw({error, "User session not found"}); + Ref -> ejabberd_c2s:set_presence(Ref, Pres) + end. user_sessions_info(User, Host) -> lists:filtermap(fun(Resource) -> From b455d93c697de6ee1607c592c20d9e51453cb28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Sep 2024 12:42:55 +0200 Subject: [PATCH 0747/1302] Fix dialyzer warnings --- src/ejabberd_http_ws.erl | 85 +++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index a807408dd..0ce51d17b 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -165,58 +165,43 @@ handle_event({new_shaper, Shaper}, StateName, #state{ws = {_, WsPid}} = StateDat {next_state, StateName, StateData}. handle_sync_event({send_xml, Packet}, _From, StateName, - #state{ws = {_, WsPid}} = StateData) -> - Packet2 = case Packet of - {xmlstreamstart, _, Attrs} -> - Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | - lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], - {xmlstreamelement, #xmlel{name = <<"open">>, attrs = Attrs2}}; - {xmlstreamend, _} -> - {xmlstreamelement, #xmlel{name = <<"close">>, - attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}}; - {xmlstreamraw, <<"\r\n\r\n">>} -> % cdata ping - skip; - {xmlstreamelement, #xmlel{name=Name2} = El2} -> - El3 = case Name2 of - <<"stream:", _/binary>> -> - fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); - _ -> - case fxml:get_tag_attr_s(<<"xmlns">>, El2) of - <<"">> -> - fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2); - _ -> - El2 - end - end, - {xmlstreamelement, El3} - end, - case Packet2 of - {xmlstreamstart, Name, Attrs3} -> - B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}), - route_text(WsPid, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>); - {xmlstreamend, Name} -> - route_text(WsPid, <<"">>); - {xmlstreamelement, El} -> - route_text(WsPid, fxml:element_to_binary(El)); - {xmlstreamraw, Bin} -> - route_text(WsPid, Bin); - {xmlstreamcdata, Bin2} -> - route_text(WsPid, Bin2); - skip -> - ok - end, - SN2 = case Packet2 of - {xmlstreamelement, #xmlel{name = <<"close">>}} -> - stream_end_sent; - _ -> - StateName - end, + #state{ws = {_, WsPid}} = StateData) -> + SN2 = case Packet of + {xmlstreamstart, _, Attrs} -> + Attrs2 = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>} | + lists:keydelete(<<"xmlns">>, 1, lists:keydelete(<<"xmlns:stream">>, 1, Attrs))], + route_el(WsPid, #xmlel{name = <<"open">>, attrs = Attrs2}), + StateName; + {xmlstreamend, _} -> + route_el(WsPid, #xmlel{name = <<"close">>, + attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}), + stream_end_sent; + {xmlstreamraw, <<"\r\n\r\n">>} -> + % cdata ping + StateName; + {xmlstreamelement, #xmlel{name = Name2} = El2} -> + El3 = case Name2 of + <<"stream:", _/binary>> -> + fxml:replace_tag_attr(<<"xmlns:stream">>, ?NS_STREAM, El2); + _ -> + case fxml:get_tag_attr_s(<<"xmlns">>, El2) of + <<"">> -> + fxml:replace_tag_attr(<<"xmlns">>, <<"jabber:client">>, El2); + _ -> + El2 + end + end, + route_el(WsPid, El3), + StateName + end, {reply, ok, SN2, StateData}; handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}} = StateData) - when StateName /= stream_end_sent -> + when StateName /= stream_end_sent -> Close = #xmlel{name = <<"close">>, attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]}, route_text(WsPid, fxml:element_to_binary(Close)), + {stop, normal, StateData}; +handle_sync_event(close, _From, _StateName, StateData) -> {stop, normal, StateData}. handle_info(closed, _StateName, StateData) -> @@ -225,7 +210,7 @@ handle_info({received, Packet}, StateName, StateDataI) -> {StateData, Parsed} = parse(StateDataI, Packet), SD = case StateData#state.active of false -> - Input = StateData#state.input ++ if is_binary(Parsed) -> [Parsed]; true -> Parsed end, + Input = StateData#state.input ++ Parsed, StateData#state{input = Input}; true -> StateData#state.c2s_pid ! {tcp, StateData#state.socket, Parsed}, @@ -330,3 +315,7 @@ route_text(Pid, Data) -> {text_reply, Pid} -> ok end. + +-spec route_el(pid(), xmlel() | cdata()) -> ok. +route_el(Pid, Data) -> + route_text(Pid, fxml:element_to_binary(Data)). From 3c896d1c6a35f0ebc5aac9de51d3f8dd4f18826e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Sep 2024 12:49:39 +0200 Subject: [PATCH 0748/1302] Better handling of malformed jids in `send_direct_invitation` command --- src/mod_muc_admin.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 3fb112c2f..313a4721b 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1501,8 +1501,10 @@ send_direct_invitation(RoomName, RoomService, Password, Reason, UsersStrings) -> get_users_to_invite(RoomJid, UsersStrings) -> OccupantsTuples = get_room_occupants(RoomJid#jid.luser, RoomJid#jid.lserver), - OccupantsJids = [jid:decode(JidString) - || {JidString, _Nick, _} <- OccupantsTuples], + OccupantsJids = try [jid:decode(JidString) + || {JidString, _Nick, _} <- OccupantsTuples] + catch _:{bad_jid, _} -> throw({error, "Malformed JID of invited user"}) + end, lists:filtermap( fun(UserString) -> UserJid = jid:decode(UserString), From 70512c711622596e8834a7cf084354cb9d7d0355 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 6 Sep 2024 19:05:12 +0200 Subject: [PATCH 0749/1302] make-binaries: Bump dependency versions --- tools/make-binaries | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index f2850b530..05236ee22 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,19 +67,19 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.26.0' termcap_vsn='1.3.1' -expat_vsn='2.6.2' +expat_vsn='2.6.3' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.3.1' -otp_vsn='26.2.5' -elixir_vsn='1.16.3' +ssl_vsn='3.3.2' +otp_vsn='26.2.5.3' +elixir_vsn='1.17.2' pam_vsn='1.6.1' -png_vsn='1.6.42' +png_vsn='1.6.43' jpeg_vsn='9f' -webp_vsn='1.3.2' +webp_vsn='1.4.0' gd_vsn='2.3.3' odbc_vsn='2.3.12' -sqlite_vsn='3450100' +sqlite_vsn='3460100' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From e3243fa35bbc263b9a52ff3392ab9acd11638d7a Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 6 Sep 2024 19:18:32 +0200 Subject: [PATCH 0750/1302] make-binaries: Update OpenSSL URLs --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 05236ee22..9c5ca7676 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -179,7 +179,7 @@ check_vsn() check_configured_dep_vsns() { check_vsn 'OpenSSL' "$ssl_vsn" \ - 'https://www.openssl.org/source/' \ + 'https://openssl-library.org/source/' \ 'openssl-\(3\.[1-9]\.[0-9.]*\)\.tar\.gz' check_vsn 'LibYAML' "$yaml_vsn" \ 'https://pyyaml.org/wiki/LibYAML' \ @@ -878,7 +878,7 @@ else curl -fsSLO "https://github.com/libexpat/libexpat/releases/download/R_$(printf '%s' "$expat_vsn" | sed 's/\./_/g')/$expat_tar" curl -fsSLO "https://zlib.net/fossils/$zlib_tar" curl -fsSLO "https://pyyaml.org/download/libyaml/$yaml_tar" - curl -fsSLO "https://www.openssl.org/source/$ssl_tar" + curl -fsSLO "https://github.com/openssl/openssl/releases/download/openssl-$ssl_vsn/$ssl_tar" curl -fsSLO "https://github.com/erlang/otp/releases/download/OTP-$otp_vsn/$otp_tar" curl -fsSLO "https://github.com/elixir-lang/elixir/archive/v$elixir_vsn.tar.gz" curl -fsSLO "https://github.com/linux-pam/linux-pam/releases/download/v$pam_vsn/$pam_tar" From 941d51a6e73234dab389b7e5328281f2d7761c8a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Sep 2024 17:05:40 +0200 Subject: [PATCH 0751/1302] Handle call by gen_event:swap_handler (#4233) --- src/ejabberd_system_monitor.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl index 8bcf81db8..4e24f4e8c 100644 --- a/src/ejabberd_system_monitor.erl +++ b/src/ejabberd_system_monitor.erl @@ -82,7 +82,9 @@ config_reloaded() -> %%%=================================================================== %%% gen_event callbacks %%%=================================================================== -init([]) -> +init({[], _}) -> % Called by gen_event:swap_handler + {ok, #state{}}; +init([]) -> % Called by gen_event:add_handler ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), {ok, #state{}}. From eec836239f5b375e199d1ae5186b3e174e319346 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 9 Sep 2024 18:42:34 +0200 Subject: [PATCH 0752/1302] Improve documentation of ldap_servers and ldap_backups options (#3977) --- src/ejabberd_options_doc.erl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index add6371e6..a4ba40095 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -750,19 +750,22 @@ doc() -> {ldap_servers, #{value => "[Host, ...]", desc => - ?T("A list of IP addresses or DNS names of your LDAP servers. " + ?T("A list of IP addresses or DNS names of your LDAP servers (see " + "_`../configuration/ldap.md#ldap-connection|LDAP connection`_). " + "ejabberd connects immediately to all of them, " + "and reconnects infinitely if connection is lost. " "The default value is '[localhost]'.")}}, {ldap_backups, #{value => "[Host, ...]", desc => - ?T("A list of IP addresses or DNS names of LDAP backup servers. " + ?T("A list of IP addresses or DNS names of LDAP backup servers (see " + "_`../configuration/ldap.md#ldap-connection|LDAP connection`_). " "When no servers listed in _`ldap_servers`_ option are reachable, " - "ejabberd will try to connect to these backup servers. " + "ejabberd connects to these backup servers. " "The default is an empty list, i.e. no backup servers specified. " - "WARNING: ejabberd doesn't try to reconnect back to the main " - "servers when they become operational again, so the only way " - "to restore these connections is to restart ejabberd. This " - "limitation might be fixed in future releases.")}}, + "Please notice that ejabberd only connects to the next server " + "when the existing connection is lost; it doesn't detect when a " + "previously-attempted server becomes available again.")}}, {ldap_encrypt, #{value => "tls | none", desc => From cc377bbebfab91766393ee07a37e4e5caff2d17f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Sep 2024 11:34:20 +0200 Subject: [PATCH 0753/1302] Update lock files --- mix.lock | 4 ++-- rebar.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index 9b98d333e..065ae647e 100644 --- a/mix.lock +++ b/mix.lock @@ -10,7 +10,7 @@ "esip": {:hex, :esip, "1.0.54", "dae8fb8278fd3b2c0d38c2e832c4b8d26700eb239b9a42c8ea574fee76f5e76a", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.14", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "8187af819d7259cdaddaf69726c239ef604c9b0b0298a5f2d3e687bf5e2237ee"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, - "fast_tls": {:hex, :fast_tls, "1.1.21", "65d7d547a09eefb37a1c0d04d8601fac4f3e6e2c1ede859a7787081670f9648d", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "131542913937025e48cd80aa81f00359686d5501b75621e72026a87b5229505b"}, + "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f", [ref: "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"]}, "fast_xml": {:git, "https://github.com/processone/fast_xml.git", "e7dc91310046831f436a03abf029587f0c2764f4", [ref: "e7dc91310046831f436a03abf029587f0c2764f4"]}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, @@ -32,6 +32,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.8.3", "acf39a8b70b066bb8f10b0862e031e8abcf92fe89d1c41d06c1e39ae5caf89c4", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ed70065f9a89a818dcff43b74c080c9e7f4f1414e1051beddb7280db809af711"}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "2a54443436dc8a942969f2ef7c5654d5acab7533", [ref: "2a54443436dc8a942969f2ef7c5654d5acab7533"]}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.lock b/rebar.lock index f2f50b144..b539c60b8 100644 --- a/rebar.lock +++ b/rebar.lock @@ -16,7 +16,7 @@ 0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, From d4b30957a384a54941cefc869cf6c3a04b2769bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 11 Sep 2024 13:20:53 +0200 Subject: [PATCH 0754/1302] Skip non-delivery errors for local pubsub generated notifications Those are ignored by pubsub service anyway, so we can skip those, and reduce number of messages processed by pubsub process. --- src/ejabberd_sm.erl | 12 ++++++++++++ src/mod_pubsub.erl | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 91fcc5896..4db90e5ee 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -206,6 +206,18 @@ bounce_offline_message(Acc) -> Acc. -spec bounce_sm_packet({bounce | term(), stanza()}) -> any(). +bounce_sm_packet({bounce, #message{meta = #{ignore_sm_bounce := true}} = Packet} = Acc) -> + ?DEBUG("Dropping packet to unavailable resource:~n~ts", + [xmpp:pp(Packet)]), + Acc; +bounce_sm_packet({bounce, #iq{meta = #{ignore_sm_bounce := true}} = Packet} = Acc) -> + ?DEBUG("Dropping packet to unavailable resource:~n~ts", + [xmpp:pp(Packet)]), + Acc; +bounce_sm_packet({bounce, #presence{meta = #{ignore_sm_bounce := true}} = Packet} = Acc) -> + ?DEBUG("Dropping packet to unavailable resource:~n~ts", + [xmpp:pp(Packet)]), + Acc; bounce_sm_packet({bounce, Packet} = Acc) -> Lang = xmpp:get_lang(Packet), Txt = ?T("User session not found"), diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index f2ffade95..650f59684 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3029,7 +3029,8 @@ broadcast_stanza(Host, _Node, _Nidx, _Type, NodeOptions, SubsByDepth, NotifyType end, lists:foreach(fun(To) -> ejabberd_router:route( - xmpp:set_to(StanzaToSend, jid:make(To))) + xmpp:set_to(xmpp:put_meta(StanzaToSend, ignore_sm_bounce, true), + jid:make(To))) end, LJIDs) end, SubIDsByJID). @@ -3052,7 +3053,8 @@ broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, Nidx, Type, NodeO Pred = fun(To) -> delivery_permitted(Owner, To, NodeOptions) end, ejabberd_sm:route(jid:make(LUser, LServer, SenderResource), {pep_message, <<((Node))/binary, "+notify">>, Stanza, Pred}), - ejabberd_router:route(xmpp:set_to(Stanza, jid:make(LUser, LServer))); + ejabberd_router:route(xmpp:set_to(xmpp:put_meta(Stanza, ignore_sm_bounce, true), + jid:make(LUser, LServer))); broadcast_stanza(Host, _Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM). From 3469a51f5896ef96b2e53d61fcfd2163b92ad11a Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 13 Sep 2024 20:50:00 +0200 Subject: [PATCH 0755/1302] mod_pubsub: Don't blindly echo PEP notification Since commit 514c25caef8dac65761075e1ce7d8118d34809d0, whenever a PEP item was published, a notification was blindly sent back to the owner. However, this should only be done subject to +notify filtering, as per XEP-0163: | the PEP service shall send notifications to all of the account owner's | available resources (subject to notification filtering). The motivation for the mentioned commit was that +notify subscriptions initially didn't work for PEP node owners (#2108). However, that issue was fixed by commit 5968bc9318bec80aef1c31d46d247c13bdbc4c0e (#2112). As a result, the owner's client was actually notified twice if the client was subscribed to the node (e.g., via +notify). Therefore, just omit the additional, blind notification. Thanks to W. Martin Borgert and Daniel Gultsch for reporting the issue. --- src/mod_pubsub.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 650f59684..9fe4ce949 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3052,9 +3052,7 @@ broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, Nidx, Type, NodeO extended_headers([Publisher])), Pred = fun(To) -> delivery_permitted(Owner, To, NodeOptions) end, ejabberd_sm:route(jid:make(LUser, LServer, SenderResource), - {pep_message, <<((Node))/binary, "+notify">>, Stanza, Pred}), - ejabberd_router:route(xmpp:set_to(xmpp:put_meta(Stanza, ignore_sm_bounce, true), - jid:make(LUser, LServer))); + {pep_message, <<((Node))/binary, "+notify">>, Stanza, Pred}); broadcast_stanza(Host, _Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) -> broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM). From 3d9a5a1635ff75e40ef676ce75a705ec5c6fec55 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 14 Sep 2024 21:54:01 +0200 Subject: [PATCH 0756/1302] Fix 'update' command output So far, ejabberd_update:update/0 returned the return value of release_handler_1:eval_script/1. That function returns the list of updated but unpurged modules, i.e., modules where one or more processes are still running an old version of the code. Since commit 5a34020d23f455f80a144bcb0d8ee94770c0dbb1, the ejabberd 'update' command assumes that value to be the list of updated modules instead. As that seems more useful, modify ejabberd_update:update/0 accordingly. This fixes the 'update' command output. --- src/ejabberd_admin.erl | 2 +- src/ejabberd_update.erl | 34 +++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 37d5e622f..7d55ce33e 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -746,7 +746,7 @@ update_module(ModuleNameString) -> case ejabberd_update:update([ModuleName]) of {ok, []} -> {ok, "Not updated: "++ModuleNameString}; - {ok, [{ModuleName, _}]} -> + {ok, [ModuleName]} -> {ok, "Updated: "++ModuleNameString}; {error, Reason} -> {error, Reason} end. diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index c9cbd906e..17f4717dc 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -38,13 +38,17 @@ %% Update all the modified modules update() -> case update_info() of - {ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} -> - Eval = - eval_script( - LowLevelScript, [], - [{ejabberd, "", filename:join(Dir, "..")}]), - ?DEBUG("Eval: ~p~n", [Eval]), - Eval; + {ok, Dir, UpdatedBeams, _Script, LowLevelScript, _Check} -> + case eval_script( + LowLevelScript, [], + [{ejabberd, "", filename:join(Dir, "..")}]) of + {ok, _} -> + ?DEBUG("Updated: ~p~n", [UpdatedBeams]), + {ok, UpdatedBeams}; + Eval -> + ?DEBUG("Eval: ~p~n", [Eval]), + Eval + end; {error, Reason} -> {error, Reason} end. @@ -56,12 +60,16 @@ update(ModulesToUpdate) -> UpdatedBeamsNow = [A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B], {_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow), - Eval = - eval_script( - LowLevelScript, [], - [{ejabberd, "", filename:join(Dir, "..")}]), - ?DEBUG("Eval: ~p~n", [Eval]), - Eval; + case eval_script( + LowLevelScript, [], + [{ejabberd, "", filename:join(Dir, "..")}]) of + {ok, _} -> + ?DEBUG("Updated: ~p~n", [UpdatedBeamsNow]), + {ok, UpdatedBeamsNow}; + Eval -> + ?DEBUG("Eval: ~p~n", [Eval]), + Eval + end; {error, Reason} -> {error, Reason} end. From ce5a8acaf7272902cd83837df7faa349361ce5a2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Sep 2024 17:03:50 +0200 Subject: [PATCH 0757/1302] Define the types of options that opt_type.sh cannot derive automatically --- src/ejabberd_options.erl | 1 + src/mod_matrix_gw.erl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index c30cc072f..66633d107 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -491,6 +491,7 @@ opt_type(jwt_auth_only_rule) -> {c2s_protocol_options, undefined | binary()} | {s2s_ciphers, undefined | binary()} | {c2s_ciphers, undefined | binary()} | + {captcha_cmd, undefined | binary()} | {websocket_origin, [binary()]} | {disable_sasl_mechanisms, [binary()]} | {s2s_zlib, boolean()} | diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 28f1ccac1..6cde2a7a3 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -873,6 +873,9 @@ mod_opt_type(matrix_id_as_jid) -> mod_opt_type(persist) -> econf:bool(). +-spec mod_options(binary()) -> [{key, {binary(), binary()}} | + {atom(), any()}]. + mod_options(Host) -> [{matrix_domain, Host}, {host, <<"matrix.", Host/binary>>}, From 642e7ecc29f02f06e27a55eaac348c91555be9bb Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Sep 2024 17:46:33 +0200 Subject: [PATCH 0758/1302] mod_matrix_gw: Remove useless option "persist" --- src/mod_matrix_gw.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 6cde2a7a3..f65131de2 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -869,8 +869,6 @@ mod_opt_type(key) -> crypto:generate_key(eddsa, ed25519, Key2) end; mod_opt_type(matrix_id_as_jid) -> - econf:bool(); -mod_opt_type(persist) -> econf:bool(). -spec mod_options(binary()) -> [{key, {binary(), binary()}} | @@ -881,8 +879,7 @@ mod_options(Host) -> {host, <<"matrix.", Host/binary>>}, {key_name, <<"">>}, {key, {<<"">>, <<"">>}}, - {matrix_id_as_jid, false}, - {persist, false}]. + {matrix_id_as_jid, false}]. mod_doc() -> #{desc => From 512285e48d182d93711164e355accbd943d326f1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Sep 2024 17:04:51 +0200 Subject: [PATCH 0759/1302] Result of running "make options" --- src/ejabberd_option.erl | 2 +- src/mod_matrix_gw_opt.erl | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index bc0571e81..68e959288 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -330,7 +330,7 @@ cache_size() -> cache_size(Host) -> ejabberd_config:get_option({cache_size, Host}). --spec captcha_cmd() -> any(). +-spec captcha_cmd() -> 'undefined' | binary(). captcha_cmd() -> ejabberd_config:get_option({captcha_cmd, global}). diff --git a/src/mod_matrix_gw_opt.erl b/src/mod_matrix_gw_opt.erl index 4648896f9..dbfc8526d 100644 --- a/src/mod_matrix_gw_opt.erl +++ b/src/mod_matrix_gw_opt.erl @@ -8,7 +8,6 @@ -export([key_name/1]). -export([matrix_domain/1]). -export([matrix_id_as_jid/1]). --export([persist/1]). -spec host(gen_mod:opts() | global | binary()) -> binary(). host(Opts) when is_map(Opts) -> @@ -16,7 +15,7 @@ host(Opts) when is_map(Opts) -> host(Host) -> gen_mod:get_module_opt(Host, mod_matrix_gw, host). --spec key(gen_mod:opts() | global | binary()) -> any(). +-spec key(gen_mod:opts() | global | binary()) -> {binary(),binary()}. key(Opts) when is_map(Opts) -> gen_mod:get_opt(key, Opts); key(Host) -> @@ -40,9 +39,3 @@ matrix_id_as_jid(Opts) when is_map(Opts) -> matrix_id_as_jid(Host) -> gen_mod:get_module_opt(Host, mod_matrix_gw, matrix_id_as_jid). --spec persist(gen_mod:opts() | global | binary()) -> boolean(). -persist(Opts) when is_map(Opts) -> - gen_mod:get_opt(persist, Opts); -persist(Host) -> - gen_mod:get_module_opt(Host, mod_matrix_gw, persist). - From d9ddbe0212bf19d45a5111c8fe8aa2bcd1349827 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Sep 2024 18:37:49 +0200 Subject: [PATCH 0760/1302] Add mam and offline tags to the related purge commands --- src/ejabberd_admin.erl | 12 ++++++------ src/mod_mam.erl | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 7d55ce33e..3834ace47 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -370,17 +370,17 @@ get_commands_spec() -> args = [{out, string}], result = {res, rescode}}, - #ejabberd_commands{name = delete_expired_messages, tags = [purge], + #ejabberd_commands{name = delete_expired_messages, tags = [offline, purge], desc = "Delete expired offline messages from database", module = ?MODULE, function = delete_expired_messages, args = [], result = {res, rescode}}, - #ejabberd_commands{name = delete_old_messages, tags = [purge], + #ejabberd_commands{name = delete_old_messages, tags = [offline, purge], desc = "Delete offline messages older than DAYS", module = ?MODULE, function = delete_old_messages, args_desc = ["Number of days"], args_example = [31], args = [{days, integer}], result = {res, rescode}}, - #ejabberd_commands{name = delete_old_messages_batch, tags = [purge], + #ejabberd_commands{name = delete_old_messages_batch, tags = [offline, purge], desc = "Delete offline messages older than DAYS", note = "added in 22.05", module = ?MODULE, function = delete_old_messages_batch, @@ -393,7 +393,7 @@ get_commands_spec() -> result = {res, restuple}, result_desc = "Result tuple", result_example = {ok, <<"Removal of 5000 messages in progress">>}}, - #ejabberd_commands{name = delete_old_messages_status, tags = [purge], + #ejabberd_commands{name = delete_old_messages_status, tags = [offline, purge], desc = "Status of delete old offline messages operation", note = "added in 22.05", module = ?MODULE, function = delete_old_messages_status, @@ -403,7 +403,7 @@ get_commands_spec() -> result = {status, string}, result_desc = "Status test", result_example = "Operation in progress, delete 5000 messages"}, - #ejabberd_commands{name = abort_delete_old_messages, tags = [purge], + #ejabberd_commands{name = abort_delete_old_messages, tags = [offline, purge], desc = "Abort currently running delete old offline messages operation", note = "added in 22.05", module = ?MODULE, function = delete_old_messages_abort, @@ -528,7 +528,7 @@ get_commands_spec() -> module = ejabberd_doc, function = man, args = [], result = {res, restuple}}, - #ejabberd_commands{name = webadmin_host_user_queue, tags = [internal], + #ejabberd_commands{name = webadmin_host_user_queue, tags = [offline, internal], desc = "Generate WebAdmin offline queue HTML", module = mod_offline, function = webadmin_host_user_queue, args = [{user, binary}, {host, binary}, {query, any}, {lang, binary}], diff --git a/src/mod_mam.erl b/src/mod_mam.erl index ea53dfc85..f347e2a9f 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1487,7 +1487,8 @@ get_jids(Js) -> [jid:tolower(jid:remove_resource(J)) || J <- Js]. get_commands_spec() -> - [#ejabberd_commands{name = delete_old_mam_messages, tags = [purge], + [ + #ejabberd_commands{name = delete_old_mam_messages, tags = [mam, purge], desc = "Delete MAM messages older than DAYS", longdesc = "Valid message TYPEs: " "`chat`, `groupchat`, `all`.", @@ -1497,7 +1498,7 @@ get_commands_spec() -> args_example = [<<"all">>, 31], args = [{type, binary}, {days, integer}], result = {res, rescode}}, - #ejabberd_commands{name = delete_old_mam_messages_batch, tags = [purge], + #ejabberd_commands{name = delete_old_mam_messages_batch, tags = [mam, purge], desc = "Delete MAM messages older than DAYS", note = "added in 22.05", longdesc = "Valid message TYPEs: " @@ -1513,7 +1514,7 @@ get_commands_spec() -> result = {res, restuple}, result_desc = "Result tuple", result_example = {ok, <<"Removal of 5000 messages in progress">>}}, - #ejabberd_commands{name = delete_old_mam_messages_status, tags = [purge], + #ejabberd_commands{name = delete_old_mam_messages_status, tags = [mam, purge], desc = "Status of delete old MAM messages operation", note = "added in 22.05", module = ?MODULE, function = delete_old_messages_status, @@ -1523,7 +1524,7 @@ get_commands_spec() -> result = {status, string}, result_desc = "Status test", result_example = "Operation in progress, delete 5000 messages"}, - #ejabberd_commands{name = abort_delete_old_mam_messages, tags = [purge], + #ejabberd_commands{name = abort_delete_old_mam_messages, tags = [mam, purge], desc = "Abort currently running delete old MAM messages operation", note = "added in 22.05", module = ?MODULE, function = delete_old_messages_abort, From 115e7d08aa194032314d05de68223a4bcbffe7c6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 12 Sep 2024 22:16:54 +0200 Subject: [PATCH 0761/1302] Add links in user page to offline and roster pages --- src/mod_offline.erl | 4 +++- src/mod_roster.erl | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 516b3ba4a..a624de363 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1002,7 +1002,9 @@ get_queue_length(LUser, LServer) -> count_offline_messages(LUser, LServer). webadmin_user(Acc, User, Server, R) -> - Acc ++ [make_command(get_offline_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])]. + Acc ++ [make_command(get_offline_count, R, [{<<"user">>, User}, {<<"host">>, Server}], + [{result_links, [{value, arg_host, 4, <<"user/", User/binary, "/queue/">>}]}] + )]. %%% %%% diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 902986943..f093e118f 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -1096,7 +1096,11 @@ make_webadmin_roster_table(Host, Username, R, RPath) -> webadmin_user(Acc, User, Server, R) -> Acc - ++ [make_command(get_roster_count, R, [{<<"user">>, User}, {<<"host">>, Server}], [])]. + ++ [make_command(get_roster_count, + R, + [{<<"user">>, User}, {<<"host">>, Server}], + [{result_links, + [{value, arg_host, 4, <<"user/", User/binary, "/roster/">>}]}])]. %%% @format-end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 2437dc4e06d6ba6199b61392e313b7780608184f Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 12 Sep 2024 22:22:19 +0200 Subject: [PATCH 0762/1302] New command get_mam_count to get number of archived messages for an account --- src/mod_mam.erl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index f347e2a9f..8a6dd2cbe 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -45,6 +45,7 @@ mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2, is_empty_for_user/2, is_empty_for_room/3, check_create_room/4, process_iq/3, store_mam_message/7, make_id/0, wrap_as_mucsub/2, select/7, + get_mam_count/2, delete_old_messages_batch/5, delete_old_messages_status/1, delete_old_messages_abort/1, remove_message_from_archive/3]). @@ -614,6 +615,20 @@ message_is_archived(false, #{lserver := LServer}, Pkt) -> false end. +%%% +%%% Commands +%%% + +get_mam_count(User, Host) -> + Jid = jid:make(User, Host), + {_, _, Count} = select(Host, Jid, Jid, [], #rsm_set{}, chat, only_count), + Count. + + +%%% +%%% Commands: Purge +%%% + delete_old_messages_batch(Server, Type, Days, BatchSize, Rate) when Type == <<"chat">>; Type == <<"groupchat">>; Type == <<"all">> -> @@ -1488,6 +1503,16 @@ get_jids(Js) -> get_commands_spec() -> [ + #ejabberd_commands{name = get_mam_count, tags = [mam], + desc = "Get number of MAM messages in a local user archive", + module = ?MODULE, function = get_mam_count, + note = "added in 24.xx", + policy = user, + args = [], + result_example = 5, + result_desc = "Number", + result = {value, integer}}, + #ejabberd_commands{name = delete_old_mam_messages, tags = [mam, purge], desc = "Delete MAM messages older than DAYS", longdesc = "Valid message TYPEs: " From b2e6749fd28a154d2b5c42e4c51d40b5f718dae5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Sep 2024 16:49:11 +0200 Subject: [PATCH 0763/1302] Fix dialyzer: captcha_cmd is a binary for sure --- src/ejabberd_captcha.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 8514f3cd3..6ff0a886a 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -616,8 +616,7 @@ return(Port, TRef, Result) -> is_feature_available() -> case get_prog_name() of - Prog when is_binary(Prog) -> true; - MF when is_list(MF) -> true; + PathOrModule when is_binary(PathOrModule) -> true; false -> false end. From c900f0ad83e7e37eb24da0cf1b99148165c0ffe9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 13 Sep 2024 12:48:07 +0200 Subject: [PATCH 0764/1302] WebAdmin: Improve pages to handle disabled modules --- src/ejabberd_web_admin.erl | 145 ++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 43 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index cc54915cf..9dce1ff70 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -747,46 +747,99 @@ list_users(Host, Level, PageSize, RPath, R, RegisterEl) -> end. list_users(Host, Level, PageSize, RPath, R, Usernames, RegisterEl) -> + IsOffline = gen_mod:is_loaded(Host, mod_offline), + IsMam = gen_mod:is_loaded(Host, mod_mam), + IsRoster = gen_mod:is_loaded(Host, mod_roster), + IsLast = gen_mod:is_loaded(Host, mod_last), Columns = [<<"user">>, - {<<"offline">>, right}, - {<<"roster">>, right}, - {<<"timestamp">>, left}, - {<<"status">>, left}], + list_users_element(IsOffline, column, offline, {}), + list_users_element(IsMam, column, mam, {}), + list_users_element(IsRoster, column, roster, {}), + list_users_element(IsLast, column, timestamp, {}), + list_users_element(IsLast, column, status, {})], Rows = - [{make_command(echo, - R, - [{<<"sentence">>, - jid:encode( - jid:make(Username, Host))}], - [{only, raw_and_value}, {result_links, [{sentence, user, Level, <<"">>}]}]), - make_command(get_offline_count, - R, - [{<<"user">>, Username}, {<<"host">>, Host}], - [{only, raw_and_value}, - {result_links, - [{value, arg_host, Level, <<"user/", Username/binary, "/queue/">>}]}]), - make_command(get_roster_count, - R, - [{<<"user">>, Username}, {<<"host">>, Host}], - [{only, raw_and_value}, - {result_links, - [{value, arg_host, Level, <<"user/", Username/binary, "/roster/">>}]}]), - ?C(element(1, - make_command_raw_value(get_last, - R, - [{<<"user">>, Username}, {<<"host">>, Host}]))), - ?C(element(2, - make_command_raw_value(get_last, - R, - [{<<"user">>, Username}, {<<"host">>, Host}])))} + [list_to_tuple(lists:flatten([make_command(echo, + R, + [{<<"sentence">>, + jid:encode( + jid:make(Username, Host))}], + [{only, raw_and_value}, + {result_links, + [{sentence, user, Level, <<"">>}]}]), + list_users_element(IsOffline, + row, + offline, + {R, Username, Host, Level}), + list_users_element(IsMam, + row, + mam, + {R, Username, Host, Level}), + list_users_element(IsRoster, + row, + roster, + {R, Username, Host, Level}), + list_users_element(IsLast, row, last, {R, Username, Host})])) || Username <- Usernames], - [RegisterEl, - make_command(registered_users, R, [], [{only, presentation}]), - make_command(get_offline_count, R, [], [{only, presentation}]), - make_command(get_roster_count, R, [], [{only, presentation}]), - make_command(get_last, R, [], [{only, presentation}]), - make_table(PageSize, RPath, Columns, Rows)]. + Table = make_table(PageSize, RPath, lists:flatten(Columns), Rows), + Result = + [RegisterEl, + make_command(registered_users, R, [], [{only, presentation}]), + list_users_element(IsOffline, presentation, offline, R), + list_users_element(IsMam, presentation, mam, R), + list_users_element(IsRoster, presentation, roster, R), + list_users_element(IsLast, presentation, last, R), + Table], + lists:flatten(Result). + +list_users_element(false, _, _, _) -> + []; +list_users_element(_, column, offline, _) -> + {<<"offline">>, right}; +list_users_element(_, column, mam, _) -> + {<<"mam">>, right}; +list_users_element(_, column, roster, _) -> + {<<"roster">>, right}; +list_users_element(_, column, timestamp, _) -> + {<<"timestamp">>, left}; +list_users_element(_, column, status, _) -> + {<<"status">>, left}; +list_users_element(_, row, offline, {R, Username, Host, Level}) -> + make_command(get_offline_count, + R, + [{<<"user">>, Username}, {<<"host">>, Host}], + [{only, raw_and_value}, + {result_links, + [{value, arg_host, Level, <<"user/", Username/binary, "/queue/">>}]}]); +list_users_element(_, row, mam, {R, Username, Host, Level}) -> + make_command(get_mam_count, + R, + [{<<"user">>, Username}, {<<"host">>, Host}], + [{only, raw_and_value}, + {result_links, + [{value, arg_host, Level, <<"user/", Username/binary, "/mam/">>}]}]); +list_users_element(_, row, roster, {R, Username, Host, Level}) -> + make_command(get_roster_count, + R, + [{<<"user">>, Username}, {<<"host">>, Host}], + [{only, raw_and_value}, + {result_links, + [{value, arg_host, Level, <<"user/", Username/binary, "/roster/">>}]}]); +list_users_element(_, row, last, {R, Username, Host}) -> + [?C(element(1, + make_command_raw_value(get_last, R, [{<<"user">>, Username}, {<<"host">>, Host}]))), + ?C(element(2, + make_command_raw_value(get_last, + R, + [{<<"user">>, Username}, {<<"host">>, Host}])))]; +list_users_element(_, presentation, offline, R) -> + make_command(get_offline_count, R, [], [{only, presentation}]); +list_users_element(_, presentation, mam, R) -> + make_command(get_mam_count, R, [], [{only, presentation}]); +list_users_element(_, presentation, roster, R) -> + make_command(get_roster_count, R, [], [{only, presentation}]); +list_users_element(_, presentation, last, R) -> + make_command(get_last, R, [], [{only, presentation}]). list_users_diapason(Host, R, Usernames, N, RegisterEl) -> URLFunc = fun url_func/1, @@ -952,6 +1005,17 @@ user_info(User, Server, #request{q = Query, lang = Lang} = R) -> Res = user_parse_query(User, Server, Query), UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [], [User, Server, R]), + Lasts = case gen_mod:is_loaded(Server, mod_last) of + true -> + [make_command(get_last, R, + [{<<"user">>, User}, {<<"host">>, Server}], + []), + make_command(set_last, R, + [{<<"user">>, User}, {<<"host">>, Server}], + [])]; + false -> + [] + end, [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("User ~ts"), [us_to_list(US)])))] ++ @@ -968,13 +1032,8 @@ user_info(User, Server, #request{q = Query, lang = Lang} = R) -> [{result_links, [{node, node, 4, <<>>}]}]), make_command(change_password, R, [{<<"user">>, User}, {<<"host">>, Server}], - [{style, danger}]), - make_command(get_last, R, - [{<<"user">>, User}, {<<"host">>, Server}], - []), - make_command(set_last, R, - [{<<"user">>, User}, {<<"host">>, Server}], - [])] ++ + [{style, danger}])] ++ + Lasts ++ UserItems ++ [?P, make_command(unregister, R, From 31b85351f295ffc369a756f16a6af7f364daf375 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Sep 2024 17:07:49 +0200 Subject: [PATCH 0765/1302] Add new "MAM Archive" page to webadmin --- src/mod_mam.erl | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 8a6dd2cbe..9b4fa83f9 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -46,13 +46,20 @@ is_empty_for_user/2, is_empty_for_room/3, check_create_room/4, process_iq/3, store_mam_message/7, make_id/0, wrap_as_mucsub/2, select/7, get_mam_count/2, + webadmin_menu_hostuser/4, + webadmin_page_hostuser/4, + get_mam_messages/2, webadmin_user/4, delete_old_messages_batch/5, delete_old_messages_status/1, delete_old_messages_abort/1, remove_message_from_archive/3]). +-import(ejabberd_web_admin, [make_command/4, make_command/2]). + -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("mod_muc_room.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -include("mod_mam.hrl"). -include("translate.hrl"). @@ -150,6 +157,12 @@ start(Host, Opts) -> set_room_option, 50), ejabberd_hooks:add(store_mam_message, Host, ?MODULE, store_mam_message, 100), + ejabberd_hooks:add(webadmin_menu_hostuser, Host, ?MODULE, + webadmin_menu_hostuser, 50), + ejabberd_hooks:add(webadmin_page_hostuser, Host, ?MODULE, + webadmin_page_hostuser, 50), + ejabberd_hooks:add(webadmin_user, Host, ?MODULE, + webadmin_user, 50), case mod_mam_opt:assume_mam_usage(Opts) of true -> ejabberd_hooks:add(message_is_archived, Host, ?MODULE, @@ -223,6 +236,12 @@ stop(Host) -> set_room_option, 50), ejabberd_hooks:delete(store_mam_message, Host, ?MODULE, store_mam_message, 100), + ejabberd_hooks:delete(webadmin_menu_hostuser, Host, ?MODULE, + webadmin_menu_hostuser, 50), + ejabberd_hooks:delete(webadmin_page_hostuser, Host, ?MODULE, + webadmin_page_hostuser, 50), + ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, + webadmin_user, 50), case mod_mam_opt:assume_mam_usage(Host) of true -> ejabberd_hooks:delete(message_is_archived, Host, ?MODULE, @@ -619,11 +638,43 @@ message_is_archived(false, #{lserver := LServer}, Pkt) -> %%% Commands %%% +%% @format-begin + get_mam_count(User, Host) -> Jid = jid:make(User, Host), {_, _, Count} = select(Host, Jid, Jid, [], #rsm_set{}, chat, only_count), Count. +get_mam_messages(User, Host) -> + Jid = jid:make(User, Host), + {Messages, _, _} = select(Host, Jid, Jid, [], #rsm_set{}, chat, only_messages), + format_user_messages(Messages). + +format_user_messages(Messages) -> + lists:map(fun({_ID, _IDInt, Fwd}) -> + El = hd(Fwd#forwarded.sub_els), + FPacket = + ejabberd_web_admin:pretty_print_xml( + xmpp:encode(El)), + SFrom = jid:encode(El#message.from), + STo = jid:encode(El#message.to), + Time = format_time(Fwd#forwarded.delay#delay.stamp), + {Time, SFrom, STo, FPacket} + end, + Messages). + +format_time(Now) -> + {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:now_to_local_time(Now), + str:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", + [Year, Month, Day, Hour, Minute, Second]). + +webadmin_user(Acc, User, Server, R) -> + Acc + ++ [make_command(get_mam_count, + R, + [{<<"user">>, User}, {<<"host">>, Server}], + [{result_links, [{value, arg_host, 4, <<"user/", User/binary, "/mam/">>}]}])]. +%% @format-end %%% %%% Commands: Purge @@ -1512,6 +1563,17 @@ get_commands_spec() -> result_example = 5, result_desc = "Number", result = {value, integer}}, + #ejabberd_commands{name = get_mam_messages, + tags = [internal, mam], + desc = "Get the mam messages", + policy = user, + module = mod_mam, function = get_mam_messages, + args = [], + result = {archive, {list, {messages, {tuple, [{time, string}, + {from, string}, + {to, string}, + {packet, string} + ]}}}}}, #ejabberd_commands{name = delete_old_mam_messages, tags = [mam, purge], desc = "Delete MAM messages older than DAYS", @@ -1581,6 +1643,49 @@ get_commands_spec() -> result_example = {ok, <<"MAM archive removed">>}} ]. + +%%% +%%% WebAdmin +%%% + +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"mam">>, <<"MAM">>}, + {<<"mam-archive">>, <<"MAM Archive">>}]. + +webadmin_page_hostuser(_, Host, U, + #request{us = _US, path = [<<"mam">>]} = R) -> + Res = ?H1GL(<<"MAM">>, <<"modules/#mod_mam">>, <<"mod_mam">>) + ++ [make_command(get_mam_count, + R, + [{<<"user">>, U}, {<<"host">>, Host}], + [{result_links, + [{value, arg_host, 5, <<"user/", U/binary, "/mam-archive/">>}]}]), + make_command(remove_mam_for_user, + R, + [{<<"user">>, U}, {<<"host">>, Host}], + [{style, danger}]), + make_command(remove_mam_for_user_with_peer, + R, + [{<<"user">>, U}, {<<"host">>, Host}], + [{style, danger}])], + {stop, Res}; +webadmin_page_hostuser(_, Host, U, + #request{us = _US, path = [<<"mam-archive">> | RPath], + lang = Lang} = R) -> + PageTitle = + str:translate_and_format(Lang, ?T("~ts's MAM Archive"), [jid:encode({U, Host, <<"">>})]), + Head = ?H1GL(PageTitle, <<"modules/#mod_mam">>, <<"mod_mam">>), + Res = make_command(get_mam_messages, R, [{<<"user">>, U}, + {<<"host">>, Host}], + [{table_options, {10, RPath}}, + {result_links, [{packet, paragraph, 1, <<"">>}]}]), + {stop, Head ++ [Res]}; +webadmin_page_hostuser(Acc, _, _, _) -> Acc. + +%%% +%%% Documentation +%%% + mod_opt_type(compress_xml) -> econf:bool(); mod_opt_type(assume_mam_usage) -> From c2d4f738933e31f29588cbfbf9a80287e9a3c099 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Sep 2024 17:09:01 +0200 Subject: [PATCH 0766/1302] Move some modules webadmin pages to their modules --- src/mod_admin_extra.erl | 69 +---------------------------------------- src/mod_privacy.erl | 26 ++++++++++++++++ src/mod_private.erl | 23 ++++++++++++++ src/mod_vcard.erl | 64 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 68 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 0c761cc8d..2612e7a71 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2206,13 +2206,7 @@ web_page_host(Acc, _, _) -> %%% HostUser web_menu_hostuser(Acc, _Host, _Username, _Lang) -> - Acc - ++ [{<<"auth">>, <<"Authentication">>}, - {<<"mam">>, <<"MAM">>}, - {<<"privacy">>, <<"Privacy Lists">>}, - {<<"private">>, <<"Private XML Storage">>}, - {<<"session">>, <<"Sessions">>}, - {<<"vcard">>, <<"vCard">>}]. + Acc ++ [{<<"auth">>, <<"Authentication">>}, {<<"session">>, <<"Sessions">>}]. web_page_hostuser(_, Host, User, #request{path = [<<"auth">>]} = R) -> Ban = make_command(ban_account, @@ -2242,26 +2236,6 @@ web_page_hostuser(_, Host, User, #request{path = [<<"auth">>]} = R) -> [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}])], {stop, Res}; -web_page_hostuser(_, Host, User, #request{path = [<<"mam">>]} = R) -> - Res = ?H1GL(<<"MAM">>, <<"modules/#mod_mam">>, <<"mod_mam">>) - ++ [make_command(remove_mam_for_user, - R, - [{<<"user">>, User}, {<<"host">>, Host}], - [{style, danger}]), - make_command(remove_mam_for_user_with_peer, - R, - [{<<"user">>, User}, {<<"host">>, Host}], - [{style, danger}])], - {stop, Res}; -web_page_hostuser(_, Host, User, #request{path = [<<"privacy">>]} = R) -> - Res = ?H1GL(<<"Privacy Lists">>, <<"modules/#mod_privacy">>, <<"mod_privacy">>) - ++ [make_command(privacy_set, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], - {stop, Res}; -web_page_hostuser(_, Host, User, #request{path = [<<"private">>]} = R) -> - Res = ?H1GL(<<"Private XML Storage">>, <<"modules/#mod_private">>, <<"mod_private">>) - ++ [make_command(private_set, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - make_command(private_get, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], - {stop, Res}; web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) -> Head = [?XC(<<"h1">>, <<"Sessions">>), ?BR], Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []), @@ -2280,47 +2254,6 @@ web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) -> make_command(get_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []), make_command(num_resources, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], {stop, Head ++ Get ++ Set}; -web_page_hostuser(_, Host, User, #request{path = [<<"vcard">>]} = R) -> - Head = ?H1GL(<<"vCard">>, <<"modules/#mod_vcard">>, <<"mod_vcard">>), - Set = [make_command(set_nickname, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - make_command(set_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - make_command(set_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - make_command(set_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], - timer:sleep(100), % setting vcard takes a while, let's delay the get commands - FieldNames = [<<"VERSION">>, <<"FN">>, <<"NICKNAME">>, <<"BDAY">>], - FieldNames2 = - [{<<"N">>, <<"FAMILY">>}, - {<<"N">>, <<"GIVEN">>}, - {<<"N">>, <<"MIDDLE">>}, - {<<"ADR">>, <<"CTRY">>}, - {<<"ADR">>, <<"LOCALITY">>}, - {<<"EMAIL">>, <<"USERID">>}], - Get = [make_command(get_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - ?XE(<<"blockquote">>, - [make_table([<<"name">>, <<"value">>], - [{?C(FieldName), - make_command(get_vcard, - R, - [{<<"user">>, User}, - {<<"host">>, Host}, - {<<"name">>, FieldName}], - [{only, value}])} - || FieldName <- FieldNames])]), - make_command(get_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), - ?XE(<<"blockquote">>, - [make_table([<<"name">>, <<"subname">>, <<"value">>], - [{?C(FieldName), - ?C(FieldSubName), - make_command(get_vcard2, - R, - [{<<"user">>, User}, - {<<"host">>, Host}, - {<<"name">>, FieldName}, - {<<"subname">>, FieldSubName}], - [{only, value}])} - || {FieldName, FieldSubName} <- FieldNames2])]), - make_command(get_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], - {stop, Head ++ Get ++ Set}; web_page_hostuser(Acc, _, _, _) -> Acc. diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index e5c69844d..eda9ac4ab 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -40,8 +40,14 @@ import_start/2, import_stop/2, import/5, import_info/0, mod_opt_type/1, mod_options/1, depends/2]). +-export([webadmin_menu_hostuser/4, webadmin_page_hostuser/4]). + +-import(ejabberd_web_admin, [make_command/4, make_command/2]). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -include("mod_privacy.hrl"). -include("translate.hrl"). @@ -78,6 +84,8 @@ start(Host, Opts) -> {hook, user_send_packet, user_send_packet, 50}, {hook, privacy_check_packet, check_packet, 50}, {hook, remove_user, remove_user, 50}, + {hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, {iq_handler, ejabberd_sm, ?NS_PRIVACY, process_iq}]}. stop(_Host) -> @@ -840,6 +848,24 @@ export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). +%%% +%%% WebAdmin +%%% + +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"privacy">>, <<"Privacy Lists">>}]. + +webadmin_page_hostuser(_, Host, User, + #request{us = _US, path = [<<"privacy">>]} = R) -> + Res = ?H1GL(<<"Privacy Lists">>, <<"modules/#mod_privacy">>, <<"mod_privacy">>) + ++ [make_command(privacy_set, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Res}; +webadmin_page_hostuser(Acc, _, _, _) -> Acc. + +%%% +%%% Documentation +%%% + depends(_Host, _Opts) -> []. diff --git a/src/mod_private.erl b/src/mod_private.erl index 9e25dbf7e..4554dbeca 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -41,10 +41,16 @@ -export([get_commands_spec/0, bookmarks_to_pep/2]). +-export([webadmin_menu_hostuser/4, webadmin_page_hostuser/4]). + +-import(ejabberd_web_admin, [make_command/4, make_command/2]). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_private.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -include("translate.hrl"). -include("pubsub.hrl"). @@ -71,6 +77,8 @@ start(Host, Opts) -> {hook, pubsub_publish_item, pubsub_publish_item, 50}, {hook, pubsub_delete_item, pubsub_delete_item, 50}, {hook, pubsub_tree_call, pubsub_tree_call, 50}, + {hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. stop(Host) -> @@ -544,6 +552,21 @@ bookmarks_to_pep(User, Server) -> {error, <<"Cannot retrieve bookmarks from private XML storage">>} end. +%%%=================================================================== +%%% WebAdmin +%%%=================================================================== + +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"private">>, <<"Private XML Storage">>}]. + +webadmin_page_hostuser(_, Host, User, + #request{path = [<<"private">>]} = R) -> + Res = ?H1GL(<<"Private XML Storage">>, <<"modules/#mod_private">>, <<"mod_private">>) + ++ [make_command(private_set, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(private_get, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Res}; +webadmin_page_hostuser(Acc, _, _, _) -> Acc. + %%%=================================================================== %%% Cache %%%=================================================================== diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 15f0cd590..6316ce537 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -43,12 +43,17 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([route/1]). +-export([webadmin_menu_hostuser/4, webadmin_page_hostuser/4]). + +-import(ejabberd_web_admin, [make_command/4, make_command/2, make_table/2]). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("mod_vcard.hrl"). -include("translate.hrl"). -include("ejabberd_stacktrace.hrl"). +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). -define(VCARD_CACHE, vcard_cache). @@ -98,6 +103,8 @@ init([Host|_]) -> ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_set, 50), + ejabberd_hooks:add(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), + ejabberd_hooks:add(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), MyHosts = gen_mod:get_opt_hosts(Opts), Search = mod_vcard_opt:search(Opts), if Search -> @@ -161,6 +168,8 @@ terminate(_Reason, #state{hosts = MyHosts, server_host = Host}) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_set, 50), + ejabberd_hooks:delete(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), + ejabberd_hooks:delete(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), Mod = gen_mod:db_mod(Host, ?MODULE), Mod:stop(Host), lists:foreach( @@ -554,6 +563,61 @@ export(LServer) -> Mod = gen_mod:db_mod(LServer, ?MODULE), Mod:export(LServer). +%%% +%%% WebAdmin +%%% + +webadmin_menu_hostuser(Acc, _Host, _Username, _Lang) -> + Acc ++ [{<<"vcard">>, <<"vCard">>}]. + +webadmin_page_hostuser(_, Host, User, + #request{path = [<<"vcard">>]} = R) -> + Head = ?H1GL(<<"vCard">>, <<"modules/#mod_vcard">>, <<"mod_vcard">>), + Set = [make_command(set_nickname, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + make_command(set_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + timer:sleep(100), % setting vcard takes a while, let's delay the get commands + FieldNames = [<<"VERSION">>, <<"FN">>, <<"NICKNAME">>, <<"BDAY">>], + FieldNames2 = + [{<<"N">>, <<"FAMILY">>}, + {<<"N">>, <<"GIVEN">>}, + {<<"N">>, <<"MIDDLE">>}, + {<<"ADR">>, <<"CTRY">>}, + {<<"ADR">>, <<"LOCALITY">>}, + {<<"EMAIL">>, <<"USERID">>}], + Get = [make_command(get_vcard, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + ?XE(<<"blockquote">>, + [make_table([<<"name">>, <<"value">>], + [{?C(FieldName), + make_command(get_vcard, + R, + [{<<"user">>, User}, + {<<"host">>, Host}, + {<<"name">>, FieldName}], + [{only, value}])} + || FieldName <- FieldNames])]), + make_command(get_vcard2, R, [{<<"user">>, User}, {<<"host">>, Host}], []), + ?XE(<<"blockquote">>, + [make_table([<<"name">>, <<"subname">>, <<"value">>], + [{?C(FieldName), + ?C(FieldSubName), + make_command(get_vcard2, + R, + [{<<"user">>, User}, + {<<"host">>, Host}, + {<<"name">>, FieldName}, + {<<"subname">>, FieldSubName}], + [{only, value}])} + || {FieldName, FieldSubName} <- FieldNames2])]), + make_command(get_vcard2_multi, R, [{<<"user">>, User}, {<<"host">>, Host}], [])], + {stop, Head ++ Get ++ Set}; +webadmin_page_hostuser(Acc, _, _, _) -> Acc. + +%%% +%%% Documentation +%%% + depends(_Host, _Opts) -> []. From 15d73b9d20d435ee7aaf0d7a3becf48e6ebcb088 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Aug 2024 13:33:27 +0200 Subject: [PATCH 0767/1302] Support to block IPs in a vhost using append_host_config (#4038) --- src/mod_register.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index d656263bf..6baea8cbb 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -573,24 +573,24 @@ may_remove_resource(From) -> From. get_ip_access(Host) -> mod_register_opt:ip_access(Host). -check_ip_access({User, Server, Resource}, IPAccess) -> +check_ip_access(Server, {User, Server, Resource}, IPAccess) -> case ejabberd_sm:get_user_ip(User, Server, Resource) of {IPAddress, _PortNumber} -> - check_ip_access(IPAddress, IPAccess); + check_ip_access(Server, IPAddress, IPAccess); _ -> deny end; -check_ip_access(undefined, _IPAccess) -> +check_ip_access(_Server, undefined, _IPAccess) -> deny; -check_ip_access(IPAddress, IPAccess) -> - acl:match_rule(global, IPAccess, IPAddress). +check_ip_access(Server, IPAddress, IPAccess) -> + acl:match_rule(Server, IPAccess, IPAddress). check_access(User, Server, Source) -> JID = jid:make(User, Server), Access = mod_register_opt:access(Server), IPAccess = get_ip_access(Server), case acl:match_rule(Server, Access, JID) of - allow -> check_ip_access(Source, IPAccess); + allow -> check_ip_access(Server, Source, IPAccess); deny -> deny end. From 4a931b42ab2edf6cfa00434dc84d66d8489f3fbd Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 24 Sep 2024 12:37:06 +0200 Subject: [PATCH 0768/1302] hooks_deps: Hide false-positive warnings about gen_mod --- tools/hook_deps.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/hook_deps.sh b/tools/hook_deps.sh index 243c9ac2d..3a3271b50 100755 --- a/tools/hook_deps.sh +++ b/tools/hook_deps.sh @@ -305,6 +305,8 @@ warn_type({var, _, 'Type'}, #state{module = mod_delegation}, "not an atom") -> ok; warn_type({var, _, 'NS'}, #state{module = mod_delegation}, "not a binary") -> ok; +warn_type({var, _, _}, #state{module = gen_mod}, _) -> + ok; warn_type(Form, State, Warning) -> log("~s:~p: Warning: " ++ Warning ++ ": ~s~n", [State#state.file, From 54a89b39fb9d78f72109847de846176c72242084 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Thu, 26 Sep 2024 21:43:43 +0200 Subject: [PATCH 0769/1302] CI: Add XMPP Interop tests Modifies the CI build to integrate the GitHub Action-based test runner from the [XMPP Interop Testing project](https://xmpp-interop-testing.github.io). This executes additional integration tests that help verify ejabberd's compliance with XMPP specifications. In this commit, none of the tests are excluded, which likely results in false positives. Therefor, the 'continue-on-error' flag is set. This should ideally be removed in a later commit. --- .github/workflows/ci.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21faf598e..0bb65438e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,12 +147,28 @@ jobs: cat $RE/logs/ejabberd.log grep -q "is stopped in" $RE/logs/ejabberd.log - - name: Check Development Release + - name: Start Development Release run: | make dev RE=_build/dev/rel/ejabberd + sed -i 's/starttls_required: true/starttls_required: false/g' $RE/conf/ejabberd.yml $RE/bin/ejabberdctl start $RE/bin/ejabberdctl started + $RE/bin/ejabberdctl register admin localhost admin + grep -q "is started in" $RE/logs/ejabberd.log + + - name: Run XMPP Interoperability Tests against CI server. + continue-on-error: true + uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 + with: + domain: 'localhost' + adminAccountUsername: 'admin' + adminAccountPassword: 'admin' + + - name: Stop Development Release + if: always() + run: | + RE=_build/dev/rel/ejabberd $RE/bin/ejabberdctl stop $RE/bin/ejabberdctl stopped cat $RE/logs/ejabberd.log From a8df58f0569d377b17634e22a1734145d93697e6 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Fri, 27 Sep 2024 09:55:18 +0200 Subject: [PATCH 0770/1302] CI: Limit execution of XMPP Interop tests Run the XMPP Interop tests on only one build of the CI-matrix. This prevents redundant testing, as well as logs overwriting each-other. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bb65438e..a8cc782c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,6 +158,7 @@ jobs: grep -q "is started in" $RE/logs/ejabberd.log - name: Run XMPP Interoperability Tests against CI server. + if: matrix.otp == '26' continue-on-error: true uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 with: From b4399291efd86f06d2d2883f5fc2c66e9d39a27b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 28 Sep 2024 16:45:59 +0200 Subject: [PATCH 0771/1302] mod_pubsub: Fix default node config parsing Don't merge 'default_node_config' settings with the default options of the first configured node plugin. Otherwise, the latter might later override those of the plugin that should handle a node creation request. For example, the following configuration would lead to the 'flat' options being used by default for 'pep' nodes as well: mod_pubsub: plugins: - flat - pep --- src/mod_pubsub.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 9fe4ce949..6e7e2e5fd 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -265,10 +265,7 @@ init([ServerHost|_]) -> ejabberd_router:register_route( Host, ServerHost, {apply, ?MODULE, route}), {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), - DefaultModule = plugin(Host, hd(Plugins)), - DefaultNodeCfg = merge_config( - [mod_pubsub_opt:default_node_config(Opts), - DefaultModule:options()]), + DefaultNodeCfg = mod_pubsub_opt:default_node_config(Opts), lists:foreach( fun(H) -> T = gen_mod:get_module_proc(H, config), From ca54f81f58f63646104845758d9bb5e661d8da84 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 28 Sep 2024 16:56:46 +0200 Subject: [PATCH 0772/1302] mod_pubsub: Fix merging of default node options Use any option specified via 'default_node_config' by default, and take the remaining defaults from the node plugin handling the request. This is the documented behavior. Before this change, the code applied the plugin defaults only if no 'default_node_config' existed at all. And even this logic didn't work as intended, since 'default_node_config' yielded an empty list in case it wasn't configured, which resulted in plugin defaults never being applied. --- src/mod_pubsub.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 6e7e2e5fd..75cde586a 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3367,9 +3367,10 @@ get_option(Options, Var, Def) -> -spec node_options(host(), binary()) -> [{atom(), any()}]. node_options(Host, Type) -> DefaultOpts = node_plugin_options(Host, Type), + ConfigOpts = config(Host, default_node_config), case lists:member(Type, config(Host, plugins)) of true -> - config(Host, default_node_config, DefaultOpts); + merge_config([ConfigOpts, DefaultOpts]); false -> DefaultOpts end. From a9583b43c37c38accfe06dc44e9f634cd8f92498 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 28 Sep 2024 17:51:28 +0200 Subject: [PATCH 0773/1302] mod_pubsub: Fix choice of node config defaults Ignore node plugin defaults if the plugin handling the request isn't enabled, rather than ignoring 'default_node_config' options and applying plugin defaults in that case. --- src/mod_pubsub.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 75cde586a..d65c93f0f 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3371,7 +3371,7 @@ node_options(Host, Type) -> case lists:member(Type, config(Host, plugins)) of true -> merge_config([ConfigOpts, DefaultOpts]); - false -> DefaultOpts + false -> ConfigOpts end. -spec node_plugin_options(host(), binary()) -> [{atom(), any()}]. From 36187e07d0652e5c2f891f48cdf958b872a70f5d Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 28 Sep 2024 17:58:07 +0200 Subject: [PATCH 0774/1302] mod_pubsub: Fall back to default plugin options If the plugin handling a node creation request isn't enabled, fall back to applying the default plugin (currently node_flat) options. --- src/mod_pubsub.erl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index d65c93f0f..e870596e7 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3366,23 +3366,19 @@ get_option(Options, Var, Def) -> -spec node_options(host(), binary()) -> [{atom(), any()}]. node_options(Host, Type) -> - DefaultOpts = node_plugin_options(Host, Type), ConfigOpts = config(Host, default_node_config), - case lists:member(Type, config(Host, plugins)) of - true -> - merge_config([ConfigOpts, DefaultOpts]); - false -> ConfigOpts - end. + PluginOpts = node_plugin_options(Host, Type), + merge_config([ConfigOpts, PluginOpts]). -spec node_plugin_options(host(), binary()) -> [{atom(), any()}]. node_plugin_options(Host, Type) -> Module = plugin(Host, Type), - case catch Module:options() of - {'EXIT', {undef, _}} -> + case {lists:member(Type, config(Host, plugins)), catch Module:options()} of + {true, Opts} when is_list(Opts) -> + Opts; + {_, _} -> DefaultModule = plugin(Host, ?STDNODE), - DefaultModule:options(); - Result -> - Result + DefaultModule:options() end. -spec node_owners_action(host(), binary(), nodeIdx(), [ljid()]) -> [ljid()]. From 4723283896eb4b50c2c48336627436a38947b852 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 29 Sep 2024 20:40:01 +0200 Subject: [PATCH 0775/1302] ejabberd_c2s: Optionally allow unencrypted SASL2 XEP-0388 says: "SASL2 MUST only be used by Clients or offered by Servers after TLS negotiation". Therefore, we reject SASL2 negotiations over unencrypted transports by default. However, TLS might be terminated outside of ejabberd. Add the 'allow_unencrypted_sasl2' option to support this use case. --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- src/ejabberd_c2s.erl | 28 ++++++++++++++++++---------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/mix.exs b/mix.exs index 171e8762b..b2784a9c3 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "2a54443436dc8a942969f2ef7c5654d5acab7533", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 065ae647e..3507f001c 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "2a54443436dc8a942969f2ef7c5654d5acab7533", [ref: "2a54443436dc8a942969f2ef7c5654d5acab7533"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127", [ref: "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"]}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index 5fcc1e5bb..7478a836a 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "2a54443436dc8a942969f2ef7c5654d5acab7533"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index b539c60b8..90bfef76f 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"2a54443436dc8a942969f2ef7c5654d5acab7533"}}, + {ref,"ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 8f7c04c41..66200fcdd 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -35,16 +35,16 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([tls_options/1, tls_required/1, tls_enabled/1, - compress_methods/1, bind/2, sasl_mechanisms/2, - get_password_fun/2, check_password_fun/2, check_password_digest_fun/2, - unauthenticated_stream_features/1, authenticated_stream_features/1, - handle_stream_start/2, handle_stream_end/2, - handle_unauthenticated_packet/2, handle_authenticated_packet/2, - handle_auth_success/4, handle_auth_failure/4, handle_send/3, - handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, - inline_stream_features/1, handle_sasl2_inline/2, - handle_sasl2_inline_post/3, handle_bind2_inline/2, - handle_bind2_inline_post/3, sasl_options/1]). + allow_unencrypted_sasl2/1, compress_methods/1, bind/2, + sasl_mechanisms/2, get_password_fun/2, check_password_fun/2, + check_password_digest_fun/2, unauthenticated_stream_features/1, + authenticated_stream_features/1, handle_stream_start/2, + handle_stream_end/2, handle_unauthenticated_packet/2, + handle_authenticated_packet/2, handle_auth_success/4, + handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, + handle_unbinded_packet/2, inline_stream_features/1, + handle_sasl2_inline/2, handle_sasl2_inline_post/3, + handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -392,6 +392,9 @@ tls_enabled(#{tls_enabled := TLSEnabled, tls_verify := TLSVerify}) -> TLSEnabled or TLSRequired or TLSVerify. +allow_unencrypted_sasl2(#{allow_unencrypted_sasl2 := AllowUnencryptedSasl2}) -> + AllowUnencryptedSasl2. + compress_methods(#{zlib := true}) -> [<<"zlib">>]; compress_methods(_) -> @@ -604,12 +607,14 @@ init([State, Opts]) -> TLSEnabled = proplists:get_bool(starttls, Opts), TLSRequired = proplists:get_bool(starttls_required, Opts), TLSVerify = proplists:get_bool(tls_verify, Opts), + AllowUnencryptedSasl2 = proplists:get_bool(allow_unencrypted_sasl2, Opts), Zlib = proplists:get_bool(zlib, Opts), Timeout = ejabberd_option:negotiation_timeout(), State1 = State#{tls_options => TLSOpts2, tls_required => TLSRequired, tls_enabled => TLSEnabled, tls_verify => TLSVerify, + allow_unencrypted_sasl2 => AllowUnencryptedSasl2, pres_a => ?SETS:new(), zlib => Zlib, lang => ejabberd_option:language(), @@ -1047,6 +1052,8 @@ listen_opt_type(starttls) -> econf:bool(); listen_opt_type(starttls_required) -> econf:bool(); +listen_opt_type(allow_unencrypted_sasl2) -> + econf:bool(); listen_opt_type(tls_verify) -> econf:bool(); listen_opt_type(zlib) -> @@ -1069,6 +1076,7 @@ listen_options() -> {tls_compression, false}, {starttls, false}, {starttls_required, false}, + {allow_unencrypted_sasl2, false}, {tls_verify, false}, {zlib, false}, {max_stanza_size, infinity}, From aa5faf1f36d2d628c1de1235dc30deb6a36bb16c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Oct 2024 13:23:53 +0200 Subject: [PATCH 0776/1302] mod_privilege: Replace try...catch with a clean alternative --- src/mod_privilege.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 7524af181..b873fe13f 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -391,8 +391,9 @@ code_change(_OldVsn, State, _Extra) -> %%%=================================================================== -spec get_permissions(binary()) -> permissions(). get_permissions(ServerHost) -> - try ets:lookup_element(?MODULE, ServerHost, 2) - catch _:badarg -> #{} + case ets:lookup(?MODULE, ServerHost) of + [] -> #{}; + [{_, Permissions}] -> Permissions end. -spec forward_message(message()) -> ok. From efb1fc9b3fe2a47ff7ea2d6a47836fcb5999679a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Oct 2024 13:54:24 +0200 Subject: [PATCH 0777/1302] mod_register: Document behavior when access is set to none (#4078) --- src/mod_register.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 6baea8cbb..c75fbc150 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -654,7 +654,10 @@ mod_doc() -> ?T("Specify rules to restrict what usernames can be registered. " "If a rule returns 'deny' on the requested username, " "registration of that user name is denied. There are no " - "restrictions by default.")}}, + "restrictions by default. " + "If 'AccessName' is 'none', then registering new accounts using " + "In-Band Registration is disabled and the corresponding " + "stream feature is not announced to clients.")}}, {access_from, #{value => ?T("AccessName"), desc => From ef93a5359b3a3abd85b79730754c48e725343205 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 8 Oct 2024 17:38:11 +0200 Subject: [PATCH 0778/1302] Bump 'xmpp' dependency tag This fixes announcing SASL2 over non-TLS connections if 'allow_unencrypted_sasl2' is configured. --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index b2784a9c3..69ca7928c 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "422c107a882b6967e615ea69b33fac4897048fbb", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 3507f001c..70b0ec7dc 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127", [ref: "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "422c107a882b6967e615ea69b33fac4897048fbb", [ref: "422c107a882b6967e615ea69b33fac4897048fbb"]}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index 7478a836a..868b16d90 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "422c107a882b6967e615ea69b33fac4897048fbb"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 90bfef76f..2e5892eb1 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"ff0dd5390acc3c1ee8cd1c7e6dc60a0c3cb1d127"}}, + {ref,"422c107a882b6967e615ea69b33fac4897048fbb"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ From 2a9dc2c7b2411c237b653f54d806fa01682f51cb Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 9 Oct 2024 15:37:47 +0200 Subject: [PATCH 0779/1302] make-binaries: Bump Erlang/OTP version to 26.2.5.4 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 9c5ca7676..d7166443f 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.6.3' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.3.2' -otp_vsn='26.2.5.3' +otp_vsn='26.2.5.4' elixir_vsn='1.17.2' pam_vsn='1.6.1' png_vsn='1.6.43' From 94c2a115d54fb24df73afc0ec54839e1e59ddb41 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 7 Oct 2024 12:45:04 +0200 Subject: [PATCH 0780/1302] CI: Disable XMPP Interop specifications that are known to fail --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8cc782c9..dbdaaea99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,12 +159,12 @@ jobs: - name: Run XMPP Interoperability Tests against CI server. if: matrix.otp == '26' - continue-on-error: true uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 with: domain: 'localhost' adminAccountUsername: 'admin' adminAccountPassword: 'admin' + disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,XEP-0363,XEP-0384 - name: Stop Development Release if: always() From 859ba3e0c237ad0082f5db8c548b7312e34b47ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 06:31:03 +0000 Subject: [PATCH 0781/1302] Bump dialyxir from 1.4.3 to 1.4.4 Bumps [dialyxir](https://github.com/jeremyjh/dialyxir) from 1.4.3 to 1.4.4. - [Release notes](https://github.com/jeremyjh/dialyxir/releases) - [Changelog](https://github.com/jeremyjh/dialyxir/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeremyjh/dialyxir/compare/1.4.3...1.4.4) --- updated-dependencies: - dependency-name: dialyxir dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 70b0ec7dc..0e034aae3 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, - "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, + "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, From 7f8519c0afd0135f0d2b563fc8b0b8022ee396c4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 2 Oct 2024 18:32:32 +0200 Subject: [PATCH 0782/1302] ejabberdctl: If ERLANG_NODE lacks host, add hostname (#4288) --- ejabberdctl.template | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 83ec7e1bd..21be6430f 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -66,6 +66,7 @@ done # shellcheck source=ejabberdctl.cfg.example [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" +[ "$ERLANG_NODE" = "${ERLANG_NODE%@*}" ] && ERLANG_NODE="$ERLANG_NODE@$(hostname -s)" [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" : "${SPOOL_DIR:="{{spool_dir}}"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" From 3669ac8aedfb5484d27c85e8df8af60541bc2849 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 3 Oct 2024 12:58:18 +0200 Subject: [PATCH 0783/1302] ejabberd_app: At server start, log Erlang and Elixir versions --- src/ejabberd_app.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index a0065a8fa..fc9830fe9 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -64,6 +64,9 @@ start(normal, _Args) -> ?INFO_MSG("ejabberd ~ts is started in the node ~p in ~.2fs", [ejabberd_option:version(), node(), (T2-T1)/1000]), + maybe_print_elixir_version(), + ?INFO_MSG("~ts", + [erlang:system_info(system_version)]), {ok, SupPid}; Err -> ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]), @@ -205,9 +208,13 @@ maybe_start_exsync() -> "true" -> rpc:call(node(), 'Elixir.ExSync.Application', start, []); _ -> ok end. + +maybe_print_elixir_version() -> + ?INFO_MSG("Elixir ~ts", [maps:get(build, 'Elixir.System':build_info())]). -else. setup_if_elixir_conf_used() -> ok. register_elixir_config_hooks() -> ok. start_elixir_application() -> ok. maybe_start_exsync() -> ok. +maybe_print_elixir_version() -> ok. -endif. From 50ef49d190aeca17ed334be2981e4b24d3c0463f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 7 Oct 2024 11:50:36 +0200 Subject: [PATCH 0784/1302] mod_vcard: Return explicit error stanza when user attempts to modify other's vcard --- src/mod_vcard.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 6316ce537..8c931bc90 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -400,6 +400,12 @@ make_vcard_search(User, LUser, LServer, VCARD) -> lorgunit = LOrgUnit}. -spec vcard_iq_set(iq()) -> iq() | {stop, stanza_error()}. +vcard_iq_set(#iq{from = #jid{user = FromUser, lserver = FromLServer}, + to = #jid{user = ToUser, lserver = ToLServer}, + lang = Lang}) + when (FromUser /= ToUser) or (FromLServer /= ToLServer) -> + Txt = ?T("User not allowed to perform an IQ set on another user's vCard."), + {stop, xmpp:err_forbidden(Txt, Lang)}; vcard_iq_set(#iq{from = From, lang = Lang, sub_els = [VCard]} = IQ) -> #jid{user = User, lserver = LServer} = From, case set_vcard(User, LServer, VCard) of From 38f11321920641d5d7d75e8487014ec5861b6b1c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 15 Oct 2024 22:50:30 +0200 Subject: [PATCH 0785/1302] ejabberd_stun: Omit 'auth_realm' log message These days, TURN authentication is usually performed using ephemeral credentials handed out by mod_stun_disco. In that case, the TURN realm is irrelevant. Therefore, omit the misleading log message that warned about a missing realm configuration. (Commit 6eb2f0727447d8e7a36f11bccbf19be819b7bcfd reduced the log level of that message already.) --- src/ejabberd_stun.erl | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index e13dbbddc..5ce6c26af 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -106,7 +106,6 @@ prepare_turn_opts(Opts) -> prepare_turn_opts(Opts, _UseTurn = false) -> set_certfile(Opts); prepare_turn_opts(Opts, _UseTurn = true) -> - NumberOfMyHosts = length(ejabberd_option:hosts()), TurnIP = case proplists:get_value(turn_ipv4_address, Opts) of undefined -> MyIP = misc:get_my_ipv4_address(), @@ -129,18 +128,9 @@ prepare_turn_opts(Opts, _UseTurn = true) -> AuthType = proplists:get_value(auth_type, Opts, user), Realm = case proplists:get_value(auth_realm, Opts) of undefined when AuthType == user -> - if NumberOfMyHosts > 1 -> - ?INFO_MSG("You have several virtual hosts " - "configured, but option 'auth_realm' is " - "undefined and 'auth_type' is set to " - "'user', so the TURN relay might not be " - "working properly. Using ~ts as a " - "fallback", - [ejabberd_config:get_myname()]); - true -> - ok - end, - [{auth_realm, ejabberd_config:get_myname()}]; + MyName = ejabberd_config:get_myname(), + ?DEBUG("Using ~ts as TURN realm", [MyName]), + [{auth_realm, MyName}]; _ -> [] end, From 50948d16199fce5f22931b0bf73f9c3adeb1926d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Oct 2024 13:08:12 +0200 Subject: [PATCH 0786/1302] mod_mam: Advertise XEP-0424 feature in server disco-info (#3340) --- src/mod_mam.erl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 9b4fa83f9..5dec822e5 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -38,6 +38,7 @@ -export([sm_receive_packet/1, user_receive_packet/1, user_send_packet/1, user_send_packet_strip_tag/1, process_iq_v0_2/1, process_iq_v0_3/1, + disco_local_features/5, disco_sm_features/5, remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/2, muc_filter_message/3, message_is_archived/3, delete_old_messages/2, get_commands_spec/0, msg_to_el/4, @@ -147,6 +148,8 @@ start(Host, Opts) -> muc_filter_message, 50), ejabberd_hooks:add(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, + disco_local_features, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:add(remove_user, Host, ?MODULE, @@ -226,6 +229,8 @@ stop(Host) -> muc_filter_message, 50), ejabberd_hooks:delete(muc_process_iq, Host, ?MODULE, muc_process_iq, 50), + ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, + disco_local_features, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), ejabberd_hooks:delete(remove_user, Host, ?MODULE, @@ -612,6 +617,20 @@ parse_query(#mam_query{xdata = #xdata{}} = Query, Lang) -> parse_query(#mam_query{}, _Lang) -> {ok, []}. +disco_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> + Acc; +disco_local_features(Acc, _From, _To, <<"">>, _Lang) -> + Features = case Acc of + {result, Fs} -> Fs; + empty -> [] + end, + {result, [?NS_MESSAGE_RETRACT | Features]}; +disco_local_features(empty, _From, _To, _Node, Lang) -> + Txt = ?T("No features available"), + {error, xmpp:err_item_not_found(Txt, Lang)}; +disco_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + disco_sm_features(empty, From, To, Node, Lang) -> disco_sm_features({result, []}, From, To, Node, Lang); disco_sm_features({result, OtherFeatures}, From 74b0f64645fb2d351bdbb05d38f463b49e83912d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 Aug 2024 11:57:28 +0200 Subject: [PATCH 0787/1302] mod_block_strangers: Add feature announcement to disco-info (#4039) --- src/mod_block_strangers.erl | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index c7a436c24..26b8e73d0 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -32,7 +32,8 @@ -export([start/2, stop/1, reload/3, mod_doc/0, depends/2, mod_opt_type/1, mod_options/1]). --export([filter_packet/1, filter_offline_msg/1, filter_subscription/2]). +-export([filter_packet/1, filter_offline_msg/1, filter_subscription/2, + get_sm_features/5]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -40,13 +41,17 @@ -define(SETS, gb_sets). +-define(NS_BLOCK_STRANGERS, <<"urn:ejabberd:block-strangers">>). + -type c2s_state() :: ejabberd_c2s:state(). %%%=================================================================== %%% Callbacks and hooks %%%=================================================================== start(_Host, _Opts) -> - {ok, [{hook, user_receive_packet, filter_packet, 25}, + {ok, [{hook, disco_local_features, get_sm_features, 50}, + {hook, disco_sm_features, get_sm_features, 50}, + {hook, user_receive_packet, filter_packet, 25}, {hook, roster_in_subscription, filter_subscription, 25}, {hook, offline_message_hook, filter_offline_msg, 25}]}. @@ -56,6 +61,16 @@ stop(_Host) -> reload(_Host, _NewOpts, _OldOpts) -> ok. +get_sm_features(Acc, _From, _To, <<"">>, _Lang) -> + Features = case Acc of + {result, I} -> I; + _ -> [] + end, + {result, [?NS_BLOCK_STRANGERS | Features]}; + +get_sm_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + -spec filter_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()} | {stop, {drop, c2s_state()}}. filter_packet({#message{from = From} = Msg, State} = Acc) -> From 81906b74edf3f292b95a37d85343af37934b8787 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Sep 2024 12:07:52 +0200 Subject: [PATCH 0788/1302] Support "IQ permission" from XEP-0356 0.4.1 (#3889) --- src/ejabberd_router.erl | 21 ++++- src/mod_privilege.erl | 191 ++++++++++++++++++++++++++++++++++++-- src/mod_privilege_opt.erl | 7 ++ 3 files changed, 210 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 94f25f80d..8f877dfaf 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -380,8 +380,9 @@ code_change(_OldVsn, State, _Extra) -> %%% Internal functions %%-------------------------------------------------------------------- -spec do_route(stanza()) -> ok. -do_route(OrigPacket) -> - ?DEBUG("Route:~n~ts", [xmpp:pp(OrigPacket)]), +do_route(OrigPacket1) -> + ?DEBUG("Route:~n~ts", [xmpp:pp(OrigPacket1)]), + OrigPacket = process_privilege_iq(OrigPacket1), case ejabberd_hooks:run_fold(filter_packet, OrigPacket, []) of drop -> ok; @@ -405,6 +406,22 @@ do_route(OrigPacket) -> end end. +%% @format-begin +process_privilege_iq(Packet) -> + To = xmpp:get_to(Packet), + case xmpp:get_meta(Packet, privilege_iq, none) of + {OriginalId, OriginalHost, ReplacedJid} when ReplacedJid == To -> + Privilege = #privilege{forwarded = #forwarded{sub_els = [Packet]}}, + #iq{type = xmpp:get_type(Packet), + id = OriginalId, + to = jid:make(OriginalHost), + from = ReplacedJid, + sub_els = [Privilege]}; + _ -> + Packet + end. +%% @format-end + -spec do_route(stanza(), #route{}) -> any(). do_route(Pkt, #route{local_hint = LocalHint, pid = Pid}) when is_pid(Pid) -> diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index b873fe13f..9808a4158 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -25,7 +25,7 @@ -author('amuhar3@gmail.com'). --protocol({xep, 356, '0.2.1', '16.09', "", ""}). +-protocol({xep, 356, '0.4.1', '24.xx', "", ""}). -behaviour(gen_server). -behaviour(gen_mod). @@ -37,6 +37,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([component_connected/1, component_disconnected/2, + component_send_packet/1, roster_access/2, process_message/1, process_presence_out/1, process_presence_in/1]). @@ -45,14 +46,17 @@ -include("translate.hrl"). -type roster_permission() :: both | get | set. +-type iq_permission() :: both | get | set. -type presence_permission() :: managed_entity | roster. -type message_permission() :: outgoing. -type roster_permissions() :: [{roster_permission(), acl:acl()}]. +-type iq_permissions() :: [{iq_permission(), acl:acl()}]. -type presence_permissions() :: [{presence_permission(), acl:acl()}]. -type message_permissions() :: [{message_permission(), acl:acl()}]. --type access() :: [{roster, roster_permissions()} | - {presence, presence_permissions()} | - {message, message_permissions()}]. +-type access() :: [{roster, roster_permission()} | + {iq, [privilege_namespace()]} | + {presence, presence_permission()} | + {message, message_permission()}]. -type permissions() :: #{binary() => access()}. -record(state, {server_host = <<"">> :: binary()}). @@ -71,6 +75,10 @@ reload(_Host, _NewOpts, _OldOpts) -> mod_opt_type(roster) -> econf:options( #{both => econf:acl(), get => econf:acl(), set => econf:acl()}); +mod_opt_type(iq) -> + econf:map( + econf:binary(), + econf:options(#{both => econf:acl(), get => econf:acl(), set => econf:acl()})); mod_opt_type(message) -> econf:options( #{outgoing => econf:acl()}); @@ -80,6 +88,7 @@ mod_opt_type(presence) -> mod_options(_) -> [{roster, [{both, none}, {get, none}, {set, none}]}, + {iq, []}, {presence, [{managed_entity, none}, {roster, none}]}, {message, [{outgoing,none}]}]. @@ -130,6 +139,27 @@ mod_doc() -> desc => ?T("Sets write access to a user's roster. " "The default value is 'none'.")}}]}, + {iq, + #{value => "{Namespace: Options}", + desc => + ?T("This option defines namespaces and their IQ permissions. " + "By default no permissions are given. " + "The 'Options' are:")}, + [{both, + #{value => ?T("AccessName"), + desc => + ?T("Allows sending IQ stanzas of type 'get' and 'set'. " + "The default value is 'none'.")}}, + {get, + #{value => ?T("AccessName"), + desc => + ?T("Allows sending IQ stanzas of type 'get'. " + "The default value is 'none'.")}}, + {set, + #{value => ?T("AccessName"), + desc => + ?T("Allows sending IQ stanzas of type 'set'. " + "The default value is 'none'.")}}]}, {message, #{value => ?T("Options"), desc => @@ -164,6 +194,9 @@ mod_doc() -> example => ["modules:", " mod_privilege:", + " iq:", + " http://jabber.org/protocol/pubsub:", + " get: all", " roster:", " get: all", " presence:", @@ -190,6 +223,10 @@ component_disconnected(Host, _Reason) -> gen_server:cast(Proc, {component_disconnected, Host}) end, ejabberd_option:hosts()). +%% +%% Message processing +%% + -spec process_message(stanza()) -> stop | ok. process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From, to = #jid{lresource = <<"">>} = To, @@ -212,9 +249,73 @@ process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From, %% Component is disconnected ok end; + process_message(_Stanza) -> ok. +%% +%% IQ processing +%% + +%% @format-begin + +component_send_packet({#iq{from = From, + to = #jid{lresource = <<"">>} = To, + id = Id, + type = Type} = + IQ, + State}) + when Type /= error -> + Host = From#jid.lserver, + ServerHost = To#jid.lserver, + Permissions = get_permissions(ServerHost), + Result = + case {maps:find(Host, Permissions), get_iq_encapsulated_details(IQ)} of + {{ok, Access}, {ok, EncapType, EncapNs, EncapFrom, EncIq}} + when (EncapType == Type) and ((EncapFrom == undefined) or (EncapFrom == To)) -> + NsPermissions = proplists:get_value(iq, Access, none), + Permission = + case lists:keyfind(EncapNs, 2, NsPermissions) of + #privilege_namespace{type = AllowedType} -> + AllowedType; + _ -> + none + end, + case Permission == both + orelse Permission == get andalso Type == get + orelse Permission == set andalso Type == set + of + true -> + forward_iq(Host, To, Id, EncIq); + false -> + ?INFO_MSG("IQ not forwarded: Permission not granted to ns=~s with type=~p", + [EncapNs, Type]), + drop + end; + {error, _} -> + %% Component is disconnected + ?INFO_MSG("IQ not forwarded: Component seems disconnected", []), + drop; + {_, {ok, E, _, _, _}} when E /= Type -> + ?INFO_MSG("IQ not forwarded: The encapsulated IQ stanza type=~p " + "does not match the top-level IQ stanza type=~p", + [E, Type]), + drop; + {_, {ok, _, _, EF, _}} when (EF /= undefined) and (EF /= To) -> + ?INFO_MSG("IQ not forwarded: The FROM attribute in the encapsulated " + "IQ stanza and the TO in top-level IQ stanza do not match", + []), + drop + end, + {Result, State}; +component_send_packet(Acc) -> + Acc. +%% @format-end + +%% +%% Roster processing +%% + -spec roster_access({true, iq()} | false, iq()) -> {true, iq()} | false. roster_access({true, _IQ} = Acc, _) -> Acc; @@ -309,6 +410,8 @@ init([Host|_]) -> process_presence_out, 50), ejabberd_hooks:add(user_receive_packet, Host, ?MODULE, process_presence_in, 50), + ejabberd_hooks:add(component_send_packet, ?MODULE, + component_send_packet, 50), {ok, #state{server_host = Host}}. handle_call(Request, From, State) -> @@ -320,22 +423,26 @@ handle_cast({component_connected, Host}, State) -> From = jid:make(ServerHost), To = jid:make(Host), RosterPerm = get_roster_permission(ServerHost, Host), + IqNamespaces = get_iq_namespaces(ServerHost, Host), PresencePerm = get_presence_permission(ServerHost, Host), MessagePerm = get_message_permission(ServerHost, Host), - if RosterPerm /= none; PresencePerm /= none; MessagePerm /= none -> + if RosterPerm /= none; IqNamespaces /= []; PresencePerm /= none; MessagePerm /= none -> Priv = #privilege{perms = [#privilege_perm{access = message, type = MessagePerm}, #privilege_perm{access = roster, type = RosterPerm}, + #privilege_perm{access = iq, + namespaces = IqNamespaces}, #privilege_perm{access = presence, type = PresencePerm}]}, ?INFO_MSG("Granting permissions to external " "component '~ts': roster = ~ts, presence = ~ts, " - "message = ~ts", - [Host, RosterPerm, PresencePerm, MessagePerm]), + "message = ~ts,~n iq = ~p", + [Host, RosterPerm, PresencePerm, MessagePerm, IqNamespaces]), Msg = #message{from = From, to = To, sub_els = [Priv]}, ejabberd_router:route(Msg), Permissions = maps:put(Host, [{roster, RosterPerm}, + {iq, IqNamespaces}, {presence, PresencePerm}, {message, MessagePerm}], get_permissions(ServerHost)), @@ -366,6 +473,8 @@ terminate(_Reason, State) -> Host = State#state.server_host, case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of false -> + ejabberd_hooks:delete(component_send_packet, ?MODULE, + component_send_packet, 50), ejabberd_hooks:delete(component_connected, ?MODULE, component_connected, 50), ejabberd_hooks:delete(component_disconnected, ?MODULE, @@ -396,6 +505,10 @@ get_permissions(ServerHost) -> [{_, Permissions}] -> Permissions end. +%% +%% Message +%% + -spec forward_message(message()) -> ok. forward_message(#message{to = To} = Msg) -> ServerHost = To#jid.lserver, @@ -436,6 +549,45 @@ forward_message(#message{to = To} = Msg) -> ejabberd_router:route_error(Msg, Err) end. +%% +%% IQ +%% + +%% @format-begin + +-spec get_iq_encapsulated_details(iq()) -> + {ok, set | get, binary(), jid(), iq()} | + {error, Why :: atom(), any(), iq()}. +get_iq_encapsulated_details(#iq{sub_els = [IqSub]} = Msg) -> + Lang = xmpp:get_lang(Msg), + try xmpp:try_subtag(Msg, #privileged_iq{}) of + #privileged_iq{iq = #iq{type = EncapsulatedType, from = From} = EncIq} -> + [IqSubSub] = xmpp:get_els(IqSub), + [Element] = xmpp:get_els(IqSubSub), + Ns = xmpp:get_ns(Element), + {ok, EncapsulatedType, Ns, From, EncIq}; + _ -> + Txt = ?T("No element found"), + Err = xmpp:err_bad_request(Txt, Lang), + {error, no_privileged_iq, Err} + catch + _:{xmpp_codec, Why} -> + Txt = xmpp:io_format_error(Why), + Err = xmpp:err_bad_request(Txt, Lang), + {error, codec_error, Err} + end. + +-spec forward_iq(binary(), jid(), binary(), iq()) -> iq(). +forward_iq(Host, ToplevelTo, Id, Iq) -> + FromJID = ToplevelTo, + NewIq0 = Iq#iq{from = FromJID}, + xmpp:put_meta(NewIq0, privilege_iq, {Id, Host, FromJID}). +%% @format-end + +%% +%% Permissions +%% + -spec get_roster_permission(binary(), binary()) -> roster_permission() | none. get_roster_permission(ServerHost, Host) -> Perms = mod_privilege_opt:roster(ServerHost), @@ -452,6 +604,26 @@ get_roster_permission(ServerHost, Host) -> end end. +-spec get_iq_namespaces(binary(), binary()) -> [privilege_namespace()]. +get_iq_namespaces(ServerHost, Host) -> + NsPerms = mod_privilege_opt:iq(ServerHost), + [#privilege_namespace{ns = Ns, type = get_iq_permission(ServerHost, Host, Perms)} || {Ns, Perms} <- NsPerms]. + +-spec get_iq_permission(binary(), binary(), [iq_permission()]) -> iq_permission() | none. +get_iq_permission(ServerHost, Host, Perms) -> + case match_rule(ServerHost, Host, Perms, both) of + allow -> + both; + deny -> + Get = match_rule(ServerHost, Host, Perms, get), + Set = match_rule(ServerHost, Host, Perms, set), + if Get == allow, Set == allow -> both; + Get == allow -> get; + Set == allow -> set; + true -> none + end + end. + -spec get_message_permission(binary(), binary()) -> message_permission() | none. get_message_permission(ServerHost, Host) -> Perms = mod_privilege_opt:message(ServerHost), @@ -473,7 +645,12 @@ get_presence_permission(ServerHost, Host) -> end end. +-ifdef(OTP_BELOW_26). +-dialyzer({no_contracts, match_rule/4}). +-endif. + -spec match_rule(binary(), binary(), roster_permissions(), roster_permission()) -> allow | deny; + (binary(), binary(), iq_permissions(), iq_permission()) -> allow | deny; (binary(), binary(), presence_permissions(), presence_permission()) -> allow | deny; (binary(), binary(), message_permissions(), message_permission()) -> allow | deny. match_rule(ServerHost, Host, Perms, Type) -> diff --git a/src/mod_privilege_opt.erl b/src/mod_privilege_opt.erl index 64198b387..36bf54efa 100644 --- a/src/mod_privilege_opt.erl +++ b/src/mod_privilege_opt.erl @@ -3,10 +3,17 @@ -module(mod_privilege_opt). +-export([iq/1]). -export([message/1]). -export([presence/1]). -export([roster/1]). +-spec iq(gen_mod:opts() | global | binary()) -> [{binary(),[{'both',acl:acl()} | {'get',acl:acl()} | {'set',acl:acl()}]}]. +iq(Opts) when is_map(Opts) -> + gen_mod:get_opt(iq, Opts); +iq(Host) -> + gen_mod:get_module_opt(Host, mod_privilege, iq). + -spec message(gen_mod:opts() | global | binary()) -> [{'outgoing','none' | acl:acl()}]. message(Opts) when is_map(Opts) -> gen_mod:get_opt(message, Opts); From 46d5ab369fb735abd377df5de1aaf4d2f95e61fa Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Oct 2024 16:36:52 +0200 Subject: [PATCH 0789/1302] Update xmpp dependency --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 69ca7928c..86048f57b 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "422c107a882b6967e615ea69b33fac4897048fbb", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "c045d4d8555e251f2212743db8af90255da2ab57", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 0e034aae3..283fd0aab 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "422c107a882b6967e615ea69b33fac4897048fbb", [ref: "422c107a882b6967e615ea69b33fac4897048fbb"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "c045d4d8555e251f2212743db8af90255da2ab57", [ref: "c045d4d8555e251f2212743db8af90255da2ab57"]}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index 868b16d90..e0ab33b35 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "422c107a882b6967e615ea69b33fac4897048fbb"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "c045d4d8555e251f2212743db8af90255da2ab57"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 2e5892eb1..6d844e27f 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"422c107a882b6967e615ea69b33fac4897048fbb"}}, + {ref,"c045d4d8555e251f2212743db8af90255da2ab57"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ From 73f5d950e55170fb9a12e5febb6a689c65e1a28b Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Sat, 26 Oct 2024 12:52:39 +0200 Subject: [PATCH 0790/1302] mix_pam: Remove 'Channels' roster group of mix channels This isn't specified and was probably only meant for debugging. In real clients having this (forced) 'Channels' group is undesireable in most cases. --- src/mod_mix_pam.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index eb94877d2..7f6c768e8 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -227,7 +227,7 @@ get_mix_roster_items(Acc, {LUser, LServer}) -> name = <<>>, subscription = both, ask = undefined, - groups = [<<"Channels">>], + groups = [], mix_channel = #mix_roster_channel{participant_id = Id} } end, Channels); From a89152a3d7d4ed0f67f71e0eb6e017959ad891e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Oct 2024 09:16:36 +0100 Subject: [PATCH 0791/1302] Add support for s2s bidi --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- src/ejabberd_s2s.erl | 22 +++++- src/ejabberd_s2s_out.erl | 5 +- src/mod_s2s_bidi.erl | 142 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 src/mod_s2s_bidi.erl diff --git a/mix.exs b/mix.exs index 86048f57b..6c53adb5e 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "c045d4d8555e251f2212743db8af90255da2ab57", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "85d4cb6c080f6328a174bdc12103fd65834a400b", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 283fd0aab..75fce99ac 100644 --- a/mix.lock +++ b/mix.lock @@ -32,6 +32,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "c045d4d8555e251f2212743db8af90255da2ab57", [ref: "c045d4d8555e251f2212743db8af90255da2ab57"]}, + "xmpp": {:git, "https://github.com/processone/xmpp.git", "85d4cb6c080f6328a174bdc12103fd65834a400b", [ref: "85d4cb6c080f6328a174bdc12103fd65834a400b"]}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index e0ab33b35..ead1c34a2 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "c045d4d8555e251f2212743db8af90255da2ab57"}}, + {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "85d4cb6c080f6328a174bdc12103fd65834a400b"}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 6d844e27f..2f444e4fe 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"c045d4d8555e251f2212743db8af90255da2ab57"}}, + {ref,"85d4cb6c080f6328a174bdc12103fd65834a400b"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 957d68208..b6949ea62 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -43,7 +43,7 @@ external_host_overloaded/1, is_temporarly_blocked/1, get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1, tls_required/1, tls_enabled/1, tls_options/3, - host_up/1, host_down/1, queue_type/1]). + host_up/1, host_down/1, queue_type/1, register_connection/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, @@ -130,6 +130,10 @@ get_connections_pids(FromTo) -> [] end. +-spec register_connection(FromTo :: {binary(), binary()}) -> ok. +register_connection(FromTo) -> + gen_server:call(ejabberd_s2s, {register_connection, FromTo, self()}). + -spec dirty_get_connections() -> [{binary(), binary()}]. dirty_get_connections() -> mnesia:dirty_all_keys(s2s). @@ -228,6 +232,8 @@ init([]) -> handle_call({new_connection, Args}, _From, State) -> {reply, erlang:apply(fun new_connection_int/7, Args), State}; +handle_call({register_connection, FromTo, Pid}, _From, State) -> + {reply, register_connection_int(FromTo, Pid), State}; handle_call(Request, From, State) -> ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), {noreply, State}. @@ -478,6 +484,20 @@ new_connection_int(MyServer, Server, From, FromTo, [] end. +-spec register_connection_int(FromTo :: {binary(), binary()}, Pid :: pid()) -> ok. +register_connection_int(FromTo, Pid) -> + F = fun() -> + mnesia:write(#s2s{fromto = FromTo, pid = Pid}) + end, + TRes = mnesia:transaction(F), + case TRes of + {atomic, _} -> + erlang:monitor(process, Pid), + ok; + _ -> + ok + end. + -spec max_s2s_connections_number({binary(), binary()}) -> pos_integer(). max_s2s_connections_number({From, To}) -> case ejabberd_shaper:match(From, max_s2s_connections, jid:make(To)) of diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 00e36acb5..55903a519 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -34,7 +34,7 @@ terminate/2, code_change/3]). %% Hooks -export([process_auth_result/2, process_closed/2, handle_unexpected_info/2, - handle_unexpected_cast/2, process_downgraded/2]). + handle_unexpected_cast/2, process_downgraded/2, handle_unauthenticated_features/2]). %% API -export([start/3, start_link/3, connect/1, close/1, close/2, stop_async/1, send/2, route/2, establish/1, update_state/2, host_up/1, host_down/1]). @@ -216,6 +216,9 @@ dns_retries(#{server_host := ServerHost}) -> dns_timeout(#{server_host := ServerHost}) -> ejabberd_option:s2s_dns_timeout(ServerHost). +handle_unauthenticated_features(Features, #{server_host := ServerHost} = State) -> + ejabberd_hooks:run_fold(s2s_out_unauthenticated_features, ServerHost, State, [Features]). + handle_auth_success(Mech, #{socket := Socket, ip := IP, remote_server := RServer, server_host := ServerHost, diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl new file mode 100644 index 000000000..af1ba2ae8 --- /dev/null +++ b/src/mod_s2s_bidi.erl @@ -0,0 +1,142 @@ +%%%------------------------------------------------------------------- +%%% Created : 20 Oct 2024 by Pawel Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(mod_s2s_bidi). +-behaviour(gen_mod). +-protocol({xep, 288, '1.0.1'}). + +%% gen_mod API +-export([start/2, stop/1, reload/3, depends/2, mod_options/1]). +-export([mod_doc/0]). +%% Hooks +-export([s2s_in_packet/2, s2s_out_packet/2, + s2s_in_features/2, s2s_in_auth_result/3, s2s_out_unauthenticated_features/2, s2s_in_handle_info/2]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +start(_Host, _Opts) -> + {ok, [{hook, s2s_in_pre_auth_features, s2s_in_features, 50}, + {hook, s2s_in_post_auth_features, s2s_in_features, 50}, + {hook, s2s_in_unauthenticated_packet, s2s_in_packet, 50}, + {hook, s2s_in_authenticated_packet, s2s_in_packet, 50}, + {hook, s2s_in_handle_info, s2s_in_handle_info, 50}, + {hook, s2s_in_auth_result, s2s_in_auth_result, 50}, + {hook, s2s_out_unauthenticated_features, s2s_out_unauthenticated_features, 50}, + {hook, s2s_out_packet, s2s_out_packet, 50}]}. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +depends(_Host, _Opts) -> + []. + +mod_options(_Host) -> + []. + +mod_doc() -> + #{desc => + [?T("The module adds support for " + "https://xmpp.org/extensions/xep-0288.html" + "[XEP-0288: Bidirectional Server-to-Server Connections] that allows using " + "single s2s connection to communicate in both directions.")], + opts => [], + example => + ["modules:", + " mod_s2s_bidi: {}"]}. + +s2s_in_features(Acc, _) -> + [#s2s_bidi{}|Acc]. + +s2s_in_packet(State, #s2s_bidi{}) -> + {stop, State#{bidi_enabled => true}}; +s2s_in_packet(State, _) -> + State. + +s2s_out_unauthenticated_features(#{db_verify := _} = State, _) -> + State; +s2s_out_unauthenticated_features(State, #stream_features{} = Pkt) -> + try xmpp:try_subtag(Pkt, #s2s_bidi{}) of + #s2s_bidi{} -> + ejabberd_s2s_out:send(State#{bidi_enabled => true}, #s2s_bidi{}) + catch _:{xmpp_codec, _Why} -> + State + end. + +s2s_out_packet(#{bidi_enabled := true, ip := {IP, _}} = State, Pkt0) + when ?is_stanza(Pkt0) -> + To = xmpp:get_to(Pkt0), + case check_from_to(State, xmpp:get_from(Pkt0), To) of + ok -> + Pkt = xmpp:put_meta(Pkt0, ip, IP), + LServer = ejabberd_router:host_of_route(To#jid.lserver), + State1 = ejabberd_hooks:run_fold(s2s_in_authenticated_packet, + LServer, State, [Pkt]), + {Pkt1, State2} = ejabberd_hooks:run_fold(s2s_receive_packet, LServer, + {Pkt, State1}, []), + case Pkt1 of + drop -> ok; + _ -> ejabberd_router:route(Pkt1) + end, + {stop, State2}; + {error, Err} -> + {stop, ejabberd_s2s_out:send(State, Err)} + end; +s2s_out_packet(#{db_verify := _} = State, #stream_features{}) -> + State; +s2s_out_packet(State, #stream_features{} = Pkt) -> + try xmpp:try_subtag(Pkt, #s2s_bidi{}) of + #s2s_bidi{} -> + ejabberd_s2s_out:send(State#{bidi_enabled => true}, #s2s_bidi{}) + catch _:{xmpp_codec, _Why} -> + State + end; +s2s_out_packet(State, _Pkt) -> + State. + +s2s_in_handle_info(State, {route, Pkt}) when ?is_stanza(Pkt) -> + ejabberd_s2s_in:send(State, Pkt); +s2s_in_handle_info(State, _Info) -> + State. + +check_from_to(#{remote_server := RServer}, #jid{lserver = FromServer}, + #jid{lserver = ToServer}) -> + if + RServer /= FromServer -> {error, xmpp:serr_invalid_from()}; + true -> + case ejabberd_router:is_my_route(ToServer) of + false -> {error, xmpp:serr_host_unknown()}; + _ -> ok + end + end. + +s2s_in_auth_result(#{server := LServer, bidi_enabled := true} = State, true, RServer) -> + ejabberd_s2s:register_connection({LServer, RServer}), + State; +s2s_in_auth_result(State, _, _) -> + State. From e9e678a994407cb52e0d4197006020041e87523f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Oct 2024 09:22:00 +0100 Subject: [PATCH 0792/1302] Add support for scram upgrade tasks --- src/ejabberd_c2s.erl | 29 +++++--- src/mod_carboncopy.erl | 4 +- src/mod_scram_upgrade.erl | 120 ++++++++++++++++++++++++++++++++++ src/mod_scram_upgrade_opt.erl | 13 ++++ src/mod_stream_mgmt.erl | 5 +- 5 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 src/mod_scram_upgrade.erl create mode 100644 src/mod_scram_upgrade_opt.erl diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 66200fcdd..278385ed9 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -44,7 +44,7 @@ handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, inline_stream_features/1, handle_sasl2_inline/2, handle_sasl2_inline_post/3, - handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1]). + handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1, handle_sasl2_task_next/4, handle_sasl2_task_data/3]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -83,7 +83,7 @@ accept(Ref) -> %%%=================================================================== %%% Common API %%%=================================================================== --spec call(pid(), term(), non_neg_integer() | infinity) -> term(). +-spec call(pid(), term(), non_neg_integer() | infinity) -> dynamic(). call(Ref, Msg, Timeout) -> xmpp_stream_in:call(Ref, Msg, Timeout). @@ -255,7 +255,7 @@ process_info(#{lserver := LServer} = State, {route, Packet}) -> process_info(State, reset_vcard_xupdate_resend_presence) -> case maps:get(pres_last, State, error) of error -> State; - Pres -> + #presence{} = Pres -> Pres2 = xmpp:remove_subtag(Pres, #vcard_xupdate{}), process_self_presence(State#{pres_last => Pres2}, Pres2) end; @@ -407,7 +407,7 @@ authenticated_stream_features(#{lserver := LServer}) -> ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]). inline_stream_features(#{lserver := LServer}) -> - ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], []}, [LServer]). + ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer]). sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) -> Type = ejabberd_auth:store_type(LServer), @@ -473,7 +473,7 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang, closenew -> {error, xmpp:err_conflict(), State}; {accept_resource, Resource} -> - JID = jid:make(U, S, Resource), + JID = #jid{} = jid:make(U, S, Resource), case acl:match_rule(LServer, Access, #{usr => jid:split(JID), ip => IP}) of allow -> @@ -583,6 +583,14 @@ handle_bind2_inline_post(Els, Results, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_bind2_inline_post, LServer, State, [Els, Results]). +handle_sasl2_task_next(Task, Els, InlineEls, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_task_next, LServer, + {abort, State}, [Task, Els, InlineEls]). + +handle_sasl2_task_data(Els, InlineEls, #{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_handle_sasl2_task_data, LServer, + {abort, State}, [Els, InlineEls]). + handle_recv(El, Pkt, #{lserver := LServer} = State) -> ejabberd_hooks:run_fold(c2s_handle_recv, LServer, State, [El, Pkt]). @@ -692,7 +700,7 @@ process_message_in(State, #message{type = T} = Msg) -> -spec process_presence_in(state(), presence()) -> {boolean(), state()}. process_presence_in(#{lserver := LServer, pres_a := PresA} = State0, - #presence{from = From, type = T} = Pres) -> + #presence{from = #jid{} = From, type = T} = Pres) -> State = ejabberd_hooks:run_fold(c2s_presence_in, LServer, State0, [Pres]), case T of probe -> @@ -742,7 +750,8 @@ route_probe_reply(_, _) -> -spec process_presence_out(state(), presence()) -> state(). process_presence_out(#{lserver := LServer, jid := JID, lang := Lang, pres_a := PresA} = State0, - #presence{from = From, to = To, type = Type} = Pres) -> + #presence{from = #jid{} = From, to = #jid{} = To, type = Type} = Pres) -> + #jid{} = From, State1 = if Type == subscribe; Type == subscribed; Type == unsubscribe; Type == unsubscribed -> @@ -850,7 +859,7 @@ broadcast_presence_unavailable(#{jid := JID, pres_a := PresA} = State, Pres, JIDs = lists:filtermap( fun(LJid) -> - To = jid:make(LJid), + To = #jid{} = jid:make(LJid), P = xmpp:set_to(Pres, To), case privacy_check_packet(State, P, out) of allow -> {true, To}; @@ -938,7 +947,7 @@ get_priority_from_presence(#presence{priority = Prio}) -> -spec route_multiple(state(), [jid()], stanza()) -> ok. route_multiple(#{lserver := LServer}, JIDs, Pkt) -> - From = xmpp:get_from(Pkt), + From = #jid{} = xmpp:get_from(Pkt), ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt, false). get_subscription(#jid{luser = LUser, lserver = LServer}, JID) -> @@ -1007,7 +1016,7 @@ get_conn_type(State) -> websocket -> websocket end. --spec fix_from_to(xmpp_element(), state()) -> stanza(). +-spec fix_from_to(xmpp_element(), state()) -> stanza() | xmpp_element(). fix_from_to(Pkt, #{jid := JID}) when ?is_stanza(Pkt) -> #jid{luser = U, lserver = S, lresource = R} = JID, case xmpp:get_from(Pkt) of diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 9b8a916d6..5239ff236 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -145,10 +145,10 @@ c2s_session_resumed(State) -> c2s_session_opened(State) -> maps:remove(carboncopy, State). -c2s_inline_features({Sasl, Bind} = Acc, Host) -> +c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> - {Sasl, [#bind2_feature{var = ?NS_CARBONS_2} | Bind]}; + {Sasl, [#bind2_feature{var = ?NS_CARBONS_2} | Bind], Extra}; false -> Acc end. diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl new file mode 100644 index 000000000..b40030b8b --- /dev/null +++ b/src/mod_scram_upgrade.erl @@ -0,0 +1,120 @@ +%%%------------------------------------------------------------------- +%%% Created : 20 Oct 2024 by Pawel Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(mod_scram_upgrade). +-behaviour(gen_mod). +-protocol({xep, 480, '0.1'}). + +%% gen_mod API +-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). +-export([mod_doc/0]). +%% Hooks +-export([c2s_inline_features/2, c2s_handle_sasl2_inline/1, + c2s_handle_sasl2_task_next/4, c2s_handle_sasl2_task_data/3]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include_lib("xmpp/include/scram.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +start(_Host, _Opts) -> + {ok, [{hook, c2s_inline_features, c2s_inline_features, 50}, + {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 10}, + {hook, c2s_handle_sasl2_task_next, c2s_handle_sasl2_task_next, 10}, + {hook, c2s_handle_sasl2_task_data, c2s_handle_sasl2_task_data, 10}]}. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +depends(_Host, _Opts) -> + []. + +mod_opt_type(offered_upgrades) -> + econf:list(econf:enum([sha256, sha512])). + +mod_options(_Host) -> + [{offered_upgrades, [sha256, sha512]}]. + +mod_doc() -> + #{desc => + [?T("The module adds support for " + "https://xmpp.org/extensions/xep-0480.html" + "[XEP-0480: SASL Upgrade Tasks] that allows users to upgrade " + "passwords to more secure representation.")], + opts => [{offered_upgrades, + #{value => "list(sha256, sha512)", + desc => ?T("List with upgrade types that should be offered")}}], + example => + ["modules:", + " mod_scram_upgrade:", + " offered_upgrades:", + " - sha256", + " - sha512"]}. + +c2s_inline_features({Sasl, Bind, Extra}, Host) -> + Methods = lists:map( + fun(sha256) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-256">>}; + (sha512) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-512">>} + end, mod_scram_upgrade_opt:offered_upgrades(Host)), + {Sasl, Bind, Methods ++ Extra}. + +c2s_handle_sasl2_inline({State, Els, _Results} = Acc) -> + case lists:keyfind(sasl_upgrade, 1, Els) of + false -> + Acc; + #sasl_upgrade{cdata = Type} -> + {stop, {State, {continue, [Type]}, []}} + end. + +c2s_handle_sasl2_task_next({_, State}, Task, _Els, _InlineEls) -> + Algo = case Task of + <<"UPGR-SCRAM-SHA-256">> -> sha256; + <<"UPGR-SCRAM-SHA-512">> -> sha512 + end, + Salt = p1_rand:bytes(16), + {task_data, [#scram_upgrade_salt{cdata = Salt, iterations = 4096}], + State#{scram_upgrade => {Algo, Salt, 4096}}}. + +c2s_handle_sasl2_task_data({_, #{user := User, server := Server, + scram_upgrade := {Algo, Salt, Iter}} = State}, + Els, InlineEls) -> + case xmpp:get_subtag(#sasl2_task_data{sub_els = Els}, #scram_upgrade_hash{}) of + #scram_upgrade_hash{data = SaltedPassword} -> + StoredKey = scram:stored_key(Algo, scram:client_key(Algo, SaltedPassword)), + ServerKey = scram:server_key(Algo, SaltedPassword), + ejabberd_auth:set_password(User, Server, + #scram{hash = Algo, iterationcount = Iter, salt = Salt, + serverkey = ServerKey, storedkey = StoredKey}), + State2 = maps:remove(scram_upgrade, State), + InlineEls = lists:keydelete(sasl_upgrade, 1, InlineEls), + case ejabberd_c2s:handle_sasl2_inline(InlineEls, State2) of + {State3, NewEls, Results} -> + {success, NewEls, Results, State3} + end; + _ -> + {abort, State} + end. diff --git a/src/mod_scram_upgrade_opt.erl b/src/mod_scram_upgrade_opt.erl new file mode 100644 index 000000000..52b75e774 --- /dev/null +++ b/src/mod_scram_upgrade_opt.erl @@ -0,0 +1,13 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_scram_upgrade_opt). + +-export([offered_upgrades/1]). + +-spec offered_upgrades(gen_mod:opts() | global | binary()) -> any(). +offered_upgrades(Opts) when is_map(Opts) -> + gen_mod:get_opt(offered_upgrades, Opts); +offered_upgrades(Host) -> + gen_mod:get_module_opt(Host, mod_scram_upgrade, offered_upgrades). + diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index b125b9e4d..844435bda 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -122,11 +122,12 @@ c2s_stream_features(Acc, Host) -> Acc end. -c2s_inline_features({Sasl, Bind} = Acc, Host) -> +c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> {[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl], - [#bind2_feature{var = ?NS_STREAM_MGMT_3} | Bind]}; + [#bind2_feature{var = ?NS_STREAM_MGMT_3} | Bind], + Extra}; false -> Acc end. From 8be0f8a0b0cd15bd6de558f669028d42dc55f063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Oct 2024 09:44:05 +0100 Subject: [PATCH 0793/1302] Revert some unrelated changes --- src/ejabberd_c2s.erl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 278385ed9..efb477c47 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -44,7 +44,8 @@ handle_auth_failure/4, handle_send/3, handle_recv/3, handle_cdata/2, handle_unbinded_packet/2, inline_stream_features/1, handle_sasl2_inline/2, handle_sasl2_inline_post/3, - handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1, handle_sasl2_task_next/4, handle_sasl2_task_data/3]). + handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1, + handle_sasl2_task_next/4, handle_sasl2_task_data/3]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -83,7 +84,7 @@ accept(Ref) -> %%%=================================================================== %%% Common API %%%=================================================================== --spec call(pid(), term(), non_neg_integer() | infinity) -> dynamic(). +-spec call(pid(), term(), non_neg_integer() | infinity) -> term(). call(Ref, Msg, Timeout) -> xmpp_stream_in:call(Ref, Msg, Timeout). @@ -255,7 +256,7 @@ process_info(#{lserver := LServer} = State, {route, Packet}) -> process_info(State, reset_vcard_xupdate_resend_presence) -> case maps:get(pres_last, State, error) of error -> State; - #presence{} = Pres -> + Pres -> Pres2 = xmpp:remove_subtag(Pres, #vcard_xupdate{}), process_self_presence(State#{pres_last => Pres2}, Pres2) end; @@ -473,7 +474,7 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang, closenew -> {error, xmpp:err_conflict(), State}; {accept_resource, Resource} -> - JID = #jid{} = jid:make(U, S, Resource), + JID = jid:make(U, S, Resource), case acl:match_rule(LServer, Access, #{usr => jid:split(JID), ip => IP}) of allow -> @@ -700,7 +701,7 @@ process_message_in(State, #message{type = T} = Msg) -> -spec process_presence_in(state(), presence()) -> {boolean(), state()}. process_presence_in(#{lserver := LServer, pres_a := PresA} = State0, - #presence{from = #jid{} = From, type = T} = Pres) -> + #presence{from = From, type = T} = Pres) -> State = ejabberd_hooks:run_fold(c2s_presence_in, LServer, State0, [Pres]), case T of probe -> @@ -750,8 +751,7 @@ route_probe_reply(_, _) -> -spec process_presence_out(state(), presence()) -> state(). process_presence_out(#{lserver := LServer, jid := JID, lang := Lang, pres_a := PresA} = State0, - #presence{from = #jid{} = From, to = #jid{} = To, type = Type} = Pres) -> - #jid{} = From, + #presence{from = From, to = To, type = Type} = Pres) -> State1 = if Type == subscribe; Type == subscribed; Type == unsubscribe; Type == unsubscribed -> @@ -859,7 +859,7 @@ broadcast_presence_unavailable(#{jid := JID, pres_a := PresA} = State, Pres, JIDs = lists:filtermap( fun(LJid) -> - To = #jid{} = jid:make(LJid), + To = jid:make(LJid), P = xmpp:set_to(Pres, To), case privacy_check_packet(State, P, out) of allow -> {true, To}; @@ -947,7 +947,7 @@ get_priority_from_presence(#presence{priority = Prio}) -> -spec route_multiple(state(), [jid()], stanza()) -> ok. route_multiple(#{lserver := LServer}, JIDs, Pkt) -> - From = #jid{} = xmpp:get_from(Pkt), + From = xmpp:get_from(Pkt), ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt, false). get_subscription(#jid{luser = LUser, lserver = LServer}, JID) -> From 71ad7c368d51b46453234b840531ce3c256efca3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Oct 2024 13:34:42 +0200 Subject: [PATCH 0794/1302] Minor improvements to support mod_tombstones New check_register_user hook in ejabberd_auth.erl to allow blocking account registration when a tombstone exists. Modified room_destroyed hook in mod_muc_room.erl Until now the hook passed as arguments: LServer, Room, Host. Now it passes: LServer, Room, Host, Persistent That new Persistent argument passes the room persistent option, required by mod_tombstones because only persistent rooms should generate a tombstone, temporary ones should not. And the persistent option should not be completely overwritten, as we must still known its real value even when room is being destroyed. mod_tombstones is available in experimental mode in ejabberd-contrib git. Initial feature request: #2546 --- include/mod_muc_room.hrl | 2 +- src/ejabberd_auth.erl | 5 +++++ src/ejabberd_web_admin.erl | 6 ++++-- src/mod_muc_room.erl | 12 +++++++----- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index a278e3ae3..1d0ca4442 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -44,7 +44,7 @@ allow_visitor_nickchange = true :: boolean(), public = true :: boolean(), public_list = true :: boolean(), - persistent = false :: boolean(), + persistent = false :: boolean() | {destroying, boolean()}, moderated = true :: boolean(), captcha_protected = false :: boolean(), members_by_default = true :: boolean(), diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index c37d4c187..3f2b65b3e 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -293,6 +293,8 @@ try_register(User, Server, Password) -> false -> case ejabberd_router:is_my_host(LServer) of true -> + case ejabberd_hooks:run_fold(check_register_user, LServer, true, [User, Server, Password]) of + true -> case lists:foldl( fun(_, ok) -> ok; @@ -307,6 +309,9 @@ try_register(User, Server, Password) -> {error, _} = Err -> Err end; + false -> + {error, not_allowed} + end; false -> {error, not_allowed} end diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 9dce1ff70..35441b700 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1726,7 +1726,7 @@ make_command_raw_value(Name, Request, BaseArguments) -> raw_value | raw_and_value} | {input_name_append, [binary()]} | - {force_execution, boolean()} | + {force_execution, boolean() | undefined} | {table_options, {PageSize :: integer(), RemainingPath :: [binary()]}} | {result_named, boolean()} | {result_links, @@ -1737,7 +1737,7 @@ make_command_raw_value(Name, Request, BaseArguments) -> {style, normal | danger}. make_command2(Name, Request, BaseArguments, Options) -> Only = proplists:get_value(only, Options, all), - ForceExecution = proplists:get_value(force_execution, Options, false), + ForceExecution = proplists:get_value(force_execution, Options, undefined), InputNameAppend = proplists:get_value(input_name_append, Options, []), Resultnamed = proplists:get_value(result_named, Options, false), ResultLinks = proplists:get_value(result_links, Options, []), @@ -1791,6 +1791,8 @@ make_command2(Name, case {ForceExecution, ResultFormatApi} of {true, _} -> auto; + {false, _} -> + manual; {_, {_, rescode}} -> manual; {_, {_, restuple}} -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 73b7a7386..da2f70b78 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -540,7 +540,7 @@ normal_state({route, <<"">>, case NewStateData of stop -> Conf = StateData#state.config, - {stop, normal, StateData#state{config = Conf#config{persistent = false}}}; + {stop, normal, StateData#state{config = Conf#config{persistent = {destroying, Conf#config.persistent}}}}; _ when NewStateData#state.just_created -> close_room_if_temporary_and_empty(NewStateData); _ -> @@ -736,7 +736,7 @@ handle_event({destroy, Reason}, _StateName, [jid:encode(StateData#state.jid), Reason]), add_to_log(room_existence, destroyed, StateData), Conf = StateData#state.config, - {stop, shutdown, StateData#state{config = Conf#config{persistent = false}}}; + {stop, shutdown, StateData#state{config = Conf#config{persistent = {destroying, Conf#config.persistent}}}}; handle_event(destroy, StateName, StateData) -> ?INFO_MSG("Destroyed MUC room ~ts", [jid:encode(StateData#state.jid)]), @@ -856,7 +856,7 @@ handle_sync_event({muc_unsubscribe, From}, _From, StateName, from = From, sub_els = [#muc_unsubscribe{}]}, case process_iq_mucsub(From, IQ, StateData) of {result, _, stop} -> - {stop, normal, StateData#state{config = Conf#config{persistent = false}}}; + {stop, normal, StateData#state{config = Conf#config{persistent = {destroying, Conf#config.persistent}}}}; {result, _, NewState} -> {reply, ok, StateName, NewState}; {ignore, NewState} -> @@ -1015,8 +1015,10 @@ terminate(Reason, _StateName, _ -> add_to_log(room_existence, stopped, StateData), case (StateData#state.config)#config.persistent of - false -> - ejabberd_hooks:run(room_destroyed, LServer, [LServer, Room, Host]); + false -> + ejabberd_hooks:run(room_destroyed, LServer, [LServer, Room, Host, false]); + {destroying, Persistent} -> + ejabberd_hooks:run(room_destroyed, LServer, [LServer, Room, Host, Persistent]); _ -> ok end From bdd8ba115ec546db491f3c05043ec0d596d24c3c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 11:04:53 +0100 Subject: [PATCH 0795/1302] Document ejabberd version of the new modules --- src/mod_mam.erl | 2 +- src/mod_privilege.erl | 2 +- src/mod_s2s_bidi.erl | 3 ++- src/mod_scram_upgrade.erl | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 5dec822e5..172d9f0a5 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1576,7 +1576,7 @@ get_commands_spec() -> #ejabberd_commands{name = get_mam_count, tags = [mam], desc = "Get number of MAM messages in a local user archive", module = ?MODULE, function = get_mam_count, - note = "added in 24.xx", + note = "added in 24.10", policy = user, args = [], result_example = 5, diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 9808a4158..8e125487a 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -25,7 +25,7 @@ -author('amuhar3@gmail.com'). --protocol({xep, 356, '0.4.1', '24.xx', "", ""}). +-protocol({xep, 356, '0.4.1', '24.10', "", ""}). -behaviour(gen_server). -behaviour(gen_mod). diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl index af1ba2ae8..de597e57d 100644 --- a/src/mod_s2s_bidi.erl +++ b/src/mod_s2s_bidi.erl @@ -21,7 +21,7 @@ %%%------------------------------------------------------------------- -module(mod_s2s_bidi). -behaviour(gen_mod). --protocol({xep, 288, '1.0.1'}). +-protocol({xep, 288, '1.0.1', '24.10', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_options/1]). @@ -65,6 +65,7 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0288.html" "[XEP-0288: Bidirectional Server-to-Server Connections] that allows using " "single s2s connection to communicate in both directions.")], + note => "added in 24.10", opts => [], example => ["modules:", diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index b40030b8b..04dae1dd5 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -21,7 +21,7 @@ %%%------------------------------------------------------------------- -module(mod_scram_upgrade). -behaviour(gen_mod). --protocol({xep, 480, '0.1'}). +-protocol({xep, 480, '0.1', '24.10', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). @@ -65,6 +65,7 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0480.html" "[XEP-0480: SASL Upgrade Tasks] that allows users to upgrade " "passwords to more secure representation.")], + note => "added in 24.10", opts => [{offered_upgrades, #{value => "list(sha256, sha512)", desc => ?T("List with upgrade types that should be offered")}}], From 322e25d18ed29ee1a5be6a09e547a40a1d3f089a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 11:06:47 +0100 Subject: [PATCH 0796/1302] CI: Get again continue-on-error for XMPP Interop Tests See explanations following the comment https://github.com/processone/ejabberd/pull/4285#issuecomment-2397047355 This partially reverts commit 94c2a115d54fb24df73afc0ec54839e1e59ddb41. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbdaaea99..916a5c1a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,6 +159,7 @@ jobs: - name: Run XMPP Interoperability Tests against CI server. if: matrix.otp == '26' + continue-on-error: true uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 with: domain: 'localhost' From 643ae7e5f977e4062dc4f2cc89d6c55181febbc1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 12:00:48 +0100 Subject: [PATCH 0797/1302] Result of running "make doap options" --- ejabberd.doap | 22 ++++++++++++++++++++-- src/mod_scram_upgrade_opt.erl | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index 9644af2cc..180b0ec00 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -566,6 +566,15 @@ mod_carboncopy + + + + 1.0.1 + 24.10 + complete + mod_s2s_bidi + + @@ -623,8 +632,8 @@ - 0.2.1 - 16.09 + 0.4.1 + 24.10 mod_privilege @@ -791,6 +800,15 @@ + + + + 0.1 + 24.10 + complete + mod_scram_upgrade + + diff --git a/src/mod_scram_upgrade_opt.erl b/src/mod_scram_upgrade_opt.erl index 52b75e774..abd6bad4b 100644 --- a/src/mod_scram_upgrade_opt.erl +++ b/src/mod_scram_upgrade_opt.erl @@ -5,7 +5,7 @@ -export([offered_upgrades/1]). --spec offered_upgrades(gen_mod:opts() | global | binary()) -> any(). +-spec offered_upgrades(gen_mod:opts() | global | binary()) -> ['sha256' | 'sha512']. offered_upgrades(Opts) when is_map(Opts) -> gen_mod:get_opt(offered_upgrades, Opts); offered_upgrades(Host) -> From 853da159ded26ba8155c6e108659c1496891030b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 12:04:50 +0100 Subject: [PATCH 0798/1302] ejabberd.yml.example: Enable mod_s2s_bidi in default configuration --- ejabberd.yml.example | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 27cf8e4c5..eaf0584fa 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -227,6 +227,7 @@ modules: ip_access: trusted_network mod_roster: versioning: true + mod_s2s_bidi: {} mod_s2s_dialback: {} mod_shared_roster: {} mod_stream_mgmt: From 05f0fbf1959c8d93f831b08f2e46e79013c1a3d8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 12:56:58 +0100 Subject: [PATCH 0799/1302] Update Bulgarian translation (thanks to MrEddX) --- priv/msgs/bg.msg | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg index 93e4d13dd..4a4969e7b 100644 --- a/priv/msgs/bg.msg +++ b/priv/msgs/bg.msg @@ -111,6 +111,11 @@ {"Dynamically specify a replyto of the item publisher","Динамично задаване на отговор към публикувалия елемента"}. {"Edit Properties","Редактиране на свойства"}. {"Either approve or decline the voice request.","Одобрете или отхвърлете заявката за гласова връзка."}. +{"ejabberd HTTP Upload service","ejabberd HTTP Upload услуга"}. +{"ejabberd MUC module","ejabberd MUC модул"}. +{"ejabberd Multicast service","ejabberd Multicast услуга"}. +{"ejabberd Publish-Subscribe module","ejabberd Publish-Subscribe модул"}. +{"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 Bytestreams модул"}. {"ejabberd vCard module","ejabberd vCard модул"}. {"ejabberd Web Admin","Уеб администрация на ejabberd"}. {"ejabberd","ejabberd"}. @@ -158,6 +163,12 @@ {"Get User Statistics","Покажи статистика за потребителя"}. {"Given Name","Име"}. {"Grant voice to this person?","Предоставяне на глас за потребителя?"}. +{"has been banned","е със забранен достъп"}. +{"has been kicked because of a system shutdown","е отстранен поради изключване на системата"}. +{"has been kicked because of an affiliation change","е отстранен поради промяна на принадлежността"}. +{"has been kicked because the room has been changed to members-only","е изгонен, защото стаята е променена и е само за членове"}. +{"has been kicked","е отстранен"}. +{"Hash of the vCard-temp avatar of this room","Хеш на временния vCard аватар на тази стая"}. {"Hat title","Заглавие на шапката"}. {"Hat URI","URI адрес за шапка"}. {"Hats limit exceeded","Превишен е лимитът за шапка"}. @@ -190,6 +201,7 @@ {"Invalid 'previd' value","Невалидна стойност на 'previd'"}. {"Invitations are not allowed in this conference","Поканите не са разрешени в тази конференция"}. {"IP addresses","IP адреси"}. +{"is now known as","е известен като"}. {"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","Не е позволено да изпращате съобщения за грешки в стаята. Участникът (~s) е изпратил съобщение за грешка (~s) и е бил отстранен от стаята"}. {"It is not allowed to send private messages of type \"groupchat\"","Изпращането на лични съобщения от тип \"групов чат\" не е разрешено"}. {"It is not allowed to send private messages to the conference","Изпращането на лични съобщения до конференцията не е разрешено"}. @@ -199,6 +211,7 @@ {"JID normalization failed","Нормализирането на JID е неуспешно"}. {"Joined MIX channels of ~ts","Свързани MIX канали на ~ts"}. {"Joined MIX channels:","Свързани MIX канали:"}. +{"joins the room","се присъединява към стаята"}. {"July","Юли"}. {"June","Юни"}. {"Just created","Току що създаден"}. @@ -208,8 +221,10 @@ {"Last month","Миналия месец"}. {"Last year","Миналата година"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Най-малко значимите битове SHA-256 хеш на текст трябва да са равни на шестнайсетичния етикет"}. +{"leaves the room","напуска стаята"}. {"List of users with hats","Списък на потребителите с шапки"}. {"List users with hats","Избройте потребителите с шапки"}. +{"Logged Out","Излязъл"}. {"Logging","Регистриране на събития"}. {"Make participants list public","Направи списъка с участниците публичен"}. {"Make room CAPTCHA protected","Защити стаята с CAPTCHA"}. @@ -360,6 +375,7 @@ {"Previous session PID has exited","Предишният PID на сесията е излязъл"}. {"Previous session PID is dead","PID от предишната сесия не съществува"}. {"Previous session timed out","Времето на предишната сесия изтече"}. +{"private, ","частна, "}. {"Public","Публичен"}. {"Publish model","Модел за публикуване"}. {"Publish-Subscribe","Публикуване-Абониране"}. @@ -423,6 +439,7 @@ {"Set message of the day on all hosts and send to online users","Задавай съобщение на деня на всички хостове и изпрати на онлайн потребителите"}. {"Shared Roster Groups","Споделени групи от списъци с контакти"}. {"Show Integral Table","Покажи интегрална таблица"}. +{"Show Occupants Join/Leave","Покажи участници Влязъл/Напускнал"}. {"Show Ordinary Table","Покажи обикновена таблица"}. {"Shut Down Service","Изключи услугата"}. {"SOCKS5 Bytestreams","SOCKS5 байтови потоци"}. @@ -482,6 +499,7 @@ {"The number of unread or undelivered messages","Броят непрочетени или недоставени съобщения"}. {"The password contains unacceptable characters","Паролата съдържа недопустими символи"}. {"The password is too weak","Паролата е твърде слаба"}. +{"the password is","паролата е"}. {"The password of your XMPP account was successfully changed.","Паролата на вашия XMPP профил беше успешно променена."}. {"The password was not changed","Паролата не е променена"}. {"The passwords are different","Паролите са различни"}. @@ -555,6 +573,7 @@ {"Value of '~s' should be datetime string","Стойността на '~s' трябва да бъде низ за дата и час"}. {"Value of '~s' should be integer","Стойността на '~s' трябва да бъде цяло число"}. {"Value 'set' of 'type' attribute is not allowed","Стойността 'set' на атрибут 'type' не е разрешена"}. +{"vCard User Search","vCard търсене на потребител"}. {"View joined MIX channels","Вижте присъединените MIX канали"}. {"Virtual Hosts","Виртуални хостове"}. {"Visitors are not allowed to change their nicknames in this room","Посетителите нямат право да променят псевдонимите си в тази стая"}. @@ -584,6 +603,7 @@ {"XMPP Show Value of Chat","XMPP покажи стойност на Чат"}. {"XMPP Show Value of DND (Do Not Disturb)","XMPP покажи стойност на DND (Не ме безпокой)"}. {"XMPP Show Value of XA (Extended Away)","XMPP покажи стойност на Продължително отсъствие"}. +{"XMPP URI of Associated Publish-Subscribe Node","XMPP URI на асоцииран Publish-Subscribe нод"}. {"You are being removed from the room because of a system shutdown","Премахнати сте от стаята поради изключване на системата"}. {"You are not allowed to send private messages","Нямате право да изпращате лични съобщения"}. {"You are not joined to the channel","Не сте присъединени към канала"}. @@ -594,6 +614,9 @@ {"You need a client that supports x:data and CAPTCHA to register","За да се регистрирате Ви е нужен клиент, който поддържа x:data и CAPTCHA"}. {"You need a client that supports x:data to register the nickname","За да регистрирате псевдонима, Ви е необходим клиент, който поддържа x:data"}. {"You need an x:data capable client to search","За да търсите, Ви е нужен клиент, който поддържа x:data"}. +{"Your active privacy list has denied the routing of this stanza.","Вашият активен списък за поверителност отказа маршрутизирането на тази строфа."}. +{"Your contact offline message queue is full. The message has been discarded.","Достигнат е максималният брой офлайн съобщения за вашия контакт. Съобщението е отхвърлено."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","Вашата заявка за абонамент и/или съобщения до ~s са блокирани. За да отблокирате заявката си за абонамент, посетете ~s"}. {"Your XMPP account was successfully registered.","Вашият XMPP акаунт, беше регистриран успешно."}. {"Your XMPP account was successfully unregistered.","Вашият XMPP акаунт, беше успешно дерегистриран."}. {"You're not allowed to create nodes","Нямате право да създавате нодове"}. From 2183f60917b4fe2209ff40632cf517be50557fb2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 12:58:39 +0100 Subject: [PATCH 0800/1302] Update Italian translation (thanks to Ermete Melchiorre) --- priv/msgs/it.msg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index 79674666c..abc39dd01 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -9,19 +9,19 @@ {"A description of the node","Una descrizione del nodo"}. {"A friendly name for the node","Un nome comodo per il nodo"}. {"A password is required to enter this room","Per entrare in questa stanza è necessaria una password"}. -{"A Web Page","Un Pagina Web"}. +{"A Web Page","Una pagina web"}. {"Accept","Accettare"}. -{"Access denied by service policy","Accesso impedito dalle politiche del servizio"}. +{"Access denied by service policy","Accesso negato dalle politiche del servizio"}. {"Access model","Modello di accesso"}. {"Account doesn't exist","L'account non esiste"}. {"Action on user","Azione sull'utente"}. {"Add a hat to a user","Aggiungere un cappello a un utente"}. -{"Add User","Aggiungere un Utente"}. +{"Add User","Aggiungere un utente"}. {"Administration of ","Amministrazione di "}. {"Administration","Amministrazione"}. {"Administrator privileges required","Sono richiesti privilegi di amministratore"}. {"All activity","Tutta l'attività"}. -{"All Users","Tutti gli Utenti"}. +{"All Users","Tutti gli utenti"}. {"Allow subscription","Consenti iscrizione"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Consentire a questo ID Jabber di iscriversi a questo nodo pubsub?"}. {"Allow this person to register with the room?","Permettere a questa persona di registrarsi con la stanza?"}. From 11e86811a099108459beb986f8d4fe7d69969839 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 12:59:39 +0100 Subject: [PATCH 0801/1302] Update Portuguese (Brazil) translation (thanks to Wellington Uemura) --- priv/msgs/pt-br.msg | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index d23f55ac0..60aeddf51 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -168,6 +168,7 @@ {"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. {"has been kicked because the room has been changed to members-only","foi desconectado porque a política da sala mudou, só membros são permitidos"}. {"has been kicked","foi removido"}. +{"Hash of the vCard-temp avatar of this room","Hash do avatar do vCard-temp desta sala"}. {"Hat title","Título do chapéu"}. {"Hat URI","URI do chapéu"}. {"Hats limit exceeded","O limite dos chapéus foi excedido"}. From c72ef537ee0a44a6363cb28190422fd9ce0bcfe4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 13:00:38 +0100 Subject: [PATCH 0802/1302] Update Chinese (Simplified) translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 34808fd90..e35ae7cb3 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -168,6 +168,7 @@ {"has been kicked because of an affiliation change","由于从属关系的更改而被踢出"}. {"has been kicked because the room has been changed to members-only","被踢出,因为群聊已更改为仅成员进入"}. {"has been kicked","已被踢出"}. +{"Hash of the vCard-temp avatar of this room","此群聊 vCard-temp 头像的散列值"}. {"Hat title","头衔标题"}. {"Hat URI","头衔 URI"}. {"Hats limit exceeded","已超过头衔限制"}. @@ -226,12 +227,12 @@ {"Logged Out","已登出"}. {"Logging","日志记录"}. {"Make participants list public","公开参与者列表"}. -{"Make room CAPTCHA protected","让群聊受验证码保护"}. -{"Make room members-only","让群聊仅成员进入"}. +{"Make room CAPTCHA protected","开启群聊验证码保护"}. +{"Make room members-only","仅成员进入的群聊"}. {"Make room moderated","开启群聊发言审核"}. -{"Make room password protected","让群聊受密码保护"}. -{"Make room persistent","让群聊持续存在"}. -{"Make room public searchable","让群聊可公开搜索"}. +{"Make room password protected","开启群聊密码保护"}. +{"Make room persistent","持续存在的群聊"}. +{"Make room public searchable","可公开搜索的群聊"}. {"Malformed username","用户名格式不正确"}. {"MAM preference modification denied by service policy","服务策略拒绝修改 MAM 首选项"}. {"March","三月"}. @@ -607,7 +608,7 @@ {"You are not allowed to send private messages","不允许您发送私信"}. {"You are not joined to the channel","您未加入频道"}. {"You can later change your password using an XMPP client.","您之后可以使用 XMPP 客户端更改密码。"}. -{"You have been banned from this room","您已被此群聊封禁"}. +{"You have been banned from this room","禁止您进入此群聊"}. {"You have joined too many conferences","您加入了太多群聊"}. {"You must fill in field \"Nickname\" in the form","您必须在表单中填写“昵称”字段"}. {"You need a client that supports x:data and CAPTCHA to register","您需要支持 x:data 和验证码的客户端来注册"}. From 855c828d1f3a88ed3a2698aee5cbc0903e164b51 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 13:00:57 +0100 Subject: [PATCH 0803/1302] Update Spanish and Catalan translations --- priv/msgs/ca.msg | 1 + priv/msgs/es.msg | 1 + 2 files changed, 2 insertions(+) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 4663f95f0..0afc2ceb8 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -168,6 +168,7 @@ {"has been kicked because of an affiliation change","ha sigut expulsat a causa d'un canvi d'afiliació"}. {"has been kicked because the room has been changed to members-only","ha sigut expulsat perquè la sala ara és només per a membres"}. {"has been kicked","ha sigut expulsat"}. +{"Hash of the vCard-temp avatar of this room","Hash del avatar a vCard-temp d'esta sala"}. {"Hat title","Títol del barret"}. {"Hat URI","URI del barret"}. {"Hats limit exceeded","El límit de tràfic ha sigut sobrepassat"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index e51c3facc..b3b16856c 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -168,6 +168,7 @@ {"has been kicked because of an affiliation change","ha sido expulsado por un cambio de su afiliación"}. {"has been kicked because the room has been changed to members-only","ha sido expulsado porque la sala es ahora solo para miembros"}. {"has been kicked","ha sido expulsado"}. +{"Hash of the vCard-temp avatar of this room","Hash del avatar vCard-temp de esta sala"}. {"Hat title","Título del sombrero"}. {"Hat URI","Dirección del sombrero"}. {"Hats limit exceeded","Se ha excedido el límite de sombreros"}. From 6b0058c89c32b67787211482113c308eb847f90a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 13:42:56 +0100 Subject: [PATCH 0804/1302] Add two more version notes --- src/ejabberd_admin.erl | 1 + src/mod_privilege.erl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 3834ace47..41aca201c 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -203,6 +203,7 @@ get_commands_spec() -> #ejabberd_commands{name = update, tags = [server], desc = "Update the given module", longdesc = "To update all the possible modules, use `all`.", + note = "improved in 24.10", module = ?MODULE, function = update, args_example = ["all"], args = [{module, string}], diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 8e125487a..046bec614 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -117,6 +117,7 @@ mod_doc() -> "carefully, only if you trust a component."), "", ?T("NOTE: This module is complementary to _`mod_delegation`_, " "but can also be used separately.")], + note => "improved in 24.10", opts => [{roster, #{value => ?T("Options"), From 319414b985e839d0336056a58b45e6aba5199be3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 13:34:58 +0100 Subject: [PATCH 0805/1302] Update man page to 24.10 --- man/ejabberd.yml.5 | 303 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 244 insertions(+), 59 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index d34a84918..7dd33bc06 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 07/18/2024 +.\" Date: 10/28/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "07/18/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "10/28/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.07/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.10/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 24\&.07\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 24\&.10\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR .RS 4 @@ -138,7 +138,8 @@ access_rules: .PP \fBacl\fR: \fI{ACLName: {ACLType: ACLValue}}\fR .RS 4 -The option defines access control lists: named sets of rules which are used to match against different targets (such as a JID or an IP address)\&. Every set of rules has name +This option defines +\fI\&.\&./configuration/basic\&.md#acl|access control lists\fR: named sets of rules which are used to match against different targets (such as a JID or an IP address)\&. Every set of rules has name \fIACLName\fR: it can be any string except \fIall\fR or @@ -319,7 +320,9 @@ means that the same username can be taken multiple times in anonymous login mode .PP \fBanonymous_protocol\fR: \fIlogin_anon | sasl_anon | both\fR .RS 4 -Define what anonymous protocol will be used: +Define what +\fIauthentication\&.md#anonymous\-login\-and\-sasl\-anonymous|anonymous\fR +protocol will be used: .sp .RS 4 .ie n \{\ @@ -368,11 +371,8 @@ Define the permissions for API access\&. Please consult the ejabberd Docs web .PP \fBappend_host_config\fR: \fI{Host: Options}\fR .RS 4 -To define specific ejabberd modules in a virtual host, you can define the global -\fImodules\fR -option with the common modules, and later add specific modules to certain virtual hosts\&. To accomplish that, -\fIappend_host_config\fR -option can be used\&. +Add a few specific options to a certain +\fI\&.\&./configuration/basic\&.md#virtual\-hosting|virtual host\fR\&. .RE .PP \fBauth_cache_life_time\fR: \fItimeout()\fR @@ -412,7 +412,9 @@ and certificate based authentication)\&. This helps with processing offline mess .PP \fBauth_method\fR: \fI[mnesia | sql | anonymous | external | jwt | ldap | pam, \&.\&.\&.]\fR .RS 4 -A list of authentication methods to use\&. If several methods are defined, authentication is considered successful as long as authentication of at least one of the methods succeeds\&. The default value is +A list of +\fIauthentication\&.md|authentication\fR +methods to use\&. If several methods are defined, authentication is considered successful as long as authentication of at least one of the methods succeeds\&. The default value is \fI[mnesia]\fR\&. .RE .PP @@ -682,7 +684,8 @@ A list of Erlang nodes to connect on ejabberd startup\&. This option is mostly i .PP \fBdefault_db\fR: \fImnesia | sql\fR .RS 4 -Default persistent storage for ejabberd\&. Modules and other components (e\&.g\&. authentication) may have its own value\&. The default value is +\fIdatabase\&.md#default\-database|Default database\fR +to store persistent data in ejabberd\&. Modules and other components (e\&.g\&. authentication) may have its own value\&. The default value is \fImnesia\fR\&. .RE .PP @@ -694,7 +697,8 @@ Default volatile (in\-memory) storage for ejabberd\&. Modules and other componen .PP \fBdefine_macro\fR: \fI{MacroName: MacroValue}\fR .RS 4 -Defines a macro\&. The value can be any valid arbitrary YAML value\&. For convenience, it\(cqs recommended to define a +Defines a +\fI\&.\&./configuration/file\-format\&.md#macros\-in\-configuration\-file|macro\fR\&. The value can be any valid arbitrary YAML value\&. For convenience, it\(cqs recommended to define a \fIMacroName\fR in capital letters\&. Duplicated macros are not allowed\&. Macros are processed after additional configuration files have been included, so it is possible to use macros that are defined in configuration files included before the usage\&. It is possible to use a \fIMacroValue\fR @@ -742,7 +746,9 @@ which enables this extension\&. .PP \fBdomain_balancing\fR: \fI{Domain: Options}\fR .RS 4 -An algorithm to load balance the components that are plugged on an ejabberd cluster\&. It means that you can plug one or several instances of the same component on each ejabberd node and that the traffic will be automatically distributed\&. The algorithm to deliver messages to the component(s) can be specified by this option\&. For any component connected as +An algorithm to +\fI\&.\&./guide/clustering\&.md#service\-load\-balancing|load\-balance\fR +the components that are plugged on an ejabberd cluster\&. It means that you can plug one or several instances of the same component on each ejabberd node and that the traffic will be automatically distributed\&. The algorithm to deliver messages to the component(s) can be specified by this option\&. For any component connected as \fIDomain\fR, available \fIOptions\fR are: @@ -821,18 +827,22 @@ Define the base URI when performing ReST requests\&. The default value is: .PP \fBextauth_pool_name\fR: \fIName\fR .RS 4 -Define the pool name appendix, so the full pool name will be +Define the pool name appendix in +\fIauthentication\&.md#external\-script|external auth\fR, so the full pool name will be \fIextauth_pool_Name\fR\&. The default value is the hostname\&. .RE .PP \fBextauth_pool_size\fR: \fISize\fR .RS 4 -The option defines the number of instances of the same external program to start for better load balancing\&. The default is the number of available CPU cores\&. +The option defines the number of instances of the same +\fIauthentication\&.md#external\-script|external auth\fR +program to start for better load balancing\&. The default is the number of available CPU cores\&. .RE .PP \fBextauth_program\fR: \fIPath\fR .RS 4 -Indicate in this option the full path to the external authentication script\&. The script must be executable by ejabberd\&. +Indicate in this option the full path to the +\fIauthentication\&.md#external\-script|external authentication script\fR\&. The script must be executable by ejabberd\&. .RE .PP \fBfqdn\fR: \fIDomain\fR @@ -851,7 +861,8 @@ for backward compatibility\&. .RS 4 The option is used to redefine \fIOptions\fR -for virtual host +for +\fI\&.\&./configuration/basic\&.md#virtual\-hosting|virtual host\fR \fIHost\fR\&. In the example below LDAP authentication method will be used on virtual host \fIdomain\&.tld\fR and SQL method will be used on virtual host @@ -882,7 +893,9 @@ host_config: .PP \fBhosts\fR: \fI[Domain1, Domain2, \&.\&.\&.]\fR .RS 4 -The option defines a list containing one or more domains that +List of one or more +\fI\&.\&./configuration/basic\&.md#host\-names|host names\fR +(or domains) that \fIejabberd\fR will serve\&. This is a \fBmandatory\fR @@ -891,7 +904,9 @@ option\&. .PP \fBinclude_config_file\fR: \fI[Filename, \&.\&.\&.] | {Filename: Options}\fR .RS 4 -Read additional configuration from +Read and +\fI\&.\&./configuration/file\-format\&.md#include\-additional\-files|include additional file\fR +from \fIFilename\fR\&. If the value is provided in \fI{Filename: Options}\fR format, the @@ -922,7 +937,9 @@ at start time\&. The default value is an empty list of modules: .PP \fBjwt_auth_only_rule\fR: \fIAccessName\fR .RS 4 -This ACL rule defines accounts that can use only this auth method, even if others are also defined in the ejabberd configuration file\&. In other words: if there are several auth methods enabled for this host (JWT, SQL, \&...), users that match this rule can only use JWT\&. The default value is +This ACL rule defines accounts that can use only the +\fIauthentication\&.md#jwt\-authentication|JWT\fR +auth method, even if others are also defined in the ejabberd configuration file\&. In other words: if there are several auth methods enabled for this host (JWT, SQL, \&...), users that match this rule can only use JWT\&. The default value is \fInone\fR\&. .RE .PP @@ -930,18 +947,24 @@ This ACL rule defines accounts that can use only this auth method, even if other .RS 4 By default, the JID is defined in the \fI"jid"\fR -JWT field\&. In this option you can specify other JWT field name where the JID is defined\&. +JWT field\&. In this option you can specify other +\fIauthentication\&.md#jwt\-authentication|JWT\fR +field name where the JID is defined\&. .RE .PP \fBjwt_key\fR: \fIFilePath\fR .RS 4 -Path to the file that contains the JWK Key\&. The default value is +Path to the file that contains the +\fIauthentication\&.md#jwt\-authentication|JWT\fR +key\&. The default value is \fIundefined\fR\&. .RE .PP \fBlanguage\fR: \fILanguage\fR .RS 4 -The option defines the default language of server strings that can be seen by XMPP clients\&. If an XMPP client does not possess +Define the +\fI\&.\&./configuration/basic\&.md#default\-language|default language\fR +of server strings that can be seen by XMPP clients\&. If an XMPP client does not possess \fIxml:lang\fR attribute, the specified language is used\&. The default value is \fI"en"\fR\&. @@ -949,9 +972,10 @@ attribute, the specified language is used\&. The default value is .PP \fBldap_backups\fR: \fI[Host, \&.\&.\&.]\fR .RS 4 -A list of IP addresses or DNS names of LDAP backup servers\&. When no servers listed in +A list of IP addresses or DNS names of LDAP backup servers (see +\fI\&.\&./configuration/ldap\&.md#ldap\-connection|LDAP connection\fR)\&. When no servers listed in \fIldap_servers\fR -option are reachable, ejabberd will try to connect to these backup servers\&. The default is an empty list, i\&.e\&. no backup servers specified\&. WARNING: ejabberd doesn\(cqt try to reconnect back to the main servers when they become operational again, so the only way to restore these connections is to restart ejabberd\&. This limitation might be fixed in future releases\&. +option are reachable, ejabberd connects to these backup servers\&. The default is an empty list, i\&.e\&. no backup servers specified\&. Please notice that ejabberd only connects to the next server when the existing connection is lost; it doesn\(cqt detect when a previously\-attempted server becomes available again\&. .RE .PP \fBldap_base\fR: \fIBase\fR @@ -1022,7 +1046,8 @@ Bind Distinguished Name\&. The default value is an empty string, which means "an .PP \fBldap_servers\fR: \fI[Host, \&.\&.\&.]\fR .RS 4 -A list of IP addresses or DNS names of your LDAP servers\&. The default value is +A list of IP addresses or DNS names of your LDAP servers (see +\fI\&.\&./configuration/ldap\&.md#ldap\-connection|LDAP connection\fR)\&. ejabberd connects immediately to all of them, and reconnects infinitely if connection is lost\&. The default value is \fI[localhost]\fR\&. .RE .PP @@ -1114,7 +1139,8 @@ The size (in bytes) of a log file to trigger rotation\&. If set to .PP \fBloglevel\fR: \fInone | emergency | alert | critical | error | warning | notice | info | debug\fR .RS 4 -Verbosity of log files generated by ejabberd\&. The default value is +Verbosity of ejabberd +\fI\&.\&./configuration/basic\&.md#logging|logging\fR\&. The default value is \fIinfo\fR\&. NOTE: previous versions of ejabberd had log levels defined in numeric format (\fI0\&.\&.5\fR)\&. The numeric values are still accepted for backward compatibility, but are not recommended\&. .RE .PP @@ -1126,9 +1152,9 @@ This option specifies the maximum number of elements in the queue of the FSM (Fi .PP \fBmodules\fR: \fI{Module: Options}\fR .RS 4 -The option for modules configuration\&. See -\fImodules\&.md|Modules\fR -section for details\&. +Set all the +\fImodules\&.md|modules\fR +configuration options\&. .RE .PP \fBnegotiation_timeout\fR: \fItimeout()\fR @@ -1147,10 +1173,9 @@ This option can be used to tune tick time parameter of .PP \fBnew_sql_schema\fR: \fItrue | false\fR .RS 4 -Whether to use -\fInew\fR -SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/24\&.07/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +Whether to use the +\fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at +https://github\&.com/processone/ejabberd/tree/24\&.10/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1296,13 +1321,17 @@ seconds\&. .PP \fBpam_service\fR: \fIName\fR .RS 4 -This option defines the PAM service name\&. Refer to the PAM documentation of your operation system for more information\&. The default value is +This option defines the +\fIauthentication\&.md#pam\-authentication|PAM\fR +service name\&. Refer to the PAM documentation of your operation system for more information\&. The default value is \fIejabberd\fR\&. .RE .PP \fBpam_userinfotype\fR: \fIusername | jid\fR .RS 4 -This option defines what type of information about the user ejabberd provides to the PAM service: only the username, or the user\(cqs JID\&. Default is +This option defines what type of information about the user ejabberd provides to the +\fIauthentication\&.md#pam\-authentication|PAM\fR +service: only the username, or the user\(cqs JID\&. Default is \fIusername\fR\&. .RE .PP @@ -1336,36 +1365,47 @@ option where file queues will be placed\&. The default value is .PP \fBredis_connect_timeout\fR: \fItimeout()\fR .RS 4 -A timeout to wait for the connection to be re\-established to the Redis server\&. The default is +A timeout to wait for the connection to be re\-established to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default is \fI1 second\fR\&. .RE .PP \fBredis_db\fR: \fINumber\fR .RS 4 -Redis database number\&. The default is +\fIdatabase\&.md#redis|Redis\fR +database number\&. The default is \fI0\fR\&. .RE .PP \fBredis_password\fR: \fIPassword\fR .RS 4 -The password to the Redis server\&. The default is an empty string, i\&.e\&. no password\&. +The password to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default is an empty string, i\&.e\&. no password\&. .RE .PP \fBredis_pool_size\fR: \fINumber\fR .RS 4 -The number of simultaneous connections to the Redis server\&. The default value is +The number of simultaneous connections to the +\fIdatabase\&.md#redis|Redis\fR +server\&. The default value is \fI10\fR\&. .RE .PP \fBredis_port\fR: \fI1\&.\&.65535\fR .RS 4 -The port where the Redis server is accepting connections\&. The default is +The port where the +\fIdatabase\&.md#redis|Redis\fR +server is accepting connections\&. The default is \fI6379\fR\&. .RE .PP \fBredis_queue_type\fR: \fIram | file\fR .RS 4 -The type of request queue for the Redis server\&. See description of +The type of request queue for the +\fIdatabase\&.md#redis|Redis\fR +server\&. See description of \fIqueue_type\fR option for the explanation\&. The default value is the value defined in \fIqueue_type\fR @@ -1376,7 +1416,9 @@ if the latter is not set\&. .PP \fBredis_server\fR: \fIHostname\fR .RS 4 -A hostname or an IP address of the Redis server\&. The default is +A hostname or an IP address of the +\fIdatabase\&.md#redis|Redis\fR +server\&.The default is \fIlocalhost\fR\&. .RE .PP @@ -1579,7 +1621,8 @@ XEP\-0138) or not\&. The default value is .PP \fBshaper\fR: \fI{ShaperName: Rate}\fR .RS 4 -The option defines a set of shapers\&. Every shaper is assigned a name +The option defines a set of +\fI\&.\&./configuration/basic\&.md#shapers|shapers\fR\&. Every shaper is assigned a name \fIShaperName\fR that can be used in other parts of the configuration file, such as \fIshaper_rules\fR @@ -1611,7 +1654,9 @@ shaper: .PP \fBshaper_rules\fR: \fI{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}\fR .RS 4 -An entry allowing to declaring shaper to use for matching user/hosts\&. Semantics is similar to +This option defines +\fI\&.\&./configuration/basic\&.md#shaper\-rules|shaper rules\fR +to use for matching user/hosts\&. Semantics is similar to \fIaccess_rules\fR option, the only difference is that instead using \fIallow\fR @@ -1858,11 +1903,11 @@ header if you enable this option as, otherwise, the client can set it itself and \fBupdate_sql_schema\fR: \fItrue | false\fR .RS 4 \fINote\fR -about this option: updated in 24\&.06\&. Allow ejabberd to update SQL schema\&. This option was added in ejabberd 23\&.10, and enabled by default since 24\&.06\&. The default value is +about this option: updated in 24\&.06\&. Allow ejabberd to update SQL schema in MySQL, PostgreSQL and SQLite databases\&. This option was added in ejabberd 23\&.10, and enabled by default since 24\&.06\&. The default value is \fItrue\fR\&. .RE .PP -\fBupdate_sql_schema_timeout 🟤\fR: \fItimeout()\fR +\fBupdate_sql_schema_timeout\fR: \fItimeout()\fR .RS 4 \fINote\fR about this option: added in 24\&.07\&. Time allocated to SQL schema update queries\&. The default value is set to 5 minutes\&. @@ -1919,12 +1964,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 24\&.07\&. The modules that changed in this version are marked with 🟤\&. -.SS "Elixir\&.ModPresenceDemo" -.sp -This is just a demonstration\&. -.sp -The module has no options\&. +This section describes modules options of ejabberd 24\&.10\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -5367,6 +5407,8 @@ option, but applied to this module only\&. .RE .SS "mod_privilege" .sp +\fINote\fR about this option: improved in 24\&.10\&. +.sp This module is an implementation of XEP\-0356: Privileged Entity\&. This extension allows components to have privileged access to other entity data (send messages on behalf of the server or on behalf of a user, get/set user roster, access presence information, etc\&.)\&. This may be used to write powerful external components, for example implementing an external PEP or MAM service\&. .sp By default a component does not have any privileged access\&. It is worth noting that the permissions grant access to the component to a specific data type for all users of the virtual host on which \fImod_privilege\fR is loaded\&. @@ -5413,6 +5455,36 @@ This module is complementary to \fImod_delegation\fR, but can also be used separ \fBAvailable options:\fR .RS 4 .PP +\fBiq\fR: \fI{Namespace: Options}\fR +.RS 4 +This option defines namespaces and their IQ permissions\&. By default no permissions are given\&. The +\fIOptions\fR +are: +.PP +\fBboth\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIget\fR +and +\fIset\fR\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBget\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIget\fR\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBset\fR: \fIAccessName\fR +.RS 4 +Allows sending IQ stanzas of type +\fIset\fR\&. The default value is +\fInone\fR\&. +.RE +.RE +.PP \fBmessage\fR: \fIOptions\fR .RS 4 This option defines permissions for messages\&. By default no permissions are given\&. The @@ -5485,6 +5557,9 @@ Sets write access to a user\(cqs roster\&. The default value is .nf modules: mod_privilege: + iq: + http://jabber\&.org/protocol/pubsub: + get: all roster: get: all presence: @@ -6120,7 +6195,10 @@ This module reads also the top\-level \fIregistration_timeout\fR option defined .RS 4 Specify rules to restrict what usernames can be registered\&. If a rule returns \fIdeny\fR -on the requested username, registration of that user name is denied\&. There are no restrictions by default\&. +on the requested username, registration of that user name is denied\&. There are no restrictions by default\&. If +\fIAccessName\fR +is +\fInone\fR, then registering new accounts using In\-Band Registration is disabled and the corresponding stream feature is not announced to clients\&. .RE .PP \fBaccess_from\fR: \fIAccessName\fR @@ -6376,6 +6454,33 @@ modules: .RE .\} .RE +.SS "mod_s2s_bidi" +.sp +\fINote\fR about this option: added in 24\&.10\&. +.sp +The module adds support for XEP\-0288: Bidirectional Server\-to\-Server Connections that allows using single s2s connection to communicate in both directions\&. +.sp +The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_s2s_bidi: {} +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_s2s_dialback" .sp The module adds support for XEP\-0220: Server Dialback to provide server identity verification based on DNS\&. @@ -6435,6 +6540,48 @@ modules: .RE .\} .RE +.SS "mod_scram_upgrade" +.sp +\fINote\fR about this option: added in 24\&.10\&. +.sp +The module adds support for XEP\-0480: SASL Upgrade Tasks that allows users to upgrade passwords to more secure representation\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBoffered_upgrades\fR: \fIlist(sha256, sha512)\fR +.RS 4 +List with upgrade types that should be offered +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_scram_upgrade: + offered_upgrades: + \- sha256 + \- sha512 +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_service_log" .sp This module forwards copies of all stanzas to remote XMPP servers or components\&. Every stanza is encapsulated into element as described in XEP\-0297: Stanza Forwarding\&. @@ -7369,6 +7516,44 @@ services: This module adds support for XEP\-0202: Entity Time\&. In other words, the module reports server\(cqs system time\&. .sp The module has no options\&. +.SS "mod_tombstones" +.sp +\fINote\fR about this option: added in 24\&.xx\&. +.sp +Keep tombstones for accounts and rooms that were removed\&. This prevents registration of that account, and creation of that room\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdb_type\fR: \fImnesia | sql | ldap\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBroom_tombstone_expiry\fR: \fItime | infinity\fR +.RS 4 +How long to keep MUC room tombstones\&. If set to +\fIinfinity\fR +the tombstones are forever\&. The default value is +\fI30 days\fR\&. +.RE +.PP +\fBuser_tombstone_expiry\fR: \fItime | infinity\fR +.RS 4 +How long to keep users tombstones, for example +\fI365 days\fR\&. If set to +\fIinfinity\fR +the tombstones are forever\&. The default value is +\fI365 days\fR\&. +.RE +.RE .SS "mod_vcard" .sp This module allows end users to store and retrieve their vCard, and to retrieve other users vCards, as defined in XEP\-0054: vcard\-temp\&. The module also implements an uncomplicated Jabber User Directory based on the vCards of these users\&. Moreover, it enables the server to send its vCard when queried\&. @@ -7833,7 +8018,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 24\&.07\&. +This section describes listeners options of ejabberd 24\&.10\&. .sp TODO .SH "AUTHOR" @@ -7841,13 +8026,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 24\&.07\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 24\&.10\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.07/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.10/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From b5dab246795a885af9d783d5c41ca4f0862be8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Oct 2024 15:58:32 +0100 Subject: [PATCH 0806/1302] Update dependencies --- mix.exs | 6 +++--- mix.lock | 16 ++++++++-------- rebar.config | 16 ++++++++-------- rebar.lock | 51 ++++++++++++++++++++++++--------------------------- 4 files changed, 43 insertions(+), 46 deletions(-) diff --git a/mix.exs b/mix.exs index 6c53adb5e..1d34c1098 100644 --- a/mix.exs +++ b/mix.exs @@ -134,8 +134,8 @@ defmodule Ejabberd.MixProject do {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f", override: true}, - {:fast_xml, git: "https://github.com/processone/fast_xml.git", ref: "e7dc91310046831f436a03abf029587f0c2764f4", override: true}, + {:fast_tls, "~> 1.1.22"}, + {:fast_xml, "~> 1.1.53"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp.git", ref: "85d4cb6c080f6328a174bdc12103fd65834a400b", override: true}, + {:xmpp, "~> 1.9"}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 75fce99ac..102415e76 100644 --- a/mix.lock +++ b/mix.lock @@ -7,11 +7,11 @@ "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "esip": {:hex, :esip, "1.0.54", "dae8fb8278fd3b2c0d38c2e832c4b8d26700eb239b9a42c8ea574fee76f5e76a", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.14", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "8187af819d7259cdaddaf69726c239ef604c9b0b0298a5f2d3e687bf5e2237ee"}, + "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, - "fast_tls": {:git, "https://github.com/processone/fast_tls.git", "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f", [ref: "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"]}, - "fast_xml": {:git, "https://github.com/processone/fast_xml.git", "e7dc91310046831f436a03abf029587f0c2764f4", [ref: "e7dc91310046831f436a03abf029587f0c2764f4"]}, + "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, + "fast_xml": {:hex, :fast_xml, "1.1.53", "1ef4f6e5995bcfa94800a46b460c3400c19c0a533948b12200a2e2fb1a2be427", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5064336d6f363eee5097aa5dc5ced9b67f05152f2e6b8520fd50d268c2ab839c"}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, @@ -22,16 +22,16 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:hex, :p1_acme, "1.0.23", "791aef0f79dc7f768b228808250c349fa9ce585cd8779da50ca93106eb3394d0", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "8ce196f26e3d22ea10b7809122950465878c127f80767e325207aed7e8d0dd59"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.24", "0ed1e098c5a4525032448c65a2715f30980aae725615a4d255fd25f26bb22507", [:rebar3], [], "hexpm", "f058865f64257f507a2c6a5aff369b1375dbcb30b3d4258dad4f1b3eaffb655f"}, + "p1_acme": {:hex, :p1_acme, "1.0.24", "e19876618eb0be22815aca99640cb88cd5c86e4239b8e8dc15b4e5d7854ef7e2", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "f1451d706595ef997ab1ca17162ddac58f874ac97e315a5fadbe3cfa26148002"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.25", "875d4cbdc7c9990270df3292cce2514e4c18a9fdfd19bef258cb4d0c45b4f243", [:rebar3], [], "hexpm", "e6187ffae95b726098e88f3ee6f2344ac259ce2c26e0ee403b05feef341ae434"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.27", "883e335d82ac062de0bde7981f8250a2e632258bb7a47df839a4cbdcb3e971e6", [:rebar3], [{:xmpp, "~> 1.8.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "8e4d1a7602cb68780e55d89dc5a9b2e1aaca3f4f1ee3d1a25f2f8c3d2364ffb9"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.28", "08eca7d2afc81ba6d757b572f4a57ef3a2383b0c7b785fde38184bc368376d54", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "655bb75df036ace55ddce17ca741143c42e0667c6206ac27a4dcbc65f71ac9ef"}, "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, - "stun": {:hex, :stun, "1.2.14", "6f538ac80c842131dbd149055570d116bfabc9b5ebff4bd6af2e7888958c660c", [:rebar3], [{:fast_tls, "1.1.21", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e134807b1b7a8dffd94e64eefee00e65c7b4042f3d14e16f8f43566d20371583"}, + "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp.git", "85d4cb6c080f6328a174bdc12103fd65834a400b", [ref: "85d4cb6c080f6328a174bdc12103fd65834a400b"]}, + "xmpp": {:hex, :xmpp, "1.9.0", "d92446bf51d36adda02db63b963fe6d4a1ede33e59b38a43d9b90afd20c25b74", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "c1b91be74a9a9503afa6766f756477516920ffbfeea0c260c2fa171355f53c27"}, "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, } diff --git a/rebar.config b/rebar.config index ead1c34a2..e6f59bebd 100644 --- a/rebar.config +++ b/rebar.config @@ -33,11 +33,11 @@ {if_var_true, redis, {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.54"}}}}, + {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.56"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", "75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", "e7dc91310046831f436a03abf029587f0c2764f4"}}, + {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.53"}}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", @@ -56,20 +56,20 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.23"}}}, + {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.24"}}}, {if_var_true, mysql, - {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.24"}}}}, + {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.25"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.27"}}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.28"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, - {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.14"}}}}, - {xmpp, "~> 1.8.3", {git, "https://github.com/processone/xmpp", "85d4cb6c080f6328a174bdc12103fd65834a400b"}}, + {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.0"}}}, {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 2f444e4fe..cdfae8f58 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,36 +4,27 @@ {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.23">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.54">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.56">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, - {<<"fast_tls">>, - {git,"https://github.com/processone/fast_tls", - {ref,"75a08772f0ffddfed0441bfdc7e7f9a5adb3862f"}}, - 0}, - {<<"fast_xml">>, - {git,"https://github.com/processone/fast_xml", - {ref,"e7dc91310046831f436a03abf029587f0c2764f4"}}, - 0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.53">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, - {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.23">>},0}, - {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.24">>},0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.24">>},0}, + {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.25">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.27">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.28">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.14">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"85d4cb6c080f6328a174bdc12103fd65834a400b"}}, - 0}, + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.0">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. [ {pkg_hash,[ @@ -42,24 +33,27 @@ {<<"eimp">>, <<"AAF32EFAB061143403DADBAA63E699EF8E81702FBFA96FD436D5E9BE4D6A8D3A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, - {<<"esip">>, <<"DAE8FB8278FD3B2C0D38C2E832C4B8D26700EB239B9A42C8EA574FEE76F5E76A">>}, + {<<"esip">>, <<"63C0FDC667BE751714E1E5C14621A9334F21B60AC1BB68BE889454CA9CA021B7">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, + {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, + {<<"fast_xml">>, <<"1EF4F6E5995BCFA94800A46B460C3400C19C0A533948B12200A2E2FB1A2BE427">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, - {<<"p1_acme">>, <<"791AEF0F79DC7F768B228808250C349FA9CE585CD8779DA50CA93106EB3394D0">>}, - {<<"p1_mysql">>, <<"0ED1E098C5A4525032448C65A2715F30980AAE725615A4D255FD25F26BB22507">>}, + {<<"p1_acme">>, <<"E19876618EB0BE22815ACA99640CB88CD5C86E4239B8E8DC15B4E5D7854EF7E2">>}, + {<<"p1_mysql">>, <<"875D4CBDC7C9990270DF3292CCE2514E4C18A9FDFD19BEF258CB4D0C45B4F243">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"883E335D82AC062DE0BDE7981F8250A2E632258BB7A47DF839A4CBDCB3E971E6">>}, + {<<"p1_pgsql">>, <<"08ECA7D2AFC81BA6D757B572F4A57EF3A2383B0C7B785FDE38184BC368376D54">>}, {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, - {<<"stun">>, <<"6F538AC80C842131DBD149055570D116BFABC9B5EBFF4BD6AF2E7888958C660C">>}, + {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"D92446BF51D36ADDA02DB63B963FE6D4A1EDE33E59B38A43D9B90AFD20C25B74">>}, {<<"yconf">>, <<"D59521D66FF89F219411B6E9277CD6FEEC7CC6FCE11554E67DE02A8D0A470479">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -67,23 +61,26 @@ {<<"eimp">>, <<"907C780023CB2893E4FC4BDBE6A4F02C355913862AC67F0ECC26605E816B628A">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, - {<<"esip">>, <<"8187AF819D7259CDADDAF69726C239EF604C9B0B0298A5F2D3E687BF5E2237EE">>}, + {<<"esip">>, <<"9EF3660CEF93B623F7368DCD5C79F4E704358631909E6DD464E335378815DA1F">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, + {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, + {<<"fast_xml">>, <<"5064336D6F363EEE5097AA5DC5CED9B67F05152F2E6B8520FD50D268C2AB839C">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, - {<<"p1_acme">>, <<"8CE196F26E3D22EA10B7809122950465878C127F80767E325207AED7E8D0DD59">>}, - {<<"p1_mysql">>, <<"F058865F64257F507A2C6A5AFF369B1375DBCB30B3D4258DAD4F1B3EAFFB655F">>}, + {<<"p1_acme">>, <<"F1451D706595EF997AB1CA17162DDAC58F874AC97E315A5FADBE3CFA26148002">>}, + {<<"p1_mysql">>, <<"E6187FFAE95B726098E88F3EE6F2344AC259CE2C26E0EE403B05FEEF341AE434">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"8E4D1A7602CB68780E55D89DC5A9B2E1AACA3F4F1EE3D1A25F2F8C3D2364FFB9">>}, + {<<"p1_pgsql">>, <<"655BB75DF036ACE55DDCE17CA741143C42E0667C6206AC27A4DCBC65F71AC9EF">>}, {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, - {<<"stun">>, <<"E134807B1B7A8DFFD94E64EEFEE00E65C7B4042F3D14E16F8F43566D20371583">>}, + {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"C1B91BE74A9A9503AFA6766F756477516920FFBFEEA0C260C2FA171355F53C27">>}, {<<"yconf">>, <<"E947813273F38711C7B2E5A8E4ACC9A51C7BBE854F744A345F60300B38586C89">>}]} ]. From 296ef8287fb342ebb3a85d207fff0a6f3d74528a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 16:32:38 +0100 Subject: [PATCH 0807/1302] Remove mod_tombstone from man, it's in ejabberd-contrib (thanks to Licaon_Kter) --- man/ejabberd.yml.5 | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 7dd33bc06..68bd2c958 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -7516,44 +7516,6 @@ services: This module adds support for XEP\-0202: Entity Time\&. In other words, the module reports server\(cqs system time\&. .sp The module has no options\&. -.SS "mod_tombstones" -.sp -\fINote\fR about this option: added in 24\&.xx\&. -.sp -Keep tombstones for accounts and rooms that were removed\&. This prevents registration of that account, and creation of that room\&. -.sp -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBAvailable options:\fR -.RS 4 -.PP -\fBdb_type\fR: \fImnesia | sql | ldap\fR -.RS 4 -Same as top\-level -\fIdefault_db\fR -option, but applied to this module only\&. -.RE -.PP -\fBroom_tombstone_expiry\fR: \fItime | infinity\fR -.RS 4 -How long to keep MUC room tombstones\&. If set to -\fIinfinity\fR -the tombstones are forever\&. The default value is -\fI30 days\fR\&. -.RE -.PP -\fBuser_tombstone_expiry\fR: \fItime | infinity\fR -.RS 4 -How long to keep users tombstones, for example -\fI365 days\fR\&. If set to -\fIinfinity\fR -the tombstones are forever\&. The default value is -\fI365 days\fR\&. -.RE -.RE .SS "mod_vcard" .sp This module allows end users to store and retrieve their vCard, and to retrieve other users vCards, as defined in XEP\-0054: vcard\-temp\&. The module also implements an uncomplicated Jabber User Directory based on the vCards of these users\&. Moreover, it enables the server to send its vCard when queried\&. From cc3a9f77225b06b0cca07dc538596f14ae262d50 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 15:36:15 +0100 Subject: [PATCH 0808/1302] Update Changelog to 24.10 --- CHANGELOG.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16dbd77ef..e13400feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,83 @@ +## Version 24.10 + +#### Miscelanea + +- `ejabberd_c2s`: Optionally allow unencrypted SASL2 +- `ejabberd_system_monitor`: Handle call by `gen_event:swap_handler` ([#4233](https://github.com/processone/ejabberd/issues/4233)) +- `ejabberd_http_ws`: Remove support for old websocket connection protocol +- `ejabberd_stun`: Omit `auth_realm` log message +- `ext_mod`: Handle `info` message when contrib module transfers table ownership +- `mod_block_strangers`: Add feature announcement to disco-info ([#4039](https://github.com/processone/ejabberd/issues/4039)) +- `mod_mam`: Advertise XEP-0424 feature in server disco-info ([#3340](https://github.com/processone/ejabberd/issues/3340)) +- `mod_muc_admin`: Better handling of malformed jids in `send_direct_invitation` command +- `mod_muc_rtbl`: Fix call to `gen_server:stop` ([#4260](https://github.com/processone/ejabberd/issues/4260)) +- `mod_privilege`: Support "IQ permission" from XEP-0356 0.4.1 ([#3889](https://github.com/processone/ejabberd/issues/3889)) +- `mod_pubsub`: Don't blindly echo PEP notification +- `mod_pubsub`: Skip non-delivery errors for local pubsub generated notifications +- `mod_pubsub`: Fall back to default plugin options +- `mod_pubsub`: Fix choice of node config defaults +- `mod_pubsub`: Fix merging of default node options +- `mod_pubsub`: Fix default node config parsing +- `mod_register`: Support to block IPs in a vhost using `append_host_config` ([#4038](https://github.com/processone/ejabberd/issues/4038)) +- `mod_s2s_bidi`: Add support for S2S Bidirectional +- `mod_scram_upgrade`: Add support for SCRAM upgrade tasks +- `mod_vcard`: Return error stanza when storage doesn't support vcard update ([#4266](https://github.com/processone/ejabberd/issues/4266)) +- `mod_vcard`: Return explicit error stanza when user attempts to modify other's vcard +- Minor improvements to support `mod_tombstones` (#2456) +- Update `fast_xml` to use `use_maps` and remove obsolete elixir files +- Update `fast_tls` and `xmpp` to improve s2s fallback for invalid direct tls connections +- `make-binaries`: Bump dependency versions: Elixir 1.17.2, OpenSSL 3.3.2, ... + +#### Administration +- `ejabberdctl`: If `ERLANG_NODE` lacks host, add hostname ([#4288](https://github.com/processone/ejabberd/issues/4288)) +- `ejabberd_app`: At server start, log Erlang and Elixir versions +- MySQL: Fix column type in the schema update of `archive` table in schema update + +#### Commands API +- `get_mam_count`: New command to get number of archived messages for an account +- `set_presence`: Return error when session not found +- `update`: Fix command output +- Add `mam` and `offline` tags to the related purge commands + +#### Code Quality +- Fix warnings about unused macro definitions reported by Erlang LS +- Fix Elvis report: Fix dollar space syntax +- Fix Elvis report: Remove spaces in weird places +- Fix Elvis report: Don't use ignored variables +- Fix Elvis report: Remove trailing whitespace characters +- Define the types of options that `opt_type.sh` cannot derive automatically +- `ejabberd_http_ws`: Fix dialyzer warnings +- `mod_matrix_gw`: Remove useless option `persist` +- `mod_privilege`: Replace `try...catch` with a clean alternative + +#### Development Help +- `elvis.config`: Fix file syntax, set vim mode, disable many tests +- `erlang_ls.config`: Let it find paths, update to Erlang 26, enable crossref +- `hooks_deps`: Hide false-positive warnings about `gen_mod` +- `Makefile`: Add support for `make elvis` when using rebar3 +- `.vscode/launch.json`: Experimental support for debugging with Neovim +- CI: Add Elvis tests +- CI: Add XMPP Interop tests +- Runtime: Cache hex.pm archive from rebar3 and mix + +#### Documentation +- Add links in top-level options documentation to their Docs website sections +- Document which SQL servers can really use `update_sql_schema` +- Improve documentation of `ldap_servers` and `ldap_backups` options ([#3977](https://github.com/processone/ejabberd/issues/3977)) +- `mod_register`: Document behavior when `access` is set to `none` ([#4078](https://github.com/processone/ejabberd/issues/4078)) + +#### Elixir +- Handle case when elixir support is enabled but not available +- Start ExSync manually to ensure it's started if (and only if) Relive +- `mix.exs`: Fix `mix release` error: `logger` being regular and included application ([#4265](https://github.com/processone/ejabberd/issues/4265)) +- `mix.exs`: Remove from `extra_applications` the apps already defined in `deps` ([#4265](https://github.com/processone/ejabberd/issues/4265)) + +#### WebAdmin +- Add links in user page to offline and roster pages +- Add new "MAM Archive" page to webadmin +- Improve many pages to handle when modules are disabled +- `mod_admin_extra`: Move some webadmin pages to their modules + ## Version 24.07 #### Core From 601fcba4cbf881942fae4ab89bc8f666f7a9213f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 28 Oct 2024 17:02:10 +0100 Subject: [PATCH 0809/1302] Set version to 24.10 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 838e596f4..c77f7e73a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.07` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.10` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From af97211ecc62298adebd48e6253704fcea6872f4 Mon Sep 17 00:00:00 2001 From: Metalhearf <6446231+Metalhearf@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:30:20 +0100 Subject: [PATCH 0810/1302] Update URLs in docs/conf files for consistency. --- COMPILE.md | 2 +- CONTRIBUTING.md | 20 ++++++++++---------- README.md | 29 ++++++++++++++--------------- ejabberd.doap | 2 +- mix.exs | 4 ++-- src/ejabberd_config.erl | 2 +- test/suite.hrl | 2 +- tools/generate-doap.sh | 2 +- 8 files changed, 31 insertions(+), 32 deletions(-) diff --git a/COMPILE.md b/COMPILE.md index 0313d0b87..67277051c 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -44,7 +44,7 @@ There are several ways to obtain the ejabberd source code: - Source code package from [ejabberd GitHub Releases][ghr] - Latest development code from [ejabberd Git repository][gitrepo] -[p1dl]: https://www.process-one.net/en/ejabberd/downloads/ +[p1dl]: https://www.process-one.net/download/ejabberd/ [ghr]: https://github.com/processone/ejabberd/releases [gitrepo]: https://github.com/processone/ejabberd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72619891b..a5d4beda8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,18 +25,18 @@ support platforms, the best being [Stack Overflow][stackoverflow]. Stack Overflow is a much better place to ask questions since: -- there are thousands of people willing to help on Stack Overflow -- questions and answers stay available for public viewing so your question / answer might help +* there are thousands of people willing to help on Stack Overflow +* questions and answers stay available for public viewing so your question / answer might help someone else -- Stack Overflow's voting system assures that the best answers are prominently visible. +* Stack Overflow's voting system assures that the best answers are prominently visible. To save your and our time, we will systematically close all issues that are requests for general support and redirect people to the section you are reading right now. Other channels for support are: -- ejabberd XMPP room: [ejabberd@conference.process-one.net][muc] -- [ejabberd XMPP room logs][logs] -- [ejabberd Mailing List][list] + +* ejabberd XMPP room: [ejabberd@conference.process-one.net][muc] +* [ejabberd Mailing List][list] ### Found an Issue or Bug? @@ -80,6 +80,7 @@ Before you submit your pull request consider the following guidelines: ```shell git checkout -b my-fix-branch master ``` + * Test your changes and, if relevant, expand the automated test suite. * Create your patch commit, including appropriate test cases. * If the changes affect public APIs, change or add relevant [documentation][doc-repo]. @@ -88,6 +89,7 @@ Before you submit your pull request consider the following guidelines: ```shell git commit -a ``` + Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. * Push your branch to GitHub: @@ -128,22 +130,20 @@ That's it! Thank you for your contribution! Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done so before. It's a quick process, we promise, and you will be able to do it all online -You can read [ProcessOne Contribution License Agreement][cla] in PDF. +Here's a link to the [ProcessOne Contribution License Agreement][cla]. This is part of the legal framework of the open-source ecosystem that adds some red tape, but protects both the contributor and the company / foundation behind the project. It also gives us the option to relicense the code with a more permissive license in the future. - [coc]: https://github.com/processone/ejabberd/blob/master/CODE_OF_CONDUCT.md [stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest [list]: https://lists.jabber.ru/mailman/listinfo/ejabberd [muc]: xmpp:ejabberd@conference.process-one.net -[logs]: https://process-one.net/logs/ejabberd@conference.process-one.net/ [github]: https://github.com/processone/ejabberd [github-issues]: https://github.com/processone/ejabberd/issues [github-new-issue]: https://github.com/processone/ejabberd/issues/new [github-pr]: https://github.com/processone/ejabberd/pulls [doc-repo]: https://github.com/processone/docs.ejabberd.im [developer-setup]: https://docs.ejabberd.im/developer/ -[cla]: https://www.process-one.net/resources/ejabberd-cla.pdf +[cla]: https://cla.process-one.net/ diff --git a/README.md b/README.md index b307277ac..f4b21468d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@

- [ejabberd][im] is an open-source, robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. @@ -39,12 +38,16 @@ Installation There are several ways to install ejabberd: - Source code: compile yourself, see [COMPILE](COMPILE.md) -- Installers: [ProcessOne Download][p1download] and [GitHub Releases][releases] for releases, [GitHub Actions](https://github.com/processone/ejabberd/actions/workflows/installers.yml) for master branch (run/deb/rpm for x64 and arm64) -- `ecs` container image: [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for x64) -- `ejabberd` container image: [Github Packages][packages] for releases and master branch, see [CONTAINER](CONTAINER.md) (for x64 and arm64) +- Installers: + - [ProcessOne Download Page][p1download] or [GitHub Releases][releases] for releases. + - [GitHub Actions](https://github.com/processone/ejabberd/actions/workflows/installers.yml) for master branch (`run`/`deb`/`rpm` for `x64` and `arm64`) +- Docker Containers: + - `ecs` container image: [Docker Hub][hubecs] and [Github Packages][packagesecs], see [ecs README][docker-ecs-readme] (for `x64`) + - `ejabberd` container image: [Github Packages][packages] for releases and master branch, see [CONTAINER](CONTAINER.md) (for `x64` and `arm64`) - Using your [Operating System package][osp] - Using the [Homebrew][homebrew] package manager +More info can be found in the `Installation` part of [ejabberd Docs](https://docs.ejabberd.im/admin/install/). Documentation ------------- @@ -61,7 +64,6 @@ Once ejabberd is installed, try: ejabberdctl help man ejabberd.yml - Development ----------- @@ -74,6 +76,7 @@ or in your local machine as explained in [Localization][localization]. Documentation for developers is available in [ejabberd docs: Developers][docs-dev]. There are nightly builds of ejabberd, both for `master` branch and for Pull Requests: + - Installers: go to [GitHub Actions: Installers](https://github.com/processone/ejabberd/actions/workflows/installers.yml), open the most recent commit, on the bottom of that commit page, download the `ejabberd-packages.zip` artifact. - `ejabberd` container image: go to [ejabberd Github Packages][packages] @@ -84,7 +87,6 @@ or some other method from [ProcessOne Contact][p1contact]. For commercial offering and support, including [ejabberd Business Edition][p1home] and [Fluux (ejabberd in the Cloud)][fluux], please check [ProcessOne ejabberd page][p1home]. - Community --------- @@ -94,13 +96,11 @@ There are several places to get in touch with other ejabberd developers and admi - [GitHub Discussions][discussions] - [Stack Overflow][stackoverflow] - License ------- -ejabberd is released under the GNU General Public License v2 (see [COPYING](COPYING)), -and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MIT License. - +- `ejabberd` is released under the __GNU General Public License v2__ (see [COPYING](COPYING)) +- [ejabberd translations](https://github.com/processone/ejabberd-po/) under __MIT License__. [discussions]: https://github.com/processone/ejabberd/discussions [docker-ecs-readme]: https://github.com/processone/docker-ejabberd/tree/master/ecs#readme @@ -117,16 +117,15 @@ and [ejabberd translations](https://github.com/processone/ejabberd-po/) under MI [mqtt]: https://mqtt.org/ [muc]: xmpp:ejabberd@conference.process-one.net [osp]: https://docs.ejabberd.im/admin/install/os-package/ -[p1contact]: https://www.process-one.net/en/company/contact/ -[p1download]: https://www.process-one.net/en/ejabberd/downloads/ -[p1home]: https://www.process-one.net/en/ejabberd/ +[p1contact]: https://www.process-one.net/contact/ +[p1download]: https://www.process-one.net/download/ejabberd/ +[p1home]: https://www.process-one.net/ejabberd/ [packages]: https://github.com/processone/ejabberd/pkgs/container/ejabberd [packagesecs]: https://github.com/processone/docker-ejabberd/pkgs/container/ecs [releases]: https://github.com/processone/ejabberd/releases [sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol [stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest [weblate]: https://hosted.weblate.org/projects/ejabberd/ejabberd-po/ -[xeps]: https://www.process-one.net/en/ejabberd/protocols/ +[xeps]: https://www.process-one.net/ejabberd-features/ [xmpp]: https://xmpp.org/ [xmppej]: https://xmpp.org/software/servers/ejabberd/ - diff --git a/ejabberd.doap b/ejabberd.doap index 180b0ec00..afc28a22a 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -19,7 +19,7 @@ - + diff --git a/mix.exs b/mix.exs index 1d34c1098..6bb5936d7 100644 --- a/mix.exs +++ b/mix.exs @@ -212,9 +212,9 @@ defmodule Ejabberd.MixProject do maintainers: ["ProcessOne"], licenses: ["GPL-2.0-or-later"], links: %{"ejabberd.im" => "https://www.ejabberd.im", - "ejabberd Docs" => "http://docs.ejabberd.im", + "ejabberd Docs" => "https://docs.ejabberd.im", "GitHub" => "https://github.com/processone/ejabberd", - "ProcessOne" => "http://www.process-one.net/"}] + "ProcessOne" => "https://www.process-one.net/"}] end defp vars do diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index bd9480513..3a77c2720 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -206,7 +206,7 @@ get_lang(Host) -> -spec get_uri() -> binary(). get_uri() -> - <<"http://www.process-one.net/en/ejabberd/">>. + <<"https://www.process-one.net/ejabberd/">>. -spec get_copyright() -> binary(). get_copyright() -> diff --git a/test/suite.hrl b/test/suite.hrl index d8ea3e23b..08bb87df1 100644 --- a/test/suite.hrl +++ b/test/suite.hrl @@ -9,7 +9,7 @@ -define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>). --define(EJABBERD_CT_URI, <<"http://www.process-one.net/en/ejabberd_ct/">>). +-define(EJABBERD_CT_URI, <<"https://docs.ejabberd.im/developer/extending-ejabberd/testing/">>). -define(recv1(P1), P1 = (fun() -> diff --git a/tools/generate-doap.sh b/tools/generate-doap.sh index 57241fef5..2892aa969 100755 --- a/tools/generate-doap.sh +++ b/tools/generate-doap.sh @@ -45,7 +45,7 @@ write_doap_head() - + From d56eae809d505ea0442a30beb26951d51e84a4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 5 Nov 2024 13:26:31 +0100 Subject: [PATCH 0811/1302] Catch extra case in check for s2s bidi element --- src/mod_s2s_bidi.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl index de597e57d..10cc0e773 100644 --- a/src/mod_s2s_bidi.erl +++ b/src/mod_s2s_bidi.erl @@ -84,10 +84,14 @@ s2s_out_unauthenticated_features(#{db_verify := _} = State, _) -> s2s_out_unauthenticated_features(State, #stream_features{} = Pkt) -> try xmpp:try_subtag(Pkt, #s2s_bidi{}) of #s2s_bidi{} -> - ejabberd_s2s_out:send(State#{bidi_enabled => true}, #s2s_bidi{}) + ejabberd_s2s_out:send(State#{bidi_enabled => true}, #s2s_bidi{}); + _ -> + State catch _:{xmpp_codec, _Why} -> State - end. + end; +s2s_out_unauthenticated_features(State, _Pkt) -> + State. s2s_out_packet(#{bidi_enabled := true, ip := {IP, _}} = State, Pkt0) when ?is_stanza(Pkt0) -> From 4843cd432f2214ced2f7d7dc003910e0f6365c73 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 5 Nov 2024 15:37:21 +0100 Subject: [PATCH 0812/1302] Disable the systemd watchdog by default Some users reported ejabberd being restarted by systemd due to missing watchdog pings despite the actual service operating just fine. So far, we weren't able to track down the issue, so we'll no longer enable the watchdog in our example service unit. Closes #4054. --- ejabberd.service.template | 1 - 1 file changed, 1 deletion(-) diff --git a/ejabberd.service.template b/ejabberd.service.template index 685a104d0..902a81cb2 100644 --- a/ejabberd.service.template +++ b/ejabberd.service.template @@ -9,7 +9,6 @@ Group=@installuser@ LimitNOFILE=65536 Restart=on-failure RestartSec=5 -WatchdogSec=30 ExecStart=@ctlscriptpath@/ejabberdctl foreground ExecStop=/bin/sh -c '@ctlscriptpath@/ejabberdctl stop && @ctlscriptpath@/ejabberdctl stopped' ExecReload=@ctlscriptpath@/ejabberdctl reload_config From b50ea7ef1f1d1afc396df429de67350128cf90eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 5 Nov 2024 16:55:47 +0100 Subject: [PATCH 0813/1302] Make mod_privilge properly handle roster iq --- src/mod_privilege.erl | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 046bec614..85eaaecbc 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -274,7 +274,7 @@ component_send_packet({#iq{from = From, case {maps:find(Host, Permissions), get_iq_encapsulated_details(IQ)} of {{ok, Access}, {ok, EncapType, EncapNs, EncapFrom, EncIq}} when (EncapType == Type) and ((EncapFrom == undefined) or (EncapFrom == To)) -> - NsPermissions = proplists:get_value(iq, Access, none), + NsPermissions = proplists:get_value(iq, Access, []), Permission = case lists:keyfind(EncapNs, 2, NsPermissions) of #privilege_namespace{type = AllowedType} -> @@ -306,6 +306,16 @@ component_send_packet({#iq{from = From, ?INFO_MSG("IQ not forwarded: The FROM attribute in the encapsulated " "IQ stanza and the TO in top-level IQ stanza do not match", []), + drop; + {_, {error, no_privileged_iq, _Err}} -> + ?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.", + []), + drop; + {_, {error, roster_query, _Err}} -> + IQ; + {_, {error, Type, _Err}} -> + ?INFO_MSG("IQ not forwarded: Component tried to send not valid IQ stanza: ~p.", + [Type]), drop end, {Result, State}; @@ -557,8 +567,8 @@ forward_message(#message{to = To} = Msg) -> %% @format-begin -spec get_iq_encapsulated_details(iq()) -> - {ok, set | get, binary(), jid(), iq()} | - {error, Why :: atom(), any(), iq()}. + {ok, iq_type(), binary(), jid(), iq()} | + {error, Why :: atom(), stanza_error()}. get_iq_encapsulated_details(#iq{sub_els = [IqSub]} = Msg) -> Lang = xmpp:get_lang(Msg), try xmpp:try_subtag(Msg, #privileged_iq{}) of @@ -568,9 +578,19 @@ get_iq_encapsulated_details(#iq{sub_els = [IqSub]} = Msg) -> Ns = xmpp:get_ns(Element), {ok, EncapsulatedType, Ns, From, EncIq}; _ -> - Txt = ?T("No element found"), - Err = xmpp:err_bad_request(Txt, Lang), - {error, no_privileged_iq, Err} + try xmpp:try_subtag(Msg, #roster_query{}) of + #roster_query{} -> + {error, roster_query, xmpp:err_bad_request()}; + _ -> + Txt = ?T("No element found"), + Err = xmpp:err_bad_request(Txt, Lang), + {error, no_privileged_iq, Err} + catch + _:{xmpp_codec, Why} -> + Txt = xmpp:io_format_error(Why), + Err = xmpp:err_bad_request(Txt, Lang), + {error, codec_error, Err} + end catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), From c7d967a2b58bcee4e6574b441e1cd09d510f4fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 5 Nov 2024 17:10:09 +0100 Subject: [PATCH 0814/1302] Fix dialyzer issue --- src/mod_privilege.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 85eaaecbc..02bac16db 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -313,9 +313,9 @@ component_send_packet({#iq{from = From, drop; {_, {error, roster_query, _Err}} -> IQ; - {_, {error, Type, _Err}} -> + {_, {error, ErrType, _Err}} -> ?INFO_MSG("IQ not forwarded: Component tried to send not valid IQ stanza: ~p.", - [Type]), + [ErrType]), drop end, {Result, State}; From 18c54f4e9eb5a8fed5fed4151c06cc47d51036aa Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 14 Nov 2024 12:21:58 +0100 Subject: [PATCH 0815/1302] ejabberd.yml.example: Use non-standard STUN port STUN via UDP can easily be abused for reflection/amplification DDoS attacks. Suggest a non-standard port to make it harder for attackers to discover the service. Modern XMPP clients discover the port via XEP-0215, so there's no advantage in sticking to the standard port. --- ejabberd.yml.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index eaf0584fa..39e423a64 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -67,7 +67,7 @@ listen: /admin: ejabberd_web_admin /.well-known/acme-challenge: ejabberd_acme - - port: 3478 + port: 5478 ip: "::" transport: udp module: ejabberd_stun From 29a59cfe482e74f37ee94140ad8325764709174a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 30 Oct 2024 13:49:03 +0100 Subject: [PATCH 0816/1302] Add "XMPP Date and Time Profiles" implemented by xmpp and used everywhere --- src/ejabberd.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index a83ab0738..989c75012 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -31,6 +31,7 @@ -protocol({rfc, 7590}). -protocol({xep, 4, '2.9'}). -protocol({xep, 59, '1.0'}). +-protocol({xep, 82, '1.1.1'}). -protocol({xep, 86, '1.0'}). -protocol({xep, 106, '1.1'}). -protocol({xep, 170, '1.0'}). From abe1d96c58eb04fdfc41c909d533ded22430baae Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 8 Nov 2024 18:49:07 +0100 Subject: [PATCH 0817/1302] Announce support for XEP-0384 OMEMO Encryption (#4305) The last required piece to support XEP-0384 was supporting max_items=max, which was added for ejabberd 21.12 in commit 8f8de0403bab0a819420db69bb44bdb2a474d137 --- src/node_pep.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node_pep.erl b/src/node_pep.erl index 2a2e044be..22846e3a6 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -30,6 +30,8 @@ -behaviour(gen_pubsub_node). -author('christophe.romain@process-one.net'). +-protocol({xep, 384, '0.8.3', '21.12', "complete", ""}). + -include("pubsub.hrl"). -export([init/3, terminate/2, options/0, features/0, From 6a428f3d02048c1e0188236e6e2ae3fb08d65a5b Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 8 Nov 2024 19:27:22 +0100 Subject: [PATCH 0818/1302] Add ejabberd version and implementation status for many XEPs --- src/ejabberd.erl | 33 +++++++++++++++------------------ src/ejabberd_auth_anonymous.erl | 2 +- src/ejabberd_bosh.erl | 4 ++-- src/ejabberd_c2s.erl | 2 +- src/ejabberd_captcha.erl | 4 ++-- src/ejabberd_piefxis.erl | 2 +- src/ejabberd_s2s.erl | 2 -- src/ejabberd_service.erl | 2 +- src/mod_adhoc.erl | 2 +- src/mod_avatar.erl | 2 +- src/mod_blocking.erl | 2 +- src/mod_caps.erl | 2 +- src/mod_carboncopy.erl | 2 +- src/mod_client_state.erl | 4 ++-- src/mod_configure.erl | 2 +- src/mod_delegation.erl | 2 +- src/mod_disco.erl | 4 ++-- src/mod_host_meta.erl | 2 +- src/mod_http_upload.erl | 2 +- src/mod_jidprep.erl | 2 +- src/mod_last.erl | 2 +- src/mod_legacy_auth.erl | 2 +- src/mod_mam.erl | 10 +++++----- src/mod_mix.erl | 2 +- src/mod_mix_pam.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_muc_log.erl | 2 +- src/mod_muc_occupantid.erl | 2 +- src/mod_muc_room.erl | 4 ++-- src/mod_multicast.erl | 2 +- src/mod_offline.erl | 12 ++++++------ src/mod_ping.erl | 2 +- src/mod_privacy.erl | 2 +- src/mod_private.erl | 6 +++--- src/mod_privilege.erl | 2 +- src/mod_proxy65.erl | 2 +- src/mod_pubsub.erl | 6 +++--- src/mod_push.erl | 2 +- src/mod_register.erl | 2 +- src/mod_roster.erl | 2 +- src/mod_s2s_dialback.erl | 4 ++-- src/mod_sic.erl | 2 +- src/mod_stats.erl | 2 +- src/mod_stream_mgmt.erl | 2 +- src/mod_stun_disco.erl | 2 +- src/mod_time.erl | 2 +- src/mod_vcard.erl | 6 +++--- src/mod_version.erl | 2 +- 48 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 989c75012..0b23188d9 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -29,24 +29,21 @@ -protocol({rfc, 6122}). -protocol({rfc, 7590}). --protocol({xep, 4, '2.9'}). --protocol({xep, 59, '1.0'}). --protocol({xep, 82, '1.1.1'}). --protocol({xep, 86, '1.0'}). --protocol({xep, 106, '1.1'}). --protocol({xep, 170, '1.0'}). --protocol({xep, 205, '1.0'}). --protocol({xep, 212, '1.0'}). --protocol({xep, 216, '1.0'}). --protocol({xep, 243, '1.0'}). --protocol({xep, 270, '1.0'}). --protocol({xep, 368, '1.1.0'}). --protocol({xep, 386, '0.3.0', '24.02', "", ""}). --protocol({xep, 388, '0.4.0', '24.02', "", ""}). --protocol({xep, 424, '0.4.0', '24.02', "", ""}). --protocol({xep, 440, '0.4.0', '24.02', "", ""}). --protocol({xep, 474, '0.3.0', '24.02', "", ""}). --protocol({xep, 485, '0.2.0', '24.02', "", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). +-protocol({xep, 4, '2.9', '0.5.0', "complete", ""}). +-protocol({xep, 59, '1.0', '2.1.0', "complete", ""}). +-protocol({xep, 82, '1.1.1', '2.1.0', "complete", ""}). +-protocol({xep, 86, '1.0', '0.5.0', "complete", ""}). +-protocol({xep, 106, '1.1', '0.5.0', "complete", ""}). +-protocol({xep, 170, '1.0', '17.12', "complete", ""}). +-protocol({xep, 178, '1.1', '17.03', "complete", ""}). +-protocol({xep, 205, '1.0', '1.1.2', "complete", ""}). +-protocol({xep, 368, '1.1.0', '17.09', "complete", ""}). +-protocol({xep, 386, '0.3.0', '24.02', "complete", ""}). +-protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). +-protocol({xep, 424, '0.4.0', '24.02', "complete", ""}). +-protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). +-protocol({xep, 474, '0.3.0', '24.02', "complete", ""}). +-protocol({xep, 485, '0.2.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 3b2de064c..7c1156ff6 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -28,7 +28,7 @@ -behaviour(ejabberd_auth). -author('mickael.remond@process-one.net'). --protocol({xep, 175, '1.2'}). +-protocol({xep, 175, '1.2', '1.1.0', "complete", ""}). -export([start/1, stop/1, diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index c196c78ce..0805f1857 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -25,8 +25,8 @@ -module(ejabberd_bosh). -behaviour(xmpp_socket). -behaviour(p1_fsm). --protocol({xep, 124, '1.11'}). --protocol({xep, 206, '1.4'}). +-protocol({xep, 124, '1.11', '16.12', "complete", ""}). +-protocol({xep, 206, '1.4', '16.12', "complete", ""}). %% API -export([start/2, start/3, start_link/3]). diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index efb477c47..55d9cebec 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -27,7 +27,7 @@ -protocol({rfc, 3921}). -protocol({rfc, 6120}). -protocol({rfc, 6121}). --protocol({xep, 138, '2.1'}). +-protocol({xep, 138, '2.1', '1.1.0', "complete", ""}). %% ejabberd_listener callbacks -export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]). diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 6ff0a886a..ed7408699 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -25,8 +25,8 @@ -module(ejabberd_captcha). --protocol({xep, 158, '1.0'}). --protocol({xep, 231, '1.0'}). +-protocol({xep, 158, '1.0', '2.1.0', "complete", ""}). +-protocol({xep, 231, '1.0', '2.1.0', "complete", ""}). -behaviour(gen_server). diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 78081b53e..4cf39328a 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -32,7 +32,7 @@ -module(ejabberd_piefxis). --protocol({xep, 227, '1.1'}). +-protocol({xep, 227, '1.1', '2.1.0', "partial", ""}). -export([import_file/1, export_server/1, export_host/2]). diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index b6949ea62..7c9a041fa 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -25,8 +25,6 @@ -module(ejabberd_s2s). --protocol({xep, 220, '1.1'}). - -author('alexey@process-one.net'). -behaviour(gen_server). diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 1220b2ce1..5bc92a0a2 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -23,7 +23,7 @@ -behaviour(xmpp_stream_in). -behaviour(ejabberd_listener). --protocol({xep, 114, '1.6'}). +-protocol({xep, 114, '1.6', '0.1.0', "complete", ""}). %% ejabberd_listener callbacks -export([start/3, start_link/3, stop/0, accept/1]). diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 0b2cfa064..7baf6bf07 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -27,7 +27,7 @@ -author('henoch@dtek.chalmers.se'). --protocol({xep, 50, '1.2'}). +-protocol({xep, 50, '1.2', '1.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index 210d5e854..e404d7d20 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -23,7 +23,7 @@ -module(mod_avatar). -behaviour(gen_mod). --protocol({xep, 398, '0.2.0', '18.03', "", ""}). +-protocol({xep, 398, '0.2.0', '18.03', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]). diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index 19e002c4d..9ed4ce65d 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -27,7 +27,7 @@ -behaviour(gen_mod). --protocol({xep, 191, '1.2'}). +-protocol({xep, 191, '1.2', '2.1.7', "complete", ""}). -export([start/2, stop/1, reload/3, process_iq/1, depends/2, disco_features/5, mod_options/1, mod_doc/0]). diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 47f2d523c..5468f8fee 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -29,7 +29,7 @@ -author('henoch@dtek.chalmers.se'). --protocol({xep, 115, '1.5'}). +-protocol({xep, 115, '1.5', '2.1.4', "complete", ""}). -behaviour(gen_server). diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 5239ff236..ef1aa97ca 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -27,7 +27,7 @@ -module (mod_carboncopy). -author ('ecestari@process-one.net'). --protocol({xep, 280, '0.13.2'}). +-protocol({xep, 280, '0.13.2', '13.06', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index a33ad1b5e..c56207464 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -25,8 +25,8 @@ -module(mod_client_state). -author('holger@zedat.fu-berlin.de'). --protocol({xep, 85, '2.1'}). --protocol({xep, 352, '0.1', '14.12', "", ""}). +-protocol({xep, 85, '2.1', '2.1.0', "complete", ""}). +-protocol({xep, 352, '0.1', '14.12', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_configure.erl b/src/mod_configure.erl index b5f45b532..947333f84 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 133, '1.3.0', '13.10', "complete", ""}). +-protocol({xep, 133, '1.3.0', '13.10', "partial", ""}). -behaviour(gen_mod). diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index d761b81b4..c47ebb9a5 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -25,7 +25,7 @@ -author('amuhar3@gmail.com'). --protocol({xep, 355, '0.4.1', '16.09', "", ""}). +-protocol({xep, 355, '0.4.1', '16.09', "complete", ""}). -behaviour(gen_server). -behaviour(gen_mod). diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 4f092da4e..34d668975 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -27,8 +27,8 @@ -author('alexey@process-one.net'). --protocol({xep, 30, '2.4'}). --protocol({xep, 157, '1.0'}). +-protocol({xep, 30, '2.4', '0.1.0', "complete", ""}). +-protocol({xep, 157, '1.0', '2.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 92595069d..1b49b6b14 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -27,7 +27,7 @@ -author('badlop@process-one.net'). --protocol({xep, 156, '1.4.0', '22.05', "", ""}). +-protocol({xep, 156, '1.4.0', '22.05', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index cf5facb56..dcb619c03 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -27,7 +27,7 @@ -author('holger@zedat.fu-berlin.de'). -behaviour(gen_server). -behaviour(gen_mod). --protocol({xep, 363, '0.2', '15.10', "", ""}). +-protocol({xep, 363, '0.3.0', '15.10', "complete", ""}). -define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds. -define(CALL_TIMEOUT, 60000). % 1 minute. diff --git a/src/mod_jidprep.erl b/src/mod_jidprep.erl index 0d827242c..75a0c7280 100644 --- a/src/mod_jidprep.erl +++ b/src/mod_jidprep.erl @@ -25,7 +25,7 @@ -module(mod_jidprep). -author('holger@zedat.fu-berlin.de'). --protocol({xep, 328, '0.1', '19.09', "", ""}). +-protocol({xep, 328, '0.1', '19.09', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_last.erl b/src/mod_last.erl index 9cdf64f76..b839ccf9a 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 12, '2.0'}). +-protocol({xep, 12, '2.0', '0.5.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl index c57ddca51..24129926e 100644 --- a/src/mod_legacy_auth.erl +++ b/src/mod_legacy_auth.erl @@ -22,7 +22,7 @@ -module(mod_legacy_auth). -behaviour(gen_mod). --protocol({xep, 78, '2.5'}). +-protocol({xep, 78, '2.5', '17.03', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_doc/0]). diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 172d9f0a5..b20bdbd09 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -25,11 +25,11 @@ -module(mod_mam). --protocol({xep, 313, '0.6.1', '15.06', "", ""}). --protocol({xep, 334, '0.2'}). --protocol({xep, 359, '0.5.0'}). --protocol({xep, 425, '0.3.0', '24.06', "", ""}). --protocol({xep, 441, '0.2.0'}). +-protocol({xep, 313, '0.6.1', '15.06', "complete", ""}). +-protocol({xep, 334, '0.2', '16.01', "complete", ""}). +-protocol({xep, 359, '0.5.0', '15.09', "complete", ""}). +-protocol({xep, 425, '0.3.0', '24.06', "complete", ""}). +-protocol({xep, 441, '0.2.0', '15.06', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_mix.erl b/src/mod_mix.erl index a776250bd..6ef5f885b 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -24,7 +24,7 @@ -module(mod_mix). -behaviour(gen_mod). -behaviour(gen_server). --protocol({xep, 369, '0.14.1', '16.03', "", ""}). +-protocol({xep, 369, '0.14.1', '16.03', "complete", ""}). %% API -export([route/1]). diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index eb94877d2..f043393ba 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -22,7 +22,7 @@ %%%---------------------------------------------------------------------- -module(mod_mix_pam). -behaviour(gen_mod). --protocol({xep, 405, '0.3.0'}). +-protocol({xep, 405, '0.3.0', '19.02', "complete", ""}). %% gen_mod callbacks -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index adc80921e..9fadc9016 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -24,7 +24,7 @@ %%%---------------------------------------------------------------------- -module(mod_muc). -author('alexey@process-one.net'). --protocol({xep, 45, '1.25'}). +-protocol({xep, 45, '1.25', '0.5.0', "complete", ""}). -protocol({xep, 249, '1.2'}). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 04dc2f3f8..84d9abf34 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -25,7 +25,7 @@ -module(mod_muc_log). --protocol({xep, 334, '0.2'}). +-protocol({xep, 334, '0.2', '15.09', "complete", ""}). -author('badlop@process-one.net'). diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index fabd69e28..6ae05a189 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -27,7 +27,7 @@ -author('badlop@process-one.net'). --protocol({xep, 421, '0.1.0', '23.10', "", ""}). +-protocol({xep, 421, '0.1.0', '23.10', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index da2f70b78..85fb54249 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -27,8 +27,8 @@ -author('alexey@process-one.net'). --protocol({xep, 317, '0.1', '21.12', "", "conversejs/prosody compatible"}). --protocol({xep, 410, '1.1.0', '18.12', "", ""}). +-protocol({xep, 317, '0.1', '21.12', "complete", "conversejs/prosody compatible"}). +-protocol({xep, 410, '1.1.0', '18.12', "complete", ""}). -behaviour(p1_fsm). diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 038c987b5..1a7dd7d14 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -27,7 +27,7 @@ -author('badlop@process-one.net'). --protocol({xep, 33, '1.1', '15.04', "", ""}). +-protocol({xep, 33, '1.1', '15.04', "complete", ""}). -behaviour(gen_server). diff --git a/src/mod_offline.erl b/src/mod_offline.erl index a624de363..eaba44a34 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -27,12 +27,12 @@ -author('alexey@process-one.net'). --protocol({xep, 13, '1.2', '16.02', "", ""}). --protocol({xep, 22, '1.4'}). --protocol({xep, 23, '1.3'}). --protocol({xep, 160, '1.0'}). --protocol({xep, 203, '2.0'}). --protocol({xep, 334, '0.2'}). +-protocol({xep, 13, '1.2', '16.02', "complete", ""}). +-protocol({xep, 22, '1.4', '0.1.0', "complete", ""}). +-protocol({xep, 23, '1.3', '0.7.5', "complete", ""}). +-protocol({xep, 160, '1.0', '16.01', "complete", ""}). +-protocol({xep, 203, '2.0', '2.1.0', "complete", ""}). +-protocol({xep, 334, '0.2', '16.01', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 2947f1454..796a83738 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -27,7 +27,7 @@ -author('bjc@kublai.com'). --protocol({xep, 199, '2.0'}). +-protocol({xep, 199, '2.0', '2.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index eda9ac4ab..0a7911e26 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 16, '1.6'}). +-protocol({xep, 16, '1.6', '0.5.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_private.erl b/src/mod_private.erl index 4554dbeca..e3bc72593 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -27,9 +27,9 @@ -author('alexey@process-one.net'). --protocol({xep, 49, '1.2'}). --protocol({xep, 411, '0.2.0', '18.12', "", ""}). --protocol({xep, 402, '1.1.3', '23.10', "", ""}). +-protocol({xep, 49, '1.2', '0.1.0', "complete", ""}). +-protocol({xep, 411, '0.2.0', '18.12', "complete", ""}). +-protocol({xep, 402, '1.1.3', '23.10', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 02bac16db..d5ceda8ee 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -25,7 +25,7 @@ -author('amuhar3@gmail.com'). --protocol({xep, 356, '0.4.1', '24.10', "", ""}). +-protocol({xep, 356, '0.4.1', '24.10', "complete", ""}). -behaviour(gen_server). -behaviour(gen_mod). diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 5805613c8..3ea9f05ba 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -27,7 +27,7 @@ -author('xram@jabber.ru'). --protocol({xep, 65, '1.8'}). +-protocol({xep, 65, '1.8', '2.0.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index e870596e7..2bf1d350f 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -35,9 +35,9 @@ -behaviour(gen_mod). -behaviour(gen_server). -author('christophe.romain@process-one.net'). --protocol({xep, 60, '1.14'}). --protocol({xep, 163, '1.2'}). --protocol({xep, 248, '0.2'}). +-protocol({xep, 60, '1.14', '0.5.0', "partial", ""}). +-protocol({xep, 163, '1.2', '2.0.0', "complete", ""}). +-protocol({xep, 248, '0.2', '2.1.0', "complete", ""}). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). diff --git a/src/mod_push.erl b/src/mod_push.erl index f893ed777..fca89fceb 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -25,7 +25,7 @@ -module(mod_push). -author('holger@zedat.fu-berlin.de'). --protocol({xep, 357, '0.2', '17.08', "", ""}). +-protocol({xep, 357, '0.2', '17.08', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_register.erl b/src/mod_register.erl index c75fbc150..08b461b28 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 77, '2.4'}). +-protocol({xep, 77, '2.4', '0.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_roster.erl b/src/mod_roster.erl index f093e118f..cc0838812 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -34,7 +34,7 @@ -module(mod_roster). --protocol({xep, 237, '1.3'}). +-protocol({xep, 237, '1.3', '2.1.0', "complete", ""}). -author('alexey@process-one.net'). diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 620ab1b8c..5a3387390 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -21,8 +21,8 @@ %%%------------------------------------------------------------------- -module(mod_s2s_dialback). -behaviour(gen_mod). --protocol({xep, 220, '1.1.1'}). --protocol({xep, 185, '1.0'}). +-protocol({xep, 220, '1.1.1', '17.03', "complete", ""}). +-protocol({xep, 185, '1.0', '17.03', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]). diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 95911a466..09b752bda 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -25,7 +25,7 @@ -module(mod_sic). --protocol({xep, 279, '0.2'}). +-protocol({xep, 279, '0.2', '2.1.3', "complete", ""}). -author('karim.gemayel@process-one.net'). diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 7040680dd..17235fa8f 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 39, '0.6.0'}). +-protocol({xep, 39, '0.6.0', '0.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 844435bda..4a58e1d93 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -23,7 +23,7 @@ -module(mod_stream_mgmt). -behaviour(gen_mod). -author('holger@zedat.fu-berlin.de'). --protocol({xep, 198, '1.5.2', '14.05', "", ""}). +-protocol({xep, 198, '1.5.2', '14.05', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]). diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index 8d2d6d58c..f1e267fc9 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -25,7 +25,7 @@ -module(mod_stun_disco). -author('holger@zedat.fu-berlin.de'). --protocol({xep, 215, '0.7', '20.04', "", ""}). +-protocol({xep, 215, '0.7', '20.04', "complete", ""}). -behaviour(gen_server). -behaviour(gen_mod). diff --git a/src/mod_time.erl b/src/mod_time.erl index 8ee814baa..1dfdd201b 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -28,7 +28,7 @@ -author('alexey@process-one.net'). --protocol({xep, 202, '2.0'}). +-protocol({xep, 202, '2.0', '2.1.0', "complete", ""}). -behaviour(gen_mod). diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index 8c931bc90..cce0ab225 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -27,9 +27,9 @@ -author('alexey@process-one.net'). --protocol({xep, 54, '1.2'}). --protocol({xep, 55, '1.3'}). --protocol({xep, 153, '1.1'}). +-protocol({xep, 54, '1.2', '0.1.0', "complete", ""}). +-protocol({xep, 55, '1.3', '0.1.0', "complete", ""}). +-protocol({xep, 153, '1.1', '17.09', "complete", ""}). -behaviour(gen_server). -behaviour(gen_mod). diff --git a/src/mod_version.erl b/src/mod_version.erl index 88235fb17..f8a8dadcf 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 92, '1.1'}). +-protocol({xep, 92, '1.1', '0.1.0', "complete", ""}). -behaviour(gen_mod). From 3650d94bb5de3098c5293480ac1cae9ec6b75f6f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Nov 2024 17:33:28 +0100 Subject: [PATCH 0819/1302] Remove XEPs that are only a client thing --- src/ejabberd_stun.erl | 1 - src/mod_muc.erl | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index 5ce6c26af..ba99712e3 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -26,7 +26,6 @@ -module(ejabberd_stun). -behaviour(ejabberd_listener). -protocol({rfc, 5766}). --protocol({xep, 176, '1.0'}). -ifndef(STUN). -include("logger.hrl"). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 9fadc9016..cee28918d 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -25,7 +25,6 @@ -module(mod_muc). -author('alexey@process-one.net'). -protocol({xep, 45, '1.25', '0.5.0', "complete", ""}). --protocol({xep, 249, '1.2'}). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. From a6577780657fca169e35f3efe6ed7727aed53a52 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 8 Nov 2024 21:36:23 +0100 Subject: [PATCH 0820/1302] Result of running "make doap" --- ejabberd.doap | 337 +++++++++++++++++++++++--------------------------- 1 file changed, 155 insertions(+), 182 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index afc28a22a..cfb3b1cb9 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -48,8 +48,8 @@ 2.9 - - + 0.5.0 + complete @@ -57,8 +57,8 @@ 2.0 - - + 0.5.0 + complete mod_last @@ -67,7 +67,7 @@ 1.2 16.02 - + complete mod_offline @@ -75,8 +75,8 @@ 1.6 - - + 0.5.0 + complete mod_privacy @@ -84,8 +84,8 @@ 1.4 - - + 0.1.0 + complete mod_offline @@ -93,8 +93,8 @@ 1.3 - - + 0.7.5 + complete mod_offline @@ -102,8 +102,8 @@ 2.4 - - + 0.1.0 + complete mod_disco @@ -112,7 +112,7 @@ 1.1 15.04 - + complete mod_multicast @@ -120,8 +120,8 @@ 0.6.0 - - + 0.1.0 + complete mod_stats @@ -129,8 +129,8 @@ 1.25 - - + 0.5.0 + complete mod_muc @@ -138,8 +138,8 @@ 1.2 - - + 0.1.0 + complete mod_private @@ -147,8 +147,8 @@ 1.2 - - + 1.1.0 + complete mod_adhoc @@ -156,8 +156,8 @@ 1.2 - - + 0.1.0 + complete mod_vcard @@ -165,8 +165,8 @@ 1.3 - - + 0.1.0 + complete mod_vcard @@ -174,8 +174,8 @@ 1.0 - - + 2.1.0 + complete @@ -183,8 +183,8 @@ 1.14 - - + 0.5.0 + partial mod_pubsub @@ -192,8 +192,8 @@ 1.8 - - + 2.0.0 + complete mod_proxy65 @@ -201,8 +201,8 @@ 2.4 - - + 0.1.0 + complete mod_register @@ -210,17 +210,26 @@ 2.5 - - + 17.03 + complete mod_legacy_auth + + + + 1.1.1 + 2.1.0 + complete + + + 2.1 - - + 2.1.0 + complete mod_client_state @@ -228,8 +237,8 @@ 1.0 - - + 0.5.0 + complete @@ -237,8 +246,8 @@ 1.1 - - + 0.1.0 + complete mod_version @@ -246,8 +255,8 @@ 1.1 - - + 0.5.0 + complete @@ -255,8 +264,8 @@ 1.6 - - + 0.1.0 + complete ejabberd_service @@ -264,8 +273,8 @@ 1.5 - - + 2.1.4 + complete mod_caps @@ -273,8 +282,8 @@ 1.11 - - + 16.12 + complete ejabberd_bosh @@ -283,7 +292,7 @@ 1.3.0 13.10 - complete + partial mod_configure @@ -291,8 +300,8 @@ 2.1 - - + 1.1.0 + complete ejabberd_c2s @@ -300,8 +309,8 @@ 1.1 - - + 17.09 + complete mod_vcard @@ -310,7 +319,7 @@ 1.4.0 22.05 - + complete mod_host_meta @@ -318,8 +327,8 @@ 1.0 - - + 2.1.0 + complete mod_disco @@ -327,8 +336,8 @@ 1.0 - - + 2.1.0 + complete ejabberd_captcha @@ -336,8 +345,8 @@ 1.0 - - + 16.01 + complete mod_offline @@ -345,8 +354,8 @@ 1.2 - - + 2.0.0 + complete mod_pubsub @@ -354,8 +363,8 @@ 1.0 - - + 17.12 + complete @@ -363,26 +372,26 @@ 1.2 - - + 1.1.0 + complete ejabberd_auth_anonymous - - 1.0 - - - ejabberd_stun + + 1.1 + 17.03 + complete + 1.0 - - + 17.03 + complete mod_s2s_dialback @@ -390,8 +399,8 @@ 1.2 - - + 2.1.7 + complete mod_blocking @@ -400,7 +409,7 @@ 1.5.2 14.05 - + complete mod_stream_mgmt @@ -408,8 +417,8 @@ 2.0 - - + 2.1.0 + complete mod_ping @@ -417,8 +426,8 @@ 2.0 - - + 2.1.0 + complete mod_time @@ -426,8 +435,8 @@ 2.0 - - + 2.1.0 + complete mod_offline @@ -435,8 +444,8 @@ 1.0 - - + 1.1.2 + complete @@ -444,53 +453,35 @@ 1.4 - - + 16.12 + complete ejabberd_bosh - - - - 1.0 - - - - - 0.7 20.04 - + complete mod_stun_disco - - - - 1.0 - - - - - - 1.1 - - - ejabberd_s2s, mod_s2s_dialback + 1.1.1 + 17.03 + complete + mod_s2s_dialback 1.1 - - + 2.1.0 + partial ejabberd_piefxis @@ -498,8 +489,8 @@ 1.0 - - + 2.1.0 + complete ejabberd_captcha @@ -507,53 +498,26 @@ 1.3 - - + 2.1.0 + complete mod_roster - - - - 1.0 - - - - - 0.2 - - + 2.1.0 + complete mod_pubsub - - - - 1.2 - - - mod_muc - - - - - - 1.0 - - - - - 0.2 - - + 2.1.3 + complete mod_sic @@ -561,8 +525,8 @@ 0.13.2 - - + 13.06 + complete mod_carboncopy @@ -580,7 +544,7 @@ 0.6.1 15.06 - + complete mod_mam @@ -589,7 +553,7 @@ 0.1 21.12 - + complete mod_muc_room, conversejs/prosody compatible @@ -598,7 +562,7 @@ 0.1 19.09 - + complete mod_jidprep @@ -606,8 +570,8 @@ 0.2 - - + 16.01 + complete mod_mam, mod_muc_log, mod_offline @@ -616,7 +580,7 @@ 0.1 14.12 - + complete mod_client_state @@ -625,7 +589,7 @@ 0.4.1 16.09 - + complete mod_delegation @@ -634,7 +598,7 @@ 0.4.1 24.10 - + complete mod_privilege @@ -643,7 +607,7 @@ 0.2 17.08 - + complete mod_push @@ -651,17 +615,17 @@ 0.5.0 - - + 15.09 + complete mod_mam - 0.2 + 0.3.0 15.10 - + complete mod_http_upload @@ -669,8 +633,8 @@ 1.1.0 - - + 17.09 + complete @@ -679,16 +643,25 @@ 0.14.1 16.03 - + complete mod_mix + + + + 0.8.3 + 21.12 + complete + node_pep + + 0.3.0 24.02 - + complete @@ -697,7 +670,7 @@ 0.4.0 24.02 - + complete @@ -706,7 +679,7 @@ 0.2.0 18.03 - + complete mod_avatar @@ -715,7 +688,7 @@ 1.1.3 23.10 - + complete mod_private @@ -723,8 +696,8 @@ 0.3.0 - - + 19.02 + complete mod_mix_pam @@ -733,7 +706,7 @@ 1.1.0 18.12 - + complete mod_muc_room @@ -742,7 +715,7 @@ 0.2.0 18.12 - + complete mod_private @@ -751,7 +724,7 @@ 0.1.0 23.10 - + complete mod_muc_occupantid @@ -760,7 +733,7 @@ 0.4.0 24.02 - + complete @@ -769,7 +742,7 @@ 0.3.0 24.06 - + complete mod_mam @@ -778,7 +751,7 @@ 0.4.0 24.02 - + complete @@ -786,8 +759,8 @@ 0.2.0 - - + 15.06 + complete mod_mam @@ -796,7 +769,7 @@ 0.3.0 24.02 - + complete @@ -814,7 +787,7 @@ 0.2.0 24.02 - + complete , mod_pubsub_serverinfo in ejabberd-contrib.git From 6f7efebb56d17d78b271508807cea1f304ea0626 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Nov 2024 22:41:03 +0100 Subject: [PATCH 0821/1302] Only delete offline msgs when user has MAM enabled (#4287) --- src/mod_mam.erl | 14 ++++++++++++++ src/mod_offline.erl | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index b20bdbd09..198a33738 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -46,6 +46,7 @@ mod_options/1, remove_mam_for_user_with_peer/3, remove_mam_for_user/2, is_empty_for_user/2, is_empty_for_room/3, check_create_room/4, process_iq/3, store_mam_message/7, make_id/0, wrap_as_mucsub/2, select/7, + is_archiving_enabled/2, get_mam_count/2, webadmin_menu_hostuser/4, webadmin_page_hostuser/4, @@ -1571,6 +1572,19 @@ get_jids(undefined) -> get_jids(Js) -> [jid:tolower(jid:remove_resource(J)) || J <- Js]. +is_archiving_enabled(LUser, LServer) -> + case gen_mod:is_loaded(LServer, mod_mam) of + true -> + case get_prefs(LUser, LServer) of + {ok, #archive_prefs{default = Default}} when Default /= never -> + true; + _ -> + false + end; + false -> + false + end. + get_commands_spec() -> [ #ejabberd_commands{name = get_mam_count, tags = [mam], diff --git a/src/mod_offline.erl b/src/mod_offline.erl index eaba44a34..96b541c59 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -306,7 +306,12 @@ c2s_copy_session(State, _) -> State. c2s_handle_bind2_inline({#{jid := #jid{luser = LUser, lserver = LServer}} = State, Els, Results}) -> - delete_all_msgs(LUser, LServer), + case mod_mam:is_archiving_enabled(LUser, LServer) of + true -> + delete_all_msgs(LUser, LServer); + false -> + ok + end, {State, Els, Results}. -spec handle_offline_query(iq()) -> iq(). From e10c0f3120ed9fa03e47670ba9a0841fc078987c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 11 Nov 2024 10:43:19 +0100 Subject: [PATCH 0822/1302] Bump Expat to 2.6.4 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index d7166443f..98cd01213 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,7 +67,7 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.26.0' termcap_vsn='1.3.1' -expat_vsn='2.6.3' +expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.3.2' From 9eb8bb6c402d2262ffd4343fc1c501aca5c23276 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 12 Nov 2024 16:20:40 +0100 Subject: [PATCH 0823/1302] mod_matrix_gw_s2s probably works correctly only in Erlang/OTP >= 25 In Erlang/OTP 25, the function inet_res:getbyname/3 returns {ok, Hostent} where the last tuple element of Hostent may be h_addr_list = [inet:ip_address()]} or H_addr_list :: [dns_data()]} However, in Erlang/OTP 24.1 and older, that element was only of type h_addr_list = [inet:ip_address()]} https://erlang.org/documentation/doc-13.0-rc3/lib/kernel-8.4/doc/html/inet_res.html#getbyname-3 https://erlang.org/documentation/doc-12.1/lib/kernel-8.1/doc/html/inet_res.html#getbyname-3 --- src/mod_matrix_gw.erl | 5 +++-- src/mod_matrix_gw_room.erl | 2 +- src/mod_matrix_gw_s2s.erl | 2 +- src/mod_matrix_gw_sup.erl | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index f65131de2..d880d745e 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -24,7 +24,7 @@ %%%---------------------------------------------------------------------- -module(mod_matrix_gw). --ifndef(OTP_BELOW_24). +-ifndef(OTP_BELOW_25). -author('alexey@process-one.net'). @@ -883,7 +883,8 @@ mod_options(Host) -> mod_doc() -> #{desc => - [?T("https://matrix.org/[Matrix] gateway.")], + [?T("https://matrix.org/[Matrix] gateway. " + "Erlang/OTP 25 or higher is required to use this module.")], note => "added in 24.02", example => ["listen:", diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index aa054eb85..6ceff2141 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -24,7 +24,7 @@ %%%------------------------------------------------------------------- -module(mod_matrix_gw_room). --ifndef(OTP_BELOW_24). +-ifndef(OTP_BELOW_25). -behaviour(gen_statem). %% API diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 6caceb032..533f250c5 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -23,7 +23,7 @@ %%% %%%------------------------------------------------------------------- -module(mod_matrix_gw_s2s). --ifndef(OTP_BELOW_24). +-ifndef(OTP_BELOW_25). -behaviour(gen_statem). %% API diff --git a/src/mod_matrix_gw_sup.erl b/src/mod_matrix_gw_sup.erl index b0c757a5c..f7730a266 100644 --- a/src/mod_matrix_gw_sup.erl +++ b/src/mod_matrix_gw_sup.erl @@ -20,7 +20,7 @@ %%% %%%---------------------------------------------------------------------- -module(mod_matrix_gw_sup). --ifndef(OTP_BELOW_24). +-ifndef(OTP_BELOW_25). -behaviour(supervisor). %% API From 07e20784cb983980b735268f5eac4efbd811c6c0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Nov 2024 12:23:44 +0100 Subject: [PATCH 0824/1302] New evacuate_kindly command: kick users and prevent login (#4309) --- src/ejabberd_admin.erl | 88 +++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 41aca201c..efc07b6af 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -33,6 +33,7 @@ status/0, stop/0, restart/0, reopen_log/0, rotate_log/0, set_loglevel/1, + evacuate_kindly/2, stop_kindly/2, send_service_message_all_mucs/2, registered_vhosts/0, reload_config/0, @@ -166,9 +167,24 @@ get_commands_spec() -> "only on log files generated by some modules.", module = ?MODULE, function = rotate_log, args = [], result = {res, rescode}}, + #ejabberd_commands{name = evacuate_kindly, tags = [server], + desc = "Evacuate kindly all users (kick and prevent login)", + longdesc = "Inform users and rooms, don't allow login, wait, " + "restart the server, and don't allow new logins.\n" + "Provide the delay in seconds, and the " + "announcement quoted, for example: \n" + "`ejabberdctl evacuate_kindly 60 " + "\\\"The server will stop in one minute.\\\"`", + note = "added in 24.xx", + module = ?MODULE, function = evacuate_kindly, + args_desc = ["Seconds to wait", "Announcement to send, with quotes"], + args_example = [60, <<"Server will stop now.">>], + args = [{delay, integer}, {announcement, string}], + result = {res, rescode}}, #ejabberd_commands{name = stop_kindly, tags = [server], - desc = "Inform users and rooms, wait, and stop the server", - longdesc = "Provide the delay in seconds, and the " + desc = "Stop kindly the server (informing users)", + longdesc = "Inform users and rooms, wait, and stop the server.\n" + "Provide the delay in seconds, and the " "announcement quoted, for example: \n" "`ejabberdctl stop_kindly 60 " "\\\"The server will stop in one minute.\\\"`", @@ -672,39 +688,49 @@ set_loglevel(LogLevel) -> %%% Stop Kindly %%% +evacuate_kindly(DelaySeconds, AnnouncementTextString) -> + perform_kindly(DelaySeconds, AnnouncementTextString, evacuate). + stop_kindly(DelaySeconds, AnnouncementTextString) -> - Subject = (str:format("Server stop in ~p seconds!", [DelaySeconds])), - WaitingDesc = (str:format("Waiting ~p seconds", [DelaySeconds])), + perform_kindly(DelaySeconds, AnnouncementTextString, stop). + +perform_kindly(DelaySeconds, AnnouncementTextString, Action) -> + Subject = str:format("Server stop in ~p seconds!", [DelaySeconds]), + WaitingDesc = str:format("Waiting ~p seconds", [DelaySeconds]), AnnouncementText = list_to_binary(AnnouncementTextString), - Steps = [ - {"Stopping ejabberd port listeners", - ejabberd_listener, stop_listeners, []}, - {"Sending announcement to connected users", - mod_announce, send_announcement_to_all, - [ejabberd_config:get_myname(), Subject, AnnouncementText]}, - {"Sending service message to MUC rooms", - ejabberd_admin, send_service_message_all_mucs, - [Subject, AnnouncementText]}, - {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, - {"Stopping ejabberd", application, stop, [ejabberd]}, - {"Stopping Mnesia", mnesia, stop, []}, - {"Stopping Erlang node", init, stop, []} - ], + PreSteps = + [{"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}, + {"Sending announcement to connected users", + mod_announce, + send_announcement_to_all, + [ejabberd_config:get_myname(), Subject, AnnouncementText]}, + {"Sending service message to MUC rooms", + ejabberd_admin, + send_service_message_all_mucs, + [Subject, AnnouncementText]}, + {WaitingDesc, timer, sleep, [DelaySeconds * 1000]}, + {"Stopping ejabberd", application, stop, [ejabberd]}], + SpecificSteps = + case Action of + evacuate -> + [{"Starting ejabberd", application, start, [ejabberd]}, + {"Stopping ejabberd port listeners", ejabberd_listener, stop_listeners, []}]; + stop -> + [{"Stopping Mnesia", mnesia, stop, []}, {"Stopping Erlang node", init, stop, []}] + end, + Steps = PreSteps ++ SpecificSteps, NumberLast = length(Steps), TimestampStart = calendar:datetime_to_gregorian_seconds({date(), time()}), - lists:foldl( - fun({Desc, Mod, Func, Args}, NumberThis) -> - SecondsDiff = - calendar:datetime_to_gregorian_seconds({date(), time()}) - - TimestampStart, - io:format("[~p/~p ~ps] ~ts... ", - [NumberThis, NumberLast, SecondsDiff, Desc]), - Result = (catch apply(Mod, Func, Args)), - io:format("~p~n", [Result]), - NumberThis+1 - end, - 1, - Steps), + lists:foldl(fun({Desc, Mod, Func, Args}, NumberThis) -> + SecondsDiff = + calendar:datetime_to_gregorian_seconds({date(), time()}) - TimestampStart, + io:format("[~p/~p ~ps] ~ts... ", [NumberThis, NumberLast, SecondsDiff, Desc]), + Result = (catch apply(Mod, Func, Args)), + io:format("~p~n", [Result]), + NumberThis + 1 + end, + 1, + Steps), ok. send_service_message_all_mucs(Subject, AnnouncementText) -> From d3baacd78eea597713e3a960537dc919dc085b04 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 12 Nov 2024 13:02:28 +0100 Subject: [PATCH 0825/1302] Workflows: Bump ubuntu from 22.04 to 24.04 when possible (#4281) --- .github/workflows/container.yml | 2 +- .github/workflows/installers.yml | 4 ++-- .github/workflows/runtime.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 33ae16960..40873eb60 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -19,7 +19,7 @@ env: jobs: container: name: Container - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: packages: write steps: diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index b820dd6d9..59db4a81e 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -21,7 +21,7 @@ on: jobs: binaries: name: Binaries - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Cache build directory uses: actions/cache@v4 @@ -70,7 +70,7 @@ jobs: release: name: Release needs: [binaries] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 if: github.ref_type == 'tag' steps: - name: Download packages diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index f3d4a1ffd..90dac9751 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -36,7 +36,7 @@ jobs: exclude: - otp: '27' rebar: 'rebar' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: image: erlang:${{ matrix.otp }} From c20ed8c7b3d62a1498a3d1c05a43eba7a37428ab Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 12 Nov 2024 13:45:22 +0100 Subject: [PATCH 0826/1302] Runtime: Try using elixir container for Rebar3+Elixir and Mix jobs (#4281) --- .github/workflows/runtime.yml | 103 ++++++++++------------------------ 1 file changed, 30 insertions(+), 73 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 90dac9751..1231c6acd 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -158,51 +158,21 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27'] - elixir: ['1.13', '1.15', '1.16', '1.17'] - exclude: - - otp: '23.0' - elixir: '1.15' - - otp: '23.0' - elixir: '1.16' - - otp: '23.0' - elixir: '1.17' - - otp: '26' - elixir: '1.13' - - otp: '27' - elixir: '1.13' - - otp: '27' - elixir: '1.15' - - otp: '27' - elixir: '1.16' - runs-on: ubuntu-20.04 + elixir: ['1.13', '1.14', '1.15', '1.16', '1.17'] + runs-on: ubuntu-24.04 + container: + image: elixir:${{ matrix.elixir }} steps: - uses: actions/checkout@v4 - - name: Get specific Erlang/OTP - uses: erlef/setup-beam@v1 - with: - otp-version: ${{matrix.otp}} - elixir-version: ${{matrix.elixir}} - - - name: Get compatible Rebar binaries - if: matrix.otp < 24 - run: | - rm rebar - rm rebar3 - wget https://github.com/processone/ejabberd/raw/21.12/rebar - wget https://github.com/processone/ejabberd/raw/21.12/rebar3 - chmod +x rebar - chmod +x rebar3 - - name: Prepare libraries run: | - sudo apt-get -qq update - sudo apt-get -y purge libgd3 nginx - sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ - libsqlite3-dev libwebp-dev libyaml-dev + apt-get -qq update + apt-get -y purge libgd3 nginx + apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev - name: Enable ModPresenceDemo and an Elixir dependency run: | @@ -216,7 +186,13 @@ jobs: with: path: | ~/.cache/rebar3/ - key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + key: ${{matrix.elixir}}-${{hashFiles('rebar.config')}} + + - name: Install Hex and Rebar3 manually on older Elixir + if: matrix.elixir <= '1.14' + run: | + mix local.hex --force + mix local.rebar --force - name: Compile run: | @@ -305,41 +281,21 @@ jobs: strategy: fail-fast: false matrix: - otp: ['23.0', '25', '26', '27'] - elixir: ['1.13', '1.15', '1.16', '1.17'] - exclude: - - otp: '23.0' - elixir: '1.15' - - otp: '23.0' - elixir: '1.16' - - otp: '23.0' - elixir: '1.17' - - otp: '26' - elixir: '1.13' - - otp: '27' - elixir: '1.13' - - otp: '27' - elixir: '1.15' - - otp: '27' - elixir: '1.16' - runs-on: ubuntu-20.04 + elixir: ['1.13', '1.14', '1.15', '1.16', '1.17'] + runs-on: ubuntu-24.04 + container: + image: elixir:${{ matrix.elixir }} steps: - uses: actions/checkout@v4 - - name: Get specific Erlang/OTP - uses: erlef/setup-beam@v1 - with: - otp-version: ${{matrix.otp}} - elixir-version: ${{matrix.elixir}} - - name: Prepare libraries run: | - sudo apt-get -qq update - sudo apt-get -y purge libgd3 nginx - sudo apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ - libsqlite3-dev libwebp-dev libyaml-dev + apt-get -qq update + apt-get -y purge libgd3 nginx + apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ + libsqlite3-dev libwebp-dev libyaml-dev - name: Remove Elixir Matchers run: | @@ -356,17 +312,18 @@ jobs: sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config cat rebar.config - - name: Unlock Jose dependency on older Erlang - if: matrix.otp < 24 - run: | - mix deps.unlock jose - - name: Cache Hex.pm uses: actions/cache@v4 with: path: | ~/.hex/ - key: ${{matrix.otp}}-${{hashFiles('mix.exs')}} + key: ${{matrix.elixir}}-${{hashFiles('mix.exs')}} + + - name: Install Hex and Rebar3 manually on older Elixir + if: matrix.elixir <= '1.14' + run: | + mix local.hex --force + mix local.rebar --force - name: Compile run: | From 715b5b64c615e1f3edaa7069a3c0ca2aef2efe02 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Nov 2024 19:37:12 +0100 Subject: [PATCH 0827/1302] Runtime: Test Dialyzer in Rebars jobs (#4281) The CI workflow uses ubuntu-24.04 and setup-beam action, so it can test only Erlang/OTP 24 and higher. To ensure Dialyzer is also ran with older Erlang versions, let's add Dialyzer testing to the Runtime workflow, which uses the erlang container, and that allows to run Erlang/OTP 20 in ubuntu-24.04. --- .github/workflows/runtime.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 1231c6acd..5b8d111ec 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -81,6 +81,8 @@ jobs: - run: make xref + - run: make dialyzer + - name: Prepare rel (rebar2) if: matrix.rebar == 'rebar' run: | From c7b29b5a9ac6f521feee5ff665c33b7045d9367e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 06:11:43 +0000 Subject: [PATCH 0828/1302] Bump dialyxir from 1.4.4 to 1.4.5 Bumps [dialyxir](https://github.com/jeremyjh/dialyxir) from 1.4.4 to 1.4.5. - [Release notes](https://github.com/jeremyjh/dialyxir/releases) - [Changelog](https://github.com/jeremyjh/dialyxir/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeremyjh/dialyxir/compare/1.4.4...1.4.5) --- updated-dependencies: - dependency-name: dialyxir dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 102415e76..55b187837 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, - "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, + "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, @@ -9,10 +9,12 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, + "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, "fast_xml": {:hex, :fast_xml, "1.1.53", "1ef4f6e5995bcfa94800a46b460c3400c19c0a533948b12200a2e2fb1a2be427", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5064336d6f363eee5097aa5dc5ced9b67f05152f2e6b8520fd50d268c2ab839c"}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, + "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, From 2137602a6a2bb06b5fed92e27f855fed45d8af34 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Nov 2024 10:48:05 +0100 Subject: [PATCH 0829/1302] Makefile: Add support to run "make format" when compiling with mix --- Makefile.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index 82073c367..050e219a0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,7 +3,8 @@ # ESCRIPT = @ESCRIPT@ -REBAR = @rebar@ +REBAR = @rebar@ # This rebar|rebar3|mix binary (or path to binary) +REBAR3 = @REBAR3@ # This is path to rebar3 binary MIX = @rebar@ AWK = @AWK@ INSTALL = @INSTALL@ @@ -266,7 +267,7 @@ _build/edoc/logo.png: edoc_compile # format: - tools/rebar3-format.sh $(REBAR) + tools/rebar3-format.sh $(REBAR3) indent: tools/emacs-indent.sh @@ -702,7 +703,7 @@ help: @echo " translations Extract translation files" @echo " TAGS Generate tags file for text editors" @echo "" - @echo " format Format source code using rebar3_format [rebar3]" + @echo " format Format source code using rebar3_format" @echo " indent Indent source code using erlang-mode [emacs]" @echo "" @echo " dialyzer Run Dialyzer static analyzer" From 22e7ce37d4d74d57cca149f5b2bc6720c3703fc4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Nov 2024 19:23:55 +0100 Subject: [PATCH 0830/1302] Fix typos in previous commit --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 050e219a0..c10ff390d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,8 +3,8 @@ # ESCRIPT = @ESCRIPT@ -REBAR = @rebar@ # This rebar|rebar3|mix binary (or path to binary) -REBAR3 = @REBAR3@ # This is path to rebar3 binary +REBAR = @rebar@ # rebar|rebar3|mix binary (or path to binary) +REBAR3 = @REBAR3@ # path to rebar3 binary MIX = @rebar@ AWK = @AWK@ INSTALL = @INSTALL@ From c291c20a3b8350a6960311afc8f08923c4b40d47 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Nov 2024 19:13:46 +0100 Subject: [PATCH 0831/1302] Fix problem starting ejabberd when first host uses SQL, other one mnesia The problem appeared when there are several vhosts, the first vhost uses SQL for persistent data (and RAM for volatile), and another vhost wants to use Mnesia Example config to trigger the problem: hosts: - mysql.localhost - localhost host_config: mysql.localhost: default_db: sql In that case, ejabberd crashed at start with an error like: [critical] Internal error of module mod_muc has occurred during start: ... ** exception exit: {aborted, {no_exists, [muc_room, [{{muc_room,{'_',<<"conference.localhost">>},'_'}, [], ['$_']}]]}} --- src/mod_muc_mnesia.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index 5be5b7928..d5a89b36d 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -57,6 +57,10 @@ init(Host, Opts) -> transient, 5000, worker, [?MODULE]}, case supervisor:start_child(ejabberd_backend_sup, Spec) of {ok, _Pid} -> ok; + %% Maybe started for a vhost which only wanted mnesia for ram + %% and this vhost wants mnesia for persitent storage too + {error, {already_started, _Pid}} -> + init([Host, Opts]); Err -> Err end. From 17b5b34e3c2f2077e923113d7e03afecfb674571 Mon Sep 17 00:00:00 2001 From: Marcos de Vera Piquero Date: Fri, 22 Nov 2024 08:30:55 +0100 Subject: [PATCH 0832/1302] feat: support loading Elixir modules for auth Allow to specify an Elixir module name in `auth_method`. If the referenced module, `M`, cannot be loaded as `ejabberd_auth_M`, try to load it as `Elixir.M`. --- lib/ejabberd_auth_example.ex | 39 ++++++++++++++++++++++++++++++++++++ src/econf.erl | 13 ++++++++---- src/ejabberd.erl | 9 +++++++++ src/ejabberd_auth.erl | 2 +- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 lib/ejabberd_auth_example.ex diff --git a/lib/ejabberd_auth_example.ex b/lib/ejabberd_auth_example.ex new file mode 100644 index 000000000..2198b0e4d --- /dev/null +++ b/lib/ejabberd_auth_example.ex @@ -0,0 +1,39 @@ +defmodule ModAuthExample do + @moduledoc """ + + This is a dummy auth module to demonstrate the usage of Elixir to + create Ejabberd Auth modules. + + """ + import Ejabberd.Logger + @behaviour :ejabberd_auth + + @impl true + def start(host) do + info("Using mod_auth_example to authenticate #{host} users") + nil + end + + @impl true + def stop(host) do + info("Stop using mod_auth_example to authenticate #{host} users") + nil + end + + @impl true + def check_password("alice", _authz_id, _host, "secret"), do: {:nocache, true} + def check_password(_username, _authz_id, _host, _secret), do: {:nocache, false} + + @impl true + def user_exists("alice", _host), do: {:nocache, true} + def user_exists(_username, _host), do: {:nocache, false} + + @impl true + def plain_password_required(_binary), do: true + + @impl true + def store_type(_host), do: :external + + @impl true + def use_cache(_host), do: false +end diff --git a/src/econf.erl b/src/econf.erl index 8501356e9..0ffd05e4e 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -505,10 +505,15 @@ db_type(M) -> and_then( atom(), fun(T) -> - case code:ensure_loaded(db_module(M, T)) of - {module, _} -> T; - {error, _} -> fail({bad_db_type, M, T}) - end + case code:ensure_loaded(db_module(M, T)) of + {module, _} -> T; + {error, _} -> + ElixirModule = "Elixir." ++ atom_to_list(T), + case code:ensure_loaded(list_to_atom(ElixirModule)) of + {module, _} -> list_to_atom(ElixirModule); + {error, _} -> fail({bad_db_type, M, T}) + end + end end). -spec queue_type() -> yconf:validator(ram | file). diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 0b23188d9..a251003b3 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -177,6 +177,15 @@ module_name([Dir, _, <> | _] = Mod) when H >= 65, H =< 90 -> Lib -> <<"Elixir.Ejabberd.", Lib/binary, ".">> end, misc:binary_to_atom(<>); + +module_name([<<"auth">> | T] = Mod) -> + case hd(T) of + %% T already starts with "Elixir" if an Elixir module is + %% loaded with that name, as per `econf:db_type/1` + <<"Elixir", _/binary>> -> misc:binary_to_atom(hd(T)); + _ -> module_name([<<"ejabberd">>] ++ Mod) + end; + module_name([<<"ejabberd">> | _] = Mod) -> Module = str:join([erlang_name(M) || M<-Mod], $_), misc:binary_to_atom(Module); diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 3f2b65b3e..998dbe10f 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -893,7 +893,7 @@ auth_modules() -> auth_modules(Server) -> LServer = jid:nameprep(Server), Methods = ejabberd_option:auth_method(LServer), - [ejabberd:module_name([<<"ejabberd">>, <<"auth">>, + [ejabberd:module_name([<<"auth">>, misc:atom_to_binary(M)]) || M <- Methods]. From f9cecca3628ae7430adddab27114759d2810e92d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Nov 2024 13:17:55 +0100 Subject: [PATCH 0833/1302] Rename mod_presence_demo.ex to mod_example.ex --- lib/{mod_presence_demo.ex => mod_example.ex} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{mod_presence_demo.ex => mod_example.ex} (100%) diff --git a/lib/mod_presence_demo.ex b/lib/mod_example.ex similarity index 100% rename from lib/mod_presence_demo.ex rename to lib/mod_example.ex From 6790ab01e8c8885820b04350485f6344c4a635f8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Nov 2024 13:07:42 +0100 Subject: [PATCH 0834/1302] Improve example Elixir modules --- lib/ejabberd/module.ex | 19 ------------------- lib/ejabberd_auth_example.ex | 17 +++++++++++------ lib/mod_example.ex | 21 +++++++++++++++++---- 3 files changed, 28 insertions(+), 29 deletions(-) delete mode 100644 lib/ejabberd/module.ex diff --git a/lib/ejabberd/module.ex b/lib/ejabberd/module.ex deleted file mode 100644 index 9fb3f040c..000000000 --- a/lib/ejabberd/module.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Ejabberd.Module do - - defmacro __using__(opts) do - logger_enabled = Keyword.get(opts, :logger, true) - - quote do - @behaviour :gen_mod - import Ejabberd.Module - - unquote(if logger_enabled do - quote do: import Ejabberd.Logger - end) - end - end - - # gen_mod callbacks - def depends(_host, _opts), do: [] - def mod_opt_type(_), do: [] -end diff --git a/lib/ejabberd_auth_example.ex b/lib/ejabberd_auth_example.ex index 2198b0e4d..5bc37e093 100644 --- a/lib/ejabberd_auth_example.ex +++ b/lib/ejabberd_auth_example.ex @@ -1,22 +1,27 @@ -defmodule ModAuthExample do +defmodule Ejabberd.Auth.Example do + @moduledoc """ + Example ejabberd auth method written in Elixir. - This is a dummy auth module to demonstrate the usage of Elixir to - create Ejabberd Auth modules. + This is an example to demonstrate the usage of Elixir to + create ejabberd auth methods. + Example configuration: + auth_method: 'Ejabberd.Auth.Example' """ - import Ejabberd.Logger + @behaviour :ejabberd_auth + import Ejabberd.Logger @impl true def start(host) do - info("Using mod_auth_example to authenticate #{host} users") + info("Starting Ejabberd.Auth.Example to authenticate '#{host}' users") nil end @impl true def stop(host) do - info("Stop using mod_auth_example to authenticate #{host} users") + info("Stopping Ejabberd.Auth.Example to authenticate '#{host}' users") nil end diff --git a/lib/mod_example.ex b/lib/mod_example.ex index c5be5bba5..12166810a 100644 --- a/lib/mod_example.ex +++ b/lib/mod_example.ex @@ -1,14 +1,27 @@ -defmodule ModPresenceDemo do - use Ejabberd.Module +defmodule Ejabberd.Module.Example do + + @moduledoc """ + Example ejabberd module written in Elixir. + + This is an example to demonstrate the usage of Elixir to + create ejabberd modules. + + Example configuration: + modules: + 'Ejabberd.Module.Example': {} + """ + + @behaviour :gen_mod + import Ejabberd.Logger def start(host, _opts) do - info("Starting ejabberd module Presence Demo") + info("Starting Ejabberd.Module.Example for host '#{host}'") Ejabberd.Hooks.add(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end def stop(host) do - info("Stopping ejabberd module Presence Demo") + info("Stopping Ejabberd.Module.Example for host '#{host}'") Ejabberd.Hooks.delete(:set_presence_hook, host, __MODULE__, :on_presence, 50) :ok end From da9c591eed5e7eee5f98761b58ff23f392d75e7a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Nov 2024 10:39:46 +0100 Subject: [PATCH 0835/1302] Improve create_rooms_file command to support vhosts with different config Until now it created all the rooms in the storage of the first vhost listed in the ejabberd configuration file. Similarly, it used only the default room options defined for the first vhost. --- src/mod_muc_admin.erl | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 313a4721b..1fc8efb05 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -160,6 +160,7 @@ get_commands_spec() -> #ejabberd_commands{name = create_rooms_file, tags = [muc], desc = "Create the rooms indicated in file", longdesc = "Provide one room JID per line. Rooms will be created after restart.", + note = "improved in 24.xx", module = ?MODULE, function = create_rooms_file, args_desc = ["Path to the text file with one room JID per line"], args_example = ["/home/ejabberd/rooms.txt"], @@ -1245,11 +1246,23 @@ create_rooms_file(Filename) -> RJID = read_room(F), Rooms = read_rooms(F, RJID, []), file:close(F), - %% Read the default room options defined for the first virtual host - DefRoomOpts = mod_muc_opt:default_room_options(ejabberd_config:get_myname()), - [muc_create_room(ejabberd_config:get_myname(), A, DefRoomOpts) || A <- Rooms], + HostsDetails = get_hosts_details(Rooms), + [muc_create_room(HostsDetails, A) || A <- Rooms], ok. +muc_create_room(HostsDetails, {_, Host, _} = RoomTuple) -> + {_Host, ServerHost, DefRoomOpts} = get_host_details(Host, HostsDetails), + muc_create_room(ServerHost, RoomTuple, DefRoomOpts). + +get_hosts_details(Rooms) -> + Hosts = lists:uniq([Host || {_, Host, _} <- Rooms]), + lists:map(fun(H) -> + SH = get_room_serverhost(H), + {H, SH, mod_muc_opt:default_room_options(SH)} + end, Hosts). + +get_host_details(Host, ServerHostsDetails) -> + lists:keyfind(Host, 1, ServerHostsDetails). %%--------------------------------- %% List/Delete Unused/Empty Rooms From cc5c9f60089f108b79b5c1a7bdf2dace7fb5c2ac Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Nov 2024 16:38:34 +0100 Subject: [PATCH 0836/1302] Fix problems introduced in two recent commits --- .github/workflows/runtime.yml | 20 ++++++++++---------- src/mod_muc_admin.erl | 10 +++++++++- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 5b8d111ec..47094414f 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -176,9 +176,9 @@ jobs: apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \ libsqlite3-dev libwebp-dev libyaml-dev - - name: Enable ModPresenceDemo and an Elixir dependency + - name: Enable Module.Example and an Elixir dependency run: | - sed -i "s|^modules:|modules:\n 'ModPresenceDemo': {}|g" ejabberd.yml.example + sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example cat ejabberd.yml.example sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config cat rebar.config @@ -257,15 +257,15 @@ jobs: grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log - grep -q 'module Presence Demo' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log test $(find _build/prod/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log - grep -q 'module Presence Demo' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log test $(find _build/dev/ -empty -name error.log) grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log - grep -q 'module Presence Demo' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures @@ -307,9 +307,9 @@ jobs: echo "::remove-matcher owner=elixir-mixTestFailure::" echo "::remove-matcher owner=elixir-dialyzerOutputDefault::" - - name: Enable ModPresenceDemo and an Elixir dependency + - name: Enable Module.Example and an Elixir dependency run: | - sed -i "s|^modules:|modules:\n 'ModPresenceDemo': {}|g" ejabberd.yml.example + sed -i "s|^modules:|modules:\n 'Ejabberd.Module.Example': {}|g" ejabberd.yml.example cat ejabberd.yml.example sed -i 's|^{deps, \(.*\)|{deps, \1\n {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config cat rebar.config @@ -391,15 +391,15 @@ jobs: grep -q '^user3$' registered.log grep -q 'is started' _build/prod/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/prod/rel/ejabberd/logs/ejabberd.log - grep -q 'module Presence Demo' _build/prod/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/prod/rel/ejabberd/logs/ejabberd.log test $(find _build/prod/ -empty -name error.log) grep -q 'is started' _build/dev/rel/ejabberd/logs/ejabberd.log grep -q 'is stopped' _build/dev/rel/ejabberd/logs/ejabberd.log - grep -q 'module Presence Demo' _build/dev/rel/ejabberd/logs/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' _build/dev/rel/ejabberd/logs/ejabberd.log test $(find _build/dev/ -empty -name error.log) grep -q 'is started' /tmp/ejabberd/var/log/ejabberd/ejabberd.log grep -q 'is stopped' /tmp/ejabberd/var/log/ejabberd/ejabberd.log - grep -q 'module Presence Demo' /tmp/ejabberd/var/log/ejabberd/ejabberd.log + grep -q 'Stopping Ejabberd.Module.Example' /tmp/ejabberd/var/log/ejabberd/ejabberd.log test $(find /tmp/ejabberd/var/log/ejabberd/ -empty -name error.log) - name: View logs failures diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 1fc8efb05..f8cb3bd0e 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1255,12 +1255,20 @@ muc_create_room(HostsDetails, {_, Host, _} = RoomTuple) -> muc_create_room(ServerHost, RoomTuple, DefRoomOpts). get_hosts_details(Rooms) -> - Hosts = lists:uniq([Host || {_, Host, _} <- Rooms]), + Hosts = lists_uniq([Host || {_, Host, _} <- Rooms]), lists:map(fun(H) -> SH = get_room_serverhost(H), {H, SH, mod_muc_opt:default_room_options(SH)} end, Hosts). +-ifdef(OTP_BELOW_25). +lists_uniq(List) -> + lists:usort(List). +-else. +lists_uniq(List) -> + lists:uniq(List). +-endif. + get_host_details(Host, ServerHostsDetails) -> lists:keyfind(Host, 1, ServerHostsDetails). From f0773c4ab8f39bf307e81f56b4ec62424b3a87c3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 25 Nov 2024 17:19:14 +0100 Subject: [PATCH 0837/1302] mix:exs: When development tools is enabled, add debugger and wx --- mix.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 6bb5936d7..494d0c7d1 100644 --- a/mix.exs +++ b/mix.exs @@ -184,7 +184,9 @@ defmodule Ejabberd.MixProject do defp cond_apps do for {:true, app} <- [{config(:stun), :stun}, {if_version_below(~c"27", true), :jiffy}, - {config(:tools), :observer}], do: + {config(:tools), :debugger}, + {config(:tools), :observer}, + {config(:tools), :wx}], do: app end From 73dbc01c2e7b506f02108bc0def799b2514a079d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 27 Nov 2024 12:03:23 +0100 Subject: [PATCH 0838/1302] WebAdmin: Shared group names are case sensitive, use original case instead of lowercase --- src/ejabberd_web_admin.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 35441b700..bf1744629 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -2289,9 +2289,9 @@ make_result(Binary, misc:url_encode(First), "%40", "@")), {GroupId, Host} = case jid:decode(FirstUrlencoded) of - #jid{luser = <<"">>, lserver = G} -> + #jid{luser = <<"">>, server = G} -> {G, Second}; - #jid{luser = G, lserver = H} -> + #jid{user = G, lserver = H} -> {G, H} end, UrlBinary = From e34c1ebcba19810cfed371c4b8b95646f8ec9eec Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 27 Nov 2024 12:38:12 +0100 Subject: [PATCH 0839/1302] WebAdmin: Fix link to displayed group when it is from another vhost --- src/ejabberd_web_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index bf1744629..ba863a2e5 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -2285,7 +2285,7 @@ make_result(Binary, First = proplists:get_value(first, ArgumentsUsed), Second = proplists:get_value(second, ArgumentsUsed), FirstUrlencoded = - hd(string:replace( + list_to_binary(string:replace( misc:url_encode(First), "%40", "@")), {GroupId, Host} = case jid:decode(FirstUrlencoded) of From ab5a2e8d10376c1c984e8b863aaa522ba1920499 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 27 Nov 2024 13:51:34 +0100 Subject: [PATCH 0840/1302] mod_shared_roster: Get back support for groupid@vhost in displayed Feature first implemented by 262157c in ejabberd 2.1.10 Bug introduced with cache improvements by 5b0f0d8 in ejabberd 21.07 --- src/mod_shared_roster.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index cf1e0f536..45376f19f 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -494,7 +494,8 @@ get_online_users(Host) -> lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]). -get_group_users_cached(Host, Group, Cache) -> +get_group_users_cached(Host1, Group1, Cache) -> + {Host, Group} = split_grouphost(Host1, Group1), {Opts, _} = get_groups_opts_cached(Host, Group, Cache), get_group_users(Host, Group, Opts). From 7d0c20e133c0ba859c6cecab204bd3da72310523 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 27 Nov 2024 16:20:07 +0100 Subject: [PATCH 0841/1302] mod_shared_roster: The name of a new group is lowercased Until now it was possible to create a shared roster group with name "Group1", and it was a different group that "group1". From now on, new group names will be stored lowercase, just like the username in a Jabber ID. This only affects commands srg_add and srg_create. All the other commands are still case sensitive, to allow admins of existing databases with case-sensitive groups manage them. --- src/mod_shared_roster.erl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 45376f19f..39c563dfb 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -381,6 +381,19 @@ create_group(Host, Group) -> create_group(Host, Group, []). create_group(Host, Group, Opts) -> + case jid:nodeprep(Group) of + error -> + {error, invalid_group_name}; + LGroup -> + case jid:nameprep(Host) of + error -> + {error, invalid_group_host}; + LHost -> + create_group2(LHost, LGroup, Opts) + end + end. + +create_group2(Host, Group, Opts) -> Mod = gen_mod:db_mod(Host, ?MODULE), case proplists:get_value(all_users, Opts, false) orelse proplists:get_value(online_users, Opts, false) of From 344775aa8e37306acf99daa7937b8c2fc00cca27 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 30 Nov 2024 20:38:33 +0100 Subject: [PATCH 0842/1302] mod_pubsub: Send notifications on PEP item retract --- src/mod_pubsub.erl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 2bf1d350f..767d10215 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -596,7 +596,7 @@ on_self_presence(Acc) -> -spec on_user_offline(ejabberd_c2s:state(), atom()) -> ejabberd_c2s:state(). on_user_offline(#{jid := JID} = C2SState, _Reason) -> - purge_offline(jid:tolower(JID)), + purge_offline(JID), C2SState; on_user_offline(C2SState, _Reason) -> C2SState. @@ -1897,14 +1897,14 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed), + broadcast_retract_items(Host, Publisher, Node, Nidx, Type, Options, Removed), set_cached_item(Host, Nidx, ItemId, Publisher, Payload), {result, Reply}; {result, {TNode, {Result, Removed}}} -> Nidx = TNode#pubsub_node.id, Type = TNode#pubsub_node.type, Options = TNode#pubsub_node.options, - broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed), + broadcast_retract_items(Host, Publisher, Node, Nidx, Type, Options, Removed), set_cached_item(Host, Nidx, ItemId, Publisher, Payload), {result, Result}; {result, {_, default}} -> @@ -1975,7 +1975,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> ServerHost = serverhost(Host), ejabberd_hooks:run(pubsub_delete_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId]), - broadcast_retract_items(Host, Node, Nidx, Type, Options, [ItemId], ForceNotify), + broadcast_retract_items(Host, Publisher, Node, Nidx, Type, Options, [ItemId], ForceNotify), case get_cached_item(Host, Nidx) of #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx); _ -> ok @@ -2834,16 +2834,16 @@ broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payloa {result, false} end. --spec broadcast_retract_items(host(), binary(), nodeIdx(), binary(), +-spec broadcast_retract_items(host(), jid(), binary(), nodeIdx(), binary(), nodeOptions(), [itemId()]) -> {result, boolean()}. -broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds) -> - broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, false). +broadcast_retract_items(Host, Publisher, Node, Nidx, Type, NodeOptions, ItemIds) -> + broadcast_retract_items(Host, Publisher, Node, Nidx, Type, NodeOptions, ItemIds, false). --spec broadcast_retract_items(host(), binary(), nodeIdx(), binary(), +-spec broadcast_retract_items(host(), jid(), binary(), nodeIdx(), binary(), nodeOptions(), [itemId()], boolean()) -> {result, boolean()}. -broadcast_retract_items(_Host, _Node, _Nidx, _Type, _NodeOptions, [], _ForceNotify) -> +broadcast_retract_items(_Host, _Publisher, _Node, _Nidx, _Type, _NodeOptions, [], _ForceNotify) -> {result, false}; -broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, ForceNotify) -> +broadcast_retract_items(Host, Publisher, Node, Nidx, Type, NodeOptions, ItemIds, ForceNotify) -> case (get_option(NodeOptions, notify_retract) or ForceNotify) of true -> case get_collection_subscriptions(Host, Node) of @@ -2854,7 +2854,7 @@ broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, ForceNotif items = #ps_items{ node = Node, retract = ItemIds}}]}, - broadcast_stanza(Host, Node, Nidx, Type, + broadcast_stanza(Host, Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, items, Stanza, true), {result, true}; _ -> @@ -4100,9 +4100,8 @@ subid_shim(SubIds) -> extended_headers(Jids) -> [#address{type = replyto, jid = Jid} || Jid <- Jids]. --spec purge_offline(ljid()) -> ok. -purge_offline(LJID) -> - Host = host(element(2, LJID)), +-spec purge_offline(jid()) -> ok. +purge_offline(#jid{lserver = Host} = JID) -> Plugins = plugins(Host), Result = lists:foldl( fun(Type, {Status, Acc}) -> @@ -4117,7 +4116,7 @@ purge_offline(LJID) -> andalso lists:member(<<"persistent-items">>, Features), if Items -> case node_action(Host, Type, - get_entity_affiliations, [Host, LJID]) of + get_entity_affiliations, [Host, JID]) of {result, Affs} -> {Status, [Affs | Acc]}; {error, _} = Err -> @@ -4138,7 +4137,7 @@ purge_offline(LJID) -> Purge = (get_option(Options, purge_offline) andalso get_option(Options, persist_items)), if (Publisher or Open) and Purge -> - purge_offline(Host, LJID, Node); + purge_offline(Host, JID, Node); true -> ok end @@ -4147,8 +4146,8 @@ purge_offline(LJID) -> ok end. --spec purge_offline(host(), ljid(), #pubsub_node{}) -> ok | {error, stanza_error()}. -purge_offline(Host, LJID, Node) -> +-spec purge_offline(host(), jid(), #pubsub_node{}) -> ok | {error, stanza_error()}. +purge_offline(Host, #jid{luser = User, lserver = Server, lresource = Resource} = JID, Node) -> Nidx = Node#pubsub_node.id, Type = Node#pubsub_node.type, Options = Node#pubsub_node.options, @@ -4156,7 +4155,6 @@ purge_offline(Host, LJID, Node) -> {result, {[], _}} -> ok; {result, {Items, _}} -> - {User, Server, Resource} = LJID, PublishModel = get_option(Options, publish_model), ForceNotify = get_option(Options, notify_retract), {_, NodeId} = Node#pubsub_node.nodeid, @@ -4165,7 +4163,7 @@ purge_offline(Host, LJID, Node) -> when (U == User) and (S == Server) and (R == Resource) -> case node_action(Host, Type, delete_item, [Nidx, {U, S, <<>>}, PublishModel, ItemId]) of {result, {_, broadcast}} -> - broadcast_retract_items(Host, NodeId, Nidx, Type, Options, [ItemId], ForceNotify), + broadcast_retract_items(Host, JID, NodeId, Nidx, Type, Options, [ItemId], ForceNotify), case get_cached_item(Host, Nidx) of #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx); _ -> ok From b90c48f8377a6b254a3612808a4f333301f59a35 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 1 Dec 2024 20:23:52 +0100 Subject: [PATCH 0843/1302] mod_scram_upgrade: Don't abort the upgrade Fix a matching mistake that made the SASL mechanism upgrade fail. --- src/mod_scram_upgrade.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index 04dae1dd5..c9a1e94f5 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -111,8 +111,8 @@ c2s_handle_sasl2_task_data({_, #{user := User, server := Server, #scram{hash = Algo, iterationcount = Iter, salt = Salt, serverkey = ServerKey, storedkey = StoredKey}), State2 = maps:remove(scram_upgrade, State), - InlineEls = lists:keydelete(sasl_upgrade, 1, InlineEls), - case ejabberd_c2s:handle_sasl2_inline(InlineEls, State2) of + InlineEls2 = lists:keydelete(sasl_upgrade, 1, InlineEls), + case ejabberd_c2s:handle_sasl2_inline(InlineEls2, State2) of {State3, NewEls, Results} -> {success, NewEls, Results, State3} end; From bd36895afe5188128c60123ae66298e60e36494c Mon Sep 17 00:00:00 2001 From: Mark Zealey Date: Tue, 3 Dec 2024 17:38:00 +0000 Subject: [PATCH 0844/1302] Enable allow_unencrypted_sasl2 on websockets https://github.com/processone/ejabberd/commit/47232838 added the allow_unencrypted_sasl2 option, but this was not added to websockets. --- src/ejabberd_http.erl | 6 ++++++ src/ejabberd_http_ws.erl | 1 + 2 files changed, 7 insertions(+) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index a1dbd240c..912b25b42 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -69,6 +69,7 @@ default_host, custom_headers, trail = <<>>, + allow_unencrypted_sasl2, addr_re, sock_peer_name = none }). @@ -133,10 +134,12 @@ init(SockMod, Socket, Opts) -> CustomHeaders = proplists:get_value(custom_headers, Opts, []), + AllowUnencryptedSasl2 = proplists:get_bool(allow_unencrypted_sasl2, Opts), State = #state{sockmod = SockMod1, socket = Socket1, custom_headers = CustomHeaders, options = Opts, + allow_unencrypted_sasl2 = AllowUnencryptedSasl2, request_handlers = RequestHandlers, sock_peer_name = SockPeer, addr_re = RE}, @@ -916,6 +919,8 @@ normalize_path([Part | Path], Norm) -> listen_opt_type(tag) -> econf:binary(); +listen_opt_type(allow_unencrypted_sasl2) -> + econf:bool(); listen_opt_type(request_handlers) -> econf:map( econf:and_then( @@ -941,6 +946,7 @@ listen_options() -> {protocol_options, undefined}, {tls, false}, {tls_compression, false}, + {allow_unencrypted_sasl2, false}, {request_handlers, []}, {tag, <<>>}, {default_host, undefined}, diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index 0ce51d17b..60daea685 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -122,6 +122,7 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) -> ({max_ack_queue, _}) -> true; ({ack_timeout, _}) -> true; ({resume_timeout, _}) -> true; + ({allow_unencrypted_sasl2, _}) -> true; ({max_resume_timeout, _}) -> true; ({resend_on_timeout, _}) -> true; ({access, _}) -> true; From c021cf34be02254c759a3af39bea1df4bb714aec Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 28 Nov 2024 12:16:02 +0100 Subject: [PATCH 0845/1302] Explain that join_cluster returns immediately (since 5a34020, 24.06) --- src/ejabberd_admin.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index efc07b6af..f326d05d1 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -268,6 +268,16 @@ get_commands_spec() -> #ejabberd_commands{name = join_cluster, tags = [cluster], desc = "Join our local node into the cluster handled by Node", + longdesc = "This command returns immediately, + even before the joining process has + completed. Consequently, if you are using + `ejabberdctl` (or some `CTL_ON_` container + environment variables) to run more commands + afterwards, you may want to precede them with + the _`started`_ command to ensure the + clustering process has completed before + proceeding. For example: `join_cluster + ejabberd@main` > `started` > `list_cluster`.", note = "improved in 24.06", module = ?MODULE, function = join_cluster, args_desc = ["Nodename of the node to join"], From 803f95050ff12e5defad61a5e968407729a66547 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Dec 2024 02:42:04 +0100 Subject: [PATCH 0846/1302] WebAdmin: Fix calculation of node's uptime days --- src/mod_admin_extra.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 2612e7a71..4e644586d 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2283,7 +2283,9 @@ web_page_node(_, Node, #request{path = [<<"stats">>]} = R) -> ejabberd_web_admin, make_command, [stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]), - UpDaysBin = integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs)) div 24000), + UpDaysBin = integer_to_binary( + binary_to_integer(fxml:get_tag_cdata(UpSecs)) + div 86400), % 24*60*60 UpDays = #xmlel{name = <<"code">>, attrs = [], From 4d625e55749c2d6ddf588fea3a6a6b202f7c8dd7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Nov 2024 20:35:28 +0100 Subject: [PATCH 0847/1302] Erlang/OTP 27 finally exports re:mp/0 --- src/misc.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/misc.erl b/src/misc.erl index c73221732..233a03f1f 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -56,15 +56,15 @@ -include_lib("xmpp/include/xmpp.hrl"). -include_lib("kernel/include/file.hrl"). +-ifdef(OTP_BELOW_27). %% Copied from erlang/otp/lib/stdlib/src/re.erl -type re_mp() :: {re_pattern, _, _, _, _}. --export_type([re_mp/0]). - --ifdef(OTP_BELOW_27). -type json_value() :: jiffy:json_value(). -else. +-type re_mp() :: re:mp(). -type json_value() :: json:encode_value(). -endif. +-export_type([re_mp/0]). -export_type([json_value/0]). -type distance_cache() :: #{{string(), string()} => non_neg_integer()}. From c72ba1f18866a1d2f0f1dabca35d2c4e942ee54e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Dec 2024 12:45:53 +0100 Subject: [PATCH 0848/1302] mod_scram_upgrade: Update XEP-0480 supported version (processone/xmpp#80) --- src/mod_scram_upgrade.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index c9a1e94f5..d18e95f66 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -21,7 +21,7 @@ %%%------------------------------------------------------------------- -module(mod_scram_upgrade). -behaviour(gen_mod). --protocol({xep, 480, '0.1', '24.10', "complete", ""}). +-protocol({xep, 480, '0.2.0', '24.10', "complete", ""}). %% gen_mod API -export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). From 39e37b6175f4c50a595486a7395c4a45cf53dfe4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Nov 2024 11:07:26 +0100 Subject: [PATCH 0849/1302] Add support to define macros as environment variables Define and macro by setting as environment variable: EJABBERD_MACRO_ + macro name For example, if you configure in ejabberd.yml: define_macro: LOGLEVEL: 4 loglevel: LOGLEVEL You can define (and overwrite) that macro definition when starting ejabberd: EJABBERD_MACRO_LOGLEVEL=5 make relive --- CONTAINER.md | 38 ++++++++++++++++++++++++++++++++++++-- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 11 ++++++----- src/ejabberd_config.erl | 17 ++++++++++++++++- 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index c62e597d7..38b37768f 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -234,6 +234,30 @@ Example usage (or check the [full example](#customized-example)): ``` +### Macros in environment + +ejabberd reads `EJABBERD_MACRO_*` environment variables +and uses them to define the `*` +[macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file), +overwriting the corresponding macro definition if it was set in the configuration file. + +For example, if you configure this in `ejabberd.yml`: + +```yaml +acl: + admin: + user: ADMINJID +``` + +now you can define the admin account JID using an environment variable: +```yaml + environment: + - EJABBERD_MACRO_ADMINJID=admin@localhost +``` + +Check the [full example](#customized-example) for other example. + + ### Clustering When setting several containers to form a @@ -371,6 +395,7 @@ docker-compose up This example shows the usage of several customizations: it uses a local configuration file, +defines a configuration macro using an environment variable, stores the mnesia database in a local path, registers an account when it's created, and checks the number of registered accounts every time it's started. @@ -381,6 +406,14 @@ wget https://raw.githubusercontent.com/processone/ejabberd/master/ejabberd.yml.e mv ejabberd.yml.example ejabberd.yml ``` +Use a macro in `ejabberd.yml` to set the served vhost, with `localhost` as default value: +```bash +define_macro: + XMPPHOST: localhost +hosts: + - XMPPHOST +``` + Create the database directory and allow the container access to it: ```bash mkdir database @@ -397,8 +430,9 @@ services: image: ghcr.io/processone/ejabberd container_name: ejabberd environment: - - CTL_ON_CREATE=register admin localhost asd - - CTL_ON_START=registered_users localhost ; + - EJABBERD_MACRO_XMPPHOST=example.com + - CTL_ON_CREATE=register admin example.com asd + - CTL_ON_START=registered_users example.com ; status ports: - "5222:5222" diff --git a/mix.exs b/mix.exs index 494d0c7d1..256ae12d8 100644 --- a/mix.exs +++ b/mix.exs @@ -145,7 +145,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:xmpp, "~> 1.9"}, - {:yconf, "~> 1.0"}] + {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 55b187837..b514cdd00 100644 --- a/mix.lock +++ b/mix.lock @@ -35,5 +35,5 @@ "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "xmpp": {:hex, :xmpp, "1.9.0", "d92446bf51d36adda02db63b963fe6d4a1ede33e59b38a43d9b90afd20c25b74", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "c1b91be74a9a9503afa6766f756477516920ffbfeea0c260c2fa171355f53c27"}, - "yconf": {:hex, :yconf, "1.0.16", "d59521d66ff89f219411b6e9277cd6feec7cc6fce11554e67de02a8d0a470479", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "e947813273f38711c7b2e5a8e4acc9a51c7bbe854f744a345f60300b38586c89"}, + "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, } diff --git a/rebar.config b/rebar.config index e6f59bebd..68d9af275 100644 --- a/rebar.config +++ b/rebar.config @@ -70,7 +70,7 @@ {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.0"}}}, - {yconf, "~> 1.0.15", {git, "https://github.com/processone/yconf", {tag, "1.0.16"}}} + {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index cdfae8f58..68d5f7061 100644 --- a/rebar.lock +++ b/rebar.lock @@ -25,7 +25,10 @@ {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.0">>},0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.16">>},0}]}. + {<<"yconf">>, + {git,"https://github.com/processone/yconf", + {ref,"9898754f16cbd4585a1c2061d72fa441ecb2e938"}}, + 0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -53,8 +56,7 @@ {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"D92446BF51D36ADDA02DB63B963FE6D4A1EDE33E59B38A43D9B90AFD20C25B74">>}, - {<<"yconf">>, <<"D59521D66FF89F219411B6E9277CD6FEEC7CC6FCE11554E67DE02A8D0A470479">>}]}, + {<<"xmpp">>, <<"D92446BF51D36ADDA02DB63B963FE6D4A1EDE33E59B38A43D9B90AFD20C25B74">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, @@ -81,6 +83,5 @@ {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"C1B91BE74A9A9503AFA6766F756477516920FFBFEEA0C260C2FA171355F53C27">>}, - {<<"yconf">>, <<"E947813273F38711C7B2E5A8E4ACC9A51C7BBE854F744A345F60300B38586C89">>}]} + {<<"xmpp">>, <<"C1B91BE74A9A9503AFA6766F756477516920FFBFEEA0C260C2FA171355F53C27">>}]} ]. diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 3a77c2720..8b63cc7bb 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -516,8 +516,23 @@ read_file(File, Opts) -> Err end. +get_additional_macros() -> + MacroStrings = lists:foldl(fun([$E, $J, $A, $B, $B, $E, $R, $D, $_, $M, $A, $C, $R, $O, $_ | MacroString], Acc) -> + [parse_macro_string(MacroString) | Acc]; + (_, Acc) -> + Acc + end, + [], + os:getenv()), + {additional_macros, MacroStrings}. + +parse_macro_string(MacroString) -> + [NameString, ValueString] = string:split(MacroString, "="), + {ok, [ValueDecoded]} = fast_yaml:decode(ValueString, [plain_as_atom]), + {list_to_atom(NameString), ValueDecoded}. + read_yaml_files(Files, Opts) -> - ParseOpts = [plain_as_atom | lists:flatten(Opts)], + ParseOpts = [plain_as_atom, get_additional_macros() | lists:flatten(Opts)], lists:foldl( fun(File, {ok, Y1}) -> case econf:parse(File, #{'_' => econf:any()}, ParseOpts) of From 1669303a40ae42416311ccb77ab6814f780f04b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:33:56 +0000 Subject: [PATCH 0850/1302] Bump ex_doc from 0.34.2 to 0.35.1 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.34.2 to 0.35.1. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.34.2...v0.35.1) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- mix.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index b514cdd00..c1073d027 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, - "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, + "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, @@ -19,8 +19,8 @@ "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, - "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, From 06e3f9f0a40df3a2775e53a3fc6a6330139a3122 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Dec 2024 11:03:35 +0100 Subject: [PATCH 0851/1302] Runtime: Disable edoc when using old problematic Elixir versions --- .github/workflows/runtime.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 47094414f..313706a12 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -340,6 +340,7 @@ jobs: - run: make dialyzer - run: make edoc + if: matrix.elixir >= '1.14' - name: Run rel run: | From 3ae636b454b33159ed4f0be59078f5c9b697bf97 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Dec 2024 13:44:18 +0100 Subject: [PATCH 0852/1302] Redis: Use eredis 1.7.1 from Nordix when using mix/rebar3 and Erlang 21+ --- .github/workflows/ci.yml | 4 ++++ .github/workflows/runtime.yml | 4 ++++ mix.exs | 2 +- mix.lock | 2 +- rebar.config | 10 +++++++++- rebar.lock | 6 +++--- 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 916a5c1a4..ebd2148a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,10 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + - name: Get compatible Rebar binaries + if: matrix.otp < 21 + run: ./rebar3 unlock eredis + - name: Download test logs if: matrix.otp == '26' && github.repository == 'processone/ejabberd' continue-on-error: true diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 313706a12..7e0b63518 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -68,6 +68,10 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} + - name: Get compatible Rebar binaries + if: matrix.rebar == 'rebar3' && matrix.otp < 21 + run: rebar3 unlock eredis + - name: Compile run: | ./autogen.sh diff --git a/mix.exs b/mix.exs index 256ae12d8..516aa9e49 100644 --- a/mix.exs +++ b/mix.exs @@ -166,7 +166,7 @@ defmodule Ejabberd.MixProject do {:ejabberd_po, git: "https://github.com/processone/ejabberd-po.git"}}, {Mix.env() == :dev, {:exsync, "~> 0.2", optional: true, runtime: false}}, - {config(:redis), {:eredis, "~> 1.2.0"}}, + {config(:redis), {:eredis, "~> 1.7.1"}}, {config(:sip), {:esip, "~> 1.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, {if_version_above(~c"23", true), {:jose, "~> 1.11.10"}}, diff --git a/mix.lock b/mix.lock index c1073d027..e59fa341a 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, - "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"}, + "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, diff --git a/rebar.config b/rebar.config index 68d9af275..75615f529 100644 --- a/rebar.config +++ b/rebar.config @@ -31,7 +31,15 @@ {if_var_true, pam, {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, - {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, + {if_not_rebar3, + {eredis, "~> 1.2.0", {git, "https://github.com/wooga/eredis/", {tag, "v1.2.0"}}} + }}, + {if_var_true, redis, + {if_rebar3, + {if_version_below, "21", + {eredis, "1.2.0", {git, "https://github.com/wooga/eredis/", {tag, "v1.2.0"}}}, + {eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}} + }}}, {if_var_true, sip, {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.56"}}}}, {if_var_true, zlib, diff --git a/rebar.lock b/rebar.lock index 68d5f7061..926234015 100644 --- a/rebar.lock +++ b/rebar.lock @@ -3,7 +3,7 @@ {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.31">>},0}, {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.23">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, - {<<"eredis">>,{pkg,<<"eredis">>,<<"1.2.0">>},0}, + {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, {<<"esip">>,{pkg,<<"esip">>,<<"1.0.56">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, @@ -35,7 +35,7 @@ {<<"cache_tab">>, <<"E4097B50A6F373AB1E0A5F01BAB0BEF6626771A4CD6C93404ED6D54810E11FBC">>}, {<<"eimp">>, <<"AAF32EFAB061143403DADBAA63E699EF8E81702FBFA96FD436D5E9BE4D6A8D3A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, - {<<"eredis">>, <<"0B8E9CFC2C00FA1374CD107EA63B49BE08D933DF2CF175E6A89B73DD9C380DE4">>}, + {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, {<<"esip">>, <<"63C0FDC667BE751714E1E5C14621A9334F21B60AC1BB68BE889454CA9CA021B7">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, @@ -62,7 +62,7 @@ {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, {<<"eimp">>, <<"907C780023CB2893E4FC4BDBE6A4F02C355913862AC67F0ECC26605E816B628A">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, - {<<"eredis">>, <<"D9B5ABEF2C2C8ABA8F32AA018203E0B3DC8B1157773B254AB1D4C2002317F1E1">>}, + {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, {<<"esip">>, <<"9EF3660CEF93B623F7368DCD5C79F4E704358631909E6DD464E335378815DA1F">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, From a84c4921308bc4fd2163c8d0dae487a2a05b4b33 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Dec 2024 10:13:51 +0100 Subject: [PATCH 0853/1302] Redis: Use the recommended eredis:start_link/1 function --- src/ejabberd_redis.erl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 45a689549..0c4a5c9aa 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -483,14 +483,26 @@ connect(#state{num = Num}) -> do_connect(1, Server, Port, Pass, _DB, _ConnTimeout) -> %% First connection in the pool is always a subscriber - Res = eredis_sub:start_link(Server, Port, Pass, no_reconnect, infinity, drop), + Options = [{host, Server}, + {port, Port}, + {password, Pass}, + {reconnect_sleep, no_reconnect}, + {max_queue_size, infinity}, + {queue_behaviour, drop}], + Res = eredis_sub:start_link(Options), case Res of {ok, Pid} -> eredis_sub:controlling_process(Pid); _ -> ok end, Res; do_connect(_, Server, Port, Pass, DB, ConnTimeout) -> - eredis:start_link(Server, Port, DB, Pass, no_reconnect, ConnTimeout). + Options = [{host, Server}, + {port, Port}, + {database, DB}, + {password, Pass}, + {reconnect_sleep, no_reconnect}, + {connect_timeout, ConnTimeout}], + eredis:start_link(Options). -spec call(pos_integer(), {q, redis_command()}, integer()) -> {ok, redis_reply()} | redis_error(); From f269d5b613b9a560b01f6081b9175a363b93b682 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Dec 2024 05:09:18 +0100 Subject: [PATCH 0854/1302] Redis: Add support for unix domain socket (#4318) --- src/ejabberd_options_doc.erl | 12 +++++++----- src/ejabberd_redis.erl | 8 +++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index a4ba40095..4e4bfe242 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1137,11 +1137,13 @@ doc() -> "The default value is the value defined in _`queue_type`_ " "or 'ram' if the latter is not set.")}}, {redis_server, - #{value => ?T("Hostname"), + #{value => "Host | IP Address | Unix Socket Path", + note => "improved in 24.xx", desc => - ?T("A hostname or an IP address of the " - "_`database.md#redis|Redis`_ server." - "The default is 'localhost'.")}}, + ?T("A hostname, IP address or unix domain socket file of the " + "_`database.md#redis|Redis`_ server. " + "Setup the path to unix domain socket like: '\"unix:/path/to/socket\"'. " + "The default value is 'localhost'.")}}, {registration_timeout, #{value => "timeout()", desc => @@ -1434,7 +1436,7 @@ doc() -> ?T("The hostname or IP address of the SQL server. For _`sql_type`_ " "'mssql' or 'odbc' this can also be an ODBC connection string. " "When _`sql_type`_ is 'mysql' or 'pgsql', this can be the path to " - "a unix domain socket expressed like: \"unix:/path/to/socket\"." + "a unix domain socket expressed like: '\"unix:/path/to/socket\"'." "The default value is 'localhost'.")}}, {sql_ssl, #{value => "true | false", diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 0c4a5c9aa..05fa5e497 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -457,11 +457,12 @@ code_change(_OldVsn, State, _Extra) -> %%%=================================================================== -spec connect(state()) -> {ok, pid()} | {error, any()}. connect(#state{num = Num}) -> - Server = ejabberd_option:redis_server(), + Server1 = ejabberd_option:redis_server(), Port = ejabberd_option:redis_port(), DB = ejabberd_option:redis_db(), Pass = ejabberd_option:redis_password(), ConnTimeout = ejabberd_option:redis_connect_timeout(), + Server = parse_server(Server1), try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of {ok, Client} -> ?DEBUG("Connection #~p established to Redis at ~ts:~p", @@ -481,6 +482,11 @@ connect(#state{num = Num}) -> {error, Reason} end. +parse_server([$u,$n,$i,$x,$: | Path]) -> + {local, Path}; +parse_server(Server) -> + Server. + do_connect(1, Server, Port, Pass, _DB, _ConnTimeout) -> %% First connection in the pool is always a subscriber Options = [{host, Server}, From ce6d5aa6a0a73e61348b62a9330d1c0235471fa9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Dec 2024 12:00:05 +0100 Subject: [PATCH 0855/1302] Redis: Disable some dialyzer warnings when using old Erlang 20 --- src/ejabberd_redis.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 05fa5e497..46fe8ce90 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -69,6 +69,14 @@ -export_type([error_reason/0]). +-ifdef(USE_OLD_HTTP_URI). % Erlang/OTP lower than 21 +-dialyzer([{no_return, do_connect/6}, + {no_unused, flush_queue/1}, + {no_match, flush_queue/1}, + {no_unused, re_subscribe/2}, + {no_match, handle_info/2}]). +-endif. + %%%=================================================================== %%% API %%%=================================================================== From eb6f242d9942fd6c112a07793768934eae896257 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 6 Dec 2024 03:30:04 +0300 Subject: [PATCH 0856/1302] Experimental support for joining Matrix rooms as MUC rooms --- src/mod_matrix_gw.erl | 33 +- src/mod_matrix_gw_room.erl | 909 ++++++++++++++++++++++++++++++------- src/mod_matrix_gw_s2s.erl | 82 ++-- 3 files changed, 800 insertions(+), 224 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index d880d745e..bd7882117 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -57,6 +57,11 @@ -define(MAX_REQUEST_SIZE, 1000000). +-define(CORS_HEADERS, + [{<<"Access-Control-Allow-Origin">>, <<"*">>}, + {<<"Access-Control-Allow-Methods">>, <<"GET, POST, PUT, DELETE, OPTIONS">>}, + {<<"Access-Control-Allow-Headers">>, <<"X-Requested-With, Content-Type, Authorization">>}]). + process([<<"key">>, <<"v2">>, <<"server">> | _], #request{method = 'GET', host = _Host} = _Request) -> Host = ejabberd_config:get_myname(), @@ -137,6 +142,7 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], #request{method = 'PUT', host = _Host} = Request) -> case preprocess_federation_request(Request) of {ok, #{<<"event">> := #{%<<"origin">> := Origin, + <<"content">> := Content, <<"room_id">> := RoomID, <<"sender">> := Sender, <<"state_key">> := UserID} = Event, @@ -155,7 +161,12 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], SEvent = sign_pruned_event(Host, PrunedEvent), ?DEBUG("sign event ~p~n", [SEvent]), ResJSON = #{<<"event">> => SEvent}, - mod_matrix_gw_room:join(Host, Origin, RoomID, Sender, UserID), + case Content of + #{<<"is_direct">> := true} -> + mod_matrix_gw_room:join_direct(Host, Origin, RoomID, Sender, UserID); + _ -> + mod_matrix_gw_room:send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) + end, ?DEBUG("res ~s~n", [misc:json_encode(ResJSON)]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], misc:json_encode(ResJSON)}; _ -> @@ -386,6 +397,13 @@ process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], {result, HTTPResult} -> HTTPResult end; +%process([<<"client">> | ClientPath], Request) -> +% {HTTPCode, Headers, JSON} = mod_matrix_gw_c2s:process(ClientPath, Request), +% ?DEBUG("resp ~p~n", [JSON]), +% {HTTPCode, +% [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>} | +% ?CORS_HEADERS] ++ Headers, +% jiffy:encode(JSON)}; process(Path, Request) -> ?DEBUG("matrix 404: ~p~n~p~n", [Path, Request]), ejabberd_web:error(not_found). @@ -494,6 +512,7 @@ init([Host]) -> process_flag(trap_exit, true), mod_matrix_gw_s2s:create_db(), mod_matrix_gw_room:create_db(), + %mod_matrix_gw_c2s:create_db(), Opts = gen_mod:get_module_opts(Host, ?MODULE), MyHost = gen_mod:get_opt(host, Opts), register_routes(Host, [MyHost]), @@ -780,10 +799,14 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, _ -> {URL, Headers, "application/json;charset=UTF-8", Content} end, - httpc:request(Method, - Request, - HTTPOptions, - Options). + ?DEBUG("httpc request ~p", [{Method, Request, HTTPOptions, Options}]), + HTTPRes = + httpc:request(Method, + Request, + HTTPOptions, + Options), + ?DEBUG("httpc request res ~p", [HTTPRes]), + HTTPRes. make_auth_header(Host, MatrixServer, Method, URI, Content) -> Origin = mod_matrix_gw_opt:matrix_domain(Host), diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 6ceff2141..1cb0d892b 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -29,11 +29,14 @@ %% API -export([start_link/2, supervisor/1, create_db/0, - get_room_pid/2, join/5, process_pdu/3, + get_room_pid/2, join_direct/5, process_pdu/3, get_missing_events/7, get_state_ids/4, get_rooms_list/0, get_event/3, make_join/4, send_join/5, + create_new_room/3, room_add_event/3, binary_to_room_version/1, + parse_user_id/1, + send_muc_invite/6, escape/1, unescape/1, route/1]). @@ -68,18 +71,28 @@ json :: #{atom() | binary() => misc:json_value()}, state_map}). +-record(direct, + {local_user :: jid() | undefined, + remote_user :: binary() | undefined, + client_state}). + +-record(multi, + {users :: #{}}). + +-record(multi_user, + {join_ts :: integer()}). + -record(data, {host :: binary(), - local_user :: jid() | undefined, - remote_user :: binary() | undefined, - remote_servers = #{}, + kind :: #direct{} | undefined, room_id :: binary(), + room_jid :: jid(), room_version :: #room_version{}, events = #{}, latest_events = sets:new([{version, 2}]), nonlatest_events = sets:new([{version, 2}]), - outgoing_txns = #{}, - client_state}). + event_queue = treap:empty(), + outgoing_txns = #{}}). -define(ROOM_CREATE, <<"m.room.create">>). -define(ROOM_MEMBER, <<"m.room.member">>). @@ -90,6 +103,7 @@ -define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>). -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). +-define(MAX_TXN_RETRIES, 5). %%%=================================================================== %%% API @@ -148,23 +162,85 @@ get_existing_room_pid(_Host, RoomID) -> {ok, Pid} end. -join(Host, MatrixServer, RoomID, Sender, UserID) -> +join_direct(Host, MatrixServer, RoomID, Sender, UserID) -> case get_room_pid(Host, RoomID) of {ok, Pid} -> - gen_statem:cast(Pid, {join, MatrixServer, RoomID, Sender, UserID}); + gen_statem:cast(Pid, {join_direct, MatrixServer, RoomID, Sender, UserID}); {error, _} = Error -> Error end. +route(#presence{from = From, to = #jid{luser = <<$!, _/binary>>} = To, + type = Type} = Packet) -> + case room_id_from_xmpp(To#jid.luser) of + {ok, RoomID} -> + Host = ejabberd_config:get_myname(), + case From#jid.lserver of + Host -> + case Type of + available -> + case get_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:cast(Pid, {join, From, Packet}); + {error, _} = Error -> + ?DEBUG("join failed ~p", [{From, To, Error}]), + ok + end; + unavailable -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:cast(Pid, {leave, From}); + _ -> + ok + end; + _ -> + ok + end; + _ -> + ok + end; + error -> + ok + end; +route(#message{from = From, to = #jid{luser = <<$!, _/binary>>} = To, + type = groupchat, + body = Body}) -> + case room_id_from_xmpp(To#jid.luser) of + {ok, RoomID} -> + Host = ejabberd_config:get_myname(), + case From#jid.lserver of + Host -> + case user_id_from_jid(From, Host) of + {ok, UserID} -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + Text = xmpp:get_text(Body), + JSON = + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>}, + <<"sender">> => UserID, + <<"type">> => ?ROOM_MESSAGE}, + gen_statem:cast(Pid, {add_event, JSON}), + ok; + _ -> + ok + end; + error -> + ok + end; + _ -> + ok + end; + error -> + ok + end; route(#message{from = From, to = To, body = Body} = _Pkt) -> - case binary:split(To#jid.luser, <<"%">>) of - [EscU, EscS] -> - U = unescape(EscU), - S = unescape(EscS), - ToMatrixID = <<$@, U/binary, $:, S/binary>>, + Host = ejabberd_config:get_myname(), + case user_id_from_jid(To, Host) of + {ok, ToMatrixID} -> Key = {{From#jid.luser, From#jid.lserver}, ToMatrixID}, Text = xmpp:get_text(Body), - Host = ejabberd_config:get_myname(), case mnesia:dirty_read(matrix_direct, Key) of [#matrix_direct{room_id = RoomID}] -> ?DEBUG("msg ~p~n", [{RoomID, From, ToMatrixID, Text}]), @@ -264,7 +340,7 @@ route(#message{from = From, to = To, body = Body} = _Pkt) -> ok end end; - _ -> + error -> ok end; route(_) -> @@ -333,6 +409,26 @@ send_join(Host, Origin, RoomID, EventID, JSON) -> Error end. +create_new_room(Host, XMPPID, MatrixID) -> + RoomID = new_room_id(), + case get_room_pid(Host, RoomID) of + {ok, Pid} -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + gen_statem:cast(Pid, {create, MatrixServer, RoomID, + XMPPID, MatrixID}), + {ok, RoomID}; + {error, _} = Error -> + Error + end. + +room_add_event(Host, RoomID, Event) -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + gen_statem:call(Pid, {add_event, Event}); + {error, _} -> + {error, room_not_found} + end. + %%%=================================================================== %%% gen_statem callbacks %%%=================================================================== @@ -347,12 +443,16 @@ send_join(Host, Origin, RoomID, EventID, JSON) -> %%-------------------------------------------------------------------- -spec init(Args :: term()) -> gen_statem:init_result(term()). init([Host, RoomID]) -> + ServiceHost = mod_matrix_gw_opt:host(Host), + {ok, RID} = room_id_to_xmpp(RoomID), + RoomJID = jid:make(RID, ServiceHost), mnesia:dirty_write( #matrix_room{room_id = RoomID, pid = self()}), {ok, state_name, #data{host = Host, room_id = RoomID, + room_jid = RoomJID, room_version = binary_to_room_version(<<"9">>)}}. %%-------------------------------------------------------------------- @@ -484,7 +584,7 @@ handle_event({call, From}, ?INFO_MSG("failed make_join: ~p", [{Class, Reason, ST}]), {keep_state, Data, [{reply, From, {error, Reason}}]} end; -handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> +handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, Data) -> Host = Data#data.host, %% TODO: check if there is another solution to "You are not invited to this room" and not receiving the first messages in the room timer:sleep(1000), @@ -536,14 +636,17 @@ handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> RoomID, EventID], [], Event4, - [{timeout, 5000}], + [{connect_timeout, 5000}, + {timeout, 60000}], [{sync, true}, {body_format, binary}]), ?DEBUG("send_join ~p~n", [SendJoinRes]), - process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, - Data#data{local_user = UserJID, - remote_user = Sender, - room_version = RoomVersion}) + process_send_join_res( + MatrixServer, SendJoinRes, RoomVersion, + Data#data{ + kind = #direct{local_user = UserJID, + remote_user = Sender}, + room_version = RoomVersion}) end; _JSON -> ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), @@ -561,6 +664,172 @@ handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> ?INFO_MSG("bad join user id: ~p", [{UserID, UserJID}]), {stop, normal} end; +handle_event(cast, {join, UserJID, Packet}, _State, Data) -> + Host = Data#data.host, + {LUser, LServer, LResource} = jid:tolower(UserJID), + case Data#data.kind of + #multi{users = #{{LUser, LServer} := #{LResource := _}}} -> + {keep_state_and_data, []}; + #multi{} = Kind -> + case user_id_from_jid(UserJID, Host) of + {ok, UserID} -> + JoinTS = erlang:system_time(millisecond), + JSON = #{<<"content">> => + #{<<"membership">> => <<"join">>}, + <<"sender">> => UserID, + <<"state_key">> => UserID, + <<"type">> => ?ROOM_MEMBER}, + Users = Kind#multi.users, + Resources = + case Users of + #{{LUser, LServer} := Rs} -> Rs; + _ -> #{} + end, + Data2 = + Data#data{ + kind = + Kind#multi{ + users = + Users#{{LUser, LServer} => + Resources#{LResource => + #multi_user{join_ts = JoinTS}}}}}, + {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; + error -> + ?INFO_MSG("bad join user id: ~p", [UserJID]), + {keep_state_and_data, []} + end; + #direct{} -> + {keep_state_and_data, []}; + _ -> + Lang = xmpp:get_lang(Packet), + case user_id_from_jid(UserJID, Host) of + {ok, UserID} -> + %% TODO: async + RoomID = Data#data.room_id, + {ok, MatrixServer} = + case binary:split(RoomID, <<":">>) of + [_, MS] -> {ok, MS}; + _ -> error + end, + MakeJoinRes = + mod_matrix_gw:send_request( + Host, get, MatrixServer, + [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, + RoomID, UserID], + [{<<"ver">>, <<"9">>}, + {<<"ver">>, <<"10">>}, + {<<"ver">>, <<"11">>}], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("make_join ~p~n", [MakeJoinRes]), + case MakeJoinRes of + {ok, {{_, 200, _}, _Headers, Body}} -> + try misc:json_decode(Body) of + #{<<"event">> := Event, + <<"room_version">> := SRoomVersion} -> + case binary_to_room_version(SRoomVersion) of + false -> + ?DEBUG("unsupported room version on make_join: ~p", [MakeJoinRes]), + {stop, normal}; + #room_version{} = RoomVersion -> + JoinTS = erlang:system_time(millisecond), + Origin = mod_matrix_gw_opt:matrix_domain(Host), + Event2 = + Event#{<<"origin">> => Origin, + <<"origin_server_ts">> => JoinTS}, + CHash = mod_matrix_gw:content_hash(Event2), + Event3 = + Event2#{<<"hashes">> => + #{<<"sha256">> => + mod_matrix_gw:base64_encode(CHash)}}, + Event4 = mod_matrix_gw:sign_event(Host, Event3, RoomVersion), + EventID = mod_matrix_gw:get_event_id(Event4, RoomVersion), + SendJoinRes = + mod_matrix_gw:send_request( + Data#data.host, put, MatrixServer, + [<<"_matrix">>, <<"federation">>, + <<"v2">>, <<"send_join">>, + RoomID, EventID], + [], + Event4, + [{connect_timeout, 5000}, + {timeout, 60000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("send_join ~p~n", [SendJoinRes]), + process_send_join_res( + MatrixServer, SendJoinRes, RoomVersion, + Data#data{ + kind = + #multi{users = + #{{LUser, LServer} => + #{LResource => #multi_user{join_ts = JoinTS}}}}, + room_version = RoomVersion}) + end; + _JSON -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + Txt = <<"received bad JSON on make_join">>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} + catch + _:_ -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + Txt = <<"received bad JSON on make_join">>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} + end; + {ok, {{_, 400, _}, _Headers, Body}} -> + ?DEBUG("failed make_join: ~p", [MakeJoinRes]), + Txt = <<"make_join failed: ", Body/binary>>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal}; + _ -> + ?DEBUG("failed make_join: ~p", [MakeJoinRes]), + Txt = "make_join failed", + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} + end; + error -> + ?INFO_MSG("bad join user id: ~p", [UserJID]), + Txt = <<"bad user id">>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} + end + end; +handle_event(cast, {leave, UserJID}, _State, Data) -> + Host = Data#data.host, + {LUser, LServer, LResource} = jid:tolower(UserJID), + case Data#data.kind of + #multi{users = #{{LUser, LServer} := #{LResource := _} = Resources} = Users} -> + Resources2 = maps:remove(LResource, Resources), + if + Resources2 == #{} -> + Users2 = maps:remove({LUser, LServer}, Users), + Kind = (Data#data.kind)#multi{users = Users2}, + Data2 = Data#data{kind = Kind}, + {ok, UserID} = user_id_from_jid(UserJID, Host), + JSON = #{<<"content">> => + #{<<"membership">> => <<"leave">>}, + <<"sender">> => UserID, + <<"state_key">> => UserID, + <<"type">> => ?ROOM_MEMBER}, + {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; + true -> + Users2 = Users#{{LUser, LServer} => Resources2}, + Kind = (Data#data.kind)#multi{users = Users2}, + Data2 = Data#data{kind = Kind}, + {keep_state, Data2, []} + end; + _ -> + {keep_state_and_data, []} + end; handle_event(cast, {create, _MatrixServer, RoomID, LocalUserID, RemoteUserID}, _State, Data) -> Host = Data#data.host, case user_id_to_jid(LocalUserID, Data) of @@ -568,25 +837,37 @@ handle_event(cast, {create, _MatrixServer, RoomID, LocalUserID, RemoteUserID}, _ mnesia:dirty_write( #matrix_direct{local_remote = {{UserJID#jid.luser, UserJID#jid.lserver}, RemoteUserID}, room_id = RoomID}), - {keep_state, Data#data{local_user = UserJID, - remote_user = RemoteUserID}, []}; + {keep_state, + Data#data{kind = #direct{local_user = UserJID, + remote_user = RemoteUserID}}, []}; UserJID -> ?INFO_MSG("bad create user id: ~p", [{LocalUserID, UserJID}]), {stop, normal} end; handle_event(cast, {add_event, JSON}, _State, Data) -> try - Data2 = add_event(JSON, Data), + {Data2, _Event} = add_event(JSON, Data), {keep_state, Data2, [{next_event, internal, update_client}]} catch Class:Reason:ST -> ?INFO_MSG("failed add_event: ~p", [{Class, Reason, ST}]), {keep_state, Data, []} end; +handle_event({call, From}, {add_event, JSON}, _State, Data) -> + try + {Data2, Event} = add_event(JSON, Data), + {keep_state, Data2, [{reply, From, {ok, Event#event.id}}, + {next_event, internal, update_client}]} + catch + Class:Reason:ST -> + ?INFO_MSG("failed add_event: ~p", [{Class, Reason, ST}]), + {keep_state, Data, []} + end; handle_event(cast, Msg, State, Data) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {next_state, State, Data, []}; -handle_event(internal, update_client, _State, Data) -> +handle_event(internal, update_client, _State, + #data{kind = #direct{local_user = JID}} = Data) -> try case update_client(Data) of {ok, Data2} -> @@ -595,7 +876,6 @@ handle_event(internal, update_client, _State, Data) -> ?INFO_MSG("leaving ~p: ~p", [Data#data.room_id, LeaveReason]), Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, JSON = #{<<"content">> => #{<<"membership">> => <<"leave">>}, @@ -609,13 +889,36 @@ handle_event(internal, update_client, _State, Data) -> catch Class:Reason:ST -> ?INFO_MSG("failed update_client: ~p", [{Class, Reason, ST}]), - {keep_state, Data, []} + {keep_state_and_data, []} end; +handle_event(internal, update_client, _State, + #data{kind = #multi{}} = Data) -> + try + case update_client(Data) of + {ok, Data2} -> + {keep_state, Data2, []}; + stop -> + {stop, normal} + end + catch + Class:Reason:ST -> + ?INFO_MSG("failed update_client: ~p", [{Class, Reason, ST}]), + {keep_state_and_data, []} + end; +handle_event(internal, update_client, _State, #data{kind = undefined}) -> + {keep_state_and_data, []}; handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) -> + ?DEBUG("send_txn_res ~p", [{RequestID, TxnID, Server, Res}]), case Data#data.outgoing_txns of - #{Server := {{RequestID, TxnID, _Events}, Queue}} -> - case Res of - {{_, 200, _}, _Headers, _Body} -> + #{Server := {{RequestID, TxnID, _Events, Count}, Queue}} -> + Done = + case Res of + {{_, 200, _}, _Headers, _Body} -> true; + _ when Count < ?MAX_TXN_RETRIES -> false; + _ -> true + end, + case Done of + true -> Data2 = case Queue of [] -> @@ -625,8 +928,7 @@ handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) send_new_txn(lists:reverse(Queue), Server, Data) end, {keep_state, Data2, []}; - _ -> - %% TODO + false -> erlang:send_after(30000, self(), {resend_txn, Server}), {keep_state, Data, []} end; @@ -635,8 +937,8 @@ handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) end; handle_event(info, {resend_txn, Server}, _State, Data) -> case Data#data.outgoing_txns of - #{Server := {{_RequestID, TxnID, Events}, Queue}} -> - Data2 = send_txn(TxnID, Events, Server, Queue, Data), + #{Server := {{_RequestID, TxnID, Events, Count}, Queue}} -> + Data2 = send_txn(TxnID, Events, Server, Count + 1, Queue, Data), {keep_state, Data2, []}; _ -> {keep_state, Data, []} @@ -661,11 +963,12 @@ terminate(Reason, _State, Data) -> #matrix_room{room_id = Data#data.room_id, pid = self()}), %% TODO: wait for messages - case Data#data.local_user of - #jid{} = LocalUserJID -> + case Data#data.kind of + #direct{local_user = #jid{} = LocalUserJID, + remote_user = RemoteUser} -> mnesia:dirty_delete_object( #matrix_direct{local_remote = {{LocalUserJID#jid.luser, LocalUserJID#jid.lserver}, - Data#data.remote_user}, + RemoteUser}, room_id = Data#data.room_id}); _ -> ok @@ -713,13 +1016,20 @@ process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> JSONState), Event = json_to_event(JSONEvent, RoomVersion), ?DEBUG("send_join res: ~p~n", [JSON]), - lists:foreach( - fun(E) -> - case check_event_sig_and_hash(Data#data.host, E) of - {ok, _} -> ok; - {error, Error} -> error(Error) - end - end, [Event] ++ AuthChain ++ State), + case Data#data.kind of + #multi{} -> + %% TODO: do check_event_sig_and_hash, but faster + ok; + _ -> + lists:foreach( + fun(E) -> + ?DEBUG("send_join res check ~p~n", [E]), + case check_event_sig_and_hash(Data#data.host, E) of + {ok, _} -> ok; + {error, Error} -> error(Error) + end + end, [Event] ++ AuthChain ++ State) + end, CreateEvents = lists:filter( fun(#event{type = ?ROOM_CREATE, @@ -756,20 +1066,20 @@ process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> end; _ -> ?DEBUG("bad create event: ~p", [CreateEvents]), - {keep_state, Data, []} + {stop, normal, Data} end end catch error:{invalid_signature, EventID} -> ?INFO_MSG("failed signature check on event ~p", [EventID]), - {keep_state, Data, []}; + {stop, normal, Data}; Class:Reason:ST -> ?INFO_MSG("failed send_join: ~p", [{Class, Reason, ST}]), - {keep_state, Data, []} + {stop, normal, Data} end; _ -> ?DEBUG("failed send_join: ~p", [SendJoinRes]), - {keep_state, Data, []} + {stop, normal, Data} end. process_send_join_res2(MatrixServer, AuthChain, Event, State, Data) -> @@ -813,8 +1123,11 @@ process_send_join_res2(MatrixServer, AuthChain, Event, State, Data) -> {body_format, binary}, {receiver, fun({_, Res}) -> - process_missing_events_res(Host, MatrixServer, Pid, RoomID, RoomVersion, - {ok, Res}) + spawn(fun() -> + process_missing_events_res( + Host, MatrixServer, Pid, RoomID, RoomVersion, + {ok, Res}) + end) end}]), Data3. @@ -1364,6 +1677,7 @@ fill_event(JSON, Data) -> (maps:get(EID, Data#data.events))#event.depth end, PrevEvents)]), Depth2 = min(Depth + 1, ?MAX_DEPTH), + ?DEBUG("fill ~p", [{PrevEvents, Data#data.events}]), StateMaps = lists:map( fun(EID) -> @@ -1417,7 +1731,7 @@ add_event(JSON, Data) -> case check_event_auth(Event2, Data) of true -> %%TODO: soft fail - store_event(Event2, Data); + {store_event(Event2, Data), Event2}; false -> error({event_auth_error, Event2#event.id}) end. @@ -1434,23 +1748,38 @@ store_event(Event, Data) -> error -> ?DEBUG("store ~p~n", [Event#event.id]), Data2 = notify_event(Event, Data), - LatestEvents = - lists:foldl(fun(E, Acc) -> sets:del_element(E, Acc) end, Data2#data.latest_events, - Event#event.prev_events), - NonLatestEvents = - lists:foldl(fun(E, Acc) -> sets:add_element(E, Acc) end, Data2#data.nonlatest_events, - Event#event.prev_events), - LatestEvents2 = - case maps:is_key(Event#event.id, NonLatestEvents) of - true -> - LatestEvents; - false -> - LatestEvents#{Event#event.id => []} + {LatestEvents, NonLatestEvents} = + case Event of + #event{state_map = undefined} -> + {Data2#data.latest_events, Data2#data.nonlatest_events}; + _ -> + SeenEvents = Event#event.prev_events ++ Event#event.auth_events, + LatestEs = + lists:foldl(fun(E, Acc) -> sets:del_element(E, Acc) end, Data2#data.latest_events, + SeenEvents), + NonLatestEs = + lists:foldl(fun(E, Acc) -> sets:add_element(E, Acc) end, Data2#data.nonlatest_events, + SeenEvents), + LatestEs2 = + case maps:is_key(Event#event.id, NonLatestEs) of + true -> + LatestEs; + false -> + LatestEs#{Event#event.id => []} + end, + %%?DEBUG("latest ~p~n", [{LatestEvents2, NonLatestEvents}]), + {LatestEs2, NonLatestEs} end, - ?DEBUG("latest ~p~n", [{LatestEvents2, NonLatestEvents}]), + EventQueue = + treap:insert( + Event#event.id, + {erlang:monotonic_time(micro_seconds), + erlang:unique_integer([monotonic])}, + [], Data2#data.event_queue), Data2#data{events = Events#{Event#event.id => Event}, - latest_events = LatestEvents2, - nonlatest_events = NonLatestEvents} + latest_events = LatestEvents, + nonlatest_events = NonLatestEvents, + event_queue = EventQueue} end. simple_toposort(Events) -> @@ -1470,7 +1799,8 @@ simple_toposort(Events) -> simple_toposort_dfs(EventID, {Res, Used}, Events) -> case maps:find(EventID, Events) of error -> - error({unknown_event, EventID}); + %error({unknown_event, EventID}); + {Res, Used}; {ok, Event} -> Used2 = Used#{EventID => gray}, {Res8, Used8} = @@ -1665,7 +1995,8 @@ request_room_state(Host, Origin, _Pid, RoomID, RoomVersion, Event) -> RoomID], [{<<"event_id">>, Event#event.id}], none, - [{timeout, 5000}], + [{connect_timeout, 5000}, + {timeout, 60000}], [{sync, true}, {body_format, binary}]), case Res of @@ -1981,6 +2312,7 @@ lexicographic_toposort_prepare(EventID, Used, EventSet, Data) -> Used4. lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> + %?DEBUG("toposort ~p", [{gb_trees:to_list(Current), IncomingCnt, Res}]), case gb_trees:is_empty(Current) of true -> case maps:size(IncomingCnt) of @@ -1992,23 +2324,27 @@ lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> false -> {{_, _, EventID}, _, Current2} = gb_trees:take_smallest(Current), Event = maps:get(EventID, Data#data.events), - IncomingCnt2 = + %?DEBUG("toposort ev ~p", [Event]), + {IncomingCnt2, Current3} = lists:foldl( - fun(EID, Acc) -> - case maps:is_key(EID, Acc) of + fun(EID, {InCnt, Cur} = Acc) -> + case maps:is_key(EID, InCnt) of true -> - C = maps:get(EID, Acc) - 1, + C = maps:get(EID, InCnt) - 1, case C of 0 -> - maps:remove(EID, Acc); + E = maps:get(EID, Data#data.events), + PowerLevel = get_sender_power_level(EID, Data), + Cur2 = gb_trees:enter({-PowerLevel, E#event.origin_server_ts, EID}, [], Cur), + {maps:remove(EID, InCnt), Cur2}; _ -> - maps:put(EID, C, Acc) + {maps:put(EID, C, InCnt), Cur} end; false -> Acc end - end, IncomingCnt, Event#event.auth_events), - lexicographic_toposort_loop(Current2, IncomingCnt2, [EventID | Res], Data) + end, {IncomingCnt, Current2}, Event#event.auth_events), + lexicographic_toposort_loop(Current3, IncomingCnt2, [EventID | Res], Data) end. get_sender_power_level(EventID, Data) -> @@ -2176,82 +2512,16 @@ check_event_content_hash(Event) -> false end. -notify_event(#event{sender = Sender, - json = #{<<"test">> := true}} = Event, - Data) -> - case user_id_to_jid(Sender, Data) of - #jid{} = SenderJID -> - LSenderServer = SenderJID#jid.lserver, - UserJID = Data#data.local_user, - LUserServer = UserJID#jid.lserver, - case LSenderServer of - LUserServer -> - %RemoteServers = maps:keys(Data#data.remote_servers), - RemoteServers = get_remote_servers(Data), - lists:foldl( - fun(Server, DataAcc) -> - case DataAcc#data.outgoing_txns of - #{Server := {T, Queue}} -> - Queue2 = [Event | Queue], - DataAcc#data{outgoing_txns = - maps:put(Server, {T, Queue2}, - DataAcc#data.outgoing_txns)}; - _ -> - send_new_txn([Event], Server, DataAcc) - end - end, Data, RemoteServers); - _ -> - Data - end; - error -> - Data - end; -notify_event(#event{type = ?ROOM_MESSAGE, sender = Sender, - json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, - <<"body">> := Body}}} = Event, - Data) -> - case user_id_to_jid(Sender, Data) of - #jid{} = SenderJID -> - LSenderJID = jid:tolower(SenderJID), - UserJID = Data#data.local_user, - LUserJID = jid:tolower(UserJID), - case LSenderJID of - LUserJID -> - %RemoteServers = maps:keys(Data#data.remote_servers), - RemoteServers = get_remote_servers(Data), - lists:foldl( - fun(Server, DataAcc) -> - case DataAcc#data.outgoing_txns of - #{Server := {T, Queue}} -> - Queue2 = [Event | Queue], - DataAcc#data{outgoing_txns = - maps:put(Server, {T, Queue2}, - DataAcc#data.outgoing_txns)}; - _ -> - send_new_txn([Event], Server, DataAcc) - end - end, Data, RemoteServers); - _ -> - RoomID = Data#data.room_id, - Msg = #message{from = SenderJID, - to = UserJID, - type = chat, - body = [#text{data = Body}], - sub_els = [#xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, <<"p1:matrix">>}, - {<<"room_id">>, RoomID}]}] - }, - ejabberd_router:route(Msg), - Data - end; - error -> - Data - end; -notify_event(#event{type = ?ROOM_MEMBER, - state_key = StateKey, - sender = Sender, - json = #{<<"content">> := #{<<"membership">> := <<"invite">>}}} = Event, - Data) -> +notify_event(Event, Data) -> + Data2 = notify_event_matrix(Event, Data), + notify_event_xmpp(Event, Data2). + +notify_event_matrix( + #event{type = ?ROOM_MEMBER, + state_key = StateKey, + sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"invite">>}}} = Event, + #data{kind = #direct{}} = Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), case mod_matrix_gw:get_id_domain_exn(StateKey) of @@ -2286,15 +2556,229 @@ notify_event(#event{type = ?ROOM_MEMBER, ?DEBUG("send invite ~p~n", [InviteRes]), Data end; -notify_event(_Event, Data) -> +notify_event_matrix(#event{sender = Sender} = Event, + Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + %RemoteServers = maps:keys(Data#data.remote_servers), + RemoteServers = get_remote_servers(Data), + Host = Data#data.host, + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + lists:foldl( + fun(Server, DataAcc) -> + case Server of + MatrixServer -> + %% TODO + %case parse_user_id(Data#data.remote_user) of + % {ok, U, MatrixServer} -> + % mod_matrix_gw_c2s:notify( + % Host, U, Event); + % _ -> + % ok + %end, + DataAcc; + _ -> + case SenderJID#jid.lserver of + Host -> + case DataAcc#data.outgoing_txns of + #{Server := {T, Queue}} -> + Queue2 = [Event | Queue], + DataAcc#data{ + outgoing_txns = + maps:put(Server, {T, Queue2}, + DataAcc#data.outgoing_txns)}; + _ -> + send_new_txn([Event], Server, DataAcc) + end; + _ -> + Data + end + end + end, Data, RemoteServers); + error -> + Data + end. + +notify_event_xmpp( + #event{type = ?ROOM_MESSAGE, sender = Sender, + json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, + <<"body">> := Body}}}, + #data{kind = #direct{local_user = UserJID}} = Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + LSenderJID = jid:tolower(SenderJID), + LUserJID = jid:tolower(UserJID), + case LSenderJID of + LUserJID -> + Data; + _ -> + RoomID = Data#data.room_id, + Msg = #message{from = SenderJID, + to = UserJID, + type = chat, + body = [#text{data = Body}], + sub_els = [#xmlel{name = <<"x">>, + attrs = [{<<"xmlns">>, <<"p1:matrix">>}, + {<<"room_id">>, RoomID}]}] + }, + ejabberd_router:route(Msg), + Data + end; + error -> + Data + end; +notify_event_xmpp( + #event{type = ?ROOM_MESSAGE, sender = Sender, + json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, + <<"body">> := Body}, + <<"origin_server_ts">> := OriginTS}}, + #data{kind = #multi{users = Users}} = Data) -> + case Sender of + <<$@, SenderUser/binary>> -> + ?DEBUG("notify xmpp ~p", [Users]), + From = jid:replace_resource(Data#data.room_jid, SenderUser), + maps:fold( + fun({LUser, LServer}, Resources, ok) -> + maps:fold( + fun(LResource, #multi_user{join_ts = JoinTS}, ok) + when JoinTS =< OriginTS -> + UserJID = jid:make(LUser, LServer, LResource), + Msg = #message{from = From, + to = UserJID, + type = groupchat, + body = [#text{data = Body}] + }, + TimeStamp = misc:usec_to_now(OriginTS * 1000), + TSMsg = misc:add_delay_info( + Msg, Data#data.room_jid, TimeStamp), + ejabberd_router:route(TSMsg); + (_, _, _) -> ok + end, ok, Resources) + end, ok, Users), + Data; + _ -> + Data + end; +notify_event_xmpp( + #event{type = ?ROOM_MEMBER, sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"join">>}, + <<"origin_server_ts">> := OriginTS}} = Event, + #data{kind = #multi{users = Users}} = Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + <<$@, SenderUser/binary>> = Sender, + From = jid:replace_resource(Data#data.room_jid, SenderUser), + maps:fold( + fun({LUser, LServer}, Resources, ok) -> + maps:fold( + fun(LResource, #multi_user{join_ts = JoinTS}, ok) + when JoinTS =< OriginTS -> + case jid:tolower(SenderJID) of + {LUser, LServer, _} -> + send_initial_presences( + SenderJID, Event, Data); + _ -> + ok + end, + UserJID = jid:make(LUser, LServer, LResource), + Pres = #presence{from = From, + to = UserJID, + type = available + }, + ejabberd_router:route(Pres); + (_, _, _) -> ok + end, ok, Resources) + end, ok, Users), + Data; + error -> + Data + end; +notify_event_xmpp( + #event{type = ?ROOM_MEMBER, + state_key = StateKey, + json = #{<<"content">> := #{<<"membership">> := Membership}, + <<"origin_server_ts">> := OriginTS}}, + #data{kind = #multi{users = Users}} = Data) + when Membership == <<"leave">>; + Membership == <<"ban">> -> + case StateKey of + <<$@, RUser/binary>> -> + From = jid:replace_resource(Data#data.room_jid, RUser), + maps:fold( + fun({LUser, LServer}, Resources, ok) -> + maps:fold( + fun(LResource, #multi_user{join_ts = JoinTS}, ok) + when JoinTS =< OriginTS -> + UserJID = jid:make(LUser, LServer, LResource), + Pres = #presence{from = From, + to = UserJID, + type = unavailable + }, + ejabberd_router:route(Pres); + (_, _, _) -> ok + end, ok, Resources) + end, ok, Users), + case user_id_to_jid(StateKey, Data) of + #jid{} = RJID -> + US = {RJID#jid.luser, RJID#jid.lserver}, + case Users of + #{US := Resources} -> + JoinTS = + maps:fold( + fun(_, #multi_user{join_ts = TS}, Acc) -> + max(Acc, TS) + end, 0, Resources), + if + JoinTS =< OriginTS -> + Users2 = maps:remove(US, Users), + Data#data{ + kind = (Data#data.kind)#multi{ + users = Users2}}; + true -> + Data + end; + _ -> + Data + end; + error -> + Data + end; + error -> + Data + end; +notify_event_xmpp(_Event, Data) -> Data. +send_initial_presences(JID, Event, Data) -> + ?DEBUG("send_initial_presences ~p", [{JID, Event}]), + maps:fold( + fun({?ROOM_MEMBER, _}, EID, ok) -> + case maps:find(EID, Data#data.events) of + {ok, #event{ + sender = <<$@, SenderUser/binary>>, + json = #{<<"content">> := + #{<<"membership">> := <<"join">>}}}} -> + From = jid:replace_resource(Data#data.room_jid, SenderUser), + Pres = #presence{from = From, + to = JID, + type = available + }, + ejabberd_router:route(Pres), + ok; + _ -> + ok + end; + (_, _, ok) -> + ok + end, ok, Event#event.state_map). + + send_new_txn(Events, Server, Data) -> TxnID = p1_rand:get_string(), - send_txn(TxnID, Events, Server, [], Data). + send_txn(TxnID, Events, Server, 1, [], Data). -send_txn(TxnID, Events, Server, Queue, Data) -> - ?DEBUG("send txn ~p~n", [TxnID]), +send_txn(TxnID, Events, Server, Count, Queue, Data) -> + ?DEBUG("send txn ~p~n", [{TxnID, Server}]), Host = Data#data.host, Origin = mod_matrix_gw_opt:matrix_domain(Host), PDUs = @@ -2307,6 +2791,7 @@ send_txn(TxnID, Events, Server, Queue, Data) -> Self = self(), Receiver = fun({RequestID, Res}) -> + ?DEBUG("send_txn_res ~p", [{RequestID, Res}]), Self ! {send_txn_res, RequestID, TxnID, Server, Res} end, {ok, RequestID} = @@ -2321,7 +2806,7 @@ send_txn(TxnID, Events, Server, Queue, Data) -> [{sync, false}, {receiver, Receiver}]), Data#data{outgoing_txns = - maps:put(Server, {{RequestID, TxnID, Events}, Queue}, + maps:put(Server, {{RequestID, TxnID, Events, Count}, Queue}, Data#data.outgoing_txns)}. do_get_missing_events(Origin, EarliestEvents, LatestEvents, Limit, MinDepth, Data) -> @@ -2447,7 +2932,7 @@ get_remote_servers(Data) -> maps:fold( fun(EventID, _, Acc) -> case maps:find(EventID, Data#data.events) of - {ok, Event} -> + {ok, Event} when is_map(Event#event.state_map) -> maps:fold( fun({?ROOM_MEMBER, UserID}, EID, Acc2) -> Server = mod_matrix_gw:get_id_domain_exn(UserID), @@ -2467,9 +2952,7 @@ get_remote_servers(Data) -> Acc end end, #{}, Data#data.latest_events), - MatrixServer = mod_matrix_gw_opt:matrix_domain(Data#data.host), - Servers2 = maps:remove(MatrixServer, Servers), - maps:keys(Servers2). + maps:keys(Servers). get_joined_users(Data) -> Users = @@ -2497,8 +2980,9 @@ get_joined_users(Data) -> end, #{}, Data#data.latest_events), maps:keys(Users). -user_id_to_jid(Str, Data) -> - Host = Data#data.host, +user_id_to_jid(Str, #data{} = Data) -> + user_id_to_jid(Str, Data#data.host); +user_id_to_jid(Str, Host) when is_binary(Host) -> ServerName = mod_matrix_gw_opt:matrix_domain(Host), case parse_user_id(Str) of {ok, U, ServerName} -> @@ -2512,6 +2996,19 @@ user_id_to_jid(Str, Data) -> error end. +user_id_from_jid(#jid{luser = U, lserver = Host}, Host) -> + ServerName = mod_matrix_gw_opt:matrix_domain(Host), + {ok, <<$@, U/binary, $:, ServerName/binary>>}; +user_id_from_jid(JID, _Host) -> + case binary:split(JID#jid.luser, <<"%">>) of + [EscU, EscS] -> + U = unescape(EscU), + S = unescape(EscS), + {ok, <<$@, U/binary, $:, S/binary>>}; + _ -> + error + end. + new_room_id() -> Host = ejabberd_config:get_myname(), Letters = <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ">>, @@ -2557,33 +3054,35 @@ compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}) -> {?ROOM_MEMBER, Sender}]. -update_client(#data{client_state = undefined, - remote_user = RemoteUserID} = Data) -> +update_client(#data{kind = #direct{client_state = undefined, + local_user = JID, + remote_user = RemoteUserID}} = Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, Users = get_joined_users(Data), case lists:member(LocalUserID, Users) of true -> case lists:delete(LocalUserID, Users) of [RemoteUserID] -> - {ok, Data#data{client_state = established}}; + {ok, Data#data{kind = (Data#data.kind)#direct{client_state = established}}}; [_] -> - {leave, unknown_remote_user, Data#data{client_state = leave}}; + {leave, unknown_remote_user, + Data#data{kind = (Data#data.kind)#direct{client_state = leave}}}; [] -> {ok, Data}; _ -> - {leave, too_many_users, Data#data{client_state = leave}} + {leave, too_many_users, + Data#data{kind = (Data#data.kind)#direct{client_state = leave}}} end; false -> {ok, Data} end; -update_client(#data{client_state = established, - remote_user = RemoteUserID} = Data) -> +update_client(#data{kind = #direct{client_state = established, + local_user = JID, + remote_user = RemoteUserID}} = Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, Users = get_joined_users(Data), case lists:member(LocalUserID, Users) of @@ -2592,13 +3091,73 @@ update_client(#data{client_state = established, true -> {ok, Data}; false -> - {leave, remote_user_left, Data#data{client_state = leave}} + {leave, remote_user_left, Data#data{kind = (Data#data.kind)#direct{client_state = leave}}} end; false -> stop end; -update_client(#data{client_state = leave}) -> - stop. +update_client(#data{kind = #direct{client_state = leave}}) -> + stop; +update_client(#data{kind = #multi{users = Users}} = Data) -> + ?DEBUG("update_client ~p", [Data#data.kind]), + if + Users == #{} -> + stop; + true -> + {ok, Data} + end. + + +send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) -> + case {user_id_to_jid(Sender, Host), user_id_to_jid(UserID, Host)} of + {#jid{} = SenderJID, #jid{lserver = Host} = UserJID} -> + process_pdu(Host, Origin, Event), + ServiceHost = mod_matrix_gw_opt:host(Host), + {ok, EscRoomID} = room_id_to_xmpp(RoomID), + RoomJID = jid:make(EscRoomID, ServiceHost), + Invite = #muc_invite{to = undefined, from = SenderJID}, + XUser = #muc_user{invites = [Invite]}, + Msg = #message{ + from = RoomJID, + to = UserJID, + sub_els = [XUser] + }, + ejabberd_router:route(Msg); + _ -> + ok + end. + +room_id_to_xmpp(RoomID) -> + case RoomID of + <<$!, Parts/binary>> -> + case binary:split(Parts, <<":">>) of + [R, S] -> + Len = 8 * size(R), + <> = R, + HR = integer_to_binary(IR, 16), + {ok, <<$!, HR/binary, $%, S/binary>>}; + _ -> error + end; + _ -> + error + end. + +room_id_from_xmpp(RID) -> + case RID of + <<$!, Parts/binary>> -> + case binary:split(Parts, <<"%">>) of + [R, S] -> + IR = binary_to_integer(R, 16), + Len = size(R) * 4, + RoomID = <>, + {ok, <<$!, RoomID/binary, $:, S/binary>>}; + _ -> error + end; + _ -> + error + end. + + escape(S) -> escape(S, <<>>). diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 533f250c5..3457607a0 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -337,50 +337,44 @@ handle_event(cast, {query, AuthParams, _Query, _JSON, _Request} = Msg, []} end; handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> - case HTTPResult of - {{_, 200, _}, _, SJSON} -> - try - JSON = misc:json_decode(SJSON), - ?DEBUG("key ~p~n", [JSON]), - #{<<"verify_keys">> := VerifyKeys} = JSON, - #{KeyID := KeyData} = VerifyKeys, - #{<<"key">> := SKey} = KeyData, - VerifyKey = mod_matrix_gw:base64_decode(SKey), - ?DEBUG("key ~p~n", [VerifyKey]), - ?DEBUG("check ~p~n", - [catch check_signature( - JSON, Data#data.matrix_server, - KeyID, VerifyKey)]), - true = check_signature( - JSON, Data#data.matrix_server, - KeyID, VerifyKey), - #{<<"valid_until_ts">> := ValidUntil} = JSON, - ValidUntil2 = - min(ValidUntil, - erlang:system_time(millisecond) + timer:hours(24 * 7)), - Keys = (Data#data.keys)#{KeyID => {ok, VerifyKey, ValidUntil2}}, - Froms = maps:get(KeyID, Data#data.key_queue, []), - KeyQueue = maps:remove(KeyID, Data#data.key_queue), - Data2 = Data#data{keys = Keys, - key_queue = KeyQueue}, - Replies = - lists:map( - fun(From) -> - {reply, From, {ok, VerifyKey, ValidUntil2}} - end, Froms), - ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), - {next_state, State, Data2, Replies} - catch - _:_ -> - %% TODO - Keys2 = (Data#data.keys)#{KeyID => error}, - {next_state, State, Data#data{keys = Keys2}, []} - end; - _ -> - %% TODO - Keys = (Data#data.keys)#{KeyID => error}, - {next_state, State, Data#data{keys = Keys}, []} - end; + KeyVal = + case HTTPResult of + {{_, 200, _}, _, SJSON} -> + try + JSON = misc:json_decode(SJSON), + ?DEBUG("key ~p~n", [JSON]), + #{<<"verify_keys">> := VerifyKeys} = JSON, + #{KeyID := KeyData} = VerifyKeys, + #{<<"key">> := SKey} = KeyData, + VerifyKey = mod_matrix_gw:base64_decode(SKey), + ?DEBUG("key ~p~n", [VerifyKey]), + ?DEBUG("check ~p~n", + [catch check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey)]), + true = check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey), + #{<<"valid_until_ts">> := ValidUntil} = JSON, + ValidUntil2 = + min(ValidUntil, + erlang:system_time(millisecond) + timer:hours(24 * 7)), + {ok, VerifyKey, ValidUntil2} + catch + _:_ -> + error + end; + _ -> + error + end, + Keys = (Data#data.keys)#{KeyID => KeyVal}, + Froms = maps:get(KeyID, Data#data.key_queue, []), + KeyQueue = maps:remove(KeyID, Data#data.key_queue), + Data2 = Data#data{keys = Keys, + key_queue = KeyQueue}, + Replies = lists:map(fun(From) -> {reply, From, KeyVal} end, Froms), + ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), + {next_state, State, Data2, Replies}; handle_event(cast, Msg, State, Data) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {next_state, State, Data, []}; From e58926592132b51b8532b907553eaf6760e6fe84 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 6 Dec 2024 21:56:02 +0300 Subject: [PATCH 0857/1302] Fix dialyzer errors --- src/mod_matrix_gw_room.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 1cb0d892b..90ecfec3f 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -76,15 +76,15 @@ remote_user :: binary() | undefined, client_state}). --record(multi, - {users :: #{}}). - -record(multi_user, {join_ts :: integer()}). +-record(multi, + {users :: #{{binary(), binary()} => #{binary() => #multi_user{}}}}). + -record(data, {host :: binary(), - kind :: #direct{} | undefined, + kind :: #direct{} | #multi{} | undefined, room_id :: binary(), room_jid :: jid(), room_version :: #room_version{}, @@ -790,7 +790,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> {stop, normal}; _ -> ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = "make_join failed", + Txt = <<"make_join failed">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), {stop, normal} @@ -2743,7 +2743,7 @@ notify_event_xmpp( error -> Data end; - error -> + _ -> Data end; notify_event_xmpp(_Event, Data) -> From f3c935d2e10c9aa69e1eb7007a4fb5795b33686a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Dec 2024 16:42:46 +0100 Subject: [PATCH 0858/1302] Fix some documentation syntax, add links to toplevel, modules and API --- src/ejabberd_admin.erl | 2 +- src/ejabberd_options_doc.erl | 58 ++++++++++++++++++++--------------- src/mod_admin_extra.erl | 22 ++++++------- src/mod_blocking.erl | 2 +- src/mod_conversejs.erl | 7 ++--- src/mod_delegation.erl | 2 +- src/mod_host_meta.erl | 3 +- src/mod_http_api.erl | 6 ++-- src/mod_http_upload.erl | 36 +++++++++++----------- src/mod_http_upload_quota.erl | 6 ++-- src/mod_mix.erl | 2 +- src/mod_mix_pam.erl | 2 +- src/mod_mqtt_bridge.erl | 29 ++++++++++++------ src/mod_muc.erl | 12 ++++---- src/mod_muc_admin.erl | 2 +- src/mod_offline.erl | 16 +++++----- src/mod_privacy.erl | 4 +-- src/mod_private.erl | 2 +- src/mod_shared_roster.erl | 2 +- 19 files changed, 116 insertions(+), 99 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index f326d05d1..fcfbb840a 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -297,7 +297,7 @@ get_commands_spec() -> longdesc = "This command can be run from any running " "node of the cluster, even the node to be removed. " "In the removed node, this command works only when " - "using ejabberdctl, not mod_http_api or other code that " + "using ejabberdctl, not _`mod_http_api`_ or other code that " "runs inside the same ejabberd node that will leave.", module = ?MODULE, function = leave_cluster, args_desc = ["Nodename of the node to kick from the cluster"], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 4e4bfe242..4a4ade0ed 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -403,7 +403,7 @@ doc() -> #{value => "true | false", note => "added in 23.10", desc => - ?T("Supplement check for user existence based on 'mod_last' data, for authentication " + ?T("Supplement check for user existence based on _`mod_last`_ data, for authentication " "methods that don't have a way to reliably tell if a user exists (like is the case for " "'jwt' and certificate based authentication). This helps with processing offline message " "for those users. The default value is 'true'.")}}, @@ -440,8 +440,8 @@ doc() -> desc => ?T("Full path to a file containing custom DH parameters " "to use for c2s connections. " - "Such a file could be created with the command \"openssl " - "dhparam -out dh.pem 2048\". If this option is not specified, " + "Such a file could be created with the command '\"openssl " + "dhparam -out dh.pem 2048\"'. If this option is not specified, " "2048-bit MODP Group with 256-bit Prime Order Subgroup will be " "used as defined in RFC5114 Section 2.3.")}}, {c2s_protocol_options, @@ -500,7 +500,7 @@ doc() -> "If set to 'auto', it builds the URL using a 'request_handler' " "already enabled, with encryption if available. " "If set to 'undefined', it builds the URL using " - "the deprecated _`captcha_host`_ + /captcha. " + "the deprecated _`captcha_host`_ '+ /captcha'. " "The default value is 'auto'.")}}, {certfiles, #{value => "[Path, ...]", @@ -595,17 +595,25 @@ doc() -> " transport.example.org:", " type: bare_source"]}, [{type, - #{value => "random | source | destination | bare_source | bare_destination", + #{value => ?T("Value"), desc => - ?T("How to deliver stanzas to connected components: " - "'random' - an instance is chosen at random; " - "'destination' - an instance is chosen by the full JID of " - "the packet's 'to' attribute; " - "'source' - by the full JID of the packet's 'from' attribute; " - "'bare_destination' - by the bare JID (without resource) " - "of the packet's 'to' attribute; " - "'bare_source' - by the bare JID (without resource) of the " - "packet's 'from' attribute is used. The default value is 'random'.")}}, + ?T("How to deliver stanzas to connected components. " + "The default value is 'random'. Possible values: ")}, + [{'- random', + #{desc => + ?T("an instance is chosen at random")}}, + {'- source', + #{desc => + ?T("by the full JID of the packet's 'from' attribute")}}, + {'- bare_destination', + #{desc => + ?T("by the bare JID (without resource) of the packet's 'to' attribute")}}, + {'- bare_source', + #{desc => + ?T("by the bare JID (without resource) of the packet's 'from' attribute is used")}}, + {'- destination', + #{desc => + ?T("an instance is chosen by the full JID of the packet's 'to' attribute")}}]}, {component_number, #{value => "2..1000", desc => @@ -881,20 +889,20 @@ doc() -> desc => ?T("The size (in bytes) of a log file to trigger rotation. " "If set to 'infinity', log rotation is disabled. " - "The default value is '10485760' (that is, 10 Mb).")}}, + "The default value is 10 Mb expressed in bytes: '10485760'.")}}, {log_burst_limit_count, #{value => ?T("Number"), note => "added in 22.10", desc => ?T("The number of messages to accept in " "`log_burst_limit_window_time` period before starting to " - "drop them. Default 500")}}, + "drop them. Default `500`")}}, {log_burst_limit_window_time, #{value => ?T("Number"), note => "added in 22.10", desc => ?T("The time period to rate-limit log messages " - "by. Defaults to 1 second.")}}, + "by. Defaults to `1` second.")}}, {log_modules_fully, #{value => "[Module, ...]", note => "added in 23.01", @@ -1032,7 +1040,7 @@ doc() -> ?T("Trigger OOM killer when some of the running Erlang processes " "have messages queue above this 'Size'. Note that " "such processes won't be killed if _`oom_killer`_ option is set " - "to 'false' or if 'oom_watermark' is not reached yet.")}}, + "to 'false' or if _`oom_watermark`_ is not reached yet.")}}, {oom_watermark, #{value => ?T("Percent"), desc => @@ -1055,7 +1063,7 @@ doc() -> note => "added in 20.12", desc => ?T("Specify the IPv4 address that will be used when establishing " - "an outgoing S2S IPv4 connection, for example \"127.0.0.1\". " + "an outgoing S2S IPv4 connection, for example '\"127.0.0.1\"'. " "The default value is 'undefined'.")}}, {outgoing_s2s_ipv6_address, #{value => "Address", @@ -1063,7 +1071,7 @@ doc() -> desc => ?T("Specify the IPv6 address that will be used when establishing " "an outgoing S2S IPv6 connection, for example " - "\"::FFFF:127.0.0.1\". The default value is 'undefined'.")}}, + "'\"::FFFF:127.0.0.1\"'. The default value is 'undefined'.")}}, {outgoing_s2s_port, #{value => "1..65535", desc => @@ -1232,8 +1240,8 @@ doc() -> desc => ?T("Full path to a file containing custom DH parameters " "to use for s2s connections. " - "Such a file could be created with the command \"openssl " - "dhparam -out dh.pem 2048\". If this option is not specified, " + "Such a file could be created with the command '\"openssl " + "dhparam -out dh.pem 2048\"'. If this option is not specified, " "2048-bit MODP Group with 256-bit Prime Order Subgroup will be " "used as defined in RFC5114 Section 2.3.")}}, {s2s_protocol_options, @@ -1396,7 +1404,7 @@ doc() -> #{value => ?T("Size"), desc => ?T("Number of connections to the SQL server that ejabberd will " - "open for each virtual host. The default value is 10. WARNING: " + "open for each virtual host. The default value is '10'. WARNING: " "for SQLite this value is '1' by default and it's not recommended " "to change it due to potential race conditions.")}}, {sql_port, @@ -1490,7 +1498,7 @@ doc() -> "possibly with masks. The default value is an empty list. " "Using this option you can know the real IP " "of the request, for admin purpose, or security configuration " - "(for example using 'mod_fail2ban'). IMPORTANT: The proxy MUST " + "(for example using _`mod_fail2ban`_). IMPORTANT: The proxy MUST " "be configured to set the 'X-Forwarded-For' header if you " "enable this option as, otherwise, the client can set it " "itself and as a result the IP value cannot be trusted for " @@ -1513,7 +1521,7 @@ doc() -> "balancer can be chosen for a specific ejabberd implementation " "while still providing a secure WebSocket connection. " "The default value is 'ignore'. An example value of the 'URL' is " - "\"https://test.example.org:8081\".")}}, + "'\"https://test.example.org:8081\"'.")}}, {websocket_ping_interval, #{value => "timeout()", desc => diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 4e644586d..4e203eb83 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -274,9 +274,9 @@ get_commands_spec() -> longdesc = "This command kicks the account sessions, " "sets a random password, and stores ban details in the " "account private storage. " - "This command requires mod_private to be enabled. " + "This command requires _`mod_private`_ to be enabled. " "Check also _`get_ban_details`_ API " - "and `_unban_account`_ API.", + "and _`unban_account`_ API.", module = ?MODULE, function = ban_account_v2, version = 2, note = "improved in 24.06", @@ -2326,22 +2326,22 @@ mod_doc() -> #{desc => [?T("This module provides additional administrative commands."), "", ?T("Details for some commands:"), "", - ?T("- 'ban_account':"), + ?T("_`ban_account`_ API:"), ?T("This command kicks all the connected sessions of the account " "from the server. It also changes their password to a randomly " "generated one, so they can't login anymore unless a server " "administrator changes their password again. It is possible to " "define the reason of the ban. The new password also includes " - "the reason and the date and time of the ban. See an example below."), - ?T("- 'pushroster': (and 'pushroster-all')"), + "the reason and the date and time of the ban. See an example below."), "", + ?T("_`push_roster`_ API (and _`push_roster_all`_ API):"), ?T("The roster file must be placed, if using Windows, on the " "directory where you installed ejabberd: " "`C:/Program Files/ejabberd` or similar. If you use other " "Operating System, place the file on the same directory where " - "the .beam files are installed. See below an example roster file."), - ?T("- 'srg_create':"), + "the .beam files are installed. See below an example roster file."), "", + ?T("_`srg_create`_ API:"), ?T("If you want to put a group Name with blank spaces, use the " - "characters \"\' and \'\" to define when the Name starts and " + "characters '\"\'' and '\'\"' to define when the Name starts and " "ends. See an example below.")], example => [{?T("With this configuration, vCards can only be modified with " @@ -2356,14 +2356,14 @@ mod_doc() -> " mod_admin_extra: {}", " mod_vcard:", " access_set: vcard_set"]}, - {?T("Content of roster file for 'pushroster' command:"), + {?T("Content of roster file for _`push_roster`_ API:"), ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},", "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},", "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]}, {?T("With this call, the sessions of the local account which JID is " - "boby@example.org will be kicked, and its password will be set " + "'boby@example.org' will be kicked, and its password will be set " "to something like " "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"), ["ejabberdctl vhost example.org ban_account boby \"Spammed rooms\""]}, - {?T("Call to srg_create using double-quotes and single-quotes:"), + {?T("Call to _`srg_create`_ API using double-quotes and single-quotes:"), ["ejabberdctl srg_create g1 example.org \"\'Group number 1\'\" this_is_g1 g1"]}]}. diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index 9ed4ce65d..da0913e93 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -269,5 +269,5 @@ mod_doc() -> [?T("The module implements " "https://xmpp.org/extensions/xep-0191.html" "[XEP-0191: Blocking Command]."), "", - ?T("This module depends on 'mod_privacy' where " + ?T("This module depends on _`mod_privacy`_ where " "all the configuration is performed.")]}. diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 9017720a4..b932d862e 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -256,9 +256,8 @@ mod_doc() -> ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "_`listen-options.md#request_handlers|request_handlers`_."), "", - ?T("Make sure either 'mod_bosh' or 'ejabberd_http_ws' " - "_`listen-options.md#request_handlers|request_handlers`_ " - "are enabled."), "", + ?T("Make sure either _`mod_bosh`_ or _`listen.md#ejabberd_http_ws|ejabberd_http_ws`_ " + "are enabled in at least one 'request_handlers'."), "", ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " "by default they point to the public Converse client.") ], @@ -308,7 +307,7 @@ mod_doc() -> #{value => ?T("auto | WebSocketURL"), desc => ?T("A WebSocket URL to which Converse can connect to. " - "The keyword '@HOST@' is replaced with the real virtual " + "The '@HOST@' keyword is replaced with the real virtual " "host name. " "If set to 'auto', it will build the URL of the first " "configured WebSocket request handler. " diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index c47ebb9a5..12e2fcd50 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -117,7 +117,7 @@ mod_doc() -> [{?T("Make sure you do not delegate the same namespace to several " "services at the same time. As in the example provided later, " "to have the 'sat-pubsub.example.org' component perform " - "correctly disable the 'mod_pubsub' module."), + "correctly disable the _`mod_pubsub`_ module."), ["access_rules:", " external_pubsub:", " allow: external_component", diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 1b49b6b14..80b4dbe84 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -214,7 +214,8 @@ mod_doc() -> ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "_`listen-options.md#request_handlers|request_handlers`_."), "", - ?T("Notice it only works if ejabberd_http has tls enabled.")], + ?T("Notice it only works if _`listen.md#ejabberd_http|ejabberd_http`_ " + "has _`listen-options.md#tls|tls`_ enabled.")], note => "added in 22.05", example => ["listen:", diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 0ad9cfbe0..44eae9733 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -561,10 +561,10 @@ mod_doc() -> "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "_`listen-options.md#request_handlers|request_handlers`_."), "", ?T("To use a specific API version N, when defining the URL path " - "in the request_handlers, add a 'vN'. " - "For example: '/api/v2: mod_http_api'"), "", + "in the request_handlers, add a vN. " + "For example: '/api/v2: mod_http_api'."), "", ?T("To run a command, send a POST request to the corresponding " - "URL: 'http://localhost:5280/api/'")], + "URL: 'http://localhost:5280/api/COMMAND-NAME'")], example => ["listen:", " -", diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index dcb619c03..1b67a518b 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -243,14 +243,14 @@ mod_doc() -> desc => ?T("This option defines the Jabber IDs of the service. " "If the 'hosts' option is not specified, the only Jabber ID will " - "be the hostname of the virtual host with the prefix \"upload.\". " + "be the hostname of the virtual host with the prefix '\"upload.\"'. " "The keyword '@HOST@' is replaced with the real virtual host name.")}}, {name, #{value => ?T("Name"), desc => ?T("A name of the service in the Service Discovery. " - "This will only be displayed by special XMPP clients. " - "The default value is \"HTTP File Upload\".")}}, + "The default value is '\"HTTP File Upload\"'. " + "Please note this will only be displayed by some XMPP clients.")}}, {access, #{value => ?T("AccessName"), desc => @@ -270,7 +270,7 @@ mod_doc() -> desc => ?T("This option defines the length of the random " "string included in the GET and PUT URLs generated " - "by 'mod_http_upload'. The minimum length is 8 characters, " + "by 'mod_http_upload'. The minimum length is '8' characters, " "but it is recommended to choose a larger value. " "The default value is '40'.")}}, {jid_in_url, @@ -293,8 +293,8 @@ mod_doc() -> #{value => ?T("Permission"), desc => ?T("This option defines the permission bits of uploaded files. " - "The bits are specified as an octal number (see the chmod(1) " - "manual page) within double quotes. For example: \"0644\". " + "The bits are specified as an octal number (see the 'chmod(1)' " + "manual page) within double quotes. For example: '\"0644\"'. " "The default is undefined, which means no explicit permissions " "will be set.")}}, {dir_mode, @@ -302,8 +302,8 @@ mod_doc() -> desc => ?T("This option defines the permission bits of the 'docroot' " "directory and any directories created during file uploads. " - "The bits are specified as an octal number (see the chmod(1) " - "manual page) within double quotes. For example: \"0755\". " + "The bits are specified as an octal number (see the 'chmod(1)' " + "manual page) within double quotes. For example: '\"0755\"'. " "The default is undefined, which means no explicit permissions " "will be set.")}}, {docroot, @@ -311,26 +311,26 @@ mod_doc() -> desc => ?T("Uploaded files are stored below the directory specified " "(as an absolute path) with this option. The keyword " - "@HOME@ is replaced with the home directory of the user " - "running ejabberd, and the keyword @HOST@ with the virtual " - "host name. The default value is \"@HOME@/upload\".")}}, + "'@HOME@' is replaced with the home directory of the user " + "running ejabberd, and the keyword '@HOST@' with the virtual " + "host name. The default value is '\"@HOME@/upload\"'.")}}, {put_url, #{value => ?T("URL"), desc => ?T("This option specifies the initial part of the PUT URLs " - "used for file uploads. The keyword @HOST@ is replaced " + "used for file uploads. The keyword '@HOST@' is replaced " "with the virtual host name. NOTE: different virtual " "hosts cannot use the same PUT URL. " - "The default value is \"https://@HOST@:5443/upload\".")}}, + "The default value is '\"https://@HOST@:5443/upload\"'.")}}, {get_url, #{value => ?T("URL"), desc => ?T("This option specifies the initial part of the GET URLs " "used for downloading the files. The default value is 'undefined'. " "When this option is 'undefined', this option is set " - "to the same value as 'put_url'. The keyword @HOST@ is " + "to the same value as 'put_url'. The keyword '@HOST@' is " "replaced with the virtual host name. NOTE: if GET requests " - "are handled by 'mod_http_upload', the 'get_url' must match the " + "are handled by this module, the 'get_url' must match the " "'put_url'. Setting it to a different value only makes " "sense if an external web server or _`mod_http_fileserver`_ " "is used to serve the uploaded files.")}}, @@ -349,9 +349,9 @@ mod_doc() -> "Upload processing to a separate HTTP server. " "Both ejabberd and the HTTP server should share this " "secret and behave exactly as described at " - "https://modules.prosody.im/mod_http_upload_external.html" - "[Prosody's mod_http_upload_external] in the " - "'Implementation' section. There is no default value.")}}, + "https://modules.prosody.im/mod_http_upload_external.html#implementation" + "[Prosody's mod_http_upload_external: Implementation]. " + "There is no default value.")}}, {rm_on_unregister, #{value => "true | false", desc => diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 3935588f7..4e522a83b 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -96,7 +96,7 @@ mod_options(_) -> mod_doc() -> #{desc => [?T("This module adds quota support for mod_http_upload."), "", - ?T("This module depends on 'mod_http_upload'.")], + ?T("This module depends on _`mod_http_upload`_.")], opts => [{max_days, #{value => ?T("Days"), @@ -126,10 +126,10 @@ mod_doc() -> "user may upload. When this threshold is exceeded, " "ejabberd deletes the oldest files uploaded by that " "user until their disk usage equals or falls below " - "the specified soft quota (see 'access_soft_quota'). " + "the specified soft quota (see also option 'access_soft_quota'). " "The default value is 'hard_upload_quota'.")}}], example => - [{?T("Please note that it's not necessary to specify the " + [{?T("Notice it's not necessary to specify the " "'access_hard_quota' and 'access_soft_quota' options in order " "to use the quota feature. You can stick to the default names " "and just specify access rules such as those in this example:"), diff --git a/src/mod_mix.erl b/src/mod_mix.erl index 6ef5f885b..c6aa60ef6 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -122,7 +122,7 @@ mod_doc() -> desc => ?T("This option defines the Jabber IDs of the service. " "If the 'hosts' option is not specified, the only Jabber ID will " - "be the hostname of the virtual host with the prefix \"mix.\". " + "be the hostname of the virtual host with the prefix '\"mix.\"'. " "The keyword '@HOST@' is replaced with the real virtual host name.")}}, {name, #{value => ?T("Name"), diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index f043393ba..afc63b8d3 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -128,7 +128,7 @@ mod_doc() -> "The module is needed if MIX compatible clients " "on your server are going to join MIX channels " "(either on your server or on any remote servers)."), "", - ?T("NOTE: 'mod_mix' is not required for this module " + ?T("NOTE: _`mod_mix`_ is not required for this module " "to work, however, without 'mod_mix_pam' the MIX " "functionality of your local XMPP clients will be impaired.")], opts => diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 94930049c..2842d18ef 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -179,24 +179,35 @@ mod_doc() -> example => ["modules:", " mod_mqtt_bridge:", + " replication_user: \"mqtt@xmpp.server.com\"", " servers:", " \"mqtt://server.com\":", + " authentication:", + " certfile: \"/etc/ejabberd/mqtt_server.pem\"", " publish:", " \"localA\": \"remoteA\" # local changes to 'localA' will be replicated on remote server as 'remoteA'", " \"topicB\": \"topicB\"", " subscribe:", - " \"remoteB\": \"localB\" # changes to 'remoteB' on remote server will be stored as 'localB' on local server", - " authentication:", - " certfile: \"/etc/ejabberd/mqtt_server.pem\"", - " replication_user: \"mqtt@xmpp.server.com\""], + " \"remoteB\": \"localB\" # changes to 'remoteB' on remote server will be stored as 'localB' on local server"], opts => [{servers, - #{value => "{ServerUrl: {publish: [TopicPairs], subscribe: [TopicPairs], authentication: [AuthInfo]}}", + #{value => "{ServerUrl: {Key: Value}}", desc => - ?T("Declaration of data to share, must contain 'publish' or 'subscribe' or both, and 'authentication' " - "section with username/password field or certfile pointing to client certificate. " - "Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), " - "ws, wss, ws5, wss5. Certificate authentication can be only used with mqtts, mqtt5s, wss, wss5.")}}, + ?T("Declaration of data to share for each ServerUrl. " + "Server URLs can use schemas: 'mqtt', 'mqtts' (mqtt with tls), 'mqtt5', " + "'mqtt5s' (both to trigger v5 protocol), 'ws', 'wss', 'ws5', 'wss5'. " + "Keys must be:")}, + [{authentication, + #{value => "{AuthKey: AuthValue}", + desc => ?T("List of authentication information, where AuthKey can be: " + "'username' and 'password' fields, or 'certfile' pointing to client certificate. " + "Certificate authentication can be used only with mqtts, mqtt5s, wss, wss5.")}}, + {publish, + #{value => "{LocalTopic: RemoteTopic}", + desc => ?T("Either publish or subscribe must be set, or both.")}}, + {subscribe, + #{value => "{RemoteTopic: LocalTopic}", + desc => ?T("Either publish or subscribe must be set, or both.")}}]}, {replication_user, #{value => "JID", desc => diff --git a/src/mod_muc.erl b/src/mod_muc.erl index cee28918d..74e62dedc 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1497,12 +1497,12 @@ mod_doc() -> ?T("A small history of the current discussion is sent to users " "when they enter the room. With this option you can define the " "number of history messages to keep and send to users joining the room. " - "The value is a non-negative integer. Setting the value to 0 disables " + "The value is a non-negative integer. Setting the value to '0' disables " "the history feature and, as a result, nothing is kept in memory. " - "The default value is 20. This value affects all rooms on the service. " + "The default value is '20'. This value affects all rooms on the service. " "NOTE: modern XMPP clients rely on Message Archives (XEP-0313), so feel " "free to disable the history feature if you're only using modern clients " - "and have 'mod_mam' module loaded.")}}, + "and have _`mod_mam`_ module loaded.")}}, {host, #{desc => ?T("Deprecated. Use 'hosts' instead.")}}, {hosts, #{value => ?T("[Host, ...]"), @@ -1593,7 +1593,7 @@ mod_doc() -> "When this option is not defined, message rate is not limited. " "This feature can be used to protect a MUC service from occupant " "abuses and limit number of messages that will be broadcasted by " - "the service. A good value for this minimum message interval is 0.4 second. " + "the service. A good value for this minimum message interval is '0.4' second. " "If an occupant tries to send messages faster, an error is send back " "explaining that the message has been discarded and describing the " "reason why the message is not acceptable.")}}, @@ -1610,7 +1610,7 @@ mod_doc() -> "the presence is cached by ejabberd and only the last presence " "is broadcasted to all occupants in the room after expiration " "of the interval delay. Intermediate presence packets are " - "silently discarded. A good value for this option is 4 seconds.")}}, + "silently discarded. A good value for this option is '4' seconds.")}}, {queue_type, #{value => "ram | file", desc => @@ -1843,7 +1843,7 @@ mod_doc() -> ?T("Maximum number of occupants in the room. " "The default value is '200'.")}}, {presence_broadcast, - #{value => "[moderator | participant | visitor, ...]", + #{value => "[Role]", desc => ?T("List of roles for which presence is broadcasted. " "The list can contain one or several of: 'moderator', " diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index f8cb3bd0e..dfc4fb182 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -2096,5 +2096,5 @@ mod_doc() -> note => "added in 22.05", desc => ?T("How many users can be subscribed to a room at once using " - "the 'subscribe_room_many' command. " + "the _`subscribe_room_many`_ API. " "The default value is '50'.")}}]}. diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 96b541c59..abb209b77 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1184,10 +1184,8 @@ mod_doc() -> "again. Thus it is very similar to how email works. A user " "is considered offline if no session presence priority > 0 " "are currently open."), "", - ?T("NOTE: 'ejabberdctl' has a command to " - "delete expired messages (see chapter " - "_`../guide/managing.md|Managing an ejabberd server`_ " - "in online documentation.")], + ?T("The _`delete_expired_messages`_ API allows to delete expired messages, " + "and _`delete_old_messages`_ API deletes older ones.")], opts => [{access_max_user_messages, #{value => ?T("AccessName"), @@ -1196,16 +1194,16 @@ mod_doc() -> "enforced to limit the maximum number of offline " "messages that a user can have (quota). When a user " "has too many offline messages, any new messages that " - "they receive are discarded, and a " + "they receive are discarded, and a '' " "error is returned to the sender. The default value is " "'max_user_offline_messages'.")}}, {store_empty_body, #{value => "true | false | unless_chat_state", desc => - ?T("Whether or not to store messages that lack a " + ?T("Whether or not to store messages that lack a '' " "element. The default value is 'unless_chat_state', " "which tells ejabberd to store messages even if they " - "lack the element, unless they only contain a " + "lack the '' element, unless they only contain a " "chat state notification (as defined in " "https://xmpp.org/extensions/xep-0085.html" "[XEP-0085: Chat State Notifications].")}}, @@ -1217,8 +1215,8 @@ mod_doc() -> {use_mam_for_storage, #{value => "true | false", desc => - ?T("This is an experimental option. Enabling this option, " - "'mod_offline' uses the 'mod_mam' archive table instead " + ?T("This is an experimental option. By enabling the option, " + "this module uses the 'archive' table from _`mod_mam`_ instead " "of its own spool table to retrieve the messages received " "when the user was offline. This allows client " "developers to slowly drop XEP-0160 and rely on XEP-0313 " diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index 0a7911e26..b52000762 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -895,8 +895,8 @@ mod_doc() -> ?T("NOTE: Nowadays modern XMPP clients rely on " "https://xmpp.org/extensions/xep-0191.html" "[XEP-0191: Blocking Command] which is implemented by " - "'mod_blocking' module. However, you still need " - "'mod_privacy' loaded in order for _`mod_blocking`_ to work.")], + "_`mod_blocking`_. However, you still need " + "'mod_privacy' loaded in order for 'mod_blocking' to work.")], opts => [{db_type, #{value => "mnesia | sql", diff --git a/src/mod_private.erl b/src/mod_private.erl index e3bc72593..2dea64c45 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -135,7 +135,7 @@ mod_doc() -> "[XEP-0048: Bookmarks])."), "", ?T("It also implements the bookmark conversion described in " "https://xmpp.org/extensions/xep-0402.html[XEP-0402: PEP Native Bookmarks]" - ", see the command _`bookmarks_to_pep`_ API.")], + ", see _`bookmarks_to_pep`_ API.")], opts => [{db_type, #{value => "mnesia | sql", diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 39c563dfb..c0c8c4b49 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -1343,7 +1343,7 @@ mod_doc() -> "contacts are merged to the relevant users at retrieval time. " "The standard user rosters thus stay unmodified."), "", ?T("Shared roster groups can be edited via the Web Admin, " - "and some API commands called 'srg_*'. " + "and some API commands called 'srg_', for example _`srg_add`_ API. " "Each group has a unique name and those parameters:"), "", ?T("- Label: Used in the rosters where this group is displayed."),"", ?T("- Description: of the group, which has no effect."), "", From 790cb104cdbe9f90e0b48ac8fe7c8020cdb9f743 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Dec 2024 01:45:05 +0100 Subject: [PATCH 0859/1302] Change arguments and result to consistent names (API v3) Commands accept as argument: user, host, room, service and return as result JIDs Commands that change in API v3: get_room_affiliations muc_register_nick muc_unregister_nick set_room_affiliation status_list status_list_host subscribe_room subscribe_room_many unsubscribe_room --- src/ejabberd_sm.erl | 4 +- src/mod_admin_extra.erl | 46 ++++++++++- src/mod_muc_admin.erl | 174 +++++++++++++++++++++++++++++++++++++--- test/muc_tests.erl | 4 +- 4 files changed, 213 insertions(+), 15 deletions(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 4db90e5ee..4745c57d9 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -1034,8 +1034,8 @@ get_commands_spec() -> desc = "List all established sessions", policy = admin, module = ?MODULE, function = connected_users, args = [], - result_desc = "List of users sessions", - result_example = [<<"user1@example.com">>, <<"user2@example.com">>], + result_desc = "List of users sessions full JID", + result_example = [<<"user1@example.com/Home">>, <<"user2@example.com/54134">>], result = {connected_users, {list, {sessions, string}}}}, #ejabberd_commands{name = connected_users_number, tags = [session, statistics], desc = "Get the number of established sessions", diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 4e203eb83..85ab9a367 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -43,7 +43,8 @@ % Sessions num_resources/2, resource_num/3, kick_session/4, status_num/2, status_num/1, - status_list/2, status_list/1, connected_users_info/0, + status_list/2, status_list_v3/2, + status_list/1, status_list_v3/1, connected_users_info/0, connected_users_vhost/1, set_presence/7, get_presence/2, user_sessions_info/2, get_last/2, set_last/4, @@ -380,6 +381,21 @@ get_commands_spec() -> {status, string} ]}} }}}, + #ejabberd_commands{name = status_list_host, tags = [session], + desc = "List of users logged in host with their statuses", + module = ?MODULE, function = status_list_v3, + version = 3, + note = "updated in 24.12", + args = [{host, binary}, {status, binary}], + args_example = [<<"myserver.com">>, <<"dnd">>], + args_desc = ["Server name", "Status type to check"], + result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}], + result = {users, {list, + {userstatus, {tuple, [{jid, string}, + {priority, integer}, + {status, string} + ]}} + }}}, #ejabberd_commands{name = status_list, tags = [session], desc = "List of logged users with this status", module = ?MODULE, function = status_list, @@ -396,6 +412,21 @@ get_commands_spec() -> {status, string} ]}} }}}, + #ejabberd_commands{name = status_list, tags = [session], + desc = "List of logged users with this status", + module = ?MODULE, function = status_list_v3, + version = 3, + note = "updated in 24.12", + args = [{status, binary}], + args_example = [<<"dnd">>], + args_desc = ["Status type to check"], + result_example = [{<<"peter@myserver.com/tka">>,6,<<"Busy">>}], + result = {users, {list, + {userstatus, {tuple, [{jid, string}, + {priority, integer}, + {status, string} + ]}} + }}}, #ejabberd_commands{name = connected_users_info, tags = [session], desc = "List all established sessions and their information", @@ -426,8 +457,9 @@ get_commands_spec() -> module = ?MODULE, function = connected_users_vhost, args_example = [<<"myexample.com">>], args_desc = ["Server name"], - result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>], args = [{host, binary}], + result_example = [<<"user1@myserver.com/tka">>, <<"user2@localhost/tka">>], + result_desc = "List of sessions full JIDs", result = {connected_users_vhost, {list, {sessions, string}}}}, #ejabberd_commands{name = user_sessions_info, tags = [session], @@ -683,6 +715,7 @@ get_commands_spec() -> module = ?MODULE, function = get_roster, args = [], args_rename = [{server, host}], + result_example = [{<<"user2@localhost">>, <<"User 2">>, <<"none">>, <<"subscribe">>, [<<"Group1">>]}], result = {contacts, {list, {contact, {tuple, [ {jid, string}, {nick, string}, @@ -696,6 +729,7 @@ get_commands_spec() -> policy = user, module = ?MODULE, function = get_roster_count, args = [], + args_example = [<<"sun">>, <<"localhost">>], args_rename = [{server, host}], result_example = 5, result_desc = "Number", @@ -1333,6 +1367,14 @@ status_list(Host, Status) -> status_list(Status) -> status_list(<<"all">>, Status). +status_list_v3(ArgHost, Status) -> + List = status_list(ArgHost, Status), + [{jid:encode(jid:make(User, Host, Resource)), Priority, StatusText} + || {User, Host, Resource, Priority, StatusText} <- List]. + +status_list_v3(Status) -> + status_list_v3(<<"all">>, Status). + get_status_list(Host, Status_required) -> %% Get list of all logged users diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index dfc4fb182..40795afee 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -30,7 +30,8 @@ -export([start/2, stop/1, reload/3, depends/2, mod_doc/0, muc_online_rooms/1, muc_online_rooms_by_regex/2, - muc_register_nick/3, muc_unregister_nick/2, + muc_register_nick/3, muc_register_nick/4, + muc_unregister_nick/2, muc_unregister_nick/3, create_room_with_opts/4, create_room/3, destroy_room/2, create_rooms_file/1, destroy_rooms_file/1, rooms_unused_list/2, rooms_unused_destroy/2, @@ -38,9 +39,11 @@ get_user_rooms/2, get_user_subscriptions/2, get_room_occupants/2, get_room_occupants_number/2, send_direct_invitation/5, change_room_option/4, get_room_options/2, - set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3, - subscribe_room/4, subscribe_room_many/3, - unsubscribe_room/2, get_subscribers/2, + set_room_affiliation/4, set_room_affiliation/5, get_room_affiliations/2, + get_room_affiliations_v3/2, get_room_affiliation/3, + subscribe_room/4, subscribe_room/6, + subscribe_room_many/3, subscribe_room_many_v3/4, + unsubscribe_room/2, unsubscribe_room/4, get_subscribers/2, get_room_serverhost/1, web_menu_main/2, web_page_main/2, web_menu_host/3, web_page_host/3, @@ -102,7 +105,7 @@ get_commands_spec() -> module = ?MODULE, function = muc_online_rooms, args_desc = ["MUC service, or `global` for all"], args_example = ["conference.example.com"], - result_desc = "List of rooms", + result_desc = "List of rooms JIDs", result_example = ["room1@conference.example.com", "room2@conference.example.com"], args = [{service, binary}], args_rename = [{host, service}], @@ -133,6 +136,16 @@ get_commands_spec() -> args = [{nick, binary}, {jid, binary}, {service, binary}], args_rename = [{host, service}], result = {res, rescode}}, + #ejabberd_commands{name = muc_register_nick, tags = [muc], + desc = "Register a nick to a User JID in a MUC service", + module = ?MODULE, function = muc_register_nick, + version = 3, + note = "updated in 24.12", + args_desc = ["nick", "user name", "user host", "MUC service"], + args_example = [<<"Tim">>, <<"tim">>, <<"example.org">>, <<"conference.example.org">>], + args = [{nick, binary}, {user, binary}, {host, binary}, {service, binary}], + args_rename = [{host, service}], + result = {res, rescode}}, #ejabberd_commands{name = muc_unregister_nick, tags = [muc], desc = "Unregister the nick registered by that account in the MUC service", module = ?MODULE, function = muc_unregister_nick, @@ -141,6 +154,16 @@ get_commands_spec() -> args = [{jid, binary}, {service, binary}], args_rename = [{host, service}], result = {res, rescode}}, + #ejabberd_commands{name = muc_unregister_nick, tags = [muc], + desc = "Unregister the nick registered by that account in the MUC service", + module = ?MODULE, function = muc_unregister_nick, + version = 3, + note = "updated in 24.12", + args_desc = ["user name", "user host", "MUC service"], + args_example = [<<"tim">>, <<"example.org">>, <<"conference.example.org">>], + args = [{user, binary}, {host, binary}, {service, binary}], + args_rename = [{host, service}], + result = {res, rescode}}, #ejabberd_commands{name = create_room, tags = [muc_room], desc = "Create a MUC room name@service in host", @@ -393,6 +416,21 @@ get_commands_spec() -> args = [{user, binary}, {nick, binary}, {room, binary}, {nodes, {list, {node, binary}}}], result = {nodes, {list, {node, string}}}}, + #ejabberd_commands{name = subscribe_room, tags = [muc_room, muc_sub], + desc = "Subscribe to a MUC conference", + module = ?MODULE, function = subscribe_room, + version = 3, + note = "updated in 24.12", + args_desc = ["user name", "user host", "user nick", + "room name", "MUC service", "list of nodes"], + args_example = ["tom", "localhost", "Tom", "room1", "conference.localhost", + ["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]], + result_desc = "The list of nodes that has subscribed", + result_example = ["urn:xmpp:mucsub:nodes:messages", + "urn:xmpp:mucsub:nodes:affiliations"], + args = [{user, binary}, {host, binary}, {nick, binary}, {room, binary}, + {service, binary}, {nodes, {list, {node, binary}}}], + result = {nodes, {list, {node, string}}}}, #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], desc = "Subscribe several users to a MUC conference", note = "added in 22.05", @@ -440,6 +478,32 @@ get_commands_spec() -> {room, binary}, {nodes, {list, {node, binary}}}], result = {res, rescode}}, + #ejabberd_commands{name = subscribe_room_many, tags = [muc_room, muc_sub], + desc = "Subscribe several users to a MUC conference", + longdesc = "This command accepts up to 50 users at once " + "(this is configurable with the _`mod_muc_admin`_ option " + "`subscribe_room_many_max_users`)", + module = ?MODULE, function = subscribe_room_many_v3, + version = 3, + note = "updated in 24.12", + args_desc = ["List of tuples with users name, host and nick", + "room name", + "MUC service", + "nodes separated by commas: `,`"], + args_example = [[{"tom", "localhost", "Tom"}, + {"jerry", "localhost", "Jerry"}], + "room1", "conference.localhost", + ["urn:xmpp:mucsub:nodes:messages", "urn:xmpp:mucsub:nodes:affiliations"]], + args = [{users, {list, + {user, {tuple, + [{user, binary}, + {host, binary}, + {nick, binary} + ]}} + }}, + {name, binary}, {service, binary}, + {nodes, {list, {node, binary}}}], + result = {res, rescode}}, #ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub], desc = "Unsubscribe from a MUC conference", module = ?MODULE, function = unsubscribe_room, @@ -447,6 +511,15 @@ get_commands_spec() -> args_example = ["tom@localhost", "room1@conference.localhost"], args = [{user, binary}, {room, binary}], result = {res, rescode}}, + #ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub], + desc = "Unsubscribe from a MUC conference", + module = ?MODULE, function = unsubscribe_room, + version = 3, + note = "updated in 24.12", + args_desc = ["user name", "user host", "room name", "MUC service"], + args_example = ["tom", "localhost", "room1", "conference.localhost"], + args = [{user, binary}, {host, binary}, {room, binary}, {service, binary}], + result = {res, rescode}}, #ejabberd_commands{name = get_subscribers, tags = [muc_room, muc_sub], desc = "List subscribers of a MUC conference", module = ?MODULE, function = get_subscribers, @@ -454,7 +527,8 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com"], result_desc = "The list of users that are subscribed to that room", result_example = ["user2@example.com", "user3@example.com"], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {subscribers, {list, {jid, string}}}}, #ejabberd_commands{name = set_room_affiliation, tags = [muc_room], desc = "Change an affiliation in a MUC room", @@ -464,6 +538,19 @@ get_commands_spec() -> args = [{name, binary}, {service, binary}, {jid, binary}, {affiliation, binary}], result = {res, rescode}}, + #ejabberd_commands{name = set_room_affiliation, tags = [muc_room], + desc = "Change an affiliation in a MUC room", + longdesc = "If affiliation is `none`, then the affiliation is removed.", + module = ?MODULE, function = set_room_affiliation, + version = 3, + note = "updated in 24.12", + args_desc = ["room name", "MUC service", "user name", "user host", "affiliation to set"], + args_example = ["room1", "conference.example.com", "sun", "localhost", "member"], + args = [{room, binary}, {service, binary}, + {user, binary}, {host, binary}, {affiliation, binary}], + result = {res, rescode}}, + + #ejabberd_commands{name = get_room_affiliations, tags = [muc_room], desc = "Get the list of affiliations of a MUC room", module = ?MODULE, function = get_room_affiliations, @@ -480,6 +567,25 @@ get_commands_spec() -> {reason, string} ]}} }}}, + #ejabberd_commands{name = get_room_affiliations, tags = [muc_room], + desc = "Get the list of affiliations of a MUC room", + module = ?MODULE, function = get_room_affiliations_v3, + version = 3, + note = "updated in 24.12", + args_desc = ["Room name", "MUC service"], + args_example = ["room1", "conference.example.com"], + result_desc = "The list of affiliations with jid, affiliation and reason", + result_example = [{"user1@example.com", member, "member"}], + args = [{name, binary}, {service, binary}], + result = {affiliations, {list, + {affiliation, {tuple, + [{jid, string}, + {affiliation, atom}, + {reason, string} + ]}} + }}}, + + #ejabberd_commands{name = get_room_affiliation, tags = [muc_room], desc = "Get affiliation of a user in MUC room", module = ?MODULE, function = get_room_affiliation, @@ -547,6 +653,9 @@ build_summary_room(Name, Host, Pid) -> Participants }. +muc_register_nick(Nick, User, Host, Service) -> + muc_register_nick(Nick, makeencode(User, Host), Service). + muc_register_nick(Nick, FromBinary, Service) -> try {get_room_serverhost(Service), jid:decode(FromBinary)} of {ServerHost, From} -> @@ -569,6 +678,9 @@ muc_register_nick(Nick, FromBinary, Service) -> throw({error, "Internal error"}) end. +muc_unregister_nick(User, Host, Service) -> + muc_unregister_nick(makeencode(User, Host), Service). + muc_unregister_nick(FromBinary, Service) -> muc_register_nick(<<"">>, FromBinary, Service). @@ -733,8 +845,11 @@ webadmin_muc_host(_Host, Get = [make_command(get_room_affiliations, R, [{<<"name">>, Name}, {<<"service">>, Service}], - [{table_options, {20, RPath}}])], + [{table_options, {20, RPath}}, + {result_links, [{jid, user, 3 + Level, <<"">>}]}])], Title ++ Breadcrumb ++ Get ++ Set; + + webadmin_muc_host(_Host, Service, [<<"rooms">>, <<"room">>, Name, <<"history">> | RPath], @@ -816,11 +931,11 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Subscribers">>, Name, R, RPath}), Set = [make_command(subscribe_room, R, - [{<<"room">>, jid:encode({Name, Service, <<"">>})}], + [{<<"name">>, Name}, {<<"service">>, Service}], []), make_command(unsubscribe_room, R, - [{<<"room">>, jid:encode({Name, Service, <<"">>})}], + [{<<"name">>, Name}, {<<"service">>, Service}], [{style, danger}])], Get = [make_command(get_subscribers, R, @@ -1784,7 +1899,7 @@ get_options(Config) -> %%---------------------------- %% @spec(Name::binary(), Service::binary()) -> -%% [{JID::string(), Domain::string(), Role::string(), Reason::string()}] +%% [{Username::string(), Domain::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations(Name, Service) -> case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of @@ -1802,6 +1917,27 @@ get_room_affiliations(Name, Service) -> throw({error, "The room does not exist."}) end. +%% @spec(Name::binary(), Service::binary()) -> +%% [{JID::string(), Role::string(), Reason::string()}] +%% @doc Get the affiliations of the room Name@Service. +get_room_affiliations_v3(Name, Service) -> + case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + {Pid, _, _} when is_pid(Pid) -> + %% Get the PID of the online room, then request its state + {ok, StateData} = mod_muc_room:get_state(Pid), + Affiliations = maps:to_list(StateData#state.affiliations), + lists:map( + fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)-> + Jid = makeencode(Uname, Domain), + {Jid, Aff, Reason}; + ({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)-> + Jid = makeencode(Uname, Domain), + {Jid, Aff, <<>>} + end, Affiliations); + _ -> + throw({error, "The room does not exist."}) + end. + get_room_history(Name, Service) -> case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> @@ -1843,6 +1979,9 @@ get_room_affiliation(Name, Service, JID) -> %% Change Room Affiliation %%---------------------------- +set_room_affiliation(Name, Service, User, Host, AffiliationString) -> + set_room_affiliation(Name, Service, makeencode(User, Host), AffiliationString). + %% @spec(Name, Service, JID, AffiliationString) -> ok | {error, Error} %% Name = binary() %% Service = binary() @@ -1880,6 +2019,10 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> %%% MUC Subscription %%% +subscribe_room(Username, Host, Nick, Name, Service, Nodes) -> + subscribe_room(makeencode(Username, Host), Nick, + makeencode(Name, Service), Nodes). + subscribe_room(_User, Nick, _Room, _Nodes) when Nick == <<"">> -> throw({error, "Nickname must be set"}); subscribe_room(User, Nick, Room, Nodes) when is_binary(Nodes) -> @@ -1912,6 +2055,10 @@ subscribe_room(User, Nick, Room, NodeList) -> throw({error, "Malformed room JID"}) end. +subscribe_room_many_v3(List, Name, Service, Nodes) -> + List2 = [{makeencode(User, Host), Nick} || {User, Host, Nick} <- List], + subscribe_room_many(List2, makeencode(Name, Service), Nodes). + subscribe_room_many(Users, Room, Nodes) -> MaxUsers = mod_muc_admin_opt:subscribe_room_many_max_users(global), if @@ -1924,6 +2071,10 @@ subscribe_room_many(Users, Room, Nodes) -> end, Users) end. +unsubscribe_room(User, Host, Name, Service) -> + unsubscribe_room(makeencode(User, Host), + makeencode(Name, Service)). + unsubscribe_room(User, Room) -> try jid:decode(Room) of #jid{luser = Name, lserver = Host} when Name /= <<"">> -> @@ -1962,6 +2113,9 @@ get_subscribers(Name, Host) -> %% Utils %%---------------------------- +makeencode(User, Host) -> + jid:encode(jid:make(User, Host)). + -spec validate_host(Name :: binary(), ArgName::binary()) -> binary(). validate_host(Name, ArgName) -> case jid:nameprep(Name) of diff --git a/test/muc_tests.erl b/test/muc_tests.erl index f7e080aee..a63567481 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -259,7 +259,9 @@ set_room_affiliation(Config) -> RequestURL = "http://" ++ ServerHost ++ ":" ++ integer_to_list(WebPort) ++ "/api/set_room_affiliation", Headers = [{"X-Admin", "true"}], ContentType = "application/json", - Body = misc:json_encode(#{name => RoomName, service => RoomService, jid => jid:encode(PeerJID), affiliation => member}), + Body = misc:json_encode(#{room => RoomName, service => RoomService, + user => PeerJID#jid.luser, host => PeerJID#jid.lserver, + affiliation => member}), {ok, {{_, 200, _}, _, _}} = httpc:request(post, {RequestURL, Headers, ContentType, Body}, [], []), #message{id = _, from = RoomJID, to = MyJID, sub_els = [ From b8360cae08d36ac4961b5fc408ca7a677d313448 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 10:11:01 +0100 Subject: [PATCH 0860/1302] Rename argument "name" to "room" for consistency And thanks to args_rename, both argument names can be used, either "name" or "room". So, this doesn't involve an API version change. --- src/mod_muc_admin.erl | 101 +++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 40795afee..5d3b80934 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -170,15 +170,17 @@ get_commands_spec() -> module = ?MODULE, function = create_room, args_desc = ["Room name", "MUC service", "Server host"], args_example = ["room1", "conference.example.com", "example.com"], - args = [{name, binary}, {service, binary}, + args = [{room, binary}, {service, binary}, {host, binary}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = destroy_room, tags = [muc_room], desc = "Destroy a MUC room", module = ?MODULE, function = destroy_room, args_desc = ["Room name", "MUC service"], args_example = ["room1", "conference.example.com"], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = create_rooms_file, tags = [muc], desc = "Create the rooms indicated in file", @@ -200,7 +202,7 @@ get_commands_spec() -> [{"members_only","true"}, {"affiliations", "owner:bob@example.com,member:peter@example.com"}, {"subscribers", "bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages"}]], - args = [{name, binary}, {service, binary}, + args = [{room, binary}, {service, binary}, {host, binary}, {options, {list, {option, {tuple, @@ -208,6 +210,7 @@ get_commands_spec() -> {value, binary} ]}} }}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = destroy_rooms_file, tags = [muc], desc = "Destroy the rooms indicated in file", @@ -313,7 +316,8 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com"], result_desc = "The list of occupants with JID, nick and affiliation", result_example = [{"user1@example.com/psi", "User 1", "owner"}], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {occupants, {list, {occupant, {tuple, [{jid, string}, @@ -329,7 +333,8 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com"], result_desc = "Number of room occupants", result_example = 7, - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {occupants, integer}}, #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], @@ -345,8 +350,9 @@ get_commands_spec() -> args_example = [<<"room1">>, <<"conference.example.com">>, <<>>, <<"Check this out!">>, "user2@localhost:user3@example.com"], - args = [{name, binary}, {service, binary}, {password, binary}, + args = [{room, binary}, {service, binary}, {password, binary}, {reason, binary}, {users, binary}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], desc = "Send a direct invitation to several destinations", @@ -362,8 +368,9 @@ get_commands_spec() -> args_example = [<<"room1">>, <<"conference.example.com">>, <<>>, <<"Check this out!">>, ["user2@localhost", "user3@example.com"]], - args = [{name, binary}, {service, binary}, {password, binary}, + args = [{room, binary}, {service, binary}, {password, binary}, {reason, binary}, {users, {list, {jid, binary}}}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = change_room_option, tags = [muc_room], @@ -371,8 +378,9 @@ get_commands_spec() -> module = ?MODULE, function = change_room_option, args_desc = ["Room name", "MUC service", "Option name", "Value to assign"], args_example = ["room1", "conference.example.com", "members_only", "true"], - args = [{name, binary}, {service, binary}, + args = [{room, binary}, {service, binary}, {option, binary}, {value, binary}], + args_rename = [{name, room}], result = {res, rescode}}, #ejabberd_commands{name = get_room_options, tags = [muc_room], desc = "Get options from a MUC room", @@ -381,7 +389,8 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com"], result_desc = "List of room options tuples with name and value", result_example = [{"members_only", "true"}], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {options, {list, {option, {tuple, [{name, string}, @@ -501,7 +510,7 @@ get_commands_spec() -> {nick, binary} ]}} }}, - {name, binary}, {service, binary}, + {room, binary}, {service, binary}, {nodes, {list, {node, binary}}}], result = {res, rescode}}, #ejabberd_commands{name = unsubscribe_room, tags = [muc_room, muc_sub], @@ -576,7 +585,7 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com"], result_desc = "The list of affiliations with jid, affiliation and reason", result_example = [{"user1@example.com", member, "member"}], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], result = {affiliations, {list, {affiliation, {tuple, [{jid, string}, @@ -593,7 +602,8 @@ get_commands_spec() -> args_example = ["room1", "conference.example.com", "user1@example.com"], result_desc = "Affiliation of the user", result_example = member, - args = [{name, binary}, {service, binary}, {jid, binary}], + args = [{room, binary}, {service, binary}, {jid, binary}], + args_rename = [{name, room}], result = {affiliation, atom}}, #ejabberd_commands{name = get_room_history, tags = [muc_room], desc = "Get history of messages stored inside MUC room state", @@ -601,7 +611,8 @@ get_commands_spec() -> module = ?MODULE, function = get_room_history, args_desc = ["Room name", "MUC service"], args_example = ["room1", "conference.example.com"], - args = [{name, binary}, {service, binary}], + args = [{room, binary}, {service, binary}], + args_rename = [{name, room}], result = {history, {list, {entry, {tuple, [{timestamp, string}, @@ -840,11 +851,11 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Affiliations">>, Name, R, RPath}), Set = [make_command(set_room_affiliation, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [])], Get = [make_command(get_room_affiliations, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{table_options, {20, RPath}}, {result_links, [{jid, user, 3 + Level, <<"">>}]}])], Title ++ Breadcrumb ++ Get ++ Set; @@ -862,7 +873,7 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"History">>, Name, R, RPath}), Get = [make_command(get_room_history, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{table_options, {10, RPath}}, {result_links, [{message, paragraph, 1, <<"">>}]}])], Title ++ Breadcrumb ++ Get; @@ -878,7 +889,7 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Invite">>, Name, R, RPath}), Set = [make_command(send_direct_invitation, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [])], Title ++ Breadcrumb ++ Set; webadmin_muc_host(_Host, @@ -893,7 +904,7 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Occupants">>, Name, R, RPath}), Get = [make_command(get_room_occupants, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{table_options, {20, RPath}}, {result_links, [{jid, user, 3 + Level, <<"">>}]}])], Title ++ Breadcrumb ++ Get; @@ -909,11 +920,11 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Options">>, Name, R, RPath}), Set = [make_command(change_room_option, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [])], Get = [make_command(get_room_options, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [])], Title ++ Breadcrumb ++ Get ++ Set; webadmin_muc_host(_Host, @@ -931,15 +942,15 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Subscribers">>, Name, R, RPath}), Set = [make_command(subscribe_room, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], []), make_command(unsubscribe_room, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{style, danger}])], Get = [make_command(get_subscribers, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{table_options, {20, RPath}}, {result_links, [{jid, user, 3 + Level, <<"">>}]}])], Title ++ Breadcrumb ++ Get ++ Set; @@ -955,7 +966,7 @@ webadmin_muc_host(_Host, make_breadcrumb({room_section, Level, Service, <<"Destroy">>, Name, R, RPath}), Set = [make_command(destroy_room, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{style, danger}])], Title ++ Breadcrumb ++ Set; webadmin_muc_host(_Host, @@ -991,7 +1002,7 @@ webadmin_muc_host(_Host, Service, [<<"rooms">> | RPath], R, _Lang, Level, PageTi {result_links, [{sentence, room, 3 + Level, <<"">>}]}]), make_command(get_room_occupants_number, R, - [{<<"name">>, Name}, {<<"service">>, Service}], + [{<<"room">>, Name}, {<<"service">>, Service}], [{only, raw_and_value}])} end, make_command_raw_value(muc_online_rooms, R, [{<<"service">>, Service}])), @@ -1264,7 +1275,7 @@ create_room(Name1, Host1, ServerHost) -> create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> ServerHost = validate_host(ServerHost1, <<"serverhost">>), - case get_room_pid_validate(Name1, Host1, <<"name">>, <<"host">>) of + case get_room_pid_validate(Name1, Host1, <<"service">>) of {room_not_found, Name, Host} -> %% Get the default room options from the muc configuration DefRoomOpts = mod_muc_opt:default_room_options(ServerHost), @@ -1294,7 +1305,7 @@ muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> %% If the room has participants, they are not notified that the room was destroyed; %% they will notice when they try to chat and receive an error that the room doesn't exist. destroy_room(Name1, Service1) -> - case get_room_pid_validate(Name1, Service1, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name1, Service1, <<"service">>) of {room_not_found, _, _} -> throw({error, "Room doesn't exists"}); {Pid, _, _} -> @@ -1590,7 +1601,7 @@ act_on_room(_Method, list, _) -> %%---------------------------- get_room_occupants(Room, Host) -> - case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of + case get_room_pid_validate(Room, Host, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> get_room_occupants(Pid); _ -> throw({error, room_not_found}) end. @@ -1606,7 +1617,7 @@ get_room_occupants(Pid) -> maps:to_list(S#state.users)). get_room_occupants_number(Room, Host) -> - case get_room_pid_validate(Room, Host, <<"name">>, <<"service">>) of + case get_room_pid_validate(Room, Host, <<"service">>) of {Pid, _, _} when is_pid(Pid)-> {ok, #{occupants_number := N}} = mod_muc_room:get_info(Pid), N; @@ -1685,7 +1696,7 @@ send_direct_invitation(FromJid, UserJid, Msg) -> %% For example: %% `change_room_option(<<"testroom">>, <<"conference.localhost">>, <<"title">>, <<"Test Room">>)' change_room_option(Name, Service, OptionString, ValueString) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {room_not_found, _, _} -> throw({error, "Room not found"}); {Pid, _, _} -> @@ -1782,10 +1793,10 @@ parse_nodes([<<"subscribers">> | Rest], Acc) -> parse_nodes(_, _) -> throw({error, "Invalid 'subscribers' - unknown node name used"}). --spec get_room_pid_validate(binary(), binary(), binary(), binary()) -> +-spec get_room_pid_validate(binary(), binary(), binary()) -> {pid() | room_not_found, binary(), binary()}. -get_room_pid_validate(Name, Service, NameArg, ServiceArg) -> - Name2 = validate_room(Name, NameArg), +get_room_pid_validate(Name, Service, ServiceArg) -> + Name2 = validate_room(Name), {ServerHost, Service2} = validate_muc2(Service, ServiceArg), case mod_muc:unhibernate_room(ServerHost, Service2, Name2) of error -> @@ -1876,7 +1887,7 @@ change_option(Option, Value, Config) -> %%---------------------------- get_room_options(Name, Service) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> get_room_options(Pid); _ -> [] end. @@ -1902,7 +1913,7 @@ get_options(Config) -> %% [{Username::string(), Domain::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations(Name, Service) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), @@ -1921,7 +1932,7 @@ get_room_affiliations(Name, Service) -> %% [{JID::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations_v3(Name, Service) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), @@ -1939,7 +1950,7 @@ get_room_affiliations_v3(Name, Service) -> end. get_room_history(Name, Service) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:get_state(Pid) of {ok, StateData} -> @@ -1965,7 +1976,7 @@ get_room_history(Name, Service) -> %% @doc Get affiliation of a user in the room Name@Service. get_room_affiliation(Name, Service, JID) -> - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), @@ -2000,7 +2011,7 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> _ -> throw({error, "Invalid affiliation"}) end, - case get_room_pid_validate(Name, Service, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Service, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> %% Get the PID for the online room so we can get the state of the room case mod_muc_room:change_item(Pid, jid:decode(JID), affiliation, Affiliation, <<"">>) of @@ -2034,7 +2045,7 @@ subscribe_room(User, Nick, Room, NodeList) -> try jid:decode(User) of UserJID1 -> UserJID = jid:replace_resource(UserJID1, <<"modmucadmin">>), - case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of + case get_room_pid_validate(Name, Host, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:subscribe( Pid, UserJID, Nick, NodeList) of @@ -2080,7 +2091,7 @@ unsubscribe_room(User, Room) -> #jid{luser = Name, lserver = Host} when Name /= <<"">> -> try jid:decode(User) of UserJID -> - case get_room_pid_validate(Name, Host, <<"name">>, <<"room">>) of + case get_room_pid_validate(Name, Host, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> case mod_muc_room:unsubscribe(Pid, UserJID) of ok -> @@ -2101,7 +2112,7 @@ unsubscribe_room(User, Room) -> end. get_subscribers(Name, Host) -> - case get_room_pid_validate(Name, Host, <<"name">>, <<"service">>) of + case get_room_pid_validate(Name, Host, <<"service">>) of {Pid, _, _} when is_pid(Pid) -> {ok, JIDList} = mod_muc_room:get_subscribers(Pid), [jid:encode(jid:remove_resource(J)) || J <- JIDList]; @@ -2171,11 +2182,11 @@ validate_muc2(Name, ArgName) -> end end. --spec validate_room(Name :: binary(), ArgName :: binary()) -> binary(). -validate_room(Name, ArgName) -> +-spec validate_room(Name :: binary()) -> binary(). +validate_room(Name) -> case jid:nodeprep(Name) of error -> - throw({error, <<"Invalid value of '",ArgName/binary,"'">>}); + throw({error, <<"Invalid value of room name">>}); Name2 -> Name2 end. From 3d2036db611fdc036131f3f2721832e4885b577e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 10 Dec 2024 17:39:08 +0100 Subject: [PATCH 0861/1302] mod_http_api: New option default_version The server administrator can define default API version for a vhost using the new module option: modules: mod_http_api: default_version: 1 The server administrator can define default API version for a port using the path: listen: - request_handlers: /api/v2: mod_http_api The client can use a specific API version, regardless of what the admin has set, by appending it in the URL: http://localhost:5280/api/v2/get_loglevel/v3 --- src/mod_http_api.erl | 49 +++++++++++++++++++++++++++++++--------- src/mod_http_api_opt.erl | 12 +++++----- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 44eae9733..037cc1e8a 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -31,7 +31,7 @@ -export([start/2, stop/1, reload/3, process/2, depends/2, format_arg/2, - mod_options/1, mod_doc/0]). + mod_opt_type/1, mod_options/1, mod_doc/0]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -201,19 +201,20 @@ extract_args(Data) -> maps:to_list(Maps). % get API version N from last "vN" element in URL path -get_api_version(#request{path = Path}) -> - get_api_version(lists:reverse(Path)); -get_api_version([<<"v", String/binary>> | Tail]) -> +get_api_version(#request{path = Path, host = Host}) -> + get_api_version(lists:reverse(Path), Host). + +get_api_version([<<"v", String/binary>> | Tail], Host) -> case catch binary_to_integer(String) of N when is_integer(N) -> N; _ -> - get_api_version(Tail) + get_api_version(Tail, Host) end; -get_api_version([_Head | Tail]) -> - get_api_version(Tail); -get_api_version([]) -> - ?DEFAULT_API_VERSION. +get_api_version([_Head | Tail], Host) -> + get_api_version(Tail, Host); +get_api_version([], Host) -> + mod_http_api_opt:default_version(Host). %% ---------------- %% command handlers @@ -549,8 +550,24 @@ hide_sensitive_args(Args=[_H|_T]) -> hide_sensitive_args(NonListArgs) -> NonListArgs. +mod_opt_type(default_version) -> + econf:either( + econf:int(0, 3), + econf:and_then( + econf:binary(), + fun(Binary) -> + case binary_to_list(Binary) of + F when F >= "24.06" -> + 2; + F when (F > "23.10") and (F < "24.06") -> + 1; + F when F =< "23.10" -> + 0 + end + end)). + mod_options(_) -> - []. + [{default_version, ?DEFAULT_API_VERSION}]. mod_doc() -> #{desc => @@ -565,6 +582,15 @@ mod_doc() -> "For example: '/api/v2: mod_http_api'."), "", ?T("To run a command, send a POST request to the corresponding " "URL: 'http://localhost:5280/api/COMMAND-NAME'")], + opts => + [{default_version, + #{value => "integer() | string()", + desc => + ?T("What API version to use when none is specified in the URL path. " + "If setting an ejabberd version, it will use the latest API " + "version that was available in that ejabberd version. " + "For example, setting '\"24.06\"' in this option implies '2'. " + "The default value is the latest version.")}}], example => ["listen:", " -", @@ -574,4 +600,5 @@ mod_doc() -> " /api: mod_http_api", "", "modules:", - " mod_http_api: {}"]}. + " mod_http_api:", + " default_version: 2"]}. diff --git a/src/mod_http_api_opt.erl b/src/mod_http_api_opt.erl index 3d928fc1b..7ab489b72 100644 --- a/src/mod_http_api_opt.erl +++ b/src/mod_http_api_opt.erl @@ -3,11 +3,11 @@ -module(mod_http_api_opt). --export([admin_ip_access/1]). +-export([default_version/1]). --spec admin_ip_access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl(). -admin_ip_access(Opts) when is_map(Opts) -> - gen_mod:get_opt(admin_ip_access, Opts); -admin_ip_access(Host) -> - gen_mod:get_module_opt(Host, mod_http_api, admin_ip_access). +-spec default_version(gen_mod:opts() | global | binary()) -> any(). +default_version(Opts) when is_map(Opts) -> + gen_mod:get_opt(default_version, Opts); +default_version(Host) -> + gen_mod:get_module_opt(Host, mod_http_api, default_version). From ae238bc9849c99f324e7bb21af0b2b09b91bd1e9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Dec 2024 12:18:58 +0100 Subject: [PATCH 0862/1302] mod_http_api: Fix crash when module not enabled (for example, in CT tests) --- src/mod_http_api.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 037cc1e8a..6df2f5612 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -214,7 +214,12 @@ get_api_version([<<"v", String/binary>> | Tail], Host) -> get_api_version([_Head | Tail], Host) -> get_api_version(Tail, Host); get_api_version([], Host) -> - mod_http_api_opt:default_version(Host). + try mod_http_api_opt:default_version(Host) + catch error:{module_not_loaded, ?MODULE, Host} -> + ?WARNING_MSG("Using module ~p for host ~s, but it isn't configured " + "in the configuration file", [?MODULE, Host]), + ?DEFAULT_API_VERSION + end. %% ---------------- %% command handlers From 2b02af13ba29a2cc1d6ddf8468ffc9820b34be20 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 11 Dec 2024 17:18:05 +0100 Subject: [PATCH 0863/1302] CONTAINER.md: Add kubernetes yaml examples to use with podman --- CONTAINER.md | 283 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 266 insertions(+), 17 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 38b37768f..84bf4536c 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -240,6 +240,7 @@ ejabberd reads `EJABBERD_MACRO_*` environment variables and uses them to define the `*` [macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file), overwriting the corresponding macro definition if it was set in the configuration file. +This is supported since ejabberd 24.12. For example, if you configure this in `ejabberd.yml`: @@ -273,12 +274,12 @@ For this you can either: Example to connect a local `ejabberdctl` to a containerized ejabberd: 1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: -```sh -docker run --name ejabberd -it \ - -e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \ - -p 5210:5210 -p 5222:5222 \ - ghcr.io/processone/ejabberd -``` + ```sh + docker run --name ejabberd -it \ + -e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \ + -p 5210:5210 -p 5222:5222 \ + ghcr.io/processone/ejabberd + ``` 2. Set `ERL_DIST_PORT=5210` in ejabberdctl.cfg of container and local ejabberd 3. Restart the container 4. Now use `ejabberdctl` in your local ejabberd deployment @@ -320,7 +321,7 @@ docker buildx build \ ### Podman build -It's also possible to use podman instead of docker, just notice: +To build the image using podman instead of docker, notice: - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile - It mentions that `healthcheck` is not supported by the Open Container Initiative image format - to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true` @@ -372,7 +373,9 @@ Composer Examples ### Minimal Example This is the barely minimal file to get a usable ejabberd. -Store it as `docker-compose.yml`: + +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: ```yaml services: @@ -386,11 +389,34 @@ services: - "5443:5443" ``` -Create and start the container with the command: -```bash -docker-compose up +If using Podman, write this `minimal.yml` file +and start it with `podman kube play minimal.yml`: + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: ejabberd + +spec: + containers: + + - name: ejabberd + image: ghcr.io/processone/ejabberd + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5269 + hostPort: 5269 + - containerPort: 5280 + hostPort: 5280 + - containerPort: 5443 + hostPort: 5443 ``` + ### Customized Example This example shows the usage of several customizations: @@ -420,7 +446,9 @@ mkdir database sudo chown 9000:9000 database ``` -Now write this `docker-compose.yml` file: +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: + ```yaml version: '3.7' @@ -444,6 +472,56 @@ services: - ./database:/opt/ejabberd/database ``` +If using Podman, write this `custom.yml` file +and start it with `podman kube play custom.yml`: + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: ejabberd + +spec: + containers: + + - name: ejabberd + image: ghcr.io/processone/ejabberd + env: + - name: CTL_ON_CREATE + value: register admin example.com asd + - name: CTL_ON_START + value: registered_users example.com ; + status + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5269 + hostPort: 5269 + - containerPort: 5280 + hostPort: 5280 + - containerPort: 5443 + hostPort: 5443 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + - mountPath: /opt/ejabberd/database + name: db + + volumes: + - name: config + hostPath: + path: ./ejabberd.yml + type: File + - name: db + hostPath: + path: ./database + type: DirectoryOrCreate +``` + + ### Clustering Example In this example, the main container is created first. @@ -458,6 +536,9 @@ and it should exist in the second node after join. Notice that in this example the main container does not have access to the exterior; the replica exports the ports and can be accessed. +If using Docker, write this `docker-compose.yml` file +and start it with `docker-compose up`: + ```yaml version: '3.7' @@ -477,15 +558,183 @@ services: depends_on: main: condition: service_healthy - ports: - - "5222:5222" - - "5269:5269" - - "5280:5280" - - "5443:5443" environment: - ERLANG_NODE_ARG=ejabberd@replica - ERLANG_COOKIE=dummycookie123 - CTL_ON_CREATE=join_cluster ejabberd@main - CTL_ON_START=registered_users localhost ; status + ports: + - "5222:5222" + - "5269:5269" + - "5280:5280" + - "5443:5443" +``` + +If using Podman, write this `cluster.yml` file +and start it with `podman kube play cluster.yml`. + +```yaml +apiVersion: v1 + +kind: Pod + +metadata: + name: cluster + +spec: + containers: + + - name: first + image: ghcr.io/processone/ejabberd + env: + - name: ERLANG_NODE_ARG + value: main@cluster + - name: ERLANG_COOKIE + value: dummycookie123 + - name: CTL_ON_CREATE + value: register admin localhost asd + - name: CTL_ON_START + value: stats registeredusers ; + status + - name: EJABBERD_MACRO_PORT_C2S + value: 6222 + - name: EJABBERD_MACRO_PORT_C2S_TLS + value: 6223 + - name: EJABBERD_MACRO_PORT_S2S + value: 6269 + - name: EJABBERD_MACRO_PORT_HTTP_TLS + value: 6443 + - name: EJABBERD_MACRO_PORT_HTTP + value: 6280 + - name: EJABBERD_MACRO_PORT_MQTT + value: 6883 + - name: EJABBERD_MACRO_PORT_PROXY65 + value: 6777 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + + - name: second + image: ghcr.io/processone/ejabberd + env: + - name: ERLANG_NODE_ARG + value: replica@cluster + - name: ERLANG_COOKIE + value: dummycookie123 + - name: CTL_ON_CREATE + value: join_cluster main@cluster ; + started ; + list_cluster + - name: CTL_ON_START + value: stats registeredusers ; + check_password admin localhost asd ; + status + ports: + - containerPort: 5222 + hostPort: 5222 + - containerPort: 5280 + hostPort: 5280 + volumeMounts: + - mountPath: /opt/ejabberd/conf/ejabberd.yml + name: config + readOnly: true + + volumes: + - name: config + hostPath: + path: ./conf/ejabberd.yml + type: File + +``` + +Your configuration file should use those macros to allow each ejabberd node +use different listening port numbers: + +```diff +diff --git a/ejabberd.yml.example b/ejabberd.yml.example +index 39e423a64..6e875b48f 100644 +--- a/ejabberd.yml.example ++++ b/ejabberd.yml.example +@@ -24,9 +24,19 @@ loglevel: info + # - /etc/letsencrypt/live/domain.tld/fullchain.pem + # - /etc/letsencrypt/live/domain.tld/privkey.pem + ++define_macro: ++ PORT_C2S: 5222 ++ PORT_C2S_TLS: 5223 ++ PORT_S2S: 5269 ++ PORT_HTTP_TLS: 5443 ++ PORT_HTTP: 5280 ++ PORT_STUN: 5478 ++ PORT_MQTT: 1883 ++ PORT_PROXY65: 7777 ++ + listen: + - +- port: 5222 ++ port: PORT_C2S + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 +@@ -34,7 +44,7 @@ listen: + access: c2s + starttls_required: true + - +- port: 5223 ++ port: PORT_C2S_TLS + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 +@@ -42,13 +52,13 @@ listen: + access: c2s + tls: true + - +- port: 5269 ++ port: PORT_S2S + ip: "::" + module: ejabberd_s2s_in + max_stanza_size: 524288 + shaper: s2s_shaper + - +- port: 5443 ++ port: PORT_HTTP_TLS + ip: "::" + module: ejabberd_http + tls: true +@@ -60,14 +70,14 @@ listen: + /upload: mod_http_upload + /ws: ejabberd_http_ws + - +- port: 5280 ++ port: PORT_HTTP + ip: "::" + module: ejabberd_http + request_handlers: + /admin: ejabberd_web_admin + /.well-known/acme-challenge: ejabberd_acme + - +- port: 5478 ++ port: PORT_STUN + ip: "::" + transport: udp + module: ejabberd_stun +@@ -77,7 +87,7 @@ listen: + ## The server's public IPv6 address: + # turn_ipv6_address: "2001:db8::3" + - +- port: 1883 ++ port: PORT_MQTT + ip: "::" + module: mod_mqtt + backlog: 1000 +@@ -207,6 +217,7 @@ modules: + mod_proxy65: + access: local + max_connections: 5 ++ port: PORT_PROXY65 + mod_pubsub: + access_createnode: pubsub_createnode + plugins: ``` From da06a500728186c0fc8351165de90d1be545005f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Dec 2024 15:00:17 +0100 Subject: [PATCH 0864/1302] WebAdmin: Use lowercase username and server authentication credentials --- src/ejabberd_web_admin.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index ba863a2e5..5a6dfa589 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -238,7 +238,7 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) -> {SJID, Pass} -> {HostOfRule, AccessRule} = get_acl_rule(RPath, Method), try jid:decode(SJID) of - #jid{user = <<"">>, server = User} -> + #jid{luser = <<"">>, lserver = User} -> case ejabberd_router:is_my_host(HostHTTP) of true -> get_auth_account(HostOfRule, AccessRule, User, HostHTTP, @@ -246,7 +246,7 @@ get_auth_admin(Auth, HostHTTP, RPath, Method) -> _ -> {unauthorized, <<"missing-server">>} end; - #jid{user = User, server = Server} -> + #jid{luser = User, lserver = Server} -> get_auth_account(HostOfRule, AccessRule, User, Server, Pass) catch _:{bad_jid, _} -> From 2caaa09c99880deb1c959e8af43729a286feebbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 10:56:11 +0100 Subject: [PATCH 0865/1302] Add support for XEP-0484: Fast Authentication Streamlining Tokens --- mix.exs | 4 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 13 +-- src/ejabberd_c2s.erl | 17 +++- src/mod_auth_fast.erl | 167 +++++++++++++++++++++++++++++++++++ src/mod_auth_fast_mnesia.erl | 123 ++++++++++++++++++++++++++ src/mod_auth_fast_opt.erl | 27 ++++++ 8 files changed, 344 insertions(+), 11 deletions(-) create mode 100644 src/mod_auth_fast.erl create mode 100644 src/mod_auth_fast_mnesia.erl create mode 100644 src/mod_auth_fast_opt.erl diff --git a/mix.exs b/mix.exs index 516aa9e49..14f96f653 100644 --- a/mix.exs +++ b/mix.exs @@ -135,7 +135,7 @@ defmodule Ejabberd.MixProject do {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, "~> 1.1.22"}, - {:fast_xml, "~> 1.1.53"}, + {:fast_xml, "~> 1.1.53", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, "~> 1.9"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "333f688da2f52c73f374a46df139789a48c45395", override: true}, {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index e59fa341a..f1fdafa90 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.9.0", "d92446bf51d36adda02db63b963fe6d4a1ede33e59b38a43d9b90afd20c25b74", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "c1b91be74a9a9503afa6766f756477516920ffbfeea0c260c2fa171355f53c27"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "333f688da2f52c73f374a46df139789a48c45395", [ref: "333f688da2f52c73f374a46df139789a48c45395"]}, "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, } diff --git a/rebar.config b/rebar.config index 75615f529..7009d5311 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.0"}}}, + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "333f688da2f52c73f374a46df139789a48c45395"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} ]}. diff --git a/rebar.lock b/rebar.lock index 926234015..f622208d0 100644 --- a/rebar.lock +++ b/rebar.lock @@ -10,7 +10,7 @@ {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.53">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, @@ -24,7 +24,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.0">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"333f688da2f52c73f374a46df139789a48c45395"}}, + 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", {ref,"9898754f16cbd4585a1c2061d72fa441ecb2e938"}}, @@ -55,8 +58,7 @@ {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"D92446BF51D36ADDA02DB63B963FE6D4A1EDE33E59B38A43D9B90AFD20C25B74">>}]}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, @@ -82,6 +84,5 @@ {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"C1B91BE74A9A9503AFA6766F756477516920FFBFEEA0C260C2FA171355F53C27">>}]} + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} ]. diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 55d9cebec..06ba6fb02 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -45,7 +45,8 @@ handle_unbinded_packet/2, inline_stream_features/1, handle_sasl2_inline/2, handle_sasl2_inline_post/3, handle_bind2_inline/2, handle_bind2_inline_post/3, sasl_options/1, - handle_sasl2_task_next/4, handle_sasl2_task_data/3]). + handle_sasl2_task_next/4, handle_sasl2_task_data/3, + get_fast_tokens_fun/2, fast_mechanisms/1]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, process_auth_result/3, reject_unauthenticated_packet/2, @@ -465,6 +466,20 @@ check_password_digest_fun(_Mech, #{lserver := LServer}) -> ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG) end. +get_fast_tokens_fun(_Mech, #{lserver := LServer}) -> + fun(User, UA) -> + case gen_mod:is_loaded(LServer, mod_auth_fast) of + false -> false; + _ -> mod_auth_fast:get_tokens(LServer, User, UA) + end + end. + +fast_mechanisms(#{lserver := LServer}) -> + case gen_mod:is_loaded(LServer, mod_auth_fast) of + false -> []; + _ -> mod_auth_fast:get_mechanisms(LServer) + end. + bind(<<"">>, State) -> bind(new_uniq_id(), State); bind(R, #{user := U, server := S, access := Access, lang := Lang, diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl new file mode 100644 index 000000000..6b15aedbb --- /dev/null +++ b/src/mod_auth_fast.erl @@ -0,0 +1,167 @@ +%%%------------------------------------------------------------------- +%%% Author : Pawel Chmielowski +%%% Created : 1 Dec 2024 by Pawel Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- +-module(mod_auth_fast). +-behaviour(gen_mod). +-protocol({xep, 484, '0.2.0', '24.12', "complete", ""}). + +%% gen_mod API +-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). +-export([mod_doc/0]). +%% Hooks +-export([c2s_inline_features/2, c2s_handle_sasl2_inline/1, + get_tokens/3, get_mechanisms/1]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include_lib("xmpp/include/scram.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). + +-callback get_tokens(binary(), binary(), binary()) -> + [{current | next, binary(), non_neg_integer()}]. +-callback rotate_token(binary(), binary(), binary()) -> + ok | {error, atom()}. +-callback del_token(binary(), binary(), binary(), current | next) -> + ok | {error, atom()}. +-callback set_token(binary(), binary(), binary(), current | next, binary(), non_neg_integer()) -> + ok | {error, atom()}. + +%%%=================================================================== +%%% API +%%%=================================================================== +-spec start(binary(), gen_mod:opts()) -> {ok, [gen_mod:registration()]}. +start(Host, Opts) -> + Mod = gen_mod:db_mod(Opts, ?MODULE), + Mod:init(Host, Opts), + {ok, [{hook, c2s_inline_features, c2s_inline_features, 50}, + {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 10}]}. + +-spec stop(binary()) -> ok. +stop(_Host) -> + ok. + +-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. +reload(Host, NewOpts, OldOpts) -> + NewMod = gen_mod:db_mod(NewOpts, ?MODULE), + OldMod = gen_mod:db_mod(OldOpts, ?MODULE), + if NewMod /= OldMod -> + NewMod:init(Host, NewOpts); + true -> + ok + end, + ok. + +-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. +depends(_Host, _Opts) -> + []. + +-spec mod_opt_type(atom()) -> econf:validator(). +mod_opt_type(db_type) -> + econf:db_type(?MODULE); +mod_opt_type(token_lifetime) -> + econf:timeout(second); +mod_opt_type(token_refresh_age) -> + econf:timeout(second). + +-spec mod_options(binary()) -> [{atom(), any()}]. +mod_options(Host) -> + [{db_type, ejabberd_config:default_db(Host, ?MODULE)}, + {token_lifetime, timer:hours(30*24)}, + {token_refresh_age, timer:hours(24)}]. + +mod_doc() -> + #{desc => + [?T("The module adds support for " + "https://xmpp.org/extensions/xep-0484.html" + "[XEP-0480: Fast Authentication Streamlining Tokens] that allows users to authenticate " + "using self managed tokens.")], + note => "added in 24.12", + opts => + [{db_type, + #{value => "mnesia | sql", + desc => + ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, + {token_lifetime, + #{value => "timeout()", + desc => ?T("Time that tokens will be keept, measured from it's creation time. " + "Default value set to 30 days")}}, + {token_refresh_age, + #{value => "timeout()", + desc => ?T("This time determines age of token, that qualifies for automatic refresh. " + "Default value set to 1 day")}}], + example => + ["modules:", + " mod_auth_fast:", + " token_timeout: 14days"]}. + +get_mechanisms(_LServer) -> + [<<"HT-SHA-256-NONE">>, <<"HT-SHA-256-UNIQ">>, <<"HT-SHA-256-EXPR">>, <<"HT-SHA-256-ENDP">>]. + +ua_hash(UA) -> + crypto:hash(sha256, UA). + +get_tokens(LServer, LUser, UA) -> + Mod = gen_mod:db_mod(LServer, ?MODULE), + ToRefresh = erlang:system_time(second) - mod_auth_fast_opt:token_refresh_age(LServer), + lists:map( + fun({Type, Token, CreatedAt}) -> + {{Type, CreatedAt < ToRefresh}, Token} + end, Mod:get_tokens(LServer, LUser, ua_hash(UA))). + +c2s_inline_features({Sasl, Bind, Extra}, Host) -> + {Sasl ++ [#fast{mechs = get_mechanisms(Host)}], Bind, Extra}. + +gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) -> + Mod = gen_mod:db_mod(Server, ?MODULE), + Token = base64url:encode(ua_hash(<>)), + ExpiresAt = erlang:system_time(second) + mod_auth_fast_opt:token_lifetime(Server), + Mod:set_token(Server, User, ua_hash(UA), next, Token, ExpiresAt), + #fast_token{token = Token, expiry = misc:usec_to_now(ExpiresAt)}. + +c2s_handle_sasl2_inline({#{server := Server, user := User, sasl2_ua_id := UA, + sasl2_axtra_auth_info := Extra} = State, Els, Results} = Acc) -> + Mod = gen_mod:db_mod(Server, ?MODULE), + ?ERROR_MSG("inl ~p", [Extra]), + NeedRegen = + case Extra of + {token, {next, Rotate}} -> + Mod:rotate_token(Server, User, ua_hash(UA)), + Rotate; + {token, {_, true}} -> + true; + _ -> + false + end, + case {lists:keyfind(fast_request_token, 1, Els), lists:keyfind(fast, 1, Els)} of + {#fast_request_token{mech = _Mech}, #fast{invalidate = true}} -> + Mod:del_token(Server, User, ua_hash(UA), current), + {State, Els, [gen_token(State) | Results]}; + {_, #fast{invalidate = true}} -> + Mod:del_token(Server, User, ua_hash(UA), current), + Acc; + {#fast_request_token{mech = _Mech}, _} -> + {State, Els, [gen_token(State) | Results]}; + _ when NeedRegen -> + {State, Els, [gen_token(State) | Results]}; + _ -> + Acc + end. diff --git a/src/mod_auth_fast_mnesia.erl b/src/mod_auth_fast_mnesia.erl new file mode 100644 index 000000000..17793e10d --- /dev/null +++ b/src/mod_auth_fast_mnesia.erl @@ -0,0 +1,123 @@ +%%%------------------------------------------------------------------- +%%% File : mod_announce_mnesia.erl +%%% Author : Pawel Chmielowski +%%% Created : 1 Dec 2024 by Pawel Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_auth_fast_mnesia). + +-behaviour(mod_auth_fast). + +%% API +-export([init/2]). +-export([get_tokens/3, del_token/4, set_token/6, rotate_token/3]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). + +-record(mod_auth_fast, {key = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary()} | '$1', + token = <<>> :: binary() | '_', + created_at = 0 :: non_neg_integer() | '_', + expires_at = 0 :: non_neg_integer() | '_'}). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ejabberd_mnesia:create(?MODULE, mod_auth_fast, + [{disc_only_copies, [node()]}, + {attributes, + record_info(fields, mod_auth_fast)}]). + +-spec get_tokens(binary(), binary(), binary()) -> + [{current | next, binary(), non_neg_integer()}]. +get_tokens(LServer, LUser, UA) -> + Now = erlang:system_time(second), + case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, next)}) of + [#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] when Expires > Now -> + [{next, Token, Created}]; + [#mod_auth_fast{}] -> + del_token(LServer, LUser, UA, next), + []; + _ -> + [] + end ++ + case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, current)}) of + [#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] when Expires > Now -> + [{current, Token, Created}]; + [#mod_auth_fast{}] -> + del_token(LServer, LUser, UA, current), + []; + _ -> + [] + end. + +-spec rotate_token(binary(), binary(), binary()) -> + ok | {error, atom()}. +rotate_token(LServer, LUser, UA) -> + F = fun() -> + case mnesia:dirty_read(mod_auth_fast, {LServer, LUser, token_id(UA, next)}) of + [#mod_auth_fast{token = Token, created_at = Created, expires_at = Expires}] -> + mnesia:write(#mod_auth_fast{key = {LServer, LUser, token_id(UA, current)}, + token = Token, created_at = Created, + expires_at = Expires}), + mnesia:delete({mod_auth_fast, {LServer, LUser, token_id(UA, next)}}); + _ -> + ok + end + end, + transaction(F). + +-spec del_token(binary(), binary(), binary(), current | next) -> + ok | {error, atom()}. +del_token(LServer, LUser, UA, Type) -> + F = fun() -> + mnesia:delete({mod_auth_fast, {LServer, LUser, token_id(UA, Type)}}) + end, + transaction(F). + +-spec set_token(binary(), binary(), binary(), current | next, binary(), non_neg_integer()) -> + ok | {error, atom()}. +set_token(LServer, LUser, UA, Type, Token, Expires) -> + F = fun() -> + mnesia:write(#mod_auth_fast{key = {LServer, LUser, token_id(UA, Type)}, + token = Token, created_at = erlang:system_time(second), + expires_at = Expires}) + end, + transaction(F). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== + +token_id(UA, current) -> + <<"c:", UA/binary>>; +token_id(UA, _) -> + <<"n:", UA/binary>>. + +transaction(F) -> + case mnesia:transaction(F) of + {atomic, Res} -> + Res; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} + end. diff --git a/src/mod_auth_fast_opt.erl b/src/mod_auth_fast_opt.erl new file mode 100644 index 000000000..19578aa2c --- /dev/null +++ b/src/mod_auth_fast_opt.erl @@ -0,0 +1,27 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_auth_fast_opt). + +-export([db_type/1]). +-export([token_lifetime/1]). +-export([token_refresh_age/1]). + +-spec db_type(gen_mod:opts() | global | binary()) -> atom(). +db_type(Opts) when is_map(Opts) -> + gen_mod:get_opt(db_type, Opts); +db_type(Host) -> + gen_mod:get_module_opt(Host, mod_auth_fast, db_type). + +-spec token_lifetime(gen_mod:opts() | global | binary()) -> pos_integer(). +token_lifetime(Opts) when is_map(Opts) -> + gen_mod:get_opt(token_lifetime, Opts); +token_lifetime(Host) -> + gen_mod:get_module_opt(Host, mod_auth_fast, token_lifetime). + +-spec token_refresh_age(gen_mod:opts() | global | binary()) -> pos_integer(). +token_refresh_age(Opts) when is_map(Opts) -> + gen_mod:get_opt(token_refresh_age, Opts); +token_refresh_age(Host) -> + gen_mod:get_module_opt(Host, mod_auth_fast, token_refresh_age). + From 549a2b07900999396c92b9e44a1d20d7913642a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 11:01:11 +0100 Subject: [PATCH 0866/1302] Remove message left from debugging --- src/mod_auth_fast.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index 6b15aedbb..d905aa41c 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -140,7 +140,6 @@ gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) -> c2s_handle_sasl2_inline({#{server := Server, user := User, sasl2_ua_id := UA, sasl2_axtra_auth_info := Extra} = State, Els, Results} = Acc) -> Mod = gen_mod:db_mod(Server, ?MODULE), - ?ERROR_MSG("inl ~p", [Extra]), NeedRegen = case Extra of {token, {next, Rotate}} -> From 01955b867db3f513ecfe098dde033013781171b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 11:13:56 +0100 Subject: [PATCH 0867/1302] Fix dialyzer warning --- src/mod_auth_fast.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index d905aa41c..aa47f4780 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -132,7 +132,7 @@ c2s_inline_features({Sasl, Bind, Extra}, Host) -> gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) -> Mod = gen_mod:db_mod(Server, ?MODULE), - Token = base64url:encode(ua_hash(<>)), + Token = base64:encode(ua_hash(<>)), ExpiresAt = erlang:system_time(second) + mod_auth_fast_opt:token_lifetime(Server), Mod:set_token(Server, User, ua_hash(UA), next, Token, ExpiresAt), #fast_token{token = Token, expiry = misc:usec_to_now(ExpiresAt)}. From 1481734f474bf16d311934a0c8871abc96d45ab9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 10:04:31 +0100 Subject: [PATCH 0868/1302] Update version number to 24.12 --- src/ejabberd_admin.erl | 2 +- src/ejabberd_options_doc.erl | 2 +- src/mod_muc_admin.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index fcfbb840a..1c5387223 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -175,7 +175,7 @@ get_commands_spec() -> "announcement quoted, for example: \n" "`ejabberdctl evacuate_kindly 60 " "\\\"The server will stop in one minute.\\\"`", - note = "added in 24.xx", + note = "added in 24.12", module = ?MODULE, function = evacuate_kindly, args_desc = ["Seconds to wait", "Announcement to send, with quotes"], args_example = [60, <<"Server will stop now.">>], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 4a4ade0ed..181599afa 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1146,7 +1146,7 @@ doc() -> "or 'ram' if the latter is not set.")}}, {redis_server, #{value => "Host | IP Address | Unix Socket Path", - note => "improved in 24.xx", + note => "improved in 24.12", desc => ?T("A hostname, IP address or unix domain socket file of the " "_`database.md#redis|Redis`_ server. " diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 5d3b80934..fc372597e 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -185,7 +185,7 @@ get_commands_spec() -> #ejabberd_commands{name = create_rooms_file, tags = [muc], desc = "Create the rooms indicated in file", longdesc = "Provide one room JID per line. Rooms will be created after restart.", - note = "improved in 24.xx", + note = "improved in 24.12", module = ?MODULE, function = create_rooms_file, args_desc = ["Path to the text file with one room JID per line"], args_example = ["/home/ejabberd/rooms.txt"], From 7726904f7902ec671b22915625975dcc8230b1b7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 11:57:53 +0100 Subject: [PATCH 0869/1302] Fix comment about file names --- src/mod_auth_fast.erl | 1 + src/mod_auth_fast_mnesia.erl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index aa47f4780..36bd9cf83 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -1,4 +1,5 @@ %%%------------------------------------------------------------------- +%%% File : mod_auth_fast.erl %%% Author : Pawel Chmielowski %%% Created : 1 Dec 2024 by Pawel Chmielowski %%% diff --git a/src/mod_auth_fast_mnesia.erl b/src/mod_auth_fast_mnesia.erl index 17793e10d..4e70bc0fa 100644 --- a/src/mod_auth_fast_mnesia.erl +++ b/src/mod_auth_fast_mnesia.erl @@ -1,5 +1,5 @@ %%%------------------------------------------------------------------- -%%% File : mod_announce_mnesia.erl +%%% File : mod_auth_fast_mnesia.erl %%% Author : Pawel Chmielowski %%% Created : 1 Dec 2024 by Pawel Chmielowski %%% From a16ef68a49c0169333cf0ede637f0978d064cda3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 11:59:48 +0100 Subject: [PATCH 0870/1302] Result of running "make format" --- src/ejabberd_web_admin.erl | 2 +- src/mod_admin_extra.erl | 6 +++--- src/mod_muc_admin.erl | 2 -- src/mod_privilege.erl | 3 +-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 5a6dfa589..1b945c794 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -2286,7 +2286,7 @@ make_result(Binary, Second = proplists:get_value(second, ArgumentsUsed), FirstUrlencoded = list_to_binary(string:replace( - misc:url_encode(First), "%40", "@")), + misc:url_encode(First), "%40", "@")), {GroupId, Host} = case jid:decode(FirstUrlencoded) of #jid{luser = <<"">>, server = G} -> diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 85ab9a367..79b523c9b 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2325,9 +2325,9 @@ web_page_node(_, Node, #request{path = [<<"stats">>]} = R) -> ejabberd_web_admin, make_command, [stats, R, [{<<"name">>, <<"uptimeseconds">>}], [{only, value}]]), - UpDaysBin = integer_to_binary( - binary_to_integer(fxml:get_tag_cdata(UpSecs)) - div 86400), % 24*60*60 + UpDaysBin = + integer_to_binary(binary_to_integer(fxml:get_tag_cdata(UpSecs)) + div 86400), % 24*60*60 UpDays = #xmlel{name = <<"code">>, attrs = [], diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index fc372597e..3f4c1c6a8 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -859,8 +859,6 @@ webadmin_muc_host(_Host, [{table_options, {20, RPath}}, {result_links, [{jid, user, 3 + Level, <<"">>}]}])], Title ++ Breadcrumb ++ Get ++ Set; - - webadmin_muc_host(_Host, Service, [<<"rooms">>, <<"room">>, Name, <<"history">> | RPath], diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index d5ceda8ee..65e962f2a 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -308,8 +308,7 @@ component_send_packet({#iq{from = From, []), drop; {_, {error, no_privileged_iq, _Err}} -> - ?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.", - []), + ?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.", []), drop; {_, {error, roster_query, _Err}} -> IQ; From 1024cbe5b3f51757fba174abbeaa77bf10b9f72b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:04:28 +0100 Subject: [PATCH 0871/1302] Result of running "make doap" --- ejabberd.doap | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ejabberd.doap b/ejabberd.doap index cfb3b1cb9..28a68863c 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -776,12 +776,21 @@ - 0.1 + 0.2.0 24.10 complete mod_scram_upgrade + + + + 0.2.0 + 24.12 + complete + mod_auth_fast + + From 8e9ea2d98c74a82d2b50a0ac47b6a3da5d2eadb1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:19:43 +0100 Subject: [PATCH 0872/1302] Update Bulgarian translation (thanks to MrEddX) --- priv/msgs/bg.msg | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg index 4a4969e7b..128de8673 100644 --- a/priv/msgs/bg.msg +++ b/priv/msgs/bg.msg @@ -161,7 +161,7 @@ {"Get Pending","Виж чакащи"}. {"Get User Last Login Time","Покажи времето, когато потребителят е влязъл за последно"}. {"Get User Statistics","Покажи статистика за потребителя"}. -{"Given Name","Име"}. +{"Given Name","Наименование"}. {"Grant voice to this person?","Предоставяне на глас за потребителя?"}. {"has been banned","е със забранен достъп"}. {"has been kicked because of a system shutdown","е отстранен поради изключване на системата"}. @@ -295,6 +295,7 @@ {"No pending subscriptions found","Не са намерени чакащи абонаменти"}. {"No privacy list with this name found","Не е намерен списък за поверителност с това име"}. {"No private data found in this query","Няма открити лични данни в тази заявка"}. +{"No element found","Елементът не е намерен"}. {"No running node found","Не е намерен работещ нод"}. {"No services available","Няма налични услуги"}. {"No statistics found for this item","Не е налична статистика за този елемент"}. @@ -512,7 +513,7 @@ {"The stanza MUST contain only one element, one element, or one element","Строфата ТРЯБВА да съдържа само един елемент, един елемент или един елемент"}. {"The subscription identifier associated with the subscription request","Идентификаторът на абонамента, свързан със заявката за абонамент"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL адрес на XSL трансформацията, която може да се приложи към прикачените данни, за да се генерира подходящ елемент от тялото на съобщението."}. -{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL адрес на XSL трансформацията, която може да се приложи към формата на прикачените данни, за да се генерира валиден резултат от Data Forms, който клиентът може да покаже с помощта на общ механизъм за визуализация на Data Forms"}. {"There was an error changing the password: ","Възникна грешка при промяна на паролата: "}. {"There was an error creating the account: ","Възникна грешка при създаването на профила: "}. {"There was an error deleting the account: ","Възникна грешка при изтриването на профила: "}. @@ -537,6 +538,7 @@ {"Too many unacked stanzas","Твърде много непотвърдени строфи"}. {"Too many users in this conference","Твърде много потребители в тази конференция"}. {"Traffic rate limit is exceeded","Лимитът за трафик е надвишен"}. +{"~ts's MAM Archive","~ts's MAM архив"}. {"~ts's Offline Messages Queue","Офлайн съобщения на ~ts"}. {"Tuesday","Вторник"}. {"Unable to generate a CAPTCHA","Не може да се генерира CAPTCHA"}. @@ -553,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","Актуализирай съобщението на деня на всички хостове (не изпращай)"}. {"Update specs to get modules source, then install desired ones.","Актуализирайте спецификациите, за да получите източник на модули, след което инсталирайте желаните."}. {"Update Specs","Актуализирай спецификациите"}. +{"Updating the vCard is not supported by the vCard storage backend","Актуализирането на vCard не се поддържа от настройката за съхранение на vCard"}. {"Upgrade","Обнови"}. {"URL for Archived Discussion Logs","URL адрес за дневници на архивирани дискусии"}. {"User already exists","Потребителят вече съществува"}. {"User (jid)","Потребител (jid)"}. {"User JID","Потребител JID"}. {"User Management","Управление на потребители"}. +{"User not allowed to perform an IQ set on another user's vCard.","Потребителят не може да извършва IQ настрийка на vCard на друг потребител."}. {"User removed","Потребителят е премахнат"}. {"User session not found","Потребителската сесия не е намерена"}. {"User session terminated","Потребителската сесия е прекратена"}. From 4fd26306fec85d34a3fa3e79eb261213aed385f8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:20:33 +0100 Subject: [PATCH 0873/1302] Update German translation (thanks to Nautilusx) --- priv/msgs/de.msg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index 484522c2a..4e4bc0b3a 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -223,6 +223,7 @@ {"leaves the room","verlässt den Raum"}. {"List of users with hats","Liste der Benutzer mit Funktionen"}. {"List users with hats","Benutzer mit Funktionen auflisten"}. +{"Logged Out","Abgemeldet"}. {"Logging","Protokollierung"}. {"Make participants list public","Teilnehmerliste öffentlich machen"}. {"Make room CAPTCHA protected","Raum mittels CAPTCHA schützen"}. @@ -293,6 +294,7 @@ {"No pending subscriptions found","Keine ausstehenden Abonnements gefunden"}. {"No privacy list with this name found","Keine Privacy-Liste mit diesem Namen gefunden"}. {"No private data found in this query","Keine privaten Daten in dieser Anfrage gefunden"}. +{"No element found","Kein -Element gefunden"}. {"No running node found","Kein laufender Knoten gefunden"}. {"No services available","Keine Dienste verfügbar"}. {"No statistics found for this item","Keine Statistiken für dieses Item gefunden"}. @@ -532,6 +534,7 @@ {"Too many unacked stanzas","Zu viele unbestätigte Stanzas"}. {"Too many users in this conference","Zu viele Benutzer in dieser Konferenz"}. {"Traffic rate limit is exceeded","Datenratenlimit wurde überschritten"}. +{"~ts's MAM Archive","~ts's MAM Archiv"}. {"~ts's Offline Messages Queue","Offline-Nachrichten-Warteschlange von ~ts"}. {"Tuesday","Dienstag"}. {"Unable to generate a CAPTCHA","Konnte kein CAPTCHA erstellen"}. @@ -588,6 +591,7 @@ {"Whether to allow subscriptions","Ob Abonnements erlaubt sind"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Ob alle Abonnements temporär gemacht werden sollen, basierend auf der Abonnentenpräsenz"}. {"Whether to notify owners about new subscribers and unsubscribes","Ob Besitzer über neue Abonnenten und Abbestellungen benachrichtigt werden sollen"}. +{"Who can send private messages","Wer kann private Nachrichten senden"}. {"Who may associate leaf nodes with a collection","Wer Blattknoten mit einer Sammlung verknüpfen darf"}. {"Wrong parameters in the web formulary","Falsche Parameter im Webformular"}. {"Wrong xmlns","Falscher xmlns"}. From b84596be57fe0699ebfb868e12d184bde353fcb9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:21:13 +0100 Subject: [PATCH 0874/1302] Update French translation (thanks to ButterflyOfFire) --- priv/msgs/fr.msg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/priv/msgs/fr.msg b/priv/msgs/fr.msg index 7d092971b..0f8e99eeb 100644 --- a/priv/msgs/fr.msg +++ b/priv/msgs/fr.msg @@ -184,6 +184,7 @@ {"Incorrect value of 'action' attribute","Valeur de l'attribut 'action' incorrecte"}. {"Incorrect value of 'action' in data form","Valeur de l'attribut 'action' incorrecte dans le formulaire"}. {"Incorrect value of 'path' in data form","Valeur de l'attribut 'path' incorrecte dans le formulaire"}. +{"Install","Installer"}. {"Insufficient privilege","Droits insuffisants"}. {"Internal server error","Erreur interne du serveur"}. {"Invalid 'from' attribute in forwarded message","L'attribut 'from' du message transféré est incorrect"}. @@ -480,12 +481,14 @@ {"Unauthorized","Non autorisé"}. {"Unexpected action","Action inattendu"}. {"Unexpected error condition: ~p","Condition d’erreur inattendue : ~p"}. +{"Uninstall","Désinstaller"}. {"Unregister an XMPP account","Annuler l’enregistrement d’un compte XMPP"}. {"Unregister","Désinscrire"}. {"Unsupported element","Elément non supporté"}. {"Unsupported version","Version non prise en charge"}. {"Update message of the day (don't send)","Mise à jour du message du jour (pas d'envoi)"}. {"Update message of the day on all hosts (don't send)","Mettre à jour le message du jour sur tous les domaines (ne pas envoyer)"}. +{"Upgrade","Mise à niveau"}. {"URL for Archived Discussion Logs","URL des journaux de discussion archivés"}. {"User already exists","L'utilisateur existe déjà"}. {"User JID","JID de l'utilisateur"}. From 7008e49675bb8a9ab640adcd0b0d902f506be9b3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:22:02 +0100 Subject: [PATCH 0875/1302] Update Italian translation (thanks to Ermete Melchiorre) --- priv/msgs/it.msg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index abc39dd01..f7b5c5451 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -168,6 +168,7 @@ {"has been kicked because of an affiliation change","è stato espulso a causa di un cambiamento di appartenenza"}. {"has been kicked because the room has been changed to members-only","è stato espulso per la limitazione della stanza ai soli membri"}. {"has been kicked","è stata/o espulsa/o"}. +{"Hash of the vCard-temp avatar of this room","Hash dell'avatar vCard-temp di questa stanza"}. {"Hat title","Titolo del Cappello"}. {"Hat URI","URI Cappello"}. {"Hats limit exceeded","Limite di cappelli superato"}. @@ -294,6 +295,7 @@ {"No pending subscriptions found","Nessuna sottoscrizione in attesa trovata"}. {"No privacy list with this name found","Nessun elenco di privacy con questo nome trovato"}. {"No private data found in this query","Non sono stati trovati dati privati in questa query"}. +{"No element found","Nessun elemento trovato"}. {"No running node found","Nessun nodo in esecuzione trovato"}. {"No services available","Nessun servizio disponibile"}. {"No statistics found for this item","Nessuna statistica trovata per questa voce"}. @@ -536,6 +538,7 @@ {"Too many unacked stanzas","Troppe stanze non riconosciute"}. {"Too many users in this conference","Troppi utenti in questa conferenza"}. {"Traffic rate limit is exceeded","Limite di traffico superato"}. +{"~ts's MAM Archive","Archivio MAM di ~ts"}. {"~ts's Offline Messages Queue","La Coda dei Messaggi Offline di ~ts"}. {"Tuesday","Martedì"}. {"Unable to generate a CAPTCHA","Impossibile generare un CAPTCHA"}. @@ -552,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","Aggiornare il messaggio del giorno (MOTD) su tutti gli host (non inviarlo)"}. {"Update specs to get modules source, then install desired ones.","Aggiorna le specifiche per ottenere il sorgente dei moduli, quindi installa quelli desiderati."}. {"Update Specs","Aggiorna Specifiche"}. +{"Updating the vCard is not supported by the vCard storage backend","L'aggiornamento della vCard non è supportato dal backend di archiviazione vCard"}. {"Upgrade","Aggiornamento"}. {"URL for Archived Discussion Logs","URL per i Registri delle Discussioni Archiviati"}. {"User already exists","L'utente esiste già"}. {"User JID","JID utente"}. {"User (jid)","Utente (jid)"}. {"User Management","Gestione degli utenti"}. +{"User not allowed to perform an IQ set on another user's vCard.","L'utente non è autorizzato a eseguire un set IQ sulla vCard di un altro utente."}. {"User removed","Utente rimosso"}. {"User session not found","Sessione utente non trovata"}. {"User session terminated","Sessione utente terminata"}. From 4334ce9c29112fb283541d24b6da278480df6e4b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:22:27 +0100 Subject: [PATCH 0876/1302] Update Portuguese (Brazil) translation (thanks to Wellington Uemura) --- priv/msgs/pt-br.msg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 60aeddf51..882c03ec2 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -295,6 +295,7 @@ {"No pending subscriptions found","Não foram encontradas subscrições"}. {"No privacy list with this name found","Nenhuma lista de privacidade encontrada com este nome"}. {"No private data found in this query","Nenhum dado privado encontrado nesta consulta"}. +{"No element found","Nenhum elemento foi encontrado"}. {"No running node found","Nenhum nó em execução foi encontrado"}. {"No services available","Não há serviços disponíveis"}. {"No statistics found for this item","Não foram encontradas estatísticas para este item"}. @@ -537,6 +538,7 @@ {"Too many unacked stanzas","Número excessivo de instâncias sem confirmação"}. {"Too many users in this conference","Há uma quantidade excessiva de usuários nesta conferência"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. +{"~ts's MAM Archive","Arquivo ~ts's MAM"}. {"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. @@ -553,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. {"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. {"Update Specs","Atualizar as especificações"}. +{"Updating the vCard is not supported by the vCard storage backend","A atualização do vCard não é compatível com o back-end de armazenamento do vCard"}. {"Upgrade","Atualização"}. {"URL for Archived Discussion Logs","A URL para o arquivamento dos registros da discussão"}. {"User already exists","Usuário já existe"}. {"User (jid)","Usuário (jid)"}. {"User JID","Usuário JID"}. {"User Management","Gerenciamento de Usuários"}. +{"User not allowed to perform an IQ set on another user's vCard.","O usuário não tem permissão para executar um conjunto de QI no vCard de outro usuário."}. {"User removed","O usuário foi removido"}. {"User session not found","A sessão do usuário não foi encontrada"}. {"User session terminated","Sessão de usuário terminada"}. From 863f2e019c9f0fea2bf0195af84b592de9c08250 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:23:18 +0100 Subject: [PATCH 0877/1302] Update Albanian translation (thanks to Besnik Bleta) --- priv/msgs/sq.msg | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index 12f356901..4469cea98 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -50,6 +50,7 @@ {"Changing role/affiliation is not allowed","Nuk lejohet ndryshim roli/përkatësie"}. {"Channel already exists","Kanali ekziston tashmë"}. {"Channel does not exist","Kanali s’ekziston"}. +{"Channel JID","JID Kanali"}. {"Channels","Kanale"}. {"Characters not allowed:","Shenja të palejuara:"}. {"Chatroom configuration modified","Ndryshoi formësimi i dhomës së fjalosjeve"}. @@ -99,6 +100,8 @@ {"Full List of Room Admins","Listë e Plotë Përgjegjësish Dhome"}. {"Full List of Room Owners","Listë e Plotë të Zotësh Dhome"}. {"Full Name","Emër i Plotë"}. +{"Get List of Online Users","Merr Listë Përdoruesish Në Linjë"}. +{"Get List of Registered Users","Merr Listë Përdoruesish të Regjistruar"}. {"Get Number of Online Users","Merr Numër Përdoruesish Në Linjë"}. {"Get Number of Registered Users","Merr Numër Përdoruesish të Regjistruar"}. {"Get User Statistics","Merr Statistika Përdoruesi"}. @@ -106,6 +109,7 @@ {"Grant voice to this person?","T’i akordohet zë këtij personi?"}. {"has been banned","është dëbuar"}. {"has been kicked","është përzënë"}. +{"Hat title","Titull kapeleje"}. {"Host unknown","Strehë e panjohur"}. {"HTTP File Upload","Ngarkim Kartelash HTTP"}. {"Idle connection","Lidhje e plogësht"}. @@ -182,6 +186,7 @@ {"No node specified","S’u përcaktua nyjë"}. {"No pending subscriptions found","S’u gjetën pajtime pezull"}. {"No privacy list with this name found","S’u gjet listë privatësie me atë emër"}. +{"No element found","S’u gjetën elementë "}. {"No running node found","S’u gjet nyjë në funksionim"}. {"No services available","S’ka shërbime të gatshme"}. {"No statistics found for this item","S’u gjetën statistika për këtë objekt"}. @@ -191,6 +196,7 @@ {"Node index not found","S’u gjet tregues nyje"}. {"Node not found","S’u gjet nyjë"}. {"Node ~p","Nyjë ~p"}. +{"Node","Nyjë"}. {"Nodes","Nyja"}. {"None","Asnjë"}. {"Not allowed","E palejuar"}. @@ -203,6 +209,7 @@ {"Number of online users","Numër përdoruesish në linjë"}. {"Number of registered users","Numër përdoruesish të regjistruar"}. {"Occupants are allowed to invite others","Të pranishmëve u është lejuar të ftojnë të tjerë"}. +{"Occupants are allowed to query others","Të pranishmëve u është lejuar t’u bëjnë kërkim të tjerëve"}. {"Occupants May Change the Subject","Të pranishmit Mund të Ndryshojnë Subjektin"}. {"October","Tetor"}. {"OK","OK"}. @@ -210,12 +217,14 @@ {"Online Users","Përdorues Në Linjë"}. {"Online","Në linjë"}. {"Only deliver notifications to available users","Dorëzo njoftime vetëm te përdoruesit e pranishëm"}. +{"Only moderators are allowed to retract messages","Vetëm të moderatorëve u lejohet të tërheqin mbrapsht mesazhe"}. {"Only occupants are allowed to send messages to the conference","Vetëm të pranishmëve u lejohet të dërgojnë mesazhe te konferenca"}. {"Only publishers may publish","Vetëm botuesit mund të botojnë"}. {"Organization Name","Emër Enti"}. {"Organization Unit","Njësi Organizative"}. {"Outgoing s2s Connections","Lidhje s2s Ikëse"}. {"Owner privileges required","Lypset privilegje të zoti"}. +{"Participant ID","ID Pjesëmarrësi"}. {"Participant","Pjesëmarrës"}. {"Password Verification","Verifikim Fjalëkalimi"}. {"Password Verification:","Verifikim Fjalëkalimi:"}. @@ -266,6 +275,7 @@ {"Specify the access model","Specifikoni model hyrjeje"}. {"Specify the event message type","Përcaktoni llojin e mesazhit për aktin"}. {"Specify the publisher model","Përcaktoni model botuesi"}. +{"Stanza id is not valid","ID Stanza s’është i vlefshëm"}. {"Stopped Nodes","Nyja të Ndalura"}. {"Subject","Subjekti"}. {"Submitted","Parashtruar"}. @@ -277,6 +287,7 @@ {"The default language of the node","Gjuha parazgjedhje e nyjës"}. {"The feature requested is not supported by the conference","Veçoria e kërkuar nuk mbulohen nga konferenca"}. {"The JID of the node creator","JID i krijjuesit të nyjës"}. +{"The list of all online users","Lista e krejt përdoruesve në linjë"}. {"The name of the node","Emri i nyjës"}. {"The number of subscribers to the node","Numri i pajtimtarëve te nyja"}. {"The number of unread or undelivered messages","Numri i mesazheve të palexuar ose të padorëzuar"}. @@ -303,6 +314,7 @@ {"Unregister an XMPP account","Çregjistroni një llogari XMPP"}. {"Unregister","Çregjistrohuni"}. {"Unsupported version","Version i pambuluar"}. +{"Updating the vCard is not supported by the vCard storage backend","Përditësimi i vCard-it nuk mbulohet nga mekanizmi i depozitimit të vCard-ve"}. {"User already exists","Ka tashmë një përdorues të tillë"}. {"User JID","JID përdoruesi"}. {"User (jid)","Përdorues (jid)"}. @@ -319,9 +331,11 @@ {"Wednesday","E mërkurë"}. {"When a new subscription is processed","Kur përpunohet një pajtim i ri"}. {"Whether to allow subscriptions","Nëse duhen lejuar apo jo pajtime"}. +{"Who can send private messages","Cilët mund të dërgojnë mesazhe private"}. {"Wrong parameters in the web formulary","Parametër i gabuar në formular web"}. {"XMPP Account Registration","Regjistrim Llogarish XMPP"}. {"XMPP Domains","Përkatësi XMPP"}. +{"You are not allowed to send private messages","S’keni leje të dërgoni mesazhe private"}. {"You are not joined to the channel","S’keni hyrë te kanali"}. {"You have been banned from this room","Jeni dëbuar prej kësaj dhome"}. {"You have joined too many conferences","Keni hyrë në shumë konferenca"}. From 9e14c7a803ee9341c198cf64c227035842180467 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:23:50 +0100 Subject: [PATCH 0878/1302] Update Chinese (Simplified) translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index e35ae7cb3..c1bfd98b2 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -295,6 +295,7 @@ {"No pending subscriptions found","未找到待处理的订阅"}. {"No privacy list with this name found","未找到具有此名称的隐私列表"}. {"No private data found in this query","在此查询中找不到专用数据"}. +{"No element found","未找到 元素"}. {"No running node found","找不到正在运行的节点"}. {"No services available","无可用服务"}. {"No statistics found for this item","未找到此项目的统计数据"}. @@ -537,6 +538,7 @@ {"Too many unacked stanzas","太多未确认的节"}. {"Too many users in this conference","此群聊中的用户太多"}. {"Traffic rate limit is exceeded","超过流量速率限制"}. +{"~ts's MAM Archive","~ts 的 MAM 存档"}. {"~ts's Offline Messages Queue","~ts 的离线消息队列"}. {"Tuesday","周二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. @@ -553,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","更新所有主机上的每日消息(不发送)"}. {"Update specs to get modules source, then install desired ones.","更新规格以获取模块源,然后安装所需的模块。"}. {"Update Specs","更新规格"}. +{"Updating the vCard is not supported by the vCard storage backend","vCard 存储后端不支持更新 vCard"}. {"Upgrade","升级"}. {"URL for Archived Discussion Logs","存档讨论日志的 URL"}. {"User already exists","用户已存在"}. {"User (jid)","用户 (jid)"}. {"User JID","用户 JID"}. {"User Management","用户管理"}. +{"User not allowed to perform an IQ set on another user's vCard.","不允许用户在其他用户的 vCard 上执行 IQ 设置。"}. {"User removed","用户已移除"}. {"User session not found","用户会话未找到"}. {"User session terminated","用户会话已终止"}. From 628f286eb64ba2ecff6329d973235df7549203ae Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:24:17 +0100 Subject: [PATCH 0879/1302] Update Spanish and Catalan translations --- priv/msgs/ca.msg | 4 ++++ priv/msgs/es.msg | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 0afc2ceb8..10da46007 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -295,6 +295,7 @@ {"No pending subscriptions found","No s'han trobat subscripcions pendents"}. {"No privacy list with this name found","No s'ha trobat cap llista de privacitat amb aquest nom"}. {"No private data found in this query","No s'ha trobat dades privades en esta petició"}. +{"No element found","No s'ha trobat cap element "}. {"No running node found","No s'ha trobat node en marxa"}. {"No services available","No n'hi ha serveis disponibles"}. {"No statistics found for this item","No n'hi ha estadístiques disponibles per a aquest element"}. @@ -537,6 +538,7 @@ {"Too many unacked stanzas","Massa missatges sense haver reconegut la seva recepció"}. {"Too many users in this conference","N'hi ha massa usuaris en esta sala de conferència"}. {"Traffic rate limit is exceeded","El límit de tràfic ha sigut sobrepassat"}. +{"~ts's MAM Archive","Arxiu MAM de ~ts"}. {"~ts's Offline Messages Queue","~ts's cua de missatges offline"}. {"Tuesday","Dimarts"}. {"Unable to generate a CAPTCHA","No s'ha pogut generar un CAPTCHA"}. @@ -553,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","Actualitza el missatge del dia en tots els hosts (no enviar)"}. {"Update specs to get modules source, then install desired ones.","Actualitza les especificacions per obtindre el codi font dels mòduls, després instal·la els que vulgues."}. {"Update Specs","Actualitzar Especificacions"}. +{"Updating the vCard is not supported by the vCard storage backend","El sistema d'almacenament de vCard no te capacitat per a actualitzar la vCard"}. {"Upgrade","Actualitza"}. {"URL for Archived Discussion Logs","URL dels Arxius de Discussions"}. {"User already exists","El usuari ja existeix"}. {"User JID","JID del usuari"}. {"User (jid)","Usuari (jid)"}. {"User Management","Gestió d'Usuaris"}. +{"User not allowed to perform an IQ set on another user's vCard.","L'usuari no te permis per a modificar la vCard d'altre usuari."}. {"User removed","Usuari borrat"}. {"User session not found","Sessió d'usuari no trobada"}. {"User session terminated","Sessió d'usuari terminada"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index b3b16856c..607b95997 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -295,6 +295,7 @@ {"No pending subscriptions found","No se han encontrado suscripciones pendientes"}. {"No privacy list with this name found","No se ha encontrado una lista de privacidad con este nombre"}. {"No private data found in this query","No se ha encontrado ningún elemento de dato privado en esta petición"}. +{"No element found","No se encontró ningún elemento "}. {"No running node found","No se ha encontrado ningún nodo activo"}. {"No services available","No hay servicios disponibles"}. {"No statistics found for this item","No se han encontrado estadísticas para este elemento"}. @@ -537,6 +538,7 @@ {"Too many unacked stanzas","Demasiados mensajes sin haber reconocido recibirlos"}. {"Too many users in this conference","Demasiados usuarios en esta sala"}. {"Traffic rate limit is exceeded","Se ha excedido el límite de tráfico"}. +{"~ts's MAM Archive","Archivo MAM de ~ts"}. {"~ts's Offline Messages Queue","Cola de mensajes diferidos de ~ts"}. {"Tuesday","Martes"}. {"Unable to generate a CAPTCHA","No se pudo generar un CAPTCHA"}. @@ -553,12 +555,14 @@ {"Update message of the day on all hosts (don't send)","Actualizar el mensaje del día en todos los dominos (pero no enviarlo)"}. {"Update specs to get modules source, then install desired ones.","Actualizar Especificaciones para conseguir el código fuente de los módulos, luego instala los que quieras."}. {"Update Specs","Actualizar Especificaciones"}. +{"Updating the vCard is not supported by the vCard storage backend","La actualización de la vCard no es compatible con el vCard almacenamiento backend"}. {"Upgrade","Actualizar"}. {"URL for Archived Discussion Logs","URL del registro de discusiones archivadas"}. {"User already exists","El usuario ya existe"}. {"User JID","Jabber ID del usuario"}. {"User (jid)","Usuario (jid)"}. {"User Management","Administración de usuarios"}. +{"User not allowed to perform an IQ set on another user's vCard.","No se permite al usuario realizar un IQ establecido en la vCard de otro usuario."}. {"User removed","Usuario eliminado"}. {"User session not found","Sesión de usuario no encontrada"}. {"User session terminated","Sesión de usuario terminada"}. From beb5bfea36d6afaa14b3a2d73bac55f1d0cfed59 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 12:29:34 +0100 Subject: [PATCH 0880/1302] Update man page to 24.12 --- man/ejabberd.yml.5 | 393 ++++++++++++++++++++++++++++----------------- 1 file changed, 249 insertions(+), 144 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 68bd2c958..52c726a74 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 10/28/2024 +.\" Date: 12/17/2024 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "10/28/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "12/17/2024" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.10/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.12/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 24\&.10\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 24\&.12\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR .RS 4 @@ -512,7 +512,8 @@ c2s_ciphers: .PP \fBc2s_dhfile\fR: \fIPath\fR .RS 4 -Full path to a file containing custom DH parameters to use for c2s connections\&. Such a file could be created with the command "openssl dhparam \-out dh\&.pem 2048"\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. +Full path to a file containing custom DH parameters to use for c2s connections\&. Such a file could be created with the command +\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. .RE .PP \fBc2s_protocol_options\fR: \fI[Option, \&.\&.\&.]\fR @@ -639,7 +640,7 @@ listener as well\&. If set to already enabled, with encryption if available\&. If set to \fIundefined\fR, it builds the URL using the deprecated \fIcaptcha_host\fR -+ /captcha\&. The default value is +\fI+ /captcha\fR\&. The default value is \fIauto\fR\&. .RE .PP @@ -758,28 +759,43 @@ are: The number of components to balance\&. .RE .PP -\fBtype\fR: \fIrandom | source | destination | bare_source | bare_destination\fR +\fBtype\fR: \fIValue\fR .RS 4 -How to deliver stanzas to connected components: -\fIrandom\fR -\- an instance is chosen at random; -\fIdestination\fR -\- an instance is chosen by the full JID of the packet\(cqs +How to deliver stanzas to connected components\&. The default value is +\fIrandom\fR\&. Possible values: +.RE +.PP +\fB\- bare_destination\fR +.RS 4 +by the bare JID (without resource) of the packet\(cqs \fIto\fR -attribute; -\fIsource\fR -\- by the full JID of the packet\(cqs +attribute +.RE +.PP +\fB\- bare_source\fR +.RS 4 +by the bare JID (without resource) of the packet\(cqs \fIfrom\fR -attribute; -\fIbare_destination\fR -\- by the bare JID (without resource) of the packet\(cqs +attribute is used +.RE +.PP +\fB\- destination\fR +.RS 4 +an instance is chosen by the full JID of the packet\(cqs \fIto\fR -attribute; -\fIbare_source\fR -\- by the bare JID (without resource) of the packet\(cqs +attribute +.RE +.PP +\fB\- random\fR +.RS 4 +an instance is chosen at random +.RE +.PP +\fB\- source\fR +.RS 4 +by the full JID of the packet\(cqs \fIfrom\fR -attribute is used\&. The default value is -\fIrandom\fR\&. +attribute .RE .sp \fBExample\fR: @@ -1104,13 +1120,16 @@ section for details\&. \fINote\fR about this option: added in 22\&.10\&. The number of messages to accept in log_burst_limit_window_time -period before starting to drop them\&. Default 500 +period before starting to drop them\&. Default +500 .RE .PP \fBlog_burst_limit_window_time\fR: \fINumber\fR .RS 4 \fINote\fR -about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to 1 second\&. +about this option: added in 22\&.10\&. The time period to rate\-limit log messages by\&. Defaults to +1 +second\&. .RE .PP \fBlog_modules_fully\fR: \fI[Module, \&.\&.\&.]\fR @@ -1132,9 +1151,8 @@ crash\&.log\&.0\&. \fBlog_rotate_size\fR: \fIpos_integer() | infinity\fR .RS 4 The size (in bytes) of a log file to trigger rotation\&. If set to -\fIinfinity\fR, log rotation is disabled\&. The default value is -\fI10485760\fR -(that is, 10 Mb)\&. +\fIinfinity\fR, log rotation is disabled\&. The default value is 10 Mb expressed in bytes: +\fI10485760\fR\&. .RE .PP \fBloglevel\fR: \fInone | emergency | alert | critical | error | warning | notice | info | debug\fR @@ -1175,7 +1193,7 @@ This option can be used to tune tick time parameter of .RS 4 Whether to use the \fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/24\&.10/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/24\&.12/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1295,14 +1313,16 @@ which means it first tries connecting with IPv6, if that fails it tries using IP \fBoutgoing_s2s_ipv4_address\fR: \fIAddress\fR .RS 4 \fINote\fR -about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example "127\&.0\&.0\&.1"\&. The default value is +about this option: added in 20\&.12\&. Specify the IPv4 address that will be used when establishing an outgoing S2S IPv4 connection, for example +\fI"127\&.0\&.0\&.1"\fR\&. The default value is \fIundefined\fR\&. .RE .PP \fBoutgoing_s2s_ipv6_address\fR: \fIAddress\fR .RS 4 \fINote\fR -about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example "::FFFF:127\&.0\&.0\&.1"\&. The default value is +about this option: added in 20\&.12\&. Specify the IPv6 address that will be used when establishing an outgoing S2S IPv6 connection, for example +\fI"::FFFF:127\&.0\&.0\&.1"\fR\&. The default value is \fIundefined\fR\&. .RE .PP @@ -1414,11 +1434,13 @@ or if the latter is not set\&. .RE .PP -\fBredis_server\fR: \fIHostname\fR +\fBredis_server 🟤\fR: \fIHost | IP Address | Unix Socket Path\fR .RS 4 -A hostname or an IP address of the +\fINote\fR +about this option: improved in 24\&.12\&. A hostname, IP address or unix domain socket file of the \fIdatabase\&.md#redis|Redis\fR -server\&.The default is +server\&. Setup the path to unix domain socket like: +\fI"unix:/path/to/socket"\fR\&. The default value is \fIlocalhost\fR\&. .RE .PP @@ -1527,7 +1549,8 @@ s2s_ciphers: .PP \fBs2s_dhfile\fR: \fIPath\fR .RS 4 -Full path to a file containing custom DH parameters to use for s2s connections\&. Such a file could be created with the command "openssl dhparam \-out dh\&.pem 2048"\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. +Full path to a file containing custom DH parameters to use for s2s connections\&. Such a file could be created with the command +\fI"openssl dhparam \-out dh\&.pem 2048"\fR\&. If this option is not specified, 2048\-bit MODP Group with 256\-bit Prime Order Subgroup will be used as defined in RFC5114 Section 2\&.3\&. .RE .PP \fBs2s_dns_retries\fR: \fINumber\fR @@ -1775,7 +1798,8 @@ The password for SQL authentication\&. The default is empty string\&. .PP \fBsql_pool_size\fR: \fISize\fR .RS 4 -Number of connections to the SQL server that ejabberd will open for each virtual host\&. The default value is 10\&. WARNING: for SQLite this value is +Number of connections to the SQL server that ejabberd will open for each virtual host\&. The default value is +\fI10\fR\&. WARNING: for SQLite this value is \fI1\fR by default and it\(cqs not recommended to change it due to potential race conditions\&. .RE @@ -1830,7 +1854,8 @@ this can also be an ODBC connection string\&. When is \fImysql\fR or -\fIpgsql\fR, this can be the path to a unix domain socket expressed like: "unix:/path/to/socket"\&.The default value is +\fIpgsql\fR, this can be the path to a unix domain socket expressed like: +\fI"unix:/path/to/socket"\fR\&.The default value is \fIlocalhost\fR\&. .RE .PP @@ -1944,7 +1969,8 @@ This option enables validation for header to protect against connections from other domains than given in the configuration file\&. In this way, the lower layer load balancer can be chosen for a specific ejabberd implementation while still providing a secure WebSocket connection\&. The default value is \fIignore\fR\&. An example value of the \fIURL\fR -is "https://test\&.example\&.org:8081"\&. +is +\fI"https://test\&.example\&.org:8081"\fR\&. .RE .PP \fBwebsocket_ping_interval\fR: \fItimeout()\fR @@ -1964,7 +1990,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 24\&.10\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 24\&.12\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -1989,41 +2015,11 @@ This module provides additional administrative commands\&. .sp Details for some commands: .sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -\fIban_account\fR: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&. -.RE +\fIban_account\fR API: This command kicks all the connected sessions of the account from the server\&. It also changes their password to a randomly generated one, so they can\(cqt login anymore unless a server administrator changes their password again\&. It is possible to define the reason of the ban\&. The new password also includes the reason and the date and time of the ban\&. See an example below\&. .sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -\fIpushroster\fR: (and -\fIpushroster\-all\fR) The roster file must be placed, if using Windows, on the directory where you installed ejabberd: -C:/Program Files/ejabberd -or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&. -.RE +\fIpush_roster\fR API (and \fIpush_roster_all\fR API): The roster file must be placed, if using Windows, on the directory where you installed ejabberd: C:/Program Files/ejabberd or similar\&. If you use other Operating System, place the file on the same directory where the \&.beam files are installed\&. See below an example roster file\&. .sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -\fIsrg_create\fR: If you want to put a group Name with blank spaces, use the characters "\*(Aq and \*(Aq" to define when the Name starts and ends\&. See an example below\&. -.RE +\fIsrg_create\fR API: If you want to put a group Name with blank spaces, use the characters \fI"\fR\*(Aq and \fI\*(Aq"\fR to define when the Name starts and ends\&. See an example below\&. .sp The module has no options\&. .sp @@ -2056,7 +2052,7 @@ modules: .RE .\} .sp -Content of roster file for \fIpushroster\fR command: +Content of roster file for \fIpush_roster\fR API: .sp .if n \{\ .RS 4 @@ -2070,7 +2066,7 @@ Content of roster file for \fIpushroster\fR command: .RE .\} .sp -With this call, the sessions of the local account which JID is boby@example\&.org will be kicked, and its password will be set to something like \fIBANNED_ACCOUNT\(em20080425T21:45:07\(em2176635\(emSpammed_rooms\fR +With this call, the sessions of the local account which JID is \fIboby@example\&.org\fR will be kicked, and its password will be set to something like \fIBANNED_ACCOUNT\(em20080425T21:45:07\(em2176635\(emSpammed_rooms\fR .sp .if n \{\ .RS 4 @@ -2082,7 +2078,7 @@ ejabberdctl vhost example\&.org ban_account boby "Spammed rooms" .RE .\} .sp -Call to srg_create using double\-quotes and single\-quotes: +Call to \fIsrg_create\fR API using double\-quotes and single\-quotes: .sp .if n \{\ .RS 4 @@ -2215,6 +2211,58 @@ Same as top\-level option, but applied to this module only\&. .RE .RE +.SS "mod_auth_fast 🟤" +.sp +\fINote\fR about this option: added in 24\&.12\&. +.sp +The module adds support for XEP\-0480: Fast Authentication Streamlining Tokens that allows users to authenticate using self managed tokens\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdb_type\fR: \fImnesia | sql\fR +.RS 4 +Same as top\-level +\fIdefault_db\fR +option, but applied to this module only\&. +.RE +.PP +\fBtoken_lifetime\fR: \fItimeout()\fR +.RS 4 +Time that tokens will be keept, measured from it\(cqs creation time\&. Default value set to 30 days +.RE +.PP +\fBtoken_refresh_age\fR: \fItimeout()\fR +.RS 4 +This time determines age of token, that qualifies for automatic refresh\&. Default value set to 1 day +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_auth_fast: + token_timeout: 14days +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_avatar" .sp The purpose of the module is to cope with legacy and modern XMPP clients posting avatars\&. The process is described in XEP\-0398: User Avatar to vCard\-Based Avatars Conversion\&. @@ -2541,7 +2589,7 @@ This module serves a simple page for the Converse XMPP web browser client\&. .sp To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp -Make sure either \fImod_bosh\fR or \fIejabberd_http_ws\fR \fIlisten\-options\&.md#request_handlers|request_handlers\fR are enabled\&. +Make sure either \fImod_bosh\fR or \fIlisten\&.md#ejabberd_http_ws|ejabberd_http_ws\fR are enabled in at least one \fIrequest_handlers\fR\&. .sp When \fIconversejs_css\fR and \fIconversejs_script\fR are \fIauto\fR, by default they point to the public Converse client\&. .sp @@ -2601,9 +2649,9 @@ is replaced with the hostname\&. The default value is .PP \fBwebsocket_url\fR: \fIauto | WebSocketURL\fR .RS 4 -A WebSocket URL to which Converse can connect to\&. The keyword +A WebSocket URL to which Converse can connect to\&. The \fI@HOST@\fR -is replaced with the real virtual host name\&. If set to +keyword is replaced with the real virtual host name\&. If set to \fIauto\fR, it will build the URL of the first configured WebSocket request handler\&. The default value is \fIauto\fR\&. .RE @@ -2930,7 +2978,7 @@ This module serves small \fIhost\-meta\fR files as described in XEP\-0156: Disco .sp To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp -Notice it only works if ejabberd_http has tls enabled\&. +Notice it only works if \fIlisten\&.md#ejabberd_http|ejabberd_http\fR has \fIlisten\-options\&.md#tls|tls\fR enabled\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -2998,11 +3046,26 @@ This module provides a ReST interface to call \fI\&.\&./\&.\&./developer/ejabber .sp To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. .sp -To use a specific API version N, when defining the URL path in the request_handlers, add a \fIvN\fR\&. For example: \fI/api/v2: mod_http_api\fR +To use a specific API version N, when defining the URL path in the request_handlers, add a vN\&. For example: \fI/api/v2: mod_http_api\fR\&. .sp -To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/\fR +To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/COMMAND\-NAME\fR .sp -The module has no options\&. +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdefault_version\fR: \fIinteger() | string()\fR +.RS 4 +What API version to use when none is specified in the URL path\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting +\fI"24\&.06"\fR +in this option implies +\fI2\fR\&. The default value is the latest version\&. +.RE +.RE .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3024,7 +3087,8 @@ listen: /api: mod_http_api modules: - mod_http_api: {} + mod_http_api: + default_version: 2 .fi .if n \{\ .RE @@ -3176,26 +3240,34 @@ This option specifies additional header fields to be included in all HTTP respon .RS 4 This option defines the permission bits of the \fIdocroot\fR -directory and any directories created during file uploads\&. The bits are specified as an octal number (see the chmod(1) manual page) within double quotes\&. For example: "0755"\&. The default is undefined, which means no explicit permissions will be set\&. +directory and any directories created during file uploads\&. The bits are specified as an octal number (see the +\fIchmod(1)\fR +manual page) within double quotes\&. For example: +\fI"0755"\fR\&. The default is undefined, which means no explicit permissions will be set\&. .RE .PP \fBdocroot\fR: \fIPath\fR .RS 4 -Uploaded files are stored below the directory specified (as an absolute path) with this option\&. The keyword @HOME@ is replaced with the home directory of the user running ejabberd, and the keyword @HOST@ with the virtual host name\&. The default value is "@HOME@/upload"\&. +Uploaded files are stored below the directory specified (as an absolute path) with this option\&. The keyword +\fI@HOME@\fR +is replaced with the home directory of the user running ejabberd, and the keyword +\fI@HOST@\fR +with the virtual host name\&. The default value is +\fI"@HOME@/upload"\fR\&. .RE .PP \fBexternal_secret\fR: \fIText\fR .RS 4 This option makes it possible to offload all HTTP Upload processing to a separate HTTP server\&. Both ejabberd and the HTTP server should share this secret and behave exactly as described at -Prosody\(cqs mod_http_upload_external -in the -\fIImplementation\fR -section\&. There is no default value\&. +Prosody\(cqs mod_http_upload_external: Implementation\&. There is no default value\&. .RE .PP \fBfile_mode\fR: \fIPermission\fR .RS 4 -This option defines the permission bits of uploaded files\&. The bits are specified as an octal number (see the chmod(1) manual page) within double quotes\&. For example: "0644"\&. The default is undefined, which means no explicit permissions will be set\&. +This option defines the permission bits of uploaded files\&. The bits are specified as an octal number (see the +\fIchmod(1)\fR +manual page) within double quotes\&. For example: +\fI"0644"\fR\&. The default is undefined, which means no explicit permissions will be set\&. .RE .PP \fBget_url\fR: \fIURL\fR @@ -3203,8 +3275,9 @@ This option defines the permission bits of uploaded files\&. The bits are specif This option specifies the initial part of the GET URLs used for downloading the files\&. The default value is \fIundefined\fR\&. When this option is \fIundefined\fR, this option is set to the same value as -\fIput_url\fR\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: if GET requests are handled by -\fImod_http_upload\fR, the +\fIput_url\fR\&. The keyword +\fI@HOST@\fR +is replaced with the virtual host name\&. NOTE: if GET requests are handled by this module, the \fIget_url\fR must match the \fIput_url\fR\&. Setting it to a different value only makes sense if an external web server or @@ -3223,7 +3296,8 @@ instead\&. .RS 4 This option defines the Jabber IDs of the service\&. If the \fIhosts\fR -option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "upload\&."\&. The keyword +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix +\fI"upload\&."\fR\&. The keyword \fI@HOST@\fR is replaced with the real virtual host name\&. .RE @@ -3246,12 +3320,16 @@ must be specified\&. The default value is .PP \fBname\fR: \fIName\fR .RS 4 -A name of the service in the Service Discovery\&. This will only be displayed by special XMPP clients\&. The default value is "HTTP File Upload"\&. +A name of the service in the Service Discovery\&. The default value is +\fI"HTTP File Upload"\fR\&. Please note this will only be displayed by some XMPP clients\&. .RE .PP \fBput_url\fR: \fIURL\fR .RS 4 -This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is "https://@HOST@:5443/upload"\&. +This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword +\fI@HOST@\fR +is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is +\fI"https://@HOST@:5443/upload"\fR\&. .RE .PP \fBrm_on_unregister\fR: \fItrue | false\fR @@ -3263,7 +3341,9 @@ This option specifies whether files uploaded by a user should be removed when th \fBsecret_length\fR: \fILength\fR .RS 4 This option defines the length of the random string included in the GET and PUT URLs generated by -\fImod_http_upload\fR\&. The minimum length is 8 characters, but it is recommended to choose a larger value\&. The default value is +\fImod_http_upload\fR\&. The minimum length is +\fI8\fR +characters, but it is recommended to choose a larger value\&. The default value is \fI40\fR\&. .RE .PP @@ -3358,7 +3438,7 @@ This module depends on \fImod_http_upload\fR\&. .PP \fBaccess_hard_quota\fR: \fIAccessName\fR .RS 4 -This option defines which access rule is used to specify the "hard quota" for the matching JIDs\&. That rule must yield a positive number for any JID that is supposed to have a quota limit\&. This is the number of megabytes a corresponding user may upload\&. When this threshold is exceeded, ejabberd deletes the oldest files uploaded by that user until their disk usage equals or falls below the specified soft quota (see +This option defines which access rule is used to specify the "hard quota" for the matching JIDs\&. That rule must yield a positive number for any JID that is supposed to have a quota limit\&. This is the number of megabytes a corresponding user may upload\&. When this threshold is exceeded, ejabberd deletes the oldest files uploaded by that user until their disk usage equals or falls below the specified soft quota (see also option \fIaccess_soft_quota\fR)\&. The default value is \fIhard_upload_quota\fR\&. .RE @@ -3388,7 +3468,7 @@ directory, once per day\&. The default value is \fBExamples:\fR .RS 4 .sp -Please note that it\(cqs not necessary to specify the \fIaccess_hard_quota\fR and \fIaccess_soft_quota\fR options in order to use the quota feature\&. You can stick to the default names and just specify access rules such as those in this example: +Notice it\(cqs not necessary to specify the \fIaccess_hard_quota\fR and \fIaccess_soft_quota\fR options in order to use the quota feature\&. You can stick to the default names and just specify access rules such as those in this example: .sp .if n \{\ .RS 4 @@ -3600,7 +3680,7 @@ When this option is disabled, for each individual subscriber a separate mucsub m .sp \fINote\fR about this option: added in 24\&.02\&. .sp -Matrix gateway\&. +Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3853,7 +3933,8 @@ instead\&. .RS 4 This option defines the Jabber IDs of the service\&. If the \fIhosts\fR -option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix "mix\&."\&. The keyword +option is not specified, the only Jabber ID will be the hostname of the virtual host with the prefix +\fI"mix\&."\fR\&. The keyword \fI@HOST@\fR is replaced with the real virtual host name\&. .RE @@ -4048,15 +4129,40 @@ This module adds ability to synchronize local MQTT topics with data on remote se Identifier of a user that will be assigned as owner of local changes\&. .RE .PP -\fBservers\fR: \fI{ServerUrl: {publish: [TopicPairs, subscribe: [TopicPairs], authentication: [AuthInfo]}}]\fR +\fBservers\fR: \fI{ServerUrl: {Key: Value}}\fR .RS 4 -Declaration of data to share, must contain -\fIpublish\fR -or -\fIsubscribe\fR -or both, and -\fIauthentication\fR -section with username/password field or certfile pointing to client certificate\&. Accepted urls can use schema mqtt, mqtts (mqtt with tls), mqtt5, mqtt5s (both to trigger v5 protocol), ws, wss, ws5, wss5\&. Certificate authentication can be only used with mqtts, mqtt5s, wss, wss5\&. +Declaration of data to share for each ServerUrl\&. Server URLs can use schemas: +\fImqtt\fR, +\fImqtts\fR +(mqtt with tls), +\fImqtt5\fR, +\fImqtt5s\fR +(both to trigger v5 protocol), +\fIws\fR, +\fIwss\fR, +\fIws5\fR, +\fIwss5\fR\&. Keys must be: +.PP +\fBauthentication\fR: \fI{AuthKey: AuthValue}\fR +.RS 4 +List of authentication information, where AuthKey can be: +\fIusername\fR +and +\fIpassword\fR +fields, or +\fIcertfile\fR +pointing to client certificate\&. Certificate authentication can be used only with mqtts, mqtt5s, wss, wss5\&. +.RE +.PP +\fBpublish\fR: \fI{LocalTopic: RemoteTopic}\fR +.RS 4 +Either publish or subscribe must be set, or both\&. +.RE +.PP +\fBsubscribe\fR: \fI{RemoteTopic: LocalTopic}\fR +.RS 4 +Either publish or subscribe must be set, or both\&. +.RE .RE .RE .sp @@ -4074,16 +4180,16 @@ section with username/password field or certfile pointing to client certificate\ .nf modules: mod_mqtt_bridge: + replication_user: "mqtt@xmpp\&.server\&.com" servers: "mqtt://server\&.com": + authentication: + certfile: "/etc/ejabberd/mqtt_server\&.pem" publish: "localA": "remoteA" # local changes to \*(AqlocalA\*(Aq will be replicated on remote server as \*(AqremoteA\*(Aq "topicB": "topicB" subscribe: "remoteB": "localB" # changes to \*(AqremoteB\*(Aq on remote server will be stored as \*(AqlocalB\*(Aq on local server - authentication: - certfile: "/etc/ejabberd/mqtt_server\&.pem" - replication_user: "mqtt@xmpp\&.server\&.com" .fi .if n \{\ .RE @@ -4311,7 +4417,7 @@ The room persists even if the last participant leaves\&. The default value is \fIfalse\fR\&. .RE .PP -\fBpresence_broadcast\fR: \fI[moderator | participant | visitor, \&.\&.\&.]\fR +\fBpresence_broadcast\fR: \fI[Role]\fR .RS 4 List of roles for which presence is broadcasted\&. The list can contain one or several of: \fImoderator\fR, @@ -4376,7 +4482,10 @@ Timeout before hibernating the room process, expressed in seconds\&. The default .PP \fBhistory_size\fR: \fISize\fR .RS 4 -A small history of the current discussion is sent to users when they enter the room\&. With this option you can define the number of history messages to keep and send to users joining the room\&. The value is a non\-negative integer\&. Setting the value to 0 disables the history feature and, as a result, nothing is kept in memory\&. The default value is 20\&. This value affects all rooms on the service\&. NOTE: modern XMPP clients rely on Message Archives (XEP\-0313), so feel free to disable the history feature if you\(cqre only using modern clients and have +A small history of the current discussion is sent to users when they enter the room\&. With this option you can define the number of history messages to keep and send to users joining the room\&. The value is a non\-negative integer\&. Setting the value to +\fI0\fR +disables the history feature and, as a result, nothing is kept in memory\&. The default value is +\fI20\fR\&. This value affects all rooms on the service\&. NOTE: modern XMPP clients rely on Message Archives (XEP\-0313), so feel free to disable the history feature if you\(cqre only using modern clients and have \fImod_mam\fR module loaded\&. .RE @@ -4462,12 +4571,16 @@ This option defines after how many users in the room, it is considered overcrowd .PP \fBmin_message_interval\fR: \fINumber\fR .RS 4 -This option defines the minimum interval between two messages send by an occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, message rate is not limited\&. This feature can be used to protect a MUC service from occupant abuses and limit number of messages that will be broadcasted by the service\&. A good value for this minimum message interval is 0\&.4 second\&. If an occupant tries to send messages faster, an error is send back explaining that the message has been discarded and describing the reason why the message is not acceptable\&. +This option defines the minimum interval between two messages send by an occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, message rate is not limited\&. This feature can be used to protect a MUC service from occupant abuses and limit number of messages that will be broadcasted by the service\&. A good value for this minimum message interval is +\fI0\&.4\fR +second\&. If an occupant tries to send messages faster, an error is send back explaining that the message has been discarded and describing the reason why the message is not acceptable\&. .RE .PP \fBmin_presence_interval\fR: \fINumber\fR .RS 4 -This option defines the minimum of time between presence changes coming from a given occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, no restriction is applied\&. This option can be used to protect a MUC service for occupants abuses\&. If an occupant tries to change its presence more often than the specified interval, the presence is cached by ejabberd and only the last presence is broadcasted to all occupants in the room after expiration of the interval delay\&. Intermediate presence packets are silently discarded\&. A good value for this option is 4 seconds\&. +This option defines the minimum of time between presence changes coming from a given occupant in seconds\&. This option is global and valid for all rooms\&. A decimal value can be used\&. When this option is not defined, no restriction is applied\&. This option can be used to protect a MUC service for occupants abuses\&. If an occupant tries to change its presence more often than the specified interval, the presence is cached by ejabberd and only the last presence is broadcasted to all occupants in the room after expiration of the interval delay\&. Intermediate presence packets are silently discarded\&. A good value for this option is +\fI4\fR +seconds\&. .RE .PP \fBname\fR: \fIstring()\fR @@ -4573,7 +4686,7 @@ This module depends on \fImod_muc\fR\&. \fINote\fR about this option: added in 22\&.05\&. How many users can be subscribed to a room at once using the \fIsubscribe_room_many\fR -command\&. The default value is +API\&. The default value is \fI50\fR\&. .RE .RE @@ -5031,22 +5144,8 @@ modules: .SS "mod_offline" .sp This module implements XEP\-0160: Best Practices for Handling Offline Messages and XEP\-0013: Flexible Offline Message Retrieval\&. This means that all messages sent to an offline user will be stored on the server until that user comes online again\&. Thus it is very similar to how email works\&. A user is considered offline if no session presence priority > 0 are currently open\&. -.if n \{\ .sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBNote\fR -.ps -1 -.br -.sp -\fIejabberdctl\fR has a command to delete expired messages (see chapter \fI\&.\&./guide/managing\&.md|Managing an ejabberd server\fR in online documentation\&. -.sp .5v -.RE +The \fIdelete_expired_messages\fR API allows to delete expired messages, and \fIdelete_old_messages\fR API deletes older ones\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -5058,7 +5157,9 @@ This module implements XEP\-0160: Best Practices for Handling Offline Messages a .PP \fBaccess_max_user_messages\fR: \fIAccessName\fR .RS 4 -This option defines which access rule will be enforced to limit the maximum number of offline messages that a user can have (quota)\&. When a user has too many offline messages, any new messages that they receive are discarded, and a error is returned to the sender\&. The default value is +This option defines which access rule will be enforced to limit the maximum number of offline messages that a user can have (quota)\&. When a user has too many offline messages, any new messages that they receive are discarded, and a +\fI\fR +error is returned to the sender\&. The default value is \fImax_user_offline_messages\fR\&. .RE .PP @@ -5092,8 +5193,12 @@ option, but applied to this module only\&. .PP \fBstore_empty_body\fR: \fItrue | false | unless_chat_state\fR .RS 4 -Whether or not to store messages that lack a element\&. The default value is -\fIunless_chat_state\fR, which tells ejabberd to store messages even if they lack the element, unless they only contain a chat state notification (as defined in +Whether or not to store messages that lack a +\fI\fR +element\&. The default value is +\fIunless_chat_state\fR, which tells ejabberd to store messages even if they lack the +\fI\fR +element, unless they only contain a chat state notification (as defined in XEP\-0085: Chat State Notifications\&. .RE .PP @@ -5112,11 +5217,11 @@ option, but applied to this module only\&. .PP \fBuse_mam_for_storage\fR: \fItrue | false\fR .RS 4 -This is an experimental option\&. Enabling this option, -\fImod_offline\fR -uses the +This is an experimental option\&. By enabling the option, this module uses the +\fIarchive\fR +table from \fImod_mam\fR -archive table instead of its own spool table to retrieve the messages received when the user was offline\&. This allows client developers to slowly drop XEP\-0160 and rely on XEP\-0313 instead\&. It also further reduces the storage required when you enable MucSub\&. Enabling this option has a known drawback for the moment: most of flexible message retrieval queries don\(cqt work (those that allow retrieval/deletion of messages by id), but this specification is not widely used\&. The default value is +instead of its own spool table to retrieve the messages received when the user was offline\&. This allows client developers to slowly drop XEP\-0160 and rely on XEP\-0313 instead\&. It also further reduces the storage required when you enable MucSub\&. Enabling this option has a known drawback for the moment: most of flexible message retrieval queries don\(cqt work (those that allow retrieval/deletion of messages by id), but this specification is not widely used\&. The default value is \fIfalse\fR to keep former behaviour as default\&. .RE @@ -5307,7 +5412,7 @@ This module implements XEP\-0016: Privacy Lists\&. .ps -1 .br .sp -Nowadays modern XMPP clients rely on XEP\-0191: Blocking Command which is implemented by \fImod_blocking\fR module\&. However, you still need \fImod_privacy\fR loaded in order for \fImod_blocking\fR to work\&. +Nowadays modern XMPP clients rely on XEP\-0191: Blocking Command which is implemented by \fImod_blocking\fR\&. However, you still need \fImod_privacy\fR loaded in order for \fImod_blocking\fR to work\&. .sp .5v .RE .sp @@ -5360,7 +5465,7 @@ This module adds support for XEP\-0049: Private XML Storage\&. .sp Using this method, XMPP entities can store private data on the server, retrieve it whenever necessary and share it between multiple connected clients of the same user\&. The data stored might be anything, as long as it is a valid XML\&. One typical usage is storing a bookmark of all user\(cqs conferences (XEP\-0048: Bookmarks)\&. .sp -It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see the command \fIbookmarks_to_pep\fR API\&. +It also implements the bookmark conversion described in XEP\-0402: PEP Native Bookmarks, see \fIbookmarks_to_pep\fR API\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -6628,7 +6733,7 @@ This module enables you to create shared roster groups: groups of accounts that .sp The big advantages of this feature are that end users do not need to manually add all users to their rosters, and that they cannot permanently delete users from the shared roster groups\&. A shared roster group can have members from any XMPP server, but the presence will only be available from and to members of the same virtual host where the group is created\&. It still allows the users to have / add their own contacts, as it does not replace the standard roster\&. Instead, the shared roster contacts are merged to the relevant users at retrieval time\&. The standard user rosters thus stay unmodified\&. .sp -Shared roster groups can be edited via the Web Admin, and some API commands called \fIsrg_*\fR\&. Each group has a unique name and those parameters: +Shared roster groups can be edited via the Web Admin, and some API commands called \fIsrg_\fR, for example \fIsrg_add\fR API\&. Each group has a unique name and those parameters: .sp .RS 4 .ie n \{\ @@ -7980,7 +8085,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 24\&.10\&. +This section describes listeners options of ejabberd 24\&.12\&. .sp TODO .SH "AUTHOR" @@ -7988,13 +8093,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 24\&.10\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 24\&.12\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.10/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.12/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 3dd7febb9884ba4118095f573d56f95bf0a19d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 13:28:54 +0100 Subject: [PATCH 0881/1302] Fix expiration date calculation in mod_auth_fast --- src/mod_auth_fast.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index 36bd9cf83..d92366074 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -86,8 +86,8 @@ mod_opt_type(token_refresh_age) -> -spec mod_options(binary()) -> [{atom(), any()}]. mod_options(Host) -> [{db_type, ejabberd_config:default_db(Host, ?MODULE)}, - {token_lifetime, timer:hours(30*24)}, - {token_refresh_age, timer:hours(24)}]. + {token_lifetime, 30*24*60*60}, + {token_refresh_age, 2*60*605}]. mod_doc() -> #{desc => @@ -136,7 +136,7 @@ gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) -> Token = base64:encode(ua_hash(<>)), ExpiresAt = erlang:system_time(second) + mod_auth_fast_opt:token_lifetime(Server), Mod:set_token(Server, User, ua_hash(UA), next, Token, ExpiresAt), - #fast_token{token = Token, expiry = misc:usec_to_now(ExpiresAt)}. + #fast_token{token = Token, expiry = misc:usec_to_now(ExpiresAt*1000000)}. c2s_handle_sasl2_inline({#{server := Server, user := User, sasl2_ua_id := UA, sasl2_axtra_auth_info := Extra} = State, Els, Results} = Acc) -> From 2b3d588f1048f52c44f67d19ea1c96ab39855a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 13:30:04 +0100 Subject: [PATCH 0882/1302] Typo --- src/mod_auth_fast.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index d92366074..6ada831c2 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -87,7 +87,7 @@ mod_opt_type(token_refresh_age) -> mod_options(Host) -> [{db_type, ejabberd_config:default_db(Host, ?MODULE)}, {token_lifetime, 30*24*60*60}, - {token_refresh_age, 2*60*605}]. + {token_refresh_age, 24*60*60}]. mod_doc() -> #{desc => From 06303ae7ab1ff7ce2d320561f2a0000d7a91d9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 13:49:05 +0100 Subject: [PATCH 0883/1302] Relax checks for channels bindings for connections using external encryption This should fix issue #4322 --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 14f96f653..1b4c0876d 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "333f688da2f52c73f374a46df139789a48c45395", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "28e4556353fbe57807a5d2f753f6ea8b707412c2", override: true}, {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index f1fdafa90..3f76aae45 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "333f688da2f52c73f374a46df139789a48c45395", [ref: "333f688da2f52c73f374a46df139789a48c45395"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "28e4556353fbe57807a5d2f753f6ea8b707412c2", [ref: "28e4556353fbe57807a5d2f753f6ea8b707412c2"]}, "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, } diff --git a/rebar.config b/rebar.config index 7009d5311..285a1c84a 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "333f688da2f52c73f374a46df139789a48c45395"}}, + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "28e4556353fbe57807a5d2f753f6ea8b707412c2"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} ]}. diff --git a/rebar.lock b/rebar.lock index f622208d0..c0a3a1c6a 100644 --- a/rebar.lock +++ b/rebar.lock @@ -26,7 +26,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"333f688da2f52c73f374a46df139789a48c45395"}}, + {ref,"28e4556353fbe57807a5d2f753f6ea8b707412c2"}}, 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", From 2aa673e780f7dcb91429a126c5460a75714ffd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 17 Dec 2024 15:11:33 +0100 Subject: [PATCH 0884/1302] Update xmpp dep once more --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 1b4c0876d..0a5468961 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "28e4556353fbe57807a5d2f753f6ea8b707412c2", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "855d974e122c8357d94c983b85342112c5f5531f", override: true}, {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 3f76aae45..635b23b41 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "28e4556353fbe57807a5d2f753f6ea8b707412c2", [ref: "28e4556353fbe57807a5d2f753f6ea8b707412c2"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "855d974e122c8357d94c983b85342112c5f5531f", [ref: "855d974e122c8357d94c983b85342112c5f5531f"]}, "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, } diff --git a/rebar.config b/rebar.config index 285a1c84a..4a028cf7f 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "28e4556353fbe57807a5d2f753f6ea8b707412c2"}}, + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "855d974e122c8357d94c983b85342112c5f5531f"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} ]}. diff --git a/rebar.lock b/rebar.lock index c0a3a1c6a..9d4c5ad67 100644 --- a/rebar.lock +++ b/rebar.lock @@ -26,7 +26,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"28e4556353fbe57807a5d2f753f6ea8b707412c2"}}, + {ref,"855d974e122c8357d94c983b85342112c5f5531f"}}, 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", From 26e86793593474bf9cf30dd56298efb04e52a016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 18 Dec 2024 15:07:13 +0100 Subject: [PATCH 0885/1302] Make rsm handling in disco items mod_muc, correctly count skipped rooms --- src/mod_muc.erl | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 74e62dedc..2e617f8a7 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -994,15 +994,38 @@ iq_disco_items(ServerHost, Host, From, Lang, MaxRoomsDiscoItems, Node, RSM) #rsm_set{max = Max} -> Max end, - {Items, HitMax} = lists:foldr( - fun(_, {Acc, _}) when length(Acc) >= MaxItems -> - {Acc, true}; - (R, {Acc, _}) -> - case get_room_disco_item(R, Query) of - {ok, Item} -> {[Item | Acc], false}; - {error, _} -> {Acc, false} + RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), + RsmSupported = RMod:rsm_supported(), + GetRooms = + fun GetRooms(AccInit, Rooms) -> + {Items, HitMax, DidSkip, Last, First} = lists:foldr( + fun(_, {Acc, _, Skip, F, L}) when length(Acc) >= MaxItems -> + {Acc, true, Skip, F, L}; + ({RN, _, _} = R, {Acc, _, Skip, F, _}) -> + F2 = if F == undefined -> RN; true -> F end, + case get_room_disco_item(R, Query) of + {ok, Item} -> {[Item | Acc], false, Skip, F2, RN}; + {error, _} -> {Acc, false, true, F2, RN} + end + end, AccInit, Rooms), + if RsmSupported andalso not HitMax andalso DidSkip -> + RSM2 = case RSM of + #rsm_set{'after' = undefined, before = undefined} -> + #rsm_set{max = MaxItems - length(Items), 'after' = Last}; + #rsm_set{'after' = undefined} -> + #rsm_set{max = MaxItems - length(Items), 'before' = First}; + _ -> + #rsm_set{max = MaxItems - length(Items), 'after' = Last} + end, + GetRooms({Items, false, false, undefined, undefined}, + get_online_rooms(ServerHost, Host, RSM2)); + true -> {Items, HitMax} end - end, {[], false}, get_online_rooms(ServerHost, Host, RSM)), + end, + + {Items, HitMax} = + GetRooms({[], false, false, undefined, undefined}, + get_online_rooms(ServerHost, Host, RSM)), ResRSM = case Items of [_|_] when RSM /= undefined; HitMax -> #disco_item{jid = #jid{luser = First}} = hd(Items), From 2eb605873c674461c57936b064a64621857aef4d Mon Sep 17 00:00:00 2001 From: Metalhearf <6446231+Metalhearf@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:51:08 +0100 Subject: [PATCH 0886/1302] Add security policy and reporting guidelines --- README.md | 5 +++++ SECURITY.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index f4b21468d..3e2f3ef13 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,11 @@ or some other method from [ProcessOne Contact][p1contact]. For commercial offering and support, including [ejabberd Business Edition][p1home] and [Fluux (ejabberd in the Cloud)][fluux], please check [ProcessOne ejabberd page][p1home]. +Security +-------- + +For information on how to report security vulnerabilities, please refer to the [SECURITY.md](SECURITY.md) file. It contains guidelines on how to report vulnerabilities privately and securely, ensuring that any issues are addressed in a timely and confidential manner. + Community --------- diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..bb2292826 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,45 @@ +# Security Policy + +## Supported Versions + +We recommend that all users always use the latest version of ejabberd. + +To ensure the best experience and security, upgrade to the latest version available on [this repo](https://github.com/processone/ejabberd). + +## Reporting a Vulnerability + +### Private Reporting + +**Preferred Method**: Use GitHub's private vulnerability reporting system by clicking the "Report a Vulnerability" button in the [Security tab of this repository](https://github.com/processone/ejabberd/security). This ensures your report is securely transmitted and tracked. + +**Alternative**: If you cannot use the GitHub system, send an email to **`contact@process-one.net`** with the following details: + +- A clear description of the vulnerability. +- Steps to reproduce the issue. +- Any potential impact or exploitation scenarios. + +### Response Time + +We aim to acknowledge receipt of your report within 72 hours. You can expect regular updates on the status of your report. + +### Resolution + +If the vulnerability is confirmed, we will work on a patch or mitigation strategy. +We will notify you once the issue is resolved and coordinate a public disclosure if needed. + +### Acknowledgements + +We value and appreciate the contributions of security researchers and community members. +If you wish, we are happy to acknowledge your efforts publicly by listing your name (or alias) below in this document. +Please let us know if you would like to be recognized when reporting the vulnerability. + +## Public Discussion + +For general inquiries or discussions about the project’s security, feel free to chat with us here: + +- XMPP room: `ejabberd@conference.process-one.net` +- [GitHub Discussions](https://github.com/processone/ejabberd/discussions) + +However, please note that if the issue is **critical** or potentially exploitable, **do not share it publicly**. Instead, we strongly recommend you contact the maintainers directly via the private reporting methods outlined above to ensure a secure and timely response. + +Thank you for helping us improve the security of ejabberd! From a8649767f26ae8ebf21f2ecb4072931bfcae726c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 18 Dec 2024 16:03:13 +0100 Subject: [PATCH 0887/1302] Pull updated xmpp --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 0a5468961..e13364d45 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "855d974e122c8357d94c983b85342112c5f5531f", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b", override: true}, {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 635b23b41..a8af7cb20 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "855d974e122c8357d94c983b85342112c5f5531f", [ref: "855d974e122c8357d94c983b85342112c5f5531f"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b", [ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b"]}, "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, } diff --git a/rebar.config b/rebar.config index 4a028cf7f..c37c0b830 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "855d974e122c8357d94c983b85342112c5f5531f"}}, + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} ]}. diff --git a/rebar.lock b/rebar.lock index 9d4c5ad67..90020eec4 100644 --- a/rebar.lock +++ b/rebar.lock @@ -26,7 +26,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"855d974e122c8357d94c983b85342112c5f5531f"}}, + {ref,"8071c86f33b9a8e9d66a10e058be088eecdc670b"}}, 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", From 50b57ada7c102f3c2e372929f584c00bca090bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Wed, 18 Dec 2024 16:22:21 +0100 Subject: [PATCH 0888/1302] Revert "Fix dialyzer errors" This reverts commit e58926592132b51b8532b907553eaf6760e6fe84. --- src/mod_matrix_gw_room.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 90ecfec3f..1cb0d892b 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -76,15 +76,15 @@ remote_user :: binary() | undefined, client_state}). +-record(multi, + {users :: #{}}). + -record(multi_user, {join_ts :: integer()}). --record(multi, - {users :: #{{binary(), binary()} => #{binary() => #multi_user{}}}}). - -record(data, {host :: binary(), - kind :: #direct{} | #multi{} | undefined, + kind :: #direct{} | undefined, room_id :: binary(), room_jid :: jid(), room_version :: #room_version{}, @@ -790,7 +790,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> {stop, normal}; _ -> ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = <<"make_join failed">>, + Txt = "make_join failed", Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), {stop, normal} @@ -2743,7 +2743,7 @@ notify_event_xmpp( error -> Data end; - _ -> + error -> Data end; notify_event_xmpp(_Event, Data) -> From 9a2a9187cda01d535355eed7bd5713578939753b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Wed, 18 Dec 2024 16:23:29 +0100 Subject: [PATCH 0889/1302] Revert "Experimental support for joining Matrix rooms as MUC rooms" This reverts commit eb6f242d9942fd6c112a07793768934eae896257. --- src/mod_matrix_gw.erl | 33 +- src/mod_matrix_gw_room.erl | 909 +++++++------------------------------ src/mod_matrix_gw_s2s.erl | 82 ++-- 3 files changed, 224 insertions(+), 800 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index bd7882117..d880d745e 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -57,11 +57,6 @@ -define(MAX_REQUEST_SIZE, 1000000). --define(CORS_HEADERS, - [{<<"Access-Control-Allow-Origin">>, <<"*">>}, - {<<"Access-Control-Allow-Methods">>, <<"GET, POST, PUT, DELETE, OPTIONS">>}, - {<<"Access-Control-Allow-Headers">>, <<"X-Requested-With, Content-Type, Authorization">>}]). - process([<<"key">>, <<"v2">>, <<"server">> | _], #request{method = 'GET', host = _Host} = _Request) -> Host = ejabberd_config:get_myname(), @@ -142,7 +137,6 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], #request{method = 'PUT', host = _Host} = Request) -> case preprocess_federation_request(Request) of {ok, #{<<"event">> := #{%<<"origin">> := Origin, - <<"content">> := Content, <<"room_id">> := RoomID, <<"sender">> := Sender, <<"state_key">> := UserID} = Event, @@ -161,12 +155,7 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], SEvent = sign_pruned_event(Host, PrunedEvent), ?DEBUG("sign event ~p~n", [SEvent]), ResJSON = #{<<"event">> => SEvent}, - case Content of - #{<<"is_direct">> := true} -> - mod_matrix_gw_room:join_direct(Host, Origin, RoomID, Sender, UserID); - _ -> - mod_matrix_gw_room:send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) - end, + mod_matrix_gw_room:join(Host, Origin, RoomID, Sender, UserID), ?DEBUG("res ~s~n", [misc:json_encode(ResJSON)]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], misc:json_encode(ResJSON)}; _ -> @@ -397,13 +386,6 @@ process([<<"federation">>, <<"v2">>, <<"send_join">>, RoomID, EventID], {result, HTTPResult} -> HTTPResult end; -%process([<<"client">> | ClientPath], Request) -> -% {HTTPCode, Headers, JSON} = mod_matrix_gw_c2s:process(ClientPath, Request), -% ?DEBUG("resp ~p~n", [JSON]), -% {HTTPCode, -% [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>} | -% ?CORS_HEADERS] ++ Headers, -% jiffy:encode(JSON)}; process(Path, Request) -> ?DEBUG("matrix 404: ~p~n~p~n", [Path, Request]), ejabberd_web:error(not_found). @@ -512,7 +494,6 @@ init([Host]) -> process_flag(trap_exit, true), mod_matrix_gw_s2s:create_db(), mod_matrix_gw_room:create_db(), - %mod_matrix_gw_c2s:create_db(), Opts = gen_mod:get_module_opts(Host, ?MODULE), MyHost = gen_mod:get_opt(host, Opts), register_routes(Host, [MyHost]), @@ -799,14 +780,10 @@ send_request(Host, Method, MatrixServer, Path, Query, JSON, _ -> {URL, Headers, "application/json;charset=UTF-8", Content} end, - ?DEBUG("httpc request ~p", [{Method, Request, HTTPOptions, Options}]), - HTTPRes = - httpc:request(Method, - Request, - HTTPOptions, - Options), - ?DEBUG("httpc request res ~p", [HTTPRes]), - HTTPRes. + httpc:request(Method, + Request, + HTTPOptions, + Options). make_auth_header(Host, MatrixServer, Method, URI, Content) -> Origin = mod_matrix_gw_opt:matrix_domain(Host), diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 1cb0d892b..6ceff2141 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -29,14 +29,11 @@ %% API -export([start_link/2, supervisor/1, create_db/0, - get_room_pid/2, join_direct/5, process_pdu/3, + get_room_pid/2, join/5, process_pdu/3, get_missing_events/7, get_state_ids/4, get_rooms_list/0, get_event/3, make_join/4, send_join/5, - create_new_room/3, room_add_event/3, binary_to_room_version/1, - parse_user_id/1, - send_muc_invite/6, escape/1, unescape/1, route/1]). @@ -71,28 +68,18 @@ json :: #{atom() | binary() => misc:json_value()}, state_map}). --record(direct, - {local_user :: jid() | undefined, - remote_user :: binary() | undefined, - client_state}). - --record(multi, - {users :: #{}}). - --record(multi_user, - {join_ts :: integer()}). - -record(data, {host :: binary(), - kind :: #direct{} | undefined, + local_user :: jid() | undefined, + remote_user :: binary() | undefined, + remote_servers = #{}, room_id :: binary(), - room_jid :: jid(), room_version :: #room_version{}, events = #{}, latest_events = sets:new([{version, 2}]), nonlatest_events = sets:new([{version, 2}]), - event_queue = treap:empty(), - outgoing_txns = #{}}). + outgoing_txns = #{}, + client_state}). -define(ROOM_CREATE, <<"m.room.create">>). -define(ROOM_MEMBER, <<"m.room.member">>). @@ -103,7 +90,6 @@ -define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>). -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). --define(MAX_TXN_RETRIES, 5). %%%=================================================================== %%% API @@ -162,85 +148,23 @@ get_existing_room_pid(_Host, RoomID) -> {ok, Pid} end. -join_direct(Host, MatrixServer, RoomID, Sender, UserID) -> +join(Host, MatrixServer, RoomID, Sender, UserID) -> case get_room_pid(Host, RoomID) of {ok, Pid} -> - gen_statem:cast(Pid, {join_direct, MatrixServer, RoomID, Sender, UserID}); + gen_statem:cast(Pid, {join, MatrixServer, RoomID, Sender, UserID}); {error, _} = Error -> Error end. -route(#presence{from = From, to = #jid{luser = <<$!, _/binary>>} = To, - type = Type} = Packet) -> - case room_id_from_xmpp(To#jid.luser) of - {ok, RoomID} -> - Host = ejabberd_config:get_myname(), - case From#jid.lserver of - Host -> - case Type of - available -> - case get_room_pid(Host, RoomID) of - {ok, Pid} -> - gen_statem:cast(Pid, {join, From, Packet}); - {error, _} = Error -> - ?DEBUG("join failed ~p", [{From, To, Error}]), - ok - end; - unavailable -> - case get_existing_room_pid(Host, RoomID) of - {ok, Pid} -> - gen_statem:cast(Pid, {leave, From}); - _ -> - ok - end; - _ -> - ok - end; - _ -> - ok - end; - error -> - ok - end; -route(#message{from = From, to = #jid{luser = <<$!, _/binary>>} = To, - type = groupchat, - body = Body}) -> - case room_id_from_xmpp(To#jid.luser) of - {ok, RoomID} -> - Host = ejabberd_config:get_myname(), - case From#jid.lserver of - Host -> - case user_id_from_jid(From, Host) of - {ok, UserID} -> - case get_existing_room_pid(Host, RoomID) of - {ok, Pid} -> - Text = xmpp:get_text(Body), - JSON = - #{<<"content">> => - #{<<"body">> => Text, - <<"msgtype">> => <<"m.text">>}, - <<"sender">> => UserID, - <<"type">> => ?ROOM_MESSAGE}, - gen_statem:cast(Pid, {add_event, JSON}), - ok; - _ -> - ok - end; - error -> - ok - end; - _ -> - ok - end; - error -> - ok - end; route(#message{from = From, to = To, body = Body} = _Pkt) -> - Host = ejabberd_config:get_myname(), - case user_id_from_jid(To, Host) of - {ok, ToMatrixID} -> + case binary:split(To#jid.luser, <<"%">>) of + [EscU, EscS] -> + U = unescape(EscU), + S = unescape(EscS), + ToMatrixID = <<$@, U/binary, $:, S/binary>>, Key = {{From#jid.luser, From#jid.lserver}, ToMatrixID}, Text = xmpp:get_text(Body), + Host = ejabberd_config:get_myname(), case mnesia:dirty_read(matrix_direct, Key) of [#matrix_direct{room_id = RoomID}] -> ?DEBUG("msg ~p~n", [{RoomID, From, ToMatrixID, Text}]), @@ -340,7 +264,7 @@ route(#message{from = From, to = To, body = Body} = _Pkt) -> ok end end; - error -> + _ -> ok end; route(_) -> @@ -409,26 +333,6 @@ send_join(Host, Origin, RoomID, EventID, JSON) -> Error end. -create_new_room(Host, XMPPID, MatrixID) -> - RoomID = new_room_id(), - case get_room_pid(Host, RoomID) of - {ok, Pid} -> - MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - gen_statem:cast(Pid, {create, MatrixServer, RoomID, - XMPPID, MatrixID}), - {ok, RoomID}; - {error, _} = Error -> - Error - end. - -room_add_event(Host, RoomID, Event) -> - case get_existing_room_pid(Host, RoomID) of - {ok, Pid} -> - gen_statem:call(Pid, {add_event, Event}); - {error, _} -> - {error, room_not_found} - end. - %%%=================================================================== %%% gen_statem callbacks %%%=================================================================== @@ -443,16 +347,12 @@ room_add_event(Host, RoomID, Event) -> %%-------------------------------------------------------------------- -spec init(Args :: term()) -> gen_statem:init_result(term()). init([Host, RoomID]) -> - ServiceHost = mod_matrix_gw_opt:host(Host), - {ok, RID} = room_id_to_xmpp(RoomID), - RoomJID = jid:make(RID, ServiceHost), mnesia:dirty_write( #matrix_room{room_id = RoomID, pid = self()}), {ok, state_name, #data{host = Host, room_id = RoomID, - room_jid = RoomJID, room_version = binary_to_room_version(<<"9">>)}}. %%-------------------------------------------------------------------- @@ -584,7 +484,7 @@ handle_event({call, From}, ?INFO_MSG("failed make_join: ~p", [{Class, Reason, ST}]), {keep_state, Data, [{reply, From, {error, Reason}}]} end; -handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, Data) -> +handle_event(cast, {join, MatrixServer, RoomID, Sender, UserID}, State, Data) -> Host = Data#data.host, %% TODO: check if there is another solution to "You are not invited to this room" and not receiving the first messages in the room timer:sleep(1000), @@ -636,17 +536,14 @@ handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, D RoomID, EventID], [], Event4, - [{connect_timeout, 5000}, - {timeout, 60000}], + [{timeout, 5000}], [{sync, true}, {body_format, binary}]), ?DEBUG("send_join ~p~n", [SendJoinRes]), - process_send_join_res( - MatrixServer, SendJoinRes, RoomVersion, - Data#data{ - kind = #direct{local_user = UserJID, - remote_user = Sender}, - room_version = RoomVersion}) + process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, + Data#data{local_user = UserJID, + remote_user = Sender, + room_version = RoomVersion}) end; _JSON -> ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), @@ -664,172 +561,6 @@ handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, D ?INFO_MSG("bad join user id: ~p", [{UserID, UserJID}]), {stop, normal} end; -handle_event(cast, {join, UserJID, Packet}, _State, Data) -> - Host = Data#data.host, - {LUser, LServer, LResource} = jid:tolower(UserJID), - case Data#data.kind of - #multi{users = #{{LUser, LServer} := #{LResource := _}}} -> - {keep_state_and_data, []}; - #multi{} = Kind -> - case user_id_from_jid(UserJID, Host) of - {ok, UserID} -> - JoinTS = erlang:system_time(millisecond), - JSON = #{<<"content">> => - #{<<"membership">> => <<"join">>}, - <<"sender">> => UserID, - <<"state_key">> => UserID, - <<"type">> => ?ROOM_MEMBER}, - Users = Kind#multi.users, - Resources = - case Users of - #{{LUser, LServer} := Rs} -> Rs; - _ -> #{} - end, - Data2 = - Data#data{ - kind = - Kind#multi{ - users = - Users#{{LUser, LServer} => - Resources#{LResource => - #multi_user{join_ts = JoinTS}}}}}, - {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; - error -> - ?INFO_MSG("bad join user id: ~p", [UserJID]), - {keep_state_and_data, []} - end; - #direct{} -> - {keep_state_and_data, []}; - _ -> - Lang = xmpp:get_lang(Packet), - case user_id_from_jid(UserJID, Host) of - {ok, UserID} -> - %% TODO: async - RoomID = Data#data.room_id, - {ok, MatrixServer} = - case binary:split(RoomID, <<":">>) of - [_, MS] -> {ok, MS}; - _ -> error - end, - MakeJoinRes = - mod_matrix_gw:send_request( - Host, get, MatrixServer, - [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, - RoomID, UserID], - [{<<"ver">>, <<"9">>}, - {<<"ver">>, <<"10">>}, - {<<"ver">>, <<"11">>}], - none, - [{timeout, 5000}], - [{sync, true}, - {body_format, binary}]), - ?DEBUG("make_join ~p~n", [MakeJoinRes]), - case MakeJoinRes of - {ok, {{_, 200, _}, _Headers, Body}} -> - try misc:json_decode(Body) of - #{<<"event">> := Event, - <<"room_version">> := SRoomVersion} -> - case binary_to_room_version(SRoomVersion) of - false -> - ?DEBUG("unsupported room version on make_join: ~p", [MakeJoinRes]), - {stop, normal}; - #room_version{} = RoomVersion -> - JoinTS = erlang:system_time(millisecond), - Origin = mod_matrix_gw_opt:matrix_domain(Host), - Event2 = - Event#{<<"origin">> => Origin, - <<"origin_server_ts">> => JoinTS}, - CHash = mod_matrix_gw:content_hash(Event2), - Event3 = - Event2#{<<"hashes">> => - #{<<"sha256">> => - mod_matrix_gw:base64_encode(CHash)}}, - Event4 = mod_matrix_gw:sign_event(Host, Event3, RoomVersion), - EventID = mod_matrix_gw:get_event_id(Event4, RoomVersion), - SendJoinRes = - mod_matrix_gw:send_request( - Data#data.host, put, MatrixServer, - [<<"_matrix">>, <<"federation">>, - <<"v2">>, <<"send_join">>, - RoomID, EventID], - [], - Event4, - [{connect_timeout, 5000}, - {timeout, 60000}], - [{sync, true}, - {body_format, binary}]), - ?DEBUG("send_join ~p~n", [SendJoinRes]), - process_send_join_res( - MatrixServer, SendJoinRes, RoomVersion, - Data#data{ - kind = - #multi{users = - #{{LUser, LServer} => - #{LResource => #multi_user{join_ts = JoinTS}}}}, - room_version = RoomVersion}) - end; - _JSON -> - ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), - Txt = <<"received bad JSON on make_join">>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal} - catch - _:_ -> - ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), - Txt = <<"received bad JSON on make_join">>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal} - end; - {ok, {{_, 400, _}, _Headers, Body}} -> - ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = <<"make_join failed: ", Body/binary>>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal}; - _ -> - ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = "make_join failed", - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal} - end; - error -> - ?INFO_MSG("bad join user id: ~p", [UserJID]), - Txt = <<"bad user id">>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal} - end - end; -handle_event(cast, {leave, UserJID}, _State, Data) -> - Host = Data#data.host, - {LUser, LServer, LResource} = jid:tolower(UserJID), - case Data#data.kind of - #multi{users = #{{LUser, LServer} := #{LResource := _} = Resources} = Users} -> - Resources2 = maps:remove(LResource, Resources), - if - Resources2 == #{} -> - Users2 = maps:remove({LUser, LServer}, Users), - Kind = (Data#data.kind)#multi{users = Users2}, - Data2 = Data#data{kind = Kind}, - {ok, UserID} = user_id_from_jid(UserJID, Host), - JSON = #{<<"content">> => - #{<<"membership">> => <<"leave">>}, - <<"sender">> => UserID, - <<"state_key">> => UserID, - <<"type">> => ?ROOM_MEMBER}, - {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; - true -> - Users2 = Users#{{LUser, LServer} => Resources2}, - Kind = (Data#data.kind)#multi{users = Users2}, - Data2 = Data#data{kind = Kind}, - {keep_state, Data2, []} - end; - _ -> - {keep_state_and_data, []} - end; handle_event(cast, {create, _MatrixServer, RoomID, LocalUserID, RemoteUserID}, _State, Data) -> Host = Data#data.host, case user_id_to_jid(LocalUserID, Data) of @@ -837,37 +568,25 @@ handle_event(cast, {create, _MatrixServer, RoomID, LocalUserID, RemoteUserID}, _ mnesia:dirty_write( #matrix_direct{local_remote = {{UserJID#jid.luser, UserJID#jid.lserver}, RemoteUserID}, room_id = RoomID}), - {keep_state, - Data#data{kind = #direct{local_user = UserJID, - remote_user = RemoteUserID}}, []}; + {keep_state, Data#data{local_user = UserJID, + remote_user = RemoteUserID}, []}; UserJID -> ?INFO_MSG("bad create user id: ~p", [{LocalUserID, UserJID}]), {stop, normal} end; handle_event(cast, {add_event, JSON}, _State, Data) -> try - {Data2, _Event} = add_event(JSON, Data), + Data2 = add_event(JSON, Data), {keep_state, Data2, [{next_event, internal, update_client}]} catch Class:Reason:ST -> ?INFO_MSG("failed add_event: ~p", [{Class, Reason, ST}]), {keep_state, Data, []} end; -handle_event({call, From}, {add_event, JSON}, _State, Data) -> - try - {Data2, Event} = add_event(JSON, Data), - {keep_state, Data2, [{reply, From, {ok, Event#event.id}}, - {next_event, internal, update_client}]} - catch - Class:Reason:ST -> - ?INFO_MSG("failed add_event: ~p", [{Class, Reason, ST}]), - {keep_state, Data, []} - end; handle_event(cast, Msg, State, Data) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {next_state, State, Data, []}; -handle_event(internal, update_client, _State, - #data{kind = #direct{local_user = JID}} = Data) -> +handle_event(internal, update_client, _State, Data) -> try case update_client(Data) of {ok, Data2} -> @@ -876,6 +595,7 @@ handle_event(internal, update_client, _State, ?INFO_MSG("leaving ~p: ~p", [Data#data.room_id, LeaveReason]), Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, JSON = #{<<"content">> => #{<<"membership">> => <<"leave">>}, @@ -889,36 +609,13 @@ handle_event(internal, update_client, _State, catch Class:Reason:ST -> ?INFO_MSG("failed update_client: ~p", [{Class, Reason, ST}]), - {keep_state_and_data, []} + {keep_state, Data, []} end; -handle_event(internal, update_client, _State, - #data{kind = #multi{}} = Data) -> - try - case update_client(Data) of - {ok, Data2} -> - {keep_state, Data2, []}; - stop -> - {stop, normal} - end - catch - Class:Reason:ST -> - ?INFO_MSG("failed update_client: ~p", [{Class, Reason, ST}]), - {keep_state_and_data, []} - end; -handle_event(internal, update_client, _State, #data{kind = undefined}) -> - {keep_state_and_data, []}; handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) -> - ?DEBUG("send_txn_res ~p", [{RequestID, TxnID, Server, Res}]), case Data#data.outgoing_txns of - #{Server := {{RequestID, TxnID, _Events, Count}, Queue}} -> - Done = - case Res of - {{_, 200, _}, _Headers, _Body} -> true; - _ when Count < ?MAX_TXN_RETRIES -> false; - _ -> true - end, - case Done of - true -> + #{Server := {{RequestID, TxnID, _Events}, Queue}} -> + case Res of + {{_, 200, _}, _Headers, _Body} -> Data2 = case Queue of [] -> @@ -928,7 +625,8 @@ handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) send_new_txn(lists:reverse(Queue), Server, Data) end, {keep_state, Data2, []}; - false -> + _ -> + %% TODO erlang:send_after(30000, self(), {resend_txn, Server}), {keep_state, Data, []} end; @@ -937,8 +635,8 @@ handle_event(info, {send_txn_res, RequestID, TxnID, Server, Res}, _State, Data) end; handle_event(info, {resend_txn, Server}, _State, Data) -> case Data#data.outgoing_txns of - #{Server := {{_RequestID, TxnID, Events, Count}, Queue}} -> - Data2 = send_txn(TxnID, Events, Server, Count + 1, Queue, Data), + #{Server := {{_RequestID, TxnID, Events}, Queue}} -> + Data2 = send_txn(TxnID, Events, Server, Queue, Data), {keep_state, Data2, []}; _ -> {keep_state, Data, []} @@ -963,12 +661,11 @@ terminate(Reason, _State, Data) -> #matrix_room{room_id = Data#data.room_id, pid = self()}), %% TODO: wait for messages - case Data#data.kind of - #direct{local_user = #jid{} = LocalUserJID, - remote_user = RemoteUser} -> + case Data#data.local_user of + #jid{} = LocalUserJID -> mnesia:dirty_delete_object( #matrix_direct{local_remote = {{LocalUserJID#jid.luser, LocalUserJID#jid.lserver}, - RemoteUser}, + Data#data.remote_user}, room_id = Data#data.room_id}); _ -> ok @@ -1016,20 +713,13 @@ process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> JSONState), Event = json_to_event(JSONEvent, RoomVersion), ?DEBUG("send_join res: ~p~n", [JSON]), - case Data#data.kind of - #multi{} -> - %% TODO: do check_event_sig_and_hash, but faster - ok; - _ -> - lists:foreach( - fun(E) -> - ?DEBUG("send_join res check ~p~n", [E]), - case check_event_sig_and_hash(Data#data.host, E) of - {ok, _} -> ok; - {error, Error} -> error(Error) - end - end, [Event] ++ AuthChain ++ State) - end, + lists:foreach( + fun(E) -> + case check_event_sig_and_hash(Data#data.host, E) of + {ok, _} -> ok; + {error, Error} -> error(Error) + end + end, [Event] ++ AuthChain ++ State), CreateEvents = lists:filter( fun(#event{type = ?ROOM_CREATE, @@ -1066,20 +756,20 @@ process_send_join_res(MatrixServer, SendJoinRes, RoomVersion, Data) -> end; _ -> ?DEBUG("bad create event: ~p", [CreateEvents]), - {stop, normal, Data} + {keep_state, Data, []} end end catch error:{invalid_signature, EventID} -> ?INFO_MSG("failed signature check on event ~p", [EventID]), - {stop, normal, Data}; + {keep_state, Data, []}; Class:Reason:ST -> ?INFO_MSG("failed send_join: ~p", [{Class, Reason, ST}]), - {stop, normal, Data} + {keep_state, Data, []} end; _ -> ?DEBUG("failed send_join: ~p", [SendJoinRes]), - {stop, normal, Data} + {keep_state, Data, []} end. process_send_join_res2(MatrixServer, AuthChain, Event, State, Data) -> @@ -1123,11 +813,8 @@ process_send_join_res2(MatrixServer, AuthChain, Event, State, Data) -> {body_format, binary}, {receiver, fun({_, Res}) -> - spawn(fun() -> - process_missing_events_res( - Host, MatrixServer, Pid, RoomID, RoomVersion, - {ok, Res}) - end) + process_missing_events_res(Host, MatrixServer, Pid, RoomID, RoomVersion, + {ok, Res}) end}]), Data3. @@ -1677,7 +1364,6 @@ fill_event(JSON, Data) -> (maps:get(EID, Data#data.events))#event.depth end, PrevEvents)]), Depth2 = min(Depth + 1, ?MAX_DEPTH), - ?DEBUG("fill ~p", [{PrevEvents, Data#data.events}]), StateMaps = lists:map( fun(EID) -> @@ -1731,7 +1417,7 @@ add_event(JSON, Data) -> case check_event_auth(Event2, Data) of true -> %%TODO: soft fail - {store_event(Event2, Data), Event2}; + store_event(Event2, Data); false -> error({event_auth_error, Event2#event.id}) end. @@ -1748,38 +1434,23 @@ store_event(Event, Data) -> error -> ?DEBUG("store ~p~n", [Event#event.id]), Data2 = notify_event(Event, Data), - {LatestEvents, NonLatestEvents} = - case Event of - #event{state_map = undefined} -> - {Data2#data.latest_events, Data2#data.nonlatest_events}; - _ -> - SeenEvents = Event#event.prev_events ++ Event#event.auth_events, - LatestEs = - lists:foldl(fun(E, Acc) -> sets:del_element(E, Acc) end, Data2#data.latest_events, - SeenEvents), - NonLatestEs = - lists:foldl(fun(E, Acc) -> sets:add_element(E, Acc) end, Data2#data.nonlatest_events, - SeenEvents), - LatestEs2 = - case maps:is_key(Event#event.id, NonLatestEs) of - true -> - LatestEs; - false -> - LatestEs#{Event#event.id => []} - end, - %%?DEBUG("latest ~p~n", [{LatestEvents2, NonLatestEvents}]), - {LatestEs2, NonLatestEs} + LatestEvents = + lists:foldl(fun(E, Acc) -> sets:del_element(E, Acc) end, Data2#data.latest_events, + Event#event.prev_events), + NonLatestEvents = + lists:foldl(fun(E, Acc) -> sets:add_element(E, Acc) end, Data2#data.nonlatest_events, + Event#event.prev_events), + LatestEvents2 = + case maps:is_key(Event#event.id, NonLatestEvents) of + true -> + LatestEvents; + false -> + LatestEvents#{Event#event.id => []} end, - EventQueue = - treap:insert( - Event#event.id, - {erlang:monotonic_time(micro_seconds), - erlang:unique_integer([monotonic])}, - [], Data2#data.event_queue), + ?DEBUG("latest ~p~n", [{LatestEvents2, NonLatestEvents}]), Data2#data{events = Events#{Event#event.id => Event}, - latest_events = LatestEvents, - nonlatest_events = NonLatestEvents, - event_queue = EventQueue} + latest_events = LatestEvents2, + nonlatest_events = NonLatestEvents} end. simple_toposort(Events) -> @@ -1799,8 +1470,7 @@ simple_toposort(Events) -> simple_toposort_dfs(EventID, {Res, Used}, Events) -> case maps:find(EventID, Events) of error -> - %error({unknown_event, EventID}); - {Res, Used}; + error({unknown_event, EventID}); {ok, Event} -> Used2 = Used#{EventID => gray}, {Res8, Used8} = @@ -1995,8 +1665,7 @@ request_room_state(Host, Origin, _Pid, RoomID, RoomVersion, Event) -> RoomID], [{<<"event_id">>, Event#event.id}], none, - [{connect_timeout, 5000}, - {timeout, 60000}], + [{timeout, 5000}], [{sync, true}, {body_format, binary}]), case Res of @@ -2312,7 +1981,6 @@ lexicographic_toposort_prepare(EventID, Used, EventSet, Data) -> Used4. lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> - %?DEBUG("toposort ~p", [{gb_trees:to_list(Current), IncomingCnt, Res}]), case gb_trees:is_empty(Current) of true -> case maps:size(IncomingCnt) of @@ -2324,27 +1992,23 @@ lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> false -> {{_, _, EventID}, _, Current2} = gb_trees:take_smallest(Current), Event = maps:get(EventID, Data#data.events), - %?DEBUG("toposort ev ~p", [Event]), - {IncomingCnt2, Current3} = + IncomingCnt2 = lists:foldl( - fun(EID, {InCnt, Cur} = Acc) -> - case maps:is_key(EID, InCnt) of + fun(EID, Acc) -> + case maps:is_key(EID, Acc) of true -> - C = maps:get(EID, InCnt) - 1, + C = maps:get(EID, Acc) - 1, case C of 0 -> - E = maps:get(EID, Data#data.events), - PowerLevel = get_sender_power_level(EID, Data), - Cur2 = gb_trees:enter({-PowerLevel, E#event.origin_server_ts, EID}, [], Cur), - {maps:remove(EID, InCnt), Cur2}; + maps:remove(EID, Acc); _ -> - {maps:put(EID, C, InCnt), Cur} + maps:put(EID, C, Acc) end; false -> Acc end - end, {IncomingCnt, Current2}, Event#event.auth_events), - lexicographic_toposort_loop(Current3, IncomingCnt2, [EventID | Res], Data) + end, IncomingCnt, Event#event.auth_events), + lexicographic_toposort_loop(Current2, IncomingCnt2, [EventID | Res], Data) end. get_sender_power_level(EventID, Data) -> @@ -2512,16 +2176,82 @@ check_event_content_hash(Event) -> false end. -notify_event(Event, Data) -> - Data2 = notify_event_matrix(Event, Data), - notify_event_xmpp(Event, Data2). - -notify_event_matrix( - #event{type = ?ROOM_MEMBER, - state_key = StateKey, - sender = Sender, - json = #{<<"content">> := #{<<"membership">> := <<"invite">>}}} = Event, - #data{kind = #direct{}} = Data) -> +notify_event(#event{sender = Sender, + json = #{<<"test">> := true}} = Event, + Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + LSenderServer = SenderJID#jid.lserver, + UserJID = Data#data.local_user, + LUserServer = UserJID#jid.lserver, + case LSenderServer of + LUserServer -> + %RemoteServers = maps:keys(Data#data.remote_servers), + RemoteServers = get_remote_servers(Data), + lists:foldl( + fun(Server, DataAcc) -> + case DataAcc#data.outgoing_txns of + #{Server := {T, Queue}} -> + Queue2 = [Event | Queue], + DataAcc#data{outgoing_txns = + maps:put(Server, {T, Queue2}, + DataAcc#data.outgoing_txns)}; + _ -> + send_new_txn([Event], Server, DataAcc) + end + end, Data, RemoteServers); + _ -> + Data + end; + error -> + Data + end; +notify_event(#event{type = ?ROOM_MESSAGE, sender = Sender, + json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, + <<"body">> := Body}}} = Event, + Data) -> + case user_id_to_jid(Sender, Data) of + #jid{} = SenderJID -> + LSenderJID = jid:tolower(SenderJID), + UserJID = Data#data.local_user, + LUserJID = jid:tolower(UserJID), + case LSenderJID of + LUserJID -> + %RemoteServers = maps:keys(Data#data.remote_servers), + RemoteServers = get_remote_servers(Data), + lists:foldl( + fun(Server, DataAcc) -> + case DataAcc#data.outgoing_txns of + #{Server := {T, Queue}} -> + Queue2 = [Event | Queue], + DataAcc#data{outgoing_txns = + maps:put(Server, {T, Queue2}, + DataAcc#data.outgoing_txns)}; + _ -> + send_new_txn([Event], Server, DataAcc) + end + end, Data, RemoteServers); + _ -> + RoomID = Data#data.room_id, + Msg = #message{from = SenderJID, + to = UserJID, + type = chat, + body = [#text{data = Body}], + sub_els = [#xmlel{name = <<"x">>, + attrs = [{<<"xmlns">>, <<"p1:matrix">>}, + {<<"room_id">>, RoomID}]}] + }, + ejabberd_router:route(Msg), + Data + end; + error -> + Data + end; +notify_event(#event{type = ?ROOM_MEMBER, + state_key = StateKey, + sender = Sender, + json = #{<<"content">> := #{<<"membership">> := <<"invite">>}}} = Event, + Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), case mod_matrix_gw:get_id_domain_exn(StateKey) of @@ -2556,229 +2286,15 @@ notify_event_matrix( ?DEBUG("send invite ~p~n", [InviteRes]), Data end; -notify_event_matrix(#event{sender = Sender} = Event, - Data) -> - case user_id_to_jid(Sender, Data) of - #jid{} = SenderJID -> - %RemoteServers = maps:keys(Data#data.remote_servers), - RemoteServers = get_remote_servers(Data), - Host = Data#data.host, - MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - lists:foldl( - fun(Server, DataAcc) -> - case Server of - MatrixServer -> - %% TODO - %case parse_user_id(Data#data.remote_user) of - % {ok, U, MatrixServer} -> - % mod_matrix_gw_c2s:notify( - % Host, U, Event); - % _ -> - % ok - %end, - DataAcc; - _ -> - case SenderJID#jid.lserver of - Host -> - case DataAcc#data.outgoing_txns of - #{Server := {T, Queue}} -> - Queue2 = [Event | Queue], - DataAcc#data{ - outgoing_txns = - maps:put(Server, {T, Queue2}, - DataAcc#data.outgoing_txns)}; - _ -> - send_new_txn([Event], Server, DataAcc) - end; - _ -> - Data - end - end - end, Data, RemoteServers); - error -> - Data - end. - -notify_event_xmpp( - #event{type = ?ROOM_MESSAGE, sender = Sender, - json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, - <<"body">> := Body}}}, - #data{kind = #direct{local_user = UserJID}} = Data) -> - case user_id_to_jid(Sender, Data) of - #jid{} = SenderJID -> - LSenderJID = jid:tolower(SenderJID), - LUserJID = jid:tolower(UserJID), - case LSenderJID of - LUserJID -> - Data; - _ -> - RoomID = Data#data.room_id, - Msg = #message{from = SenderJID, - to = UserJID, - type = chat, - body = [#text{data = Body}], - sub_els = [#xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, <<"p1:matrix">>}, - {<<"room_id">>, RoomID}]}] - }, - ejabberd_router:route(Msg), - Data - end; - error -> - Data - end; -notify_event_xmpp( - #event{type = ?ROOM_MESSAGE, sender = Sender, - json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, - <<"body">> := Body}, - <<"origin_server_ts">> := OriginTS}}, - #data{kind = #multi{users = Users}} = Data) -> - case Sender of - <<$@, SenderUser/binary>> -> - ?DEBUG("notify xmpp ~p", [Users]), - From = jid:replace_resource(Data#data.room_jid, SenderUser), - maps:fold( - fun({LUser, LServer}, Resources, ok) -> - maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) - when JoinTS =< OriginTS -> - UserJID = jid:make(LUser, LServer, LResource), - Msg = #message{from = From, - to = UserJID, - type = groupchat, - body = [#text{data = Body}] - }, - TimeStamp = misc:usec_to_now(OriginTS * 1000), - TSMsg = misc:add_delay_info( - Msg, Data#data.room_jid, TimeStamp), - ejabberd_router:route(TSMsg); - (_, _, _) -> ok - end, ok, Resources) - end, ok, Users), - Data; - _ -> - Data - end; -notify_event_xmpp( - #event{type = ?ROOM_MEMBER, sender = Sender, - json = #{<<"content">> := #{<<"membership">> := <<"join">>}, - <<"origin_server_ts">> := OriginTS}} = Event, - #data{kind = #multi{users = Users}} = Data) -> - case user_id_to_jid(Sender, Data) of - #jid{} = SenderJID -> - <<$@, SenderUser/binary>> = Sender, - From = jid:replace_resource(Data#data.room_jid, SenderUser), - maps:fold( - fun({LUser, LServer}, Resources, ok) -> - maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) - when JoinTS =< OriginTS -> - case jid:tolower(SenderJID) of - {LUser, LServer, _} -> - send_initial_presences( - SenderJID, Event, Data); - _ -> - ok - end, - UserJID = jid:make(LUser, LServer, LResource), - Pres = #presence{from = From, - to = UserJID, - type = available - }, - ejabberd_router:route(Pres); - (_, _, _) -> ok - end, ok, Resources) - end, ok, Users), - Data; - error -> - Data - end; -notify_event_xmpp( - #event{type = ?ROOM_MEMBER, - state_key = StateKey, - json = #{<<"content">> := #{<<"membership">> := Membership}, - <<"origin_server_ts">> := OriginTS}}, - #data{kind = #multi{users = Users}} = Data) - when Membership == <<"leave">>; - Membership == <<"ban">> -> - case StateKey of - <<$@, RUser/binary>> -> - From = jid:replace_resource(Data#data.room_jid, RUser), - maps:fold( - fun({LUser, LServer}, Resources, ok) -> - maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) - when JoinTS =< OriginTS -> - UserJID = jid:make(LUser, LServer, LResource), - Pres = #presence{from = From, - to = UserJID, - type = unavailable - }, - ejabberd_router:route(Pres); - (_, _, _) -> ok - end, ok, Resources) - end, ok, Users), - case user_id_to_jid(StateKey, Data) of - #jid{} = RJID -> - US = {RJID#jid.luser, RJID#jid.lserver}, - case Users of - #{US := Resources} -> - JoinTS = - maps:fold( - fun(_, #multi_user{join_ts = TS}, Acc) -> - max(Acc, TS) - end, 0, Resources), - if - JoinTS =< OriginTS -> - Users2 = maps:remove(US, Users), - Data#data{ - kind = (Data#data.kind)#multi{ - users = Users2}}; - true -> - Data - end; - _ -> - Data - end; - error -> - Data - end; - error -> - Data - end; -notify_event_xmpp(_Event, Data) -> +notify_event(_Event, Data) -> Data. -send_initial_presences(JID, Event, Data) -> - ?DEBUG("send_initial_presences ~p", [{JID, Event}]), - maps:fold( - fun({?ROOM_MEMBER, _}, EID, ok) -> - case maps:find(EID, Data#data.events) of - {ok, #event{ - sender = <<$@, SenderUser/binary>>, - json = #{<<"content">> := - #{<<"membership">> := <<"join">>}}}} -> - From = jid:replace_resource(Data#data.room_jid, SenderUser), - Pres = #presence{from = From, - to = JID, - type = available - }, - ejabberd_router:route(Pres), - ok; - _ -> - ok - end; - (_, _, ok) -> - ok - end, ok, Event#event.state_map). - - send_new_txn(Events, Server, Data) -> TxnID = p1_rand:get_string(), - send_txn(TxnID, Events, Server, 1, [], Data). + send_txn(TxnID, Events, Server, [], Data). -send_txn(TxnID, Events, Server, Count, Queue, Data) -> - ?DEBUG("send txn ~p~n", [{TxnID, Server}]), +send_txn(TxnID, Events, Server, Queue, Data) -> + ?DEBUG("send txn ~p~n", [TxnID]), Host = Data#data.host, Origin = mod_matrix_gw_opt:matrix_domain(Host), PDUs = @@ -2791,7 +2307,6 @@ send_txn(TxnID, Events, Server, Count, Queue, Data) -> Self = self(), Receiver = fun({RequestID, Res}) -> - ?DEBUG("send_txn_res ~p", [{RequestID, Res}]), Self ! {send_txn_res, RequestID, TxnID, Server, Res} end, {ok, RequestID} = @@ -2806,7 +2321,7 @@ send_txn(TxnID, Events, Server, Count, Queue, Data) -> [{sync, false}, {receiver, Receiver}]), Data#data{outgoing_txns = - maps:put(Server, {{RequestID, TxnID, Events, Count}, Queue}, + maps:put(Server, {{RequestID, TxnID, Events}, Queue}, Data#data.outgoing_txns)}. do_get_missing_events(Origin, EarliestEvents, LatestEvents, Limit, MinDepth, Data) -> @@ -2932,7 +2447,7 @@ get_remote_servers(Data) -> maps:fold( fun(EventID, _, Acc) -> case maps:find(EventID, Data#data.events) of - {ok, Event} when is_map(Event#event.state_map) -> + {ok, Event} -> maps:fold( fun({?ROOM_MEMBER, UserID}, EID, Acc2) -> Server = mod_matrix_gw:get_id_domain_exn(UserID), @@ -2952,7 +2467,9 @@ get_remote_servers(Data) -> Acc end end, #{}, Data#data.latest_events), - maps:keys(Servers). + MatrixServer = mod_matrix_gw_opt:matrix_domain(Data#data.host), + Servers2 = maps:remove(MatrixServer, Servers), + maps:keys(Servers2). get_joined_users(Data) -> Users = @@ -2980,9 +2497,8 @@ get_joined_users(Data) -> end, #{}, Data#data.latest_events), maps:keys(Users). -user_id_to_jid(Str, #data{} = Data) -> - user_id_to_jid(Str, Data#data.host); -user_id_to_jid(Str, Host) when is_binary(Host) -> +user_id_to_jid(Str, Data) -> + Host = Data#data.host, ServerName = mod_matrix_gw_opt:matrix_domain(Host), case parse_user_id(Str) of {ok, U, ServerName} -> @@ -2996,19 +2512,6 @@ user_id_to_jid(Str, Host) when is_binary(Host) -> error end. -user_id_from_jid(#jid{luser = U, lserver = Host}, Host) -> - ServerName = mod_matrix_gw_opt:matrix_domain(Host), - {ok, <<$@, U/binary, $:, ServerName/binary>>}; -user_id_from_jid(JID, _Host) -> - case binary:split(JID#jid.luser, <<"%">>) of - [EscU, EscS] -> - U = unescape(EscU), - S = unescape(EscS), - {ok, <<$@, U/binary, $:, S/binary>>}; - _ -> - error - end. - new_room_id() -> Host = ejabberd_config:get_myname(), Letters = <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ">>, @@ -3054,35 +2557,33 @@ compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}) -> {?ROOM_MEMBER, Sender}]. -update_client(#data{kind = #direct{client_state = undefined, - local_user = JID, - remote_user = RemoteUserID}} = Data) -> +update_client(#data{client_state = undefined, + remote_user = RemoteUserID} = Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, Users = get_joined_users(Data), case lists:member(LocalUserID, Users) of true -> case lists:delete(LocalUserID, Users) of [RemoteUserID] -> - {ok, Data#data{kind = (Data#data.kind)#direct{client_state = established}}}; + {ok, Data#data{client_state = established}}; [_] -> - {leave, unknown_remote_user, - Data#data{kind = (Data#data.kind)#direct{client_state = leave}}}; + {leave, unknown_remote_user, Data#data{client_state = leave}}; [] -> {ok, Data}; _ -> - {leave, too_many_users, - Data#data{kind = (Data#data.kind)#direct{client_state = leave}}} + {leave, too_many_users, Data#data{client_state = leave}} end; false -> {ok, Data} end; -update_client(#data{kind = #direct{client_state = established, - local_user = JID, - remote_user = RemoteUserID}} = Data) -> +update_client(#data{client_state = established, + remote_user = RemoteUserID} = Data) -> Host = Data#data.host, MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + JID = Data#data.local_user, LocalUserID = <<$@, (JID#jid.luser)/binary, $:, MatrixServer/binary>>, Users = get_joined_users(Data), case lists:member(LocalUserID, Users) of @@ -3091,73 +2592,13 @@ update_client(#data{kind = #direct{client_state = established, true -> {ok, Data}; false -> - {leave, remote_user_left, Data#data{kind = (Data#data.kind)#direct{client_state = leave}}} + {leave, remote_user_left, Data#data{client_state = leave}} end; false -> stop end; -update_client(#data{kind = #direct{client_state = leave}}) -> - stop; -update_client(#data{kind = #multi{users = Users}} = Data) -> - ?DEBUG("update_client ~p", [Data#data.kind]), - if - Users == #{} -> - stop; - true -> - {ok, Data} - end. - - -send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) -> - case {user_id_to_jid(Sender, Host), user_id_to_jid(UserID, Host)} of - {#jid{} = SenderJID, #jid{lserver = Host} = UserJID} -> - process_pdu(Host, Origin, Event), - ServiceHost = mod_matrix_gw_opt:host(Host), - {ok, EscRoomID} = room_id_to_xmpp(RoomID), - RoomJID = jid:make(EscRoomID, ServiceHost), - Invite = #muc_invite{to = undefined, from = SenderJID}, - XUser = #muc_user{invites = [Invite]}, - Msg = #message{ - from = RoomJID, - to = UserJID, - sub_els = [XUser] - }, - ejabberd_router:route(Msg); - _ -> - ok - end. - -room_id_to_xmpp(RoomID) -> - case RoomID of - <<$!, Parts/binary>> -> - case binary:split(Parts, <<":">>) of - [R, S] -> - Len = 8 * size(R), - <> = R, - HR = integer_to_binary(IR, 16), - {ok, <<$!, HR/binary, $%, S/binary>>}; - _ -> error - end; - _ -> - error - end. - -room_id_from_xmpp(RID) -> - case RID of - <<$!, Parts/binary>> -> - case binary:split(Parts, <<"%">>) of - [R, S] -> - IR = binary_to_integer(R, 16), - Len = size(R) * 4, - RoomID = <>, - {ok, <<$!, RoomID/binary, $:, S/binary>>}; - _ -> error - end; - _ -> - error - end. - - +update_client(#data{client_state = leave}) -> + stop. escape(S) -> escape(S, <<>>). diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 3457607a0..533f250c5 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -337,44 +337,50 @@ handle_event(cast, {query, AuthParams, _Query, _JSON, _Request} = Msg, []} end; handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> - KeyVal = - case HTTPResult of - {{_, 200, _}, _, SJSON} -> - try - JSON = misc:json_decode(SJSON), - ?DEBUG("key ~p~n", [JSON]), - #{<<"verify_keys">> := VerifyKeys} = JSON, - #{KeyID := KeyData} = VerifyKeys, - #{<<"key">> := SKey} = KeyData, - VerifyKey = mod_matrix_gw:base64_decode(SKey), - ?DEBUG("key ~p~n", [VerifyKey]), - ?DEBUG("check ~p~n", - [catch check_signature( - JSON, Data#data.matrix_server, - KeyID, VerifyKey)]), - true = check_signature( - JSON, Data#data.matrix_server, - KeyID, VerifyKey), - #{<<"valid_until_ts">> := ValidUntil} = JSON, - ValidUntil2 = - min(ValidUntil, - erlang:system_time(millisecond) + timer:hours(24 * 7)), - {ok, VerifyKey, ValidUntil2} - catch - _:_ -> - error - end; - _ -> - error - end, - Keys = (Data#data.keys)#{KeyID => KeyVal}, - Froms = maps:get(KeyID, Data#data.key_queue, []), - KeyQueue = maps:remove(KeyID, Data#data.key_queue), - Data2 = Data#data{keys = Keys, - key_queue = KeyQueue}, - Replies = lists:map(fun(From) -> {reply, From, KeyVal} end, Froms), - ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), - {next_state, State, Data2, Replies}; + case HTTPResult of + {{_, 200, _}, _, SJSON} -> + try + JSON = misc:json_decode(SJSON), + ?DEBUG("key ~p~n", [JSON]), + #{<<"verify_keys">> := VerifyKeys} = JSON, + #{KeyID := KeyData} = VerifyKeys, + #{<<"key">> := SKey} = KeyData, + VerifyKey = mod_matrix_gw:base64_decode(SKey), + ?DEBUG("key ~p~n", [VerifyKey]), + ?DEBUG("check ~p~n", + [catch check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey)]), + true = check_signature( + JSON, Data#data.matrix_server, + KeyID, VerifyKey), + #{<<"valid_until_ts">> := ValidUntil} = JSON, + ValidUntil2 = + min(ValidUntil, + erlang:system_time(millisecond) + timer:hours(24 * 7)), + Keys = (Data#data.keys)#{KeyID => {ok, VerifyKey, ValidUntil2}}, + Froms = maps:get(KeyID, Data#data.key_queue, []), + KeyQueue = maps:remove(KeyID, Data#data.key_queue), + Data2 = Data#data{keys = Keys, + key_queue = KeyQueue}, + Replies = + lists:map( + fun(From) -> + {reply, From, {ok, VerifyKey, ValidUntil2}} + end, Froms), + ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), + {next_state, State, Data2, Replies} + catch + _:_ -> + %% TODO + Keys2 = (Data#data.keys)#{KeyID => error}, + {next_state, State, Data#data{keys = Keys2}, []} + end; + _ -> + %% TODO + Keys = (Data#data.keys)#{KeyID => error}, + {next_state, State, Data#data{keys = Keys}, []} + end; handle_event(cast, Msg, State, Data) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {next_state, State, Data, []}; From 1107cefdb6e94d42a45108676c95f985d8258e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 18 Dec 2024 17:29:37 +0100 Subject: [PATCH 0890/1302] Use tagged deps --- mix.exs | 4 ++-- mix.lock | 10 +++++----- rebar.config | 10 +++++----- rebar.lock | 42 ++++++++++++++++++++---------------------- 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/mix.exs b/mix.exs index e13364d45..9a48e3c36 100644 --- a/mix.exs +++ b/mix.exs @@ -144,8 +144,8 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b", override: true}, - {:yconf, git: "https://github.com/processone/yconf.git", ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938", override: true}] + {:xmpp, "~> 1.9.1"}, + {:yconf, "~> 1.0.17"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index a8af7cb20..621cf5a53 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, - "fast_xml": {:hex, :fast_xml, "1.1.53", "1ef4f6e5995bcfa94800a46b460c3400c19c0a533948b12200a2e2fb1a2be427", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5064336d6f363eee5097aa5dc5ced9b67f05152f2e6b8520fd50d268c2ab839c"}, + "fast_xml": {:hex, :fast_xml, "1.1.55", "ace020f2521f2a484ac8467d2822af85534a346e2aae03ffcbc34f29318befaf", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "83f3e23a780ed5f567cdec73953f06c95b838d709dbfa86b59a98a8d23c99f85"}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, @@ -24,16 +24,16 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "p1_acme": {:hex, :p1_acme, "1.0.24", "e19876618eb0be22815aca99640cb88cd5c86e4239b8e8dc15b4e5d7854ef7e2", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.15", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "f1451d706595ef997ab1ca17162ddac58f874ac97e315a5fadbe3cfa26148002"}, + "p1_acme": {:hex, :p1_acme, "1.0.25", "db91f0d6c193cd1d5c0b0fa3939a898dbf56a6075db4347cde26e802715de50c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a7b55b47495ddb4f98a15e65451ec3ad43f4637b955c74cd695d98e6a645d08c"}, "p1_mysql": {:hex, :p1_mysql, "1.0.25", "875d4cbdc7c9990270df3292cce2514e4c18a9fdfd19bef258cb4d0c45b4f243", [:rebar3], [], "hexpm", "e6187ffae95b726098e88f3ee6f2344ac259ce2c26e0ee403b05feef341ae434"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.28", "08eca7d2afc81ba6d757b572f4a57ef3a2383b0c7b785fde38184bc368376d54", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "655bb75df036ace55ddce17ca741143c42e0667c6206ac27a4dcbc65f71ac9ef"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.29", "fae0c90cbc5931865958150f1b667fb0d20b063f6a46a17770a4e5232ded632c", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "a6ff58e8b174993f3895da3ea6211a9f9d0c54d1a6e28bb321da3b3cd68b38c1"}, "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b", [ref: "8071c86f33b9a8e9d66a10e058be088eecdc670b"]}, - "yconf": {:git, "https://github.com/processone/yconf.git", "9898754f16cbd4585a1c2061d72fa441ecb2e938", [ref: "9898754f16cbd4585a1c2061d72fa441ecb2e938"]}, + "xmpp": {:hex, :xmpp, "1.9.1", "a1642d93cdbdf947f32344b0e05fcc8efcfb9f11c32832acc9bd826b52adbe48", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "d2b1431af6e4c1a4c8bf90caf0cc11cdeb047b8323b87e9d7e4826d4913275dc"}, + "yconf": {:hex, :yconf, "1.0.17", "dcf242e27f3fc5d0743d6b8175dd39bc14a1f4ed7e6ea986366a44a6ff3b2a3a", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "dd2892923241449a46cc8457b9ec0fb14030700735a5885955677c735c341a25"}, } diff --git a/rebar.config b/rebar.config index c37c0b830..b4b2cdb96 100644 --- a/rebar.config +++ b/rebar.config @@ -45,7 +45,7 @@ {if_var_true, zlib, {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.53"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.55"}}}, {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", @@ -64,12 +64,12 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.24"}}}, + {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.25"}}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.25"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.28"}}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.29"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, @@ -77,8 +77,8 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", "8071c86f33b9a8e9d66a10e058be088eecdc670b"}}, - {yconf, ".*", {git, "https://github.com/processone/yconf", "9898754f16cbd4585a1c2061d72fa441ecb2e938"}} + {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.1"}}}, + {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index 90020eec4..e326677c6 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,31 +7,25 @@ {<<"esip">>,{pkg,<<"esip">>,<<"1.0.56">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, - {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.53">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.55">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, - {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.0">>},0}, + {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, - {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.24">>},0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.25">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.25">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.28">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.29">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"8071c86f33b9a8e9d66a10e058be088eecdc670b"}}, - 0}, - {<<"yconf">>, - {git,"https://github.com/processone/yconf", - {ref,"9898754f16cbd4585a1c2061d72fa441ecb2e938"}}, - 0}]}. + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.1">>},0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.17">>},0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -42,23 +36,25 @@ {<<"esip">>, <<"63C0FDC667BE751714E1E5C14621A9334F21B60AC1BB68BE889454CA9CA021B7">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, - {<<"fast_xml">>, <<"1EF4F6E5995BCFA94800A46B460C3400C19C0A533948B12200A2E2FB1A2BE427">>}, + {<<"fast_xml">>, <<"ACE020F2521F2A484AC8467D2822AF85534A346E2AAE03FFCBC34F29318BEFAF">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, - {<<"luerl">>, <<"60F05F4240F0E7C148DDB79B67B8FF972734AAD237AA74C83D0748B8214C8EF0">>}, + {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, - {<<"p1_acme">>, <<"E19876618EB0BE22815ACA99640CB88CD5C86E4239B8E8DC15B4E5D7854EF7E2">>}, + {<<"p1_acme">>, <<"DB91F0D6C193CD1D5C0B0FA3939A898DBF56A6075DB4347CDE26E802715DE50C">>}, {<<"p1_mysql">>, <<"875D4CBDC7C9990270DF3292CCE2514E4C18A9FDFD19BEF258CB4D0C45B4F243">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"08ECA7D2AFC81BA6D757B572F4A57EF3A2383B0C7B785FDE38184BC368376D54">>}, + {<<"p1_pgsql">>, <<"FAE0C90CBC5931865958150F1B667FB0D20B063F6A46A17770A4E5232DED632C">>}, {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"A1642D93CDBDF947F32344B0E05FCC8EFCFB9F11C32832ACC9BD826B52ADBE48">>}, + {<<"yconf">>, <<"DCF242E27F3FC5D0743D6B8175DD39BC14A1F4ED7E6EA986366A44A6FF3B2A3A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, @@ -68,21 +64,23 @@ {<<"esip">>, <<"9EF3660CEF93B623F7368DCD5C79F4E704358631909E6DD464E335378815DA1F">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, - {<<"fast_xml">>, <<"5064336D6F363EEE5097AA5DC5CED9B67F05152F2E6B8520FD50D268C2AB839C">>}, + {<<"fast_xml">>, <<"83F3E23A780ED5F567CDEC73953F06C95B838D709DBFA86B59A98A8D23C99F85">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, - {<<"luerl">>, <<"9CAFD4F6094FF0F5A9D278FD81D60D3E026C820BDFB6CACD4B1BD909F21B525D">>}, + {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, - {<<"p1_acme">>, <<"F1451D706595EF997AB1CA17162DDAC58F874AC97E315A5FADBE3CFA26148002">>}, + {<<"p1_acme">>, <<"A7B55B47495DDB4F98A15E65451EC3AD43F4637B955C74CD695D98E6A645D08C">>}, {<<"p1_mysql">>, <<"E6187FFAE95B726098E88F3EE6F2344AC259CE2C26E0EE403B05FEEF341AE434">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"655BB75DF036ACE55DDCE17CA741143C42E0667C6206AC27A4DCBC65F71AC9EF">>}, + {<<"p1_pgsql">>, <<"A6FF58E8B174993F3895DA3EA6211A9F9D0C54D1A6E28BB321DA3B3CD68B38C1">>}, {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"D2B1431AF6E4C1A4C8BF90CAF0CC11CDEB047B8323B87E9D7E4826D4913275DC">>}, + {<<"yconf">>, <<"DD2892923241449A46CC8457B9EC0FB14030700735A5885955677C735C341A25">>}]} ]. From d93a8e341f0b122b128ec5df0bd071954ed6f305 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Dec 2024 18:13:25 +0100 Subject: [PATCH 0891/1302] mod_http_api: Annotate that default_version was added in new release --- src/mod_http_api.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 6df2f5612..b78f7c6e3 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -590,6 +590,7 @@ mod_doc() -> opts => [{default_version, #{value => "integer() | string()", + note => "added in 24.12", desc => ?T("What API version to use when none is specified in the URL path. " "If setting an ejabberd version, it will use the latest API " From 1fe9e3aa679c5985833468baaf17db1396759ee5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Dec 2024 13:35:56 +0100 Subject: [PATCH 0892/1302] CONTAINER.md: Fix some typos --- CONTAINER.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 84bf4536c..5284afd7f 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -44,7 +44,7 @@ docker run --name ejabberd -d -p 5222:5222 ghcr.io/processone/ejabberd ``` That runs the container as a daemon, -using ejabberd default configuration file and XMPP domain "localhost". +using ejabberd default configuration file and XMPP domain `localhost`. Stop the running container: @@ -67,7 +67,7 @@ Start ejabberd with an Erlang console attached using the `live` command: docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live ``` -That uses the default configuration file and XMPP domain "localhost". +That uses the default configuration file and XMPP domain `localhost`. ### Start with your configuration and database @@ -269,10 +269,12 @@ and the same [Erlang Cookie](https://docs.ejabberd.im/admin/guide/security/#erlang-cookie). For this you can either: + - edit `conf/ejabberdctl.cfg` and set variables `ERLANG_NODE` and `ERLANG_COOKIE` - set the environment variables `ERLANG_NODE_ARG` and `ERLANG_COOKIE` Example to connect a local `ejabberdctl` to a containerized ejabberd: + 1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: ```sh docker run --name ejabberd -it \ @@ -321,10 +323,12 @@ docker buildx build \ ### Podman build -To build the image using podman instead of docker, notice: +To build the image using Podman, please notice: + - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile - It mentions that `healthcheck` is not supported by the Open Container Initiative image format - to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true` + ```bash podman build \ -t ejabberd \ @@ -433,9 +437,10 @@ mv ejabberd.yml.example ejabberd.yml ``` Use a macro in `ejabberd.yml` to set the served vhost, with `localhost` as default value: -```bash +```yaml define_macro: XMPPHOST: localhost + hosts: - XMPPHOST ``` @@ -530,7 +535,7 @@ and once ejabberd is started in it, it joins the first one. An account is registered in the first node when created (and we ignore errors that can happen when doing that - for example -whenn account already exists), +when account already exists), and it should exist in the second node after join. Notice that in this example the main container does not have access @@ -572,7 +577,7 @@ services: ``` If using Podman, write this `cluster.yml` file -and start it with `podman kube play cluster.yml`. +and start it with `podman kube play cluster.yml`: ```yaml apiVersion: v1 From ce3bc85d3296ea7d4a675e37ef902990678b925f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Dec 2024 16:50:02 +0100 Subject: [PATCH 0893/1302] CHANGELOG.md: Update to 24.12, fix some newlines --- CHANGELOG.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e13400feb..393c35b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,49 @@ +## Version 24.12 + +#### Miscelanea + +- Elixir: support loading Elixir modules for auth ([#4315](https://github.com/processone/ejabberd/issues/4315)) +- Environment variables `EJABBERD_MACRO` to define macros +- Fix problem starting ejabberd when first host uses SQL, other one mnesia +- HTTP Websocket: Enable `allow_unencrypted_sasl2` on websockets ([#4323](https://github.com/processone/ejabberd/issues/4323)) +- Relax checks for channels bindings for connections using external encryption +- Redis: Add support for unix domain socket ([#4318](https://github.com/processone/ejabberd/issues/4318)) +- Redis: Use eredis 1.7.1 from Nordix when using mix/rebar3 and Erlang 21+ +- `mod_auth_fast`: New module with support XEP-0484: Fast Authentication Streamlining Tokens +- `mod_http_api`: Fix crash when module not enabled (for example, in CT tests) +- `mod_http_api`: New option `default_version` +- `mod_muc`: Make rsm handling in disco items, correctly count skipped rooms +- `mod_offline`: Only delete offline msgs when user has MAM enabled ([#4287](https://github.com/processone/ejabberd/issues/4287)) +- `mod_priviled`: Handle properly roster iq +- `mod_pubsub`: Send notifications on PEP item retract +- `mod_s2s_bidi`: Catch extra case in check for s2s bidi element +- `mod_scram_upgrade`: Don't abort the upgrade +- `mod_shared_roster`: The name of a new group is lowercased +- `mod_shared_roster`: Get back support for `groupid@vhost` in `displayed` + +#### Commands API + +- Change arguments and result to consistent names (API v3) +- `create_rooms_file`: Improve to support vhosts with different config +- `evacuate_kindly`: New command to kick users and prevent login ([#4309](https://github.com/processone/ejabberd/issues/4309)) +- `join_cluster`: Explain that this returns immediately (since 5a34020, 24.06) +- `mod_muc_admin`: Rename argument `name` to `room` for consistency + +#### Documentation + +- Fix some documentation syntax, add links to toplevel, modules and API +- `CONTAINER.md`: Add kubernetes yaml examples to use with podman +- `SECURITY.md`: Add security policy and reporting guidelines +- `ejabberd.service`: Disable the systemd watchdog by default +- `ejabberd.yml.example`: Use non-standard STUN port + +#### WebAdmin + +- Shared group names are case sensitive, use original case instead of lowercase +- Use lowercase username and server authentication credentials +- Fix calculation of node's uptime days +- Fix link to displayed group when it is from another vhost + ## Version 24.10 #### Miscelanea @@ -29,17 +75,20 @@ - `make-binaries`: Bump dependency versions: Elixir 1.17.2, OpenSSL 3.3.2, ... #### Administration + - `ejabberdctl`: If `ERLANG_NODE` lacks host, add hostname ([#4288](https://github.com/processone/ejabberd/issues/4288)) - `ejabberd_app`: At server start, log Erlang and Elixir versions - MySQL: Fix column type in the schema update of `archive` table in schema update #### Commands API + - `get_mam_count`: New command to get number of archived messages for an account - `set_presence`: Return error when session not found - `update`: Fix command output - Add `mam` and `offline` tags to the related purge commands #### Code Quality + - Fix warnings about unused macro definitions reported by Erlang LS - Fix Elvis report: Fix dollar space syntax - Fix Elvis report: Remove spaces in weird places @@ -51,6 +100,7 @@ - `mod_privilege`: Replace `try...catch` with a clean alternative #### Development Help + - `elvis.config`: Fix file syntax, set vim mode, disable many tests - `erlang_ls.config`: Let it find paths, update to Erlang 26, enable crossref - `hooks_deps`: Hide false-positive warnings about `gen_mod` @@ -61,18 +111,21 @@ - Runtime: Cache hex.pm archive from rebar3 and mix #### Documentation + - Add links in top-level options documentation to their Docs website sections - Document which SQL servers can really use `update_sql_schema` - Improve documentation of `ldap_servers` and `ldap_backups` options ([#3977](https://github.com/processone/ejabberd/issues/3977)) - `mod_register`: Document behavior when `access` is set to `none` ([#4078](https://github.com/processone/ejabberd/issues/4078)) #### Elixir + - Handle case when elixir support is enabled but not available - Start ExSync manually to ensure it's started if (and only if) Relive - `mix.exs`: Fix `mix release` error: `logger` being regular and included application ([#4265](https://github.com/processone/ejabberd/issues/4265)) - `mix.exs`: Remove from `extra_applications` the apps already defined in `deps` ([#4265](https://github.com/processone/ejabberd/issues/4265)) #### WebAdmin + - Add links in user page to offline and roster pages - Add new "MAM Archive" page to webadmin - Improve many pages to handle when modules are disabled From 8cfcc69100cf14dee9f97c4a57b104dcbec237fa Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 18 Dec 2024 20:43:03 +0100 Subject: [PATCH 0894/1302] mod_stun_disco: Fix syntax of credentials response As per XEP-0215 (#3.3), the response to a credentials request must use the element rather than . Thanks to Thilo Molitor for spotting the issue. --- src/mod_stun_disco.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index f1e267fc9..d70651a3a 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -484,13 +484,15 @@ process_iq(#iq{lang = Lang} = IQ) -> -spec process_iq_get(iq(), request()) -> iq(). process_iq_get(#iq{from = From, to = #jid{lserver = Host}, lang = Lang} = IQ, - Request) -> + #request{restricted = Restricted} = Request) -> Access = mod_stun_disco_opt:access(Host), case acl:match_rule(Host, Access, From) of allow -> ?DEBUG("Performing external service discovery for ~ts", [jid:encode(From)]), case get_services(Host, From, Request) of + {ok, Services} when Restricted -> % A request. + xmpp:make_iq_result(IQ, #credentials{services = Services}); {ok, Services} -> xmpp:make_iq_result(IQ, #services{list = Services}); {error, timeout} -> % Has been logged already. From 3bc66a7054a84f9df26fb729126e28f95a6840c6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 18 Dec 2024 21:17:07 +0100 Subject: [PATCH 0895/1302] stundisco_tests: Check correct credentials syntax --- test/stundisco_tests.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/stundisco_tests.erl b/test/stundisco_tests.erl index d699cebf7..e6e744d15 100644 --- a/test/stundisco_tests.erl +++ b/test/stundisco_tests.erl @@ -132,18 +132,17 @@ turn_credentials(Config) -> port = Port, type = Type}]}, #iq{type = result, - sub_els = [#services{ - type = undefined, - list = [#service{host = Host, - port = Port, - type = Type, - transport = Transport, - restricted = true, - username = Username, - password = Password, - expires = Expires, - action = undefined, - xdata = undefined}]}]} = + sub_els = [#credentials{ + services = [#service{host = Host, + port = Port, + type = Type, + transport = Transport, + restricted = true, + username = Username, + password = Password, + expires = Expires, + action = undefined, + xdata = undefined}]}]} = send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), true = check_password(Username, Password), true = check_expires(Expires), From eaebfc795eb45920e8ff531a04c467e03314b924 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 18 Dec 2024 21:31:55 +0100 Subject: [PATCH 0896/1302] rebar.config: Depend on current xmpp version We now hard-depend on xmpp 1.9.1. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index b4b2cdb96..939c7399a 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.1"}}}, + {xmpp, "~> 1.9.1", {git, "https://github.com/processone/xmpp", {tag, "1.9.1"}}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} ]}. From e967a409d3b226e4da8d547ace638fc5677ed229 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 18 Dec 2024 22:24:53 +0100 Subject: [PATCH 0897/1302] stundisco_tests: Fix TURNS credentials syntax --- test/stundisco_tests.erl | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/stundisco_tests.erl b/test/stundisco_tests.erl index e6e744d15..f892805b2 100644 --- a/test/stundisco_tests.erl +++ b/test/stundisco_tests.erl @@ -158,18 +158,17 @@ turns_credentials(Config) -> port = Port, type = Type}]}, #iq{type = result, - sub_els = [#services{ - type = undefined, - list = [#service{host = Host, - port = Port, - type = Type, - transport = Transport, - restricted = true, - username = Username, - password = Password, - expires = Expires, - action = undefined, - xdata = undefined}]}]} = + sub_els = [#credentials{ + services = [#service{host = Host, + port = Port, + type = Type, + transport = Transport, + restricted = true, + username = Username, + password = Password, + expires = Expires, + action = undefined, + xdata = undefined}]}]} = send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), true = check_password(Username, Password), true = check_expires(Expires), From 51f4382b9f92cfdfca4950e245ceb06353d3fc80 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Dec 2024 10:45:19 +0100 Subject: [PATCH 0898/1302] CHANGELOG.md: Update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 393c35b2c..7e1fc3bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `mod_scram_upgrade`: Don't abort the upgrade - `mod_shared_roster`: The name of a new group is lowercased - `mod_shared_roster`: Get back support for `groupid@vhost` in `displayed` +- `mod_stun_disco`: Fix syntax of credentials response #### Commands API From 405437b0864c81ab0c8993c0f79b70363d7a50d8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Dec 2024 10:56:30 +0100 Subject: [PATCH 0899/1302] mix.lock, rebar.lock: Update to recent versions --- mix.lock | 4 ++-- rebar.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.lock b/mix.lock index 621cf5a53..9627ce0a3 100644 --- a/mix.lock +++ b/mix.lock @@ -18,9 +18,9 @@ "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, - "luerl": {:hex, :luerl, "1.2.0", "60f05f4240f0e7c148ddb79b67b8ff972734aad237aa74c83d0748b8214c8ef0", [:rebar3], [], "hexpm", "9cafd4f6094ff0f5a9d278fd81d60d3e026c820bdfb6cacd4b1bd909f21b525d"}, + "luerl": {:hex, :luerl, "1.2.3", "df25f41944e57a7c4d9ef09d238bc3e850276c46039cfc12b8bb42eccf36fcb1", [:rebar3], [], "hexpm", "1b4b9d0ca5d7d280d1d2787a6a5ee9f5a212641b62bff91556baa53805df3aed"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, diff --git a/rebar.lock b/rebar.lock index e326677c6..9a4b76f03 100644 --- a/rebar.lock +++ b/rebar.lock @@ -10,7 +10,7 @@ {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.55">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, - {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, From 1cadc6b1dc38b39b235591687a80b827d0c0354b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Dec 2024 11:20:56 +0100 Subject: [PATCH 0900/1302] CI: Revert old feature that published failed CT logs to website Nowadays the CT logs can be downloaded from each action run in: https://github.com/processone/ejabberd/actions/workflows/ci.yml This partially reverts commit 8ccad7f Publish CT logs and Cover on failure to an external GH Pages repo --- .github/workflows/ci.yml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebd2148a4..e6cfb4b6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,15 +111,6 @@ jobs: if: matrix.otp < 21 run: ./rebar3 unlock eredis - - name: Download test logs - if: matrix.otp == '26' && github.repository == 'processone/ejabberd' - continue-on-error: true - run: | - mkdir -p _build/test - curl -sSL https://github.com/processone/ecil/tarball/gh-pages | - tar -C _build/test --strip-components=1 --wildcards -xzf - - rm -rf _build/test/logs/last/ - - name: Compile run: | ./autogen.sh @@ -224,22 +215,6 @@ jobs: "payload":{"build_num":$GITHUB_RUN_ID, "status":"done"}}' - - name: Upload test logs - if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' - uses: peaceiris/actions-gh-pages@v4 - with: - publish_dir: _build/test - exclude_assets: '.github,lib,plugins' - external_repository: processone/ecil - deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} - keep_files: true - - - name: View ECIL address - if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' - run: | - CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'` - echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/" - - name: Check for changes to trigger schema upgrade test uses: dorny/paths-filter@v3 id: filter From 7511307868ae0e9222cd5ee67154c107b80e78c3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Dec 2024 11:50:21 +0100 Subject: [PATCH 0901/1302] Set version to 24.12 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c77f7e73a..9e68cd390 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.10` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.12` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From f38f81159d1a46c9de817b67d3ea548d694901fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 19 Dec 2024 14:04:38 +0100 Subject: [PATCH 0902/1302] Fix issue with wrong namespace in mod_s2s_bidi --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 4 ++-- rebar.lock | 7 ++++--- src/mod_s2s_bidi.erl | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mix.exs b/mix.exs index 9a48e3c36..45f2479b9 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, "~> 1.9.1"}, + {:xmpp, "~> 1.9.2"}, {:yconf, "~> 1.0.17"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 9627ce0a3..38c7d2aa5 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.9.1", "a1642d93cdbdf947f32344b0e05fcc8efcfb9f11c32832acc9bd826b52adbe48", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "d2b1431af6e4c1a4c8bf90caf0cc11cdeb047b8323b87e9d7e4826d4913275dc"}, + "xmpp": {:hex, :xmpp, "1.9.2", "e0f5f8543d8bb92d433ab75a962bc53dba4601ea3606dac67a50e754e86d43ab", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "4fa1624666d695149d651ce5c104ca669c233f91db81d38b2d31ccf97fd0454e"}, "yconf": {:hex, :yconf, "1.0.17", "dcf242e27f3fc5d0743d6b8175dd39bc14a1f4ed7e6ea986366a44a6ff3b2a3a", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "dd2892923241449a46cc8457b9ec0fb14030700735a5885955677c735c341a25"}, } diff --git a/rebar.config b/rebar.config index 939c7399a..219e62f73 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.25"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.29"}}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.30"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.1", {git, "https://github.com/processone/xmpp", {tag, "1.9.1"}}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", {tag, "1.9.2"}}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 9a4b76f03..5a7445546 100644 --- a/rebar.lock +++ b/rebar.lock @@ -24,7 +24,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.1">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"a1dd8d3ab94fd251f20598e6f002eba38905e218"}}, + 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.17">>},0}]}. [ {pkg_hash,[ @@ -53,7 +56,6 @@ {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"A1642D93CDBDF947F32344B0E05FCC8EFCFB9F11C32832ACC9BD826B52ADBE48">>}, {<<"yconf">>, <<"DCF242E27F3FC5D0743D6B8175DD39BC14A1F4ED7E6EA986366A44A6FF3B2A3A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -81,6 +83,5 @@ {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"D2B1431AF6E4C1A4C8BF90CAF0CC11CDEB047B8323B87E9D7E4826D4913275DC">>}, {<<"yconf">>, <<"DD2892923241449A46CC8457B9EC0FB14030700735A5885955677C735C341A25">>}]} ]. diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl index 10cc0e773..5d0ba699d 100644 --- a/src/mod_s2s_bidi.erl +++ b/src/mod_s2s_bidi.erl @@ -72,7 +72,7 @@ mod_doc() -> " mod_s2s_bidi: {}"]}. s2s_in_features(Acc, _) -> - [#s2s_bidi{}|Acc]. + [#s2s_bidi_feature{}|Acc]. s2s_in_packet(State, #s2s_bidi{}) -> {stop, State#{bidi_enabled => true}}; @@ -115,8 +115,8 @@ s2s_out_packet(#{bidi_enabled := true, ip := {IP, _}} = State, Pkt0) s2s_out_packet(#{db_verify := _} = State, #stream_features{}) -> State; s2s_out_packet(State, #stream_features{} = Pkt) -> - try xmpp:try_subtag(Pkt, #s2s_bidi{}) of - #s2s_bidi{} -> + try xmpp:try_subtag(Pkt, #s2s_bidi_feature{}) of + #s2s_bidi_feature{} -> ejabberd_s2s_out:send(State#{bidi_enabled => true}, #s2s_bidi{}) catch _:{xmpp_codec, _Why} -> State From 5b3b29565ca46115042ab0c65cf399bdcf1e2839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 19 Dec 2024 16:27:16 +0100 Subject: [PATCH 0903/1302] Remove overide on fast_xml --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 45f2479b9..7671c3ffb 100644 --- a/mix.exs +++ b/mix.exs @@ -135,7 +135,7 @@ defmodule Ejabberd.MixProject do {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, {:fast_tls, "~> 1.1.22"}, - {:fast_xml, "~> 1.1.53", override: true}, + {:fast_xml, "~> 1.1.53"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, From aa65e626f4c9a2093043b97e6a76480cfa644b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 20 Dec 2024 09:38:03 +0100 Subject: [PATCH 0904/1302] Fix values allowed in db_type of mod_auth_fast documentation --- src/mod_auth_fast.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index 6ada831c2..c6a79167c 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -98,7 +98,7 @@ mod_doc() -> note => "added in 24.12", opts => [{db_type, - #{value => "mnesia | sql", + #{value => "mnesia", desc => ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {token_lifetime, From 213a513f54f0dec816082e3b9eccb5ebeff2d8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 20 Dec 2024 10:32:21 +0100 Subject: [PATCH 0905/1302] Fix name option in documentation --- src/mod_auth_fast.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index c6a79167c..8ccf00e91 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -112,7 +112,7 @@ mod_doc() -> example => ["modules:", " mod_auth_fast:", - " token_timeout: 14days"]}. + " token_lifetime: 14days"]}. get_mechanisms(_LServer) -> [<<"HT-SHA-256-NONE">>, <<"HT-SHA-256-UNIQ">>, <<"HT-SHA-256-EXPR">>, <<"HT-SHA-256-ENDP">>]. From 364ee0f8edcb2a6b62d9b44a0f55f40ee47e5421 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 21 Dec 2024 11:04:08 +0100 Subject: [PATCH 0906/1302] ejabberd.yml.example: Enable mod_muc_occupantid Add mod_muc_occupantid to the list of modules enabled in the sample configuration. It's not necessarily obvious that it's required for using certain modern features in group chat, and there's no downside in activating this module. --- ejabberd.yml.example | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 39e423a64..0964afa06 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -199,6 +199,7 @@ modules: default_room_options: mam: true mod_muc_admin: {} + mod_muc_occupantid: {} mod_offline: access_max_user_messages: max_user_offline_messages mod_ping: {} From 7d5413ce95f3e9395f8d584469c5e016ebaaa689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Sat, 21 Dec 2024 20:03:18 +0100 Subject: [PATCH 0907/1302] Update xmpp to bring fix for ssdp hash calculation --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 4 ++-- rebar.lock | 13 ++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index 7671c3ffb..4f46caf86 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, "~> 1.9.2"}, + {:xmpp, "~> 1.9.3"}, {:yconf, "~> 1.0.17"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 38c7d2aa5..eeceba2ab 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.9.2", "e0f5f8543d8bb92d433ab75a962bc53dba4601ea3606dac67a50e754e86d43ab", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "4fa1624666d695149d651ce5c104ca669c233f91db81d38b2d31ccf97fd0454e"}, + "xmpp": {:hex, :xmpp, "1.9.3", "fa4f9eda52d789f4b9f5ff11fed17da2c43fa8d01ed7c4fadcb09ab602be9c18", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "d5c96fcb2cf8a8da43a5f81935b2e43fa9b959e1626fcecf082f4af9139bdbd2"}, "yconf": {:hex, :yconf, "1.0.17", "dcf242e27f3fc5d0743d6b8175dd39bc14a1f4ed7e6ea986366a44a6ff3b2a3a", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "dd2892923241449a46cc8457b9ec0fb14030700735a5885955677c735c341a25"}, } diff --git a/rebar.config b/rebar.config index 219e62f73..dbaf948e1 100644 --- a/rebar.config +++ b/rebar.config @@ -69,7 +69,7 @@ {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.25"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.30"}}}}, + {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.31"}}}}, {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", {tag, "1.9.2"}}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", {tag, "1.9.3"}}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 5a7445546..7a9e810bf 100644 --- a/rebar.lock +++ b/rebar.lock @@ -17,17 +17,14 @@ {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.25">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.25">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.29">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.31">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"a1dd8d3ab94fd251f20598e6f002eba38905e218"}}, - 0}, + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.3">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.17">>},0}]}. [ {pkg_hash,[ @@ -49,13 +46,14 @@ {<<"p1_acme">>, <<"DB91F0D6C193CD1D5C0B0FA3939A898DBF56A6075DB4347CDE26E802715DE50C">>}, {<<"p1_mysql">>, <<"875D4CBDC7C9990270DF3292CCE2514E4C18A9FDFD19BEF258CB4D0C45B4F243">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"FAE0C90CBC5931865958150F1B667FB0D20B063F6A46A17770A4E5232DED632C">>}, + {<<"p1_pgsql">>, <<"8339BEAC1F0F4A45F476FF5306BE5135020F02979A61DF0D8CF7B1C67E85E2FD">>}, {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"FA4F9EDA52D789F4B9F5FF11FED17DA2C43FA8D01ED7C4FADCB09AB602BE9C18">>}, {<<"yconf">>, <<"DCF242E27F3FC5D0743D6B8175DD39BC14A1F4ED7E6EA986366A44A6FF3B2A3A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -76,12 +74,13 @@ {<<"p1_acme">>, <<"A7B55B47495DDB4F98A15E65451EC3AD43F4637B955C74CD695D98E6A645D08C">>}, {<<"p1_mysql">>, <<"E6187FFAE95B726098E88F3EE6F2344AC259CE2C26E0EE403B05FEEF341AE434">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"A6FF58E8B174993F3895DA3EA6211A9F9D0C54D1A6E28BB321DA3B3CD68B38C1">>}, + {<<"p1_pgsql">>, <<"B7FC45DFB2549187347871B7FD0573638AD5EA337F6263FBA1B3840EFAB2FF49">>}, {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"D5C96FCB2CF8A8DA43A5F81935B2E43FA9B959E1626FCECF082F4AF9139BDBD2">>}, {<<"yconf">>, <<"DD2892923241449A46CC8457B9EC0FB14030700735A5885955677C735C341A25">>}]} ]. From 5945dba412927acb10b364ab4c5131146aa2030f Mon Sep 17 00:00:00 2001 From: Dmitriy Bogdanov Date: Thu, 26 Dec 2024 14:55:31 +0100 Subject: [PATCH 0908/1302] Fix a couple of typos in documentation --- src/mod_auth_fast.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index 8ccf00e91..aa40e8f1a 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -93,8 +93,8 @@ mod_doc() -> #{desc => [?T("The module adds support for " "https://xmpp.org/extensions/xep-0484.html" - "[XEP-0480: Fast Authentication Streamlining Tokens] that allows users to authenticate " - "using self managed tokens.")], + "[XEP-0484: Fast Authentication Streamlining Tokens] that allows users to authenticate " + "using self-managed tokens.")], note => "added in 24.12", opts => [{db_type, @@ -103,7 +103,7 @@ mod_doc() -> ?T("Same as top-level _`default_db`_ option, but applied to this module only.")}}, {token_lifetime, #{value => "timeout()", - desc => ?T("Time that tokens will be keept, measured from it's creation time. " + desc => ?T("Time that tokens will be kept, measured from it's creation time. " "Default value set to 30 days")}}, {token_refresh_age, #{value => "timeout()", From aa8957f1373a602b389584484267f6511efd553e Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 2 Jan 2025 18:24:30 +0100 Subject: [PATCH 0909/1302] make-binaries: Bump dependency versions --- .github/workflows/runtime.yml | 4 ++-- tools/make-binaries | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 7e0b63518..076798aba 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -164,7 +164,7 @@ jobs: strategy: fail-fast: false matrix: - elixir: ['1.13', '1.14', '1.15', '1.16', '1.17'] + elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: image: elixir:${{ matrix.elixir }} @@ -287,7 +287,7 @@ jobs: strategy: fail-fast: false matrix: - elixir: ['1.13', '1.14', '1.15', '1.16', '1.17'] + elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: image: elixir:${{ matrix.elixir }} diff --git a/tools/make-binaries b/tools/make-binaries index 98cd01213..36b29d364 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,16 +70,16 @@ termcap_vsn='1.3.1' expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.3.2' +ssl_vsn='3.4.0' otp_vsn='26.2.5.4' -elixir_vsn='1.17.2' +elixir_vsn='1.18.1' pam_vsn='1.6.1' -png_vsn='1.6.43' +png_vsn='1.6.44' jpeg_vsn='9f' -webp_vsn='1.4.0' +webp_vsn='1.5.0' gd_vsn='2.3.3' odbc_vsn='2.3.12' -sqlite_vsn='3460100' +sqlite_vsn='3470200' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From 2b2551bc5070f01a99fc88cfa2bc24b474285ea1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 2 Jan 2025 18:25:24 +0100 Subject: [PATCH 0910/1302] make-binaries: Bump Erlang/OTP version to 27.2 --- .github/workflows/ci.yml | 6 +++--- tools/make-binaries | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6cfb4b6b..d95bcc155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: - uses: actions/checkout@v4 - name: Test shell scripts - if: matrix.otp == '26' + if: matrix.otp == '27' run: | shellcheck test/ejabberd_SUITE_data/gencerts.sh shellcheck tools/captcha.sh @@ -153,7 +153,7 @@ jobs: grep -q "is started in" $RE/logs/ejabberd.log - name: Run XMPP Interoperability Tests against CI server. - if: matrix.otp == '26' + if: matrix.otp == '27' continue-on-error: true uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 with: @@ -203,7 +203,7 @@ jobs: find logs/ -name exunit.log -exec cat '{}' ';' - name: Send to coveralls - if: matrix.otp == '26' + if: matrix.otp == '27' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/tools/make-binaries b/tools/make-binaries index 36b29d364..5c92b241b 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.4.0' -otp_vsn='26.2.5.4' +otp_vsn='27.2' elixir_vsn='1.18.1' pam_vsn='1.6.1' png_vsn='1.6.44' From 172847bc761facc440e71b958406d251a6a2f6b0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 30 Dec 2024 23:22:14 +0100 Subject: [PATCH 0911/1302] Update path to ejabberd-contrib section in Docs site --- src/ejabberd_options_doc.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 181599afa..a3d081d49 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -307,7 +307,7 @@ doc() -> #{value => "true | false", desc => ?T("Whether to allow installation of third-party modules or not. " - "See _`../../developer/extending-ejabberd/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "See _`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " "documentation section. " "The default value is 'true'.")}}, {allow_multiple_connections, @@ -368,7 +368,7 @@ doc() -> desc => ?T("This is used by the contributed module " "'ejabberd_auth_http' that can be installed from the " - "https://github.com/processone/ejabberd-contrib[ejabberd-contrib] " + "_`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " "Git repository. Please refer to that " "module's README file for details.")}}, {auth_password_format, @@ -719,7 +719,7 @@ doc() -> note => "added in 23.10", desc => ?T("Modules to install from " - "_`../../developer/extending-ejabberd/modules.md#ejabberd-contrib|ejabberd-contrib`_ " + "_`../../admin/guide/modules.md#ejabberd-contrib|ejabberd-contrib`_ " "at start time. " "The default value is an empty list of modules: '[]'.")}}, {jwt_auth_only_rule, From 13dae75d01be002d2890d398480f7b3f4d328371 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Jan 2025 11:24:51 +0100 Subject: [PATCH 0912/1302] Partially revert "Workflows: Bump ubuntu from 22.04 to 24.04 when possible (#4281)" ubuntu-24.04 includes texinfo 7.1, which has a problematic bug. Let's revert to ubuntu-22.04 until 24.04 includes a fixed texinfo. URL to keep an eye: https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md This partially reverts commit d3baacd78eea597713e3a960537dc919dc085b04. --- .github/workflows/container.yml | 2 +- .github/workflows/installers.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 40873eb60..33ae16960 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -19,7 +19,7 @@ env: jobs: container: name: Container - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 permissions: packages: write steps: diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 59db4a81e..b820dd6d9 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -21,7 +21,7 @@ on: jobs: binaries: name: Binaries - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 steps: - name: Cache build directory uses: actions/cache@v4 @@ -70,7 +70,7 @@ jobs: release: name: Release needs: [binaries] - runs-on: ubuntu-24.04 + runs-on: ubuntu-22.04 if: github.ref_type == 'tag' steps: - name: Download packages From 8ebbb45a19d3ab9f194e3a805af2114238828f3c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Jan 2025 11:40:17 +0100 Subject: [PATCH 0913/1302] Update copyright year to 2025 (#4139) --- include/bosh.hrl | 2 +- include/ejabberd_auth.hrl | 2 +- include/ejabberd_commands.hrl | 2 +- include/ejabberd_ctl.hrl | 2 +- include/ejabberd_http.hrl | 2 +- include/ejabberd_oauth.hrl | 2 +- include/ejabberd_sm.hrl | 2 +- include/ejabberd_sql.hrl | 2 +- include/ejabberd_sql_pt.hrl | 2 +- include/ejabberd_stacktrace.hrl | 2 +- include/ejabberd_web_admin.hrl | 2 +- include/eldap.hrl | 2 +- include/http_bind.hrl | 2 +- include/logger.hrl | 2 +- include/mod_announce.hrl | 2 +- include/mod_caps.hrl | 2 +- include/mod_last.hrl | 2 +- include/mod_mam.hrl | 2 +- include/mod_matrix_gw.hrl | 2 +- include/mod_muc.hrl | 2 +- include/mod_muc_room.hrl | 2 +- include/mod_offline.hrl | 2 +- include/mod_privacy.hrl | 2 +- include/mod_private.hrl | 2 +- include/mod_proxy65.hrl | 2 +- include/mod_push.hrl | 2 +- include/mod_roster.hrl | 2 +- include/mod_shared_roster.hrl | 2 +- include/mod_vcard.hrl | 2 +- include/mqtt.hrl | 2 +- include/pubsub.hrl | 2 +- man/ejabberd.yml.5 | 2 +- rebar.config | 2 +- rebar.config.script | 2 +- rel/reltool.config.script | 2 +- sql/lite.new.sql | 2 +- sql/lite.sql | 2 +- sql/mssql.new.sql | 2 +- sql/mssql.sql | 2 +- sql/mysql.new.sql | 2 +- sql/mysql.sql | 2 +- sql/pg.new.sql | 2 +- sql/pg.sql | 2 +- src/acl.erl | 2 +- src/econf.erl | 2 +- src/ejabberd.erl | 2 +- src/ejabberd_access_permissions.erl | 2 +- src/ejabberd_acme.erl | 2 +- src/ejabberd_admin.erl | 2 +- src/ejabberd_app.erl | 2 +- src/ejabberd_auth.erl | 2 +- src/ejabberd_auth_anonymous.erl | 2 +- src/ejabberd_auth_external.erl | 2 +- src/ejabberd_auth_jwt.erl | 2 +- src/ejabberd_auth_ldap.erl | 2 +- src/ejabberd_auth_mnesia.erl | 2 +- src/ejabberd_auth_pam.erl | 2 +- src/ejabberd_auth_sql.erl | 2 +- src/ejabberd_backend_sup.erl | 2 +- src/ejabberd_batch.erl | 2 +- src/ejabberd_bosh.erl | 2 +- src/ejabberd_c2s.erl | 2 +- src/ejabberd_c2s_config.erl | 2 +- src/ejabberd_captcha.erl | 2 +- src/ejabberd_cluster.erl | 2 +- src/ejabberd_cluster_mnesia.erl | 2 +- src/ejabberd_commands.erl | 2 +- src/ejabberd_commands_doc.erl | 2 +- src/ejabberd_config.erl | 2 +- src/ejabberd_config_transformer.erl | 2 +- src/ejabberd_ctl.erl | 2 +- src/ejabberd_db_sup.erl | 2 +- src/ejabberd_doc.erl | 2 +- src/ejabberd_hooks.erl | 2 +- src/ejabberd_http.erl | 2 +- src/ejabberd_http_ws.erl | 2 +- src/ejabberd_iq.erl | 2 +- src/ejabberd_listener.erl | 2 +- src/ejabberd_local.erl | 2 +- src/ejabberd_logger.erl | 2 +- src/ejabberd_mnesia.erl | 2 +- src/ejabberd_oauth.erl | 2 +- src/ejabberd_oauth_mnesia.erl | 2 +- src/ejabberd_oauth_rest.erl | 2 +- src/ejabberd_oauth_sql.erl | 2 +- src/ejabberd_old_config.erl | 2 +- src/ejabberd_options.erl | 2 +- src/ejabberd_options_doc.erl | 2 +- src/ejabberd_piefxis.erl | 2 +- src/ejabberd_pkix.erl | 2 +- src/ejabberd_redis.erl | 2 +- src/ejabberd_redis_sup.erl | 2 +- src/ejabberd_regexp.erl | 2 +- src/ejabberd_router.erl | 2 +- src/ejabberd_router_mnesia.erl | 2 +- src/ejabberd_router_multicast.erl | 2 +- src/ejabberd_router_redis.erl | 2 +- src/ejabberd_router_sql.erl | 2 +- src/ejabberd_s2s.erl | 2 +- src/ejabberd_s2s_in.erl | 2 +- src/ejabberd_s2s_out.erl | 2 +- src/ejabberd_service.erl | 2 +- src/ejabberd_shaper.erl | 2 +- src/ejabberd_sip.erl | 2 +- src/ejabberd_sm.erl | 2 +- src/ejabberd_sm_mnesia.erl | 2 +- src/ejabberd_sm_redis.erl | 2 +- src/ejabberd_sm_sql.erl | 2 +- src/ejabberd_sql.erl | 2 +- src/ejabberd_sql_pt.erl | 2 +- src/ejabberd_sql_schema.erl | 2 +- src/ejabberd_sql_sup.erl | 2 +- src/ejabberd_stun.erl | 2 +- src/ejabberd_sup.erl | 2 +- src/ejabberd_system_monitor.erl | 2 +- src/ejabberd_tmp_sup.erl | 2 +- src/ejabberd_update.erl | 2 +- src/ejabberd_web.erl | 2 +- src/ejabberd_web_admin.erl | 4 ++-- src/ejabberd_websocket.erl | 2 +- src/ejabberd_websocket_codec.erl | 2 +- src/ejabberd_xmlrpc.erl | 2 +- src/ejd2sql.erl | 2 +- src/eldap_filter.erl | 2 +- src/eldap_pool.erl | 2 +- src/eldap_utils.erl | 2 +- src/elixir_logger_backend.erl | 2 +- src/ext_mod.erl | 2 +- src/extauth.erl | 2 +- src/extauth_sup.erl | 2 +- src/gen_iq_handler.erl | 2 +- src/gen_mod.erl | 2 +- src/gen_pubsub_node.erl | 2 +- src/gen_pubsub_nodetree.erl | 2 +- src/jd2ejd.erl | 2 +- src/misc.erl | 2 +- src/mod_adhoc.erl | 2 +- src/mod_admin_extra.erl | 2 +- src/mod_admin_update_sql.erl | 2 +- src/mod_announce.erl | 2 +- src/mod_announce_mnesia.erl | 2 +- src/mod_announce_sql.erl | 2 +- src/mod_auth_fast.erl | 2 +- src/mod_auth_fast_mnesia.erl | 2 +- src/mod_avatar.erl | 2 +- src/mod_block_strangers.erl | 2 +- src/mod_blocking.erl | 2 +- src/mod_bosh.erl | 2 +- src/mod_bosh_mnesia.erl | 2 +- src/mod_bosh_redis.erl | 2 +- src/mod_bosh_sql.erl | 2 +- src/mod_caps.erl | 2 +- src/mod_caps_mnesia.erl | 2 +- src/mod_caps_sql.erl | 2 +- src/mod_carboncopy.erl | 2 +- src/mod_client_state.erl | 2 +- src/mod_configure.erl | 2 +- src/mod_conversejs.erl | 2 +- src/mod_delegation.erl | 2 +- src/mod_disco.erl | 2 +- src/mod_fail2ban.erl | 2 +- src/mod_http_api.erl | 2 +- src/mod_http_fileserver.erl | 2 +- src/mod_http_upload.erl | 2 +- src/mod_http_upload_quota.erl | 2 +- src/mod_jidprep.erl | 2 +- src/mod_last.erl | 2 +- src/mod_last_mnesia.erl | 2 +- src/mod_last_sql.erl | 2 +- src/mod_legacy_auth.erl | 2 +- src/mod_mam.erl | 2 +- src/mod_mam_mnesia.erl | 2 +- src/mod_mam_sql.erl | 2 +- src/mod_matrix_gw.erl | 2 +- src/mod_matrix_gw_room.erl | 2 +- src/mod_matrix_gw_s2s.erl | 2 +- src/mod_matrix_gw_sup.erl | 2 +- src/mod_metrics.erl | 2 +- src/mod_mqtt.erl | 2 +- src/mod_mqtt_bridge.erl | 2 +- src/mod_mqtt_bridge_session.erl | 2 +- src/mod_mqtt_mnesia.erl | 2 +- src/mod_mqtt_session.erl | 2 +- src/mod_mqtt_sql.erl | 2 +- src/mod_mqtt_ws.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_muc_admin.erl | 2 +- src/mod_muc_log.erl | 2 +- src/mod_muc_mnesia.erl | 2 +- src/mod_muc_occupantid.erl | 2 +- src/mod_muc_room.erl | 2 +- src/mod_muc_rtbl.erl | 2 +- src/mod_muc_sql.erl | 2 +- src/mod_muc_sup.erl | 2 +- src/mod_multicast.erl | 2 +- src/mod_offline.erl | 2 +- src/mod_offline_mnesia.erl | 2 +- src/mod_offline_sql.erl | 2 +- src/mod_ping.erl | 2 +- src/mod_pres_counter.erl | 2 +- src/mod_privacy.erl | 2 +- src/mod_privacy_mnesia.erl | 2 +- src/mod_privacy_sql.erl | 2 +- src/mod_private.erl | 2 +- src/mod_private_mnesia.erl | 2 +- src/mod_private_sql.erl | 2 +- src/mod_privilege.erl | 2 +- src/mod_proxy65.erl | 2 +- src/mod_proxy65_lib.erl | 2 +- src/mod_proxy65_mnesia.erl | 2 +- src/mod_proxy65_redis.erl | 2 +- src/mod_proxy65_service.erl | 2 +- src/mod_proxy65_sql.erl | 2 +- src/mod_proxy65_stream.erl | 2 +- src/mod_pubsub.erl | 2 +- src/mod_pubsub_mnesia.erl | 2 +- src/mod_pubsub_sql.erl | 2 +- src/mod_push.erl | 2 +- src/mod_push_keepalive.erl | 2 +- src/mod_push_mnesia.erl | 2 +- src/mod_push_sql.erl | 2 +- src/mod_register.erl | 2 +- src/mod_register_web.erl | 2 +- src/mod_roster.erl | 2 +- src/mod_roster_mnesia.erl | 2 +- src/mod_roster_sql.erl | 2 +- src/mod_s2s_bidi.erl | 2 +- src/mod_s2s_dialback.erl | 2 +- src/mod_scram_upgrade.erl | 2 +- src/mod_service_log.erl | 2 +- src/mod_shared_roster.erl | 2 +- src/mod_shared_roster_ldap.erl | 2 +- src/mod_shared_roster_mnesia.erl | 2 +- src/mod_shared_roster_sql.erl | 2 +- src/mod_sic.erl | 2 +- src/mod_sip.erl | 2 +- src/mod_sip_proxy.erl | 2 +- src/mod_sip_registrar.erl | 2 +- src/mod_stats.erl | 2 +- src/mod_stream_mgmt.erl | 2 +- src/mod_stun_disco.erl | 2 +- src/mod_time.erl | 2 +- src/mod_vcard.erl | 2 +- src/mod_vcard_ldap.erl | 2 +- src/mod_vcard_mnesia.erl | 2 +- src/mod_vcard_sql.erl | 2 +- src/mod_vcard_xupdate.erl | 2 +- src/mod_version.erl | 2 +- src/mqtt_codec.erl | 2 +- src/node_flat.erl | 2 +- src/node_flat_sql.erl | 2 +- src/node_pep.erl | 2 +- src/node_pep_sql.erl | 2 +- src/nodetree_tree.erl | 2 +- src/nodetree_tree_sql.erl | 2 +- src/nodetree_virtual.erl | 2 +- src/prosody2ejabberd.erl | 2 +- src/proxy_protocol.erl | 2 +- src/pubsub_db_sql.erl | 2 +- src/pubsub_index.erl | 2 +- src/pubsub_migrate.erl | 2 +- src/pubsub_subscription.erl | 2 +- src/pubsub_subscription_sql.erl | 2 +- src/rest.erl | 2 +- src/str.erl | 2 +- src/translate.erl | 2 +- src/win32_dns.erl | 2 +- test/announce_tests.erl | 2 +- test/carbons_tests.erl | 2 +- test/commands_tests.erl | 2 +- test/csi_tests.erl | 2 +- test/ejabberd_SUITE.erl | 2 +- test/example_tests.erl | 2 +- test/jidprep_tests.erl | 2 +- test/ldap_srv.erl | 2 +- test/mam_tests.erl | 2 +- test/muc_tests.erl | 2 +- test/offline_tests.erl | 2 +- test/privacy_tests.erl | 2 +- test/private_tests.erl | 2 +- test/proxy65_tests.erl | 2 +- test/pubsub_tests.erl | 2 +- test/push_tests.erl | 2 +- test/replaced_tests.erl | 2 +- test/roster_tests.erl | 2 +- test/sm_tests.erl | 2 +- test/stundisco_tests.erl | 2 +- test/suite.erl | 2 +- test/upload_tests.erl | 2 +- test/vcard_tests.erl | 2 +- test/webadmin_tests.erl | 2 +- tools/xml_compress_gen.erl | 2 +- vars.config.in | 2 +- 293 files changed, 294 insertions(+), 294 deletions(-) diff --git a/include/bosh.hrl b/include/bosh.hrl index dd853e6b2..dd9f1b6a1 100644 --- a/include/bosh.hrl +++ b/include/bosh.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_auth.hrl b/include/ejabberd_auth.hrl index 443cc3228..1f9117efb 100644 --- a/include/ejabberd_auth.hrl +++ b/include/ejabberd_auth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl index a6a960d8c..14d19d2e1 100644 --- a/include/ejabberd_commands.hrl +++ b/include/ejabberd_commands.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_ctl.hrl b/include/ejabberd_ctl.hrl index a93544b5c..cad82da89 100644 --- a/include/ejabberd_ctl.hrl +++ b/include/ejabberd_ctl.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_http.hrl b/include/ejabberd_http.hrl index 77ef5edb1..9e1373ce6 100644 --- a/include/ejabberd_http.hrl +++ b/include/ejabberd_http.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_oauth.hrl b/include/ejabberd_oauth.hrl index 7cb712a49..4798d9070 100644 --- a/include/ejabberd_oauth.hrl +++ b/include/ejabberd_oauth.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sm.hrl b/include/ejabberd_sm.hrl index cb9e9ae19..54a828e1a 100644 --- a/include/ejabberd_sm.hrl +++ b/include/ejabberd_sm.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sql.hrl b/include/ejabberd_sql.hrl index b5a4933fc..d0ab55cba 100644 --- a/include/ejabberd_sql.hrl +++ b/include/ejabberd_sql.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_sql_pt.hrl b/include/ejabberd_sql_pt.hrl index 36ce40225..f89f5c969 100644 --- a/include/ejabberd_sql_pt.hrl +++ b/include/ejabberd_sql_pt.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_stacktrace.hrl b/include/ejabberd_stacktrace.hrl index 03cd78f6a..e65c5264f 100644 --- a/include/ejabberd_stacktrace.hrl +++ b/include/ejabberd_stacktrace.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/ejabberd_web_admin.hrl b/include/ejabberd_web_admin.hrl index f2e5e582e..45e4beada 100644 --- a/include/ejabberd_web_admin.hrl +++ b/include/ejabberd_web_admin.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/eldap.hrl b/include/eldap.hrl index 3ae1047d0..0b6dc97e5 100644 --- a/include/eldap.hrl +++ b/include/eldap.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/http_bind.hrl b/include/http_bind.hrl index d55e7bcca..ab1294e7d 100644 --- a/include/http_bind.hrl +++ b/include/http_bind.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/logger.hrl b/include/logger.hrl index 5518f5a1a..27c5ffdc8 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_announce.hrl b/include/mod_announce.hrl index 7d680514e..77badf90e 100644 --- a/include/mod_announce.hrl +++ b/include/mod_announce.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_caps.hrl b/include/mod_caps.hrl index 29c1528fe..ee1bbe44e 100644 --- a/include/mod_caps.hrl +++ b/include/mod_caps.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_last.hrl b/include/mod_last.hrl index b0da17ce0..b1c13621a 100644 --- a/include/mod_last.hrl +++ b/include/mod_last.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_mam.hrl b/include/mod_mam.hrl index bca473c62..77ea54a5e 100644 --- a/include/mod_mam.hrl +++ b/include/mod_mam.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_matrix_gw.hrl b/include/mod_matrix_gw.hrl index 7ebaa0854..18efbf252 100644 --- a/include/mod_matrix_gw.hrl +++ b/include/mod_matrix_gw.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_muc.hrl b/include/mod_muc.hrl index 127b71579..f801b29e1 100644 --- a/include/mod_muc.hrl +++ b/include/mod_muc.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index 1d0ca4442..1e90a7912 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_offline.hrl b/include/mod_offline.hrl index 58e52aef2..e1bb236f6 100644 --- a/include/mod_offline.hrl +++ b/include/mod_offline.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_privacy.hrl b/include/mod_privacy.hrl index d6a1286e2..8118a6de6 100644 --- a/include/mod_privacy.hrl +++ b/include/mod_privacy.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_private.hrl b/include/mod_private.hrl index 412ca8f29..05adc7d8b 100644 --- a/include/mod_private.hrl +++ b/include/mod_private.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_proxy65.hrl b/include/mod_proxy65.hrl index 1a309a663..4f017124a 100644 --- a/include/mod_proxy65.hrl +++ b/include/mod_proxy65.hrl @@ -2,7 +2,7 @@ %%% RFC 1928 constants. %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_push.hrl b/include/mod_push.hrl index 5b6ea40c5..8a9de102b 100644 --- a/include/mod_push.hrl +++ b/include/mod_push.hrl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_roster.hrl b/include/mod_roster.hrl index 7e213fe69..a056dd22c 100644 --- a/include/mod_roster.hrl +++ b/include/mod_roster.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_shared_roster.hrl b/include/mod_shared_roster.hrl index 5fc9fd3c5..4c35878e8 100644 --- a/include/mod_shared_roster.hrl +++ b/include/mod_shared_roster.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mod_vcard.hrl b/include/mod_vcard.hrl index 56506f207..d97e5c900 100644 --- a/include/mod_vcard.hrl +++ b/include/mod_vcard.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/include/mqtt.hrl b/include/mqtt.hrl index e618914a6..bf910368f 100644 --- a/include/mqtt.hrl +++ b/include/mqtt.hrl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/include/pubsub.hrl b/include/pubsub.hrl index e625e7f23..316be342a 100644 --- a/include/pubsub.hrl +++ b/include/pubsub.hrl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 52c726a74..493aaee19 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -8110,4 +8110,4 @@ Configuration Guide: https://docs\&.ejabberd\&.im/admin/configuration Source code: https://github\&.com/processone/ejabberd .SH "COPYING" .sp -Copyright (c) 2002\-2024 ProcessOne\&. +Copyright (c) 2002\-2025 ProcessOne\&. diff --git a/rebar.config b/rebar.config index dbaf948e1..f367477cf 100644 --- a/rebar.config +++ b/rebar.config @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/rebar.config.script b/rebar.config.script index 0b5c75d15..e476df448 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/rel/reltool.config.script b/rel/reltool.config.script index f1f0852d2..0a87ce735 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeniy Khramtsov -%%% @copyright (C) 2013-2024, Evgeniy Khramtsov +%%% @copyright (C) 2013-2025, Evgeniy Khramtsov %%% @doc %%% %%% @end diff --git a/sql/lite.new.sql b/sql/lite.new.sql index 34d3953a1..7248d080b 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/lite.sql b/sql/lite.sql index 8ef925eae..2958a9628 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql index d4220aabf..3e6817012 100644 --- a/sql/mssql.new.sql +++ b/sql/mssql.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mssql.sql b/sql/mssql.sql index dc8e8cc12..5ee11d880 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index afd9c6a07..c5a4675b0 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/mysql.sql b/sql/mysql.sql index d013d782c..479ccb435 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/pg.new.sql b/sql/pg.new.sql index ffc88368d..a7e79fee3 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/sql/pg.sql b/sql/pg.sql index ee5590564..2cc8d4c95 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -1,5 +1,5 @@ -- --- ejabberd, Copyright (C) 2002-2024 ProcessOne +-- ejabberd, Copyright (C) 2002-2025 ProcessOne -- -- This program is free software; you can redistribute it and/or -- modify it under the terms of the GNU General Public License as diff --git a/src/acl.erl b/src/acl.erl index 88ea0119e..9daa373b3 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/econf.erl b/src/econf.erl index 0ffd05e4e..a2a884e76 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -3,7 +3,7 @@ %%% Purpose : Validator for ejabberd configuration options %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd.erl b/src/ejabberd.erl index a251003b3..579c9c628 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index b4962488b..1889ca8df 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -5,7 +5,7 @@ %%% Created : 7 Sep 2016 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index dcd2bb428..8b16fc727 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 1c5387223..2452e86b7 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -5,7 +5,7 @@ %%% Created : 7 May 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index fc9830fe9..9f9324494 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -5,7 +5,7 @@ %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 998dbe10f..4ead00f48 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -5,7 +5,7 @@ %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 7c1156ff6..8f67b695b 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -5,7 +5,7 @@ %%% Created : 17 Feb 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index 33a58e833..1b69a9a10 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_jwt.erl b/src/ejabberd_auth_jwt.erl index 6f8c16727..7fac3e4f7 100644 --- a/src/ejabberd_auth_jwt.erl +++ b/src/ejabberd_auth_jwt.erl @@ -5,7 +5,7 @@ %%% Created : 16 Mar 2019 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 096b56fc6..091e567a8 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl index 81faab2d2..6ed303fae 100644 --- a/src/ejabberd_auth_mnesia.erl +++ b/src/ejabberd_auth_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl index 17940640d..d795b0d6f 100644 --- a/src/ejabberd_auth_pam.erl +++ b/src/ejabberd_auth_pam.erl @@ -5,7 +5,7 @@ %%% Created : 5 Jul 2007 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 49da353f7..fefabdf76 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -5,7 +5,7 @@ %%% Created : 12 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_backend_sup.erl b/src/ejabberd_backend_sup.erl index 8a724955c..1b3495e36 100644 --- a/src/ejabberd_backend_sup.erl +++ b/src/ejabberd_backend_sup.erl @@ -2,7 +2,7 @@ %%% Created : 24 Feb 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_batch.erl b/src/ejabberd_batch.erl index 750c5978f..5a907c74b 100644 --- a/src/ejabberd_batch.erl +++ b/src/ejabberd_batch.erl @@ -5,7 +5,7 @@ %%% Created : 8 mar 2022 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index 0805f1857..8d1dbd595 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 06ba6fb02..c68e6ad30 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -2,7 +2,7 @@ %%% Created : 8 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl index 2abbbebd3..0c80ebec9 100644 --- a/src/ejabberd_c2s_config.erl +++ b/src/ejabberd_c2s_config.erl @@ -6,7 +6,7 @@ %%% Created : 2 Nov 2007 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index ed7408699..794041a19 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -5,7 +5,7 @@ %%% Created : 26 Apr 2008 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl index ff85de059..38a378d30 100644 --- a/src/ejabberd_cluster.erl +++ b/src/ejabberd_cluster.erl @@ -3,7 +3,7 @@ %%% Created : 5 Jul 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_cluster_mnesia.erl b/src/ejabberd_cluster_mnesia.erl index 91910ad67..ada0703be 100644 --- a/src/ejabberd_cluster_mnesia.erl +++ b/src/ejabberd_cluster_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 7 Oct 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index a2d4bf525..5028f05fc 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index 7b0b46246..b4780fee9 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -5,7 +5,7 @@ %%% Created : 20 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 8b63cc7bb..8b55be2db 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -5,7 +5,7 @@ %%% Created : 14 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl index 87a87a824..a04e8771f 100644 --- a/src/ejabberd_config_transformer.erl +++ b/src/ejabberd_config_transformer.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 14780278a..6fcd46e1c 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jan 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_db_sup.erl b/src/ejabberd_db_sup.erl index 9f9ce11df..192c355c3 100644 --- a/src/ejabberd_db_sup.erl +++ b/src/ejabberd_db_sup.erl @@ -2,7 +2,7 @@ %%% Created : 13 June 2019 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index 053e5826c..ce030c221 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -2,7 +2,7 @@ %%% File : ejabberd_doc.erl %%% Purpose : Options documentation generator %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 4ef95d0ff..39a8c812c 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -5,7 +5,7 @@ %%% Created : 8 Aug 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 912b25b42..76fd80c40 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -5,7 +5,7 @@ %%% Created : 27 Feb 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index 60daea685..c14ed2d58 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -5,7 +5,7 @@ %%% Created : 09-10-2010 by Eric Cestari %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl index bddfb5198..507820b20 100644 --- a/src/ejabberd_iq.erl +++ b/src/ejabberd_iq.erl @@ -5,7 +5,7 @@ %%% Created : 10 Nov 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index e749d23e9..2412eb545 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -5,7 +5,7 @@ %%% Created : 16 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 6c9ca918d..34735cd2c 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -5,7 +5,7 @@ %%% Created : 30 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index ced5378fc..746925b29 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -5,7 +5,7 @@ %%% Created : 12 May 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2024 ProcessOne +%%% ejabberd, Copyright (C) 2013-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index da817f27f..f3191b910 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 17 Nov 2016 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 8c42dbe77..5275673fe 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -5,7 +5,7 @@ %%% Created : 20 Mar 2015 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl index 649fdb87c..37fa3285c 100644 --- a/src/ejabberd_oauth_mnesia.erl +++ b/src/ejabberd_oauth_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl index e38dceffe..b7200872a 100644 --- a/src/ejabberd_oauth_rest.erl +++ b/src/ejabberd_oauth_rest.erl @@ -5,7 +5,7 @@ %%% Created : 26 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl index 17c38f20c..fe0a159ad 100644 --- a/src/ejabberd_oauth_sql.erl +++ b/src/ejabberd_oauth_sql.erl @@ -5,7 +5,7 @@ %%% Created : 27 Jul 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl index cee8f6194..670dd7158 100644 --- a/src/ejabberd_old_config.erl +++ b/src/ejabberd_old_config.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% Purpose: Transform old-style Erlang config to YAML config %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 66633d107..673de29d7 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index a3d081d49..380b2b605 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 4cf39328a..789be7359 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -5,7 +5,7 @@ %%% Created : 17 Jul 2008 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl index 6a9f08e8a..b699454dd 100644 --- a/src/ejabberd_pkix.erl +++ b/src/ejabberd_pkix.erl @@ -3,7 +3,7 @@ %%% Created : 4 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 46fe8ce90..27e8775e1 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -4,7 +4,7 @@ %%% Created : 8 May 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl index 350f1292d..8d49f4632 100644 --- a/src/ejabberd_redis_sup.erl +++ b/src/ejabberd_redis_sup.erl @@ -3,7 +3,7 @@ %%% Created : 6 Apr 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl index d474f1127..7644810cd 100644 --- a/src/ejabberd_regexp.erl +++ b/src/ejabberd_regexp.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2011 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 8f877dfaf..2d80314c7 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -5,7 +5,7 @@ %%% Created : 27 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl index f53ee679f..66ae02208 100644 --- a/src/ejabberd_router_mnesia.erl +++ b/src/ejabberd_router_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 11 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl index 312718fd7..df8473c2b 100644 --- a/src/ejabberd_router_multicast.erl +++ b/src/ejabberd_router_multicast.erl @@ -5,7 +5,7 @@ %%% Created : 11 Aug 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl index 8ffde1f6a..0ddd63aa7 100644 --- a/src/ejabberd_router_redis.erl +++ b/src/ejabberd_router_redis.erl @@ -3,7 +3,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index 03e617654..ad120d82f 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -3,7 +3,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 7c9a041fa..448773612 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 7 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index 1af45a161..e065f8418 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -2,7 +2,7 @@ %%% Created : 12 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 55903a519..0bb384732 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -2,7 +2,7 @@ %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 5bc92a0a2..5947fea2b 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -2,7 +2,7 @@ %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl index 930f8674b..c47d72dbd 100644 --- a/src/ejabberd_shaper.erl +++ b/src/ejabberd_shaper.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sip.erl b/src/ejabberd_sip.erl index 499162966..d39aa5d30 100644 --- a/src/ejabberd_sip.erl +++ b/src/ejabberd_sip.erl @@ -5,7 +5,7 @@ %%% Created : 30 Apr 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2024 ProcessOne +%%% ejabberd, Copyright (C) 2013-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 4745c57d9..c1d970d84 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -5,7 +5,7 @@ %%% Created : 24 Nov 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_mnesia.erl b/src/ejabberd_sm_mnesia.erl index 26b4c6806..a82687963 100644 --- a/src/ejabberd_sm_mnesia.erl +++ b/src/ejabberd_sm_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_redis.erl b/src/ejabberd_sm_redis.erl index 3a7a0f99a..8e63cdcec 100644 --- a/src/ejabberd_sm_redis.erl +++ b/src/ejabberd_sm_redis.erl @@ -4,7 +4,7 @@ %%% Created : 11 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index df8e0970d..bef3774e1 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -4,7 +4,7 @@ %%% Created : 9 Mar 2015 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 117609182..e6e06bc40 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 8484183d9..365cc2b47 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -5,7 +5,7 @@ %%% Created : 20 Jan 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index 644844714..f798af5b8 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -5,7 +5,7 @@ %%% Created : 15 Aug 2023 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index 984f59f62..414828358 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -5,7 +5,7 @@ %%% Created : 22 Dec 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index ba99712e3..cc7bb472f 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -5,7 +5,7 @@ %%% Created : 8 May 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2024 ProcessOne +%%% ejabberd, Copyright (C) 2013-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index 0977265a6..3fa0fee0f 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -5,7 +5,7 @@ %%% Created : 31 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl index 4e24f4e8c..35a1f0370 100644 --- a/src/ejabberd_system_monitor.erl +++ b/src/ejabberd_system_monitor.erl @@ -5,7 +5,7 @@ %%% Created : 21 Mar 2007 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_tmp_sup.erl b/src/ejabberd_tmp_sup.erl index 70d01a236..7d26e6154 100644 --- a/src/ejabberd_tmp_sup.erl +++ b/src/ejabberd_tmp_sup.erl @@ -5,7 +5,7 @@ %%% Created : 18 Jul 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl index 17f4717dc..6c80fe8e7 100644 --- a/src/ejabberd_update.erl +++ b/src/ejabberd_update.erl @@ -5,7 +5,7 @@ %%% Created : 27 Jan 2006 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_web.erl b/src/ejabberd_web.erl index b9dcd7943..e6090f2d8 100644 --- a/src/ejabberd_web.erl +++ b/src/ejabberd_web.erl @@ -5,7 +5,7 @@ %%% Created : 28 Feb 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 1b945c794..02cd3c13d 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -5,7 +5,7 @@ %%% Created : 9 Apr 2004 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -369,7 +369,7 @@ make_xhtml(Els, Host, Node, Username, #request{lang = Lang} = R, JID, Level) -> [?XE(<<"p">>, [?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>), ?C(<<" ">>), ?C(ejabberd_option:version()), - ?C(<<" (c) 2002-2024 ">>), + ?C(<<" (c) 2002-2025 ">>), ?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)] )])])])]}}. diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 474a64c51..43b6673d4 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -33,7 +33,7 @@ %%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %%% ========================================================================================================== -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%%---------------------------------------------------------------------- -module(ejabberd_websocket). diff --git a/src/ejabberd_websocket_codec.erl b/src/ejabberd_websocket_codec.erl index fb8c5586d..8dde29f53 100644 --- a/src/ejabberd_websocket_codec.erl +++ b/src/ejabberd_websocket_codec.erl @@ -5,7 +5,7 @@ % Created : 9 sty 2023 by Paweł Chmielowski % % -% ejabberd, Copyright (C) 2002-2024 ProcessOne +% ejabberd, Copyright (C) 2002-2025 ProcessOne % % This program is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License as diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl index e0e922166..d257fea02 100644 --- a/src/ejabberd_xmlrpc.erl +++ b/src/ejabberd_xmlrpc.erl @@ -5,7 +5,7 @@ %%% Created : 21 Aug 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl index 090bb32f0..263e98f1a 100644 --- a/src/ejd2sql.erl +++ b/src/ejd2sql.erl @@ -5,7 +5,7 @@ %%% Created : 22 Aug 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_filter.erl b/src/eldap_filter.erl index be266954f..3483e8b02 100644 --- a/src/eldap_filter.erl +++ b/src/eldap_filter.erl @@ -6,7 +6,7 @@ %%% Author: Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_pool.erl b/src/eldap_pool.erl index d35edbd7a..78391eb1d 100644 --- a/src/eldap_pool.erl +++ b/src/eldap_pool.erl @@ -5,7 +5,7 @@ %%% Created : 12 Nov 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl index ddf9b8d55..6225c5cce 100644 --- a/src/eldap_utils.erl +++ b/src/eldap_utils.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl index 0579528b7..e431dcef3 100644 --- a/src/elixir_logger_backend.erl +++ b/src/elixir_logger_backend.erl @@ -5,7 +5,7 @@ %%% Created : 9 March 2016 by Mickael Remond %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 7416848da..451467c66 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -5,7 +5,7 @@ %%% Created : 19 Feb 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2006-2024 ProcessOne +%%% ejabberd, Copyright (C) 2006-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/extauth.erl b/src/extauth.erl index 85bf4e6a7..b1acd4a7e 100644 --- a/src/extauth.erl +++ b/src/extauth.erl @@ -2,7 +2,7 @@ %%% Created : 7 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/extauth_sup.erl b/src/extauth_sup.erl index 723d73f74..40769cbc9 100644 --- a/src/extauth_sup.erl +++ b/src/extauth_sup.erl @@ -2,7 +2,7 @@ %%% Created : 7 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index f4d6babbe..a02e9178a 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -5,7 +5,7 @@ %%% Created : 22 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 58d855938..9d40e0c17 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -5,7 +5,7 @@ %%% Created : 24 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl index 82e308bc8..48b43a05b 100644 --- a/src/gen_pubsub_node.erl +++ b/src/gen_pubsub_node.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl index cd4be7f47..6d958ae62 100644 --- a/src/gen_pubsub_nodetree.erl +++ b/src/gen_pubsub_nodetree.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl index f6bba4b8f..cf17acdd5 100644 --- a/src/jd2ejd.erl +++ b/src/jd2ejd.erl @@ -5,7 +5,7 @@ %%% Created : 2 Feb 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/misc.erl b/src/misc.erl index 233a03f1f..2bfb6ded8 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -8,7 +8,7 @@ %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 7baf6bf07..39f03ba53 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -5,7 +5,7 @@ %%% Created : 15 Nov 2005 by Magnus Henoch %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 79b523c9b..d5149708f 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -5,7 +5,7 @@ %%% Created : 10 Aug 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 85d039495..917c8d2bc 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -5,7 +5,7 @@ %%% Created : 9 Aug 2017 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce.erl b/src/mod_announce.erl index d7f983716..366a81e42 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -5,7 +5,7 @@ %%% Created : 11 Aug 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce_mnesia.erl b/src/mod_announce_mnesia.erl index e004d151c..6e578e001 100644 --- a/src/mod_announce_mnesia.erl +++ b/src/mod_announce_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl index 7b1f9bbc9..2a2692d37 100644 --- a/src/mod_announce_sql.erl +++ b/src/mod_announce_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index aa40e8f1a..0cad39b85 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -4,7 +4,7 @@ %%% Created : 1 Dec 2024 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_auth_fast_mnesia.erl b/src/mod_auth_fast_mnesia.erl index 4e70bc0fa..53337b390 100644 --- a/src/mod_auth_fast_mnesia.erl +++ b/src/mod_auth_fast_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 1 Dec 2024 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index e404d7d20..e5fc3503b 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -3,7 +3,7 @@ %%% Created : 13 Sep 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index 26b8e73d0..c0a85faf1 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -5,7 +5,7 @@ %%% Created : 25 Dec 2016 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index da0913e93..562899c3b 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -5,7 +5,7 @@ %%% Created : 24 Aug 2008 by Stephan Maka %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl index cd2fea99b..5f70a4ea9 100644 --- a/src/mod_bosh.erl +++ b/src/mod_bosh.erl @@ -7,7 +7,7 @@ %%% Created : 20 Jul 2011 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_mnesia.erl b/src/mod_bosh_mnesia.erl index e396dbbd9..7ac19df01 100644 --- a/src/mod_bosh_mnesia.erl +++ b/src/mod_bosh_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 12 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_redis.erl b/src/mod_bosh_redis.erl index 6337540c0..efd05a7ba 100644 --- a/src/mod_bosh_redis.erl +++ b/src/mod_bosh_redis.erl @@ -5,7 +5,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl index 643928fe1..29549ed49 100644 --- a/src/mod_bosh_sql.erl +++ b/src/mod_bosh_sql.erl @@ -5,7 +5,7 @@ %%% Created : 28 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 5468f8fee..79ae36edc 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -5,7 +5,7 @@ %%% Created : 7 Oct 2006 by Magnus Henoch %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps_mnesia.erl b/src/mod_caps_mnesia.erl index 1b83705eb..a0dc48c4f 100644 --- a/src/mod_caps_mnesia.erl +++ b/src/mod_caps_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl index 06449175a..9d96697e7 100644 --- a/src/mod_caps_sql.erl +++ b/src/mod_caps_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index ef1aa97ca..dfd723c99 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -7,7 +7,7 @@ %%% {mod_carboncopy, []} %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl index c56207464..67f973784 100644 --- a/src/mod_client_state.erl +++ b/src/mod_client_state.erl @@ -5,7 +5,7 @@ %%% Created : 11 Sep 2014 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2014-2024 ProcessOne +%%% ejabberd, Copyright (C) 2014-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 947333f84..d213ae9d9 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -5,7 +5,7 @@ %%% Created : 19 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index b932d862e..3673f345f 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -5,7 +5,7 @@ %%% Created : 8 Nov 2021 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index 12e2fcd50..f6879ea7f 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -4,7 +4,7 @@ %%% Purpose : XEP-0355: Namespace Delegation %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 34d668975..3dbecf6a9 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -5,7 +5,7 @@ %%% Created : 1 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index 3ed3a9810..2680248e5 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -5,7 +5,7 @@ %%% Created : 15 Aug 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2024 ProcessOne +%%% ejabberd, Copyright (C) 2014-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index b78f7c6e3..219267ef9 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -5,7 +5,7 @@ %%% Created : 15 Sep 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index 19d59c958..3f8db94f5 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -5,7 +5,7 @@ %%% Created : %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 1b67a518b..fddf27f46 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -5,7 +5,7 @@ %%% Created : 20 Aug 2015 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2015-2024 ProcessOne +%%% ejabberd, Copyright (C) 2015-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl index 4e522a83b..76b99ca05 100644 --- a/src/mod_http_upload_quota.erl +++ b/src/mod_http_upload_quota.erl @@ -5,7 +5,7 @@ %%% Created : 15 Oct 2015 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2015-2024 ProcessOne +%%% ejabberd, Copyright (C) 2015-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_jidprep.erl b/src/mod_jidprep.erl index 75a0c7280..3de051156 100644 --- a/src/mod_jidprep.erl +++ b/src/mod_jidprep.erl @@ -5,7 +5,7 @@ %%% Created : 11 Sep 2019 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2019-2024 ProcessOne +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last.erl b/src/mod_last.erl index b839ccf9a..ed701ea50 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -5,7 +5,7 @@ %%% Created : 24 Oct 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl index f4ce111cf..f108101c9 100644 --- a/src/mod_last_mnesia.erl +++ b/src/mod_last_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl index 317dc1daa..b61300fd2 100644 --- a/src/mod_last_sql.erl +++ b/src/mod_last_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl index 24129926e..1fb772d2c 100644 --- a/src/mod_legacy_auth.erl +++ b/src/mod_legacy_auth.erl @@ -2,7 +2,7 @@ %%% Created : 11 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 198a33738..1e2d13f3d 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -5,7 +5,7 @@ %%% Created : 4 Jul 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2013-2024 ProcessOne +%%% ejabberd, Copyright (C) 2013-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index f643da1fc..0895c994f 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 2646b0397..de97eaaa5 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index d880d745e..aefbba924 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -5,7 +5,7 @@ %%% Created : 23 Apr 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 6ceff2141..aeedc599f 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -5,7 +5,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 533f250c5..ceeb0708c 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -5,7 +5,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_matrix_gw_sup.erl b/src/mod_matrix_gw_sup.erl index f7730a266..f08f36e68 100644 --- a/src/mod_matrix_gw_sup.erl +++ b/src/mod_matrix_gw_sup.erl @@ -2,7 +2,7 @@ %%% Created : 1 May 2022 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl index 00257ed42..77b690867 100644 --- a/src/mod_metrics.erl +++ b/src/mod_metrics.erl @@ -5,7 +5,7 @@ %%% Created : 22 Oct 2015 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl index 974b03549..e38c7aae6 100644 --- a/src/mod_mqtt.erl +++ b/src/mod_mqtt.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_bridge.erl b/src/mod_mqtt_bridge.erl index 2842d18ef..cb60594e9 100644 --- a/src/mod_mqtt_bridge.erl +++ b/src/mod_mqtt_bridge.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Pawel Chmielowski -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_bridge_session.erl b/src/mod_mqtt_bridge_session.erl index 887883ac4..1c5f53f9d 100644 --- a/src/mod_mqtt_bridge_session.erl +++ b/src/mod_mqtt_bridge_session.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Pawel Chmielowski -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_mnesia.erl b/src/mod_mqtt_mnesia.erl index efab73224..5c2902d2b 100644 --- a/src/mod_mqtt_mnesia.erl +++ b/src/mod_mqtt_mnesia.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl index c5a177565..c6d0338d9 100644 --- a/src/mod_mqtt_session.erl +++ b/src/mod_mqtt_session.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl index f7bfea717..0f2b05b35 100644 --- a/src/mod_mqtt_sql.erl +++ b/src/mod_mqtt_sql.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_mqtt_ws.erl b/src/mod_mqtt_ws.erl index b9e1623b8..fd1e7d871 100644 --- a/src/mod_mqtt_ws.erl +++ b/src/mod_mqtt_ws.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 2e617f8a7..ca9d21800 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -5,7 +5,7 @@ %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 3f4c1c6a8..6c4c9d3a4 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -5,7 +5,7 @@ %%% Created : 8 Sep 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 84d9abf34..c075b954c 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -5,7 +5,7 @@ %%% Created : 12 Mar 2006 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index d5a89b36d..9e16a56c5 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 6ae05a189..338697b34 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -5,7 +5,7 @@ %%% Created : %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 85fb54249..f9fb9f882 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5,7 +5,7 @@ %%% Created : 19 Mar 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index 4a63e05df..d7504ab7c 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -5,7 +5,7 @@ %%% Created : 17 kwi 2023 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index 9e193a7df..b6cff55d1 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_muc_sup.erl b/src/mod_muc_sup.erl index 01dd10a1c..744e20c45 100644 --- a/src/mod_muc_sup.erl +++ b/src/mod_muc_sup.erl @@ -2,7 +2,7 @@ %%% Created : 4 Jul 2019 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 1a7dd7d14..369d5f92d 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -5,7 +5,7 @@ %%% Created : 29 May 2007 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline.erl b/src/mod_offline.erl index abb209b77..32277ba7d 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -5,7 +5,7 @@ %%% Created : 5 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl index 77839c30c..24406c5ac 100644 --- a/src/mod_offline_mnesia.erl +++ b/src/mod_offline_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index ea4901539..9078b082c 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -4,7 +4,7 @@ %%% Created : 15 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_ping.erl b/src/mod_ping.erl index 796a83738..b760db68c 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jul 2009 by Brian Cully %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl index 58029eadc..bb1b43af8 100644 --- a/src/mod_pres_counter.erl +++ b/src/mod_pres_counter.erl @@ -5,7 +5,7 @@ %%% Created : 23 Sep 2010 by Ahmed Omar %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl index b52000762..c28e6fd89 100644 --- a/src/mod_privacy.erl +++ b/src/mod_privacy.erl @@ -5,7 +5,7 @@ %%% Created : 21 Jul 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl index c39221f3e..b8657e719 100644 --- a/src/mod_privacy_mnesia.erl +++ b/src/mod_privacy_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl index b00b62741..e0dc4476b 100644 --- a/src/mod_privacy_sql.erl +++ b/src/mod_privacy_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private.erl b/src/mod_private.erl index 2dea64c45..3f73a3bca 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -5,7 +5,7 @@ %%% Created : 16 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private_mnesia.erl b/src/mod_private_mnesia.erl index f08756d62..42bab447f 100644 --- a/src/mod_private_mnesia.erl +++ b/src/mod_private_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl index 2c242a78a..d493b0587 100644 --- a/src/mod_private_sql.erl +++ b/src/mod_private_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 65e962f2a..bdf512f34 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -4,7 +4,7 @@ %%% Purpose : XEP-0356: Privileged Entity %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl index 3ea9f05ba..4143defaa 100644 --- a/src/mod_proxy65.erl +++ b/src/mod_proxy65.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_lib.erl b/src/mod_proxy65_lib.erl index 554b2e070..1f5b25ed0 100644 --- a/src/mod_proxy65_lib.erl +++ b/src/mod_proxy65_lib.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_mnesia.erl b/src/mod_proxy65_mnesia.erl index 94c892b51..3661b62c0 100644 --- a/src/mod_proxy65_mnesia.erl +++ b/src/mod_proxy65_mnesia.erl @@ -2,7 +2,7 @@ %%% Created : 16 Jan 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_redis.erl b/src/mod_proxy65_redis.erl index a6de6c2b0..588bd55f3 100644 --- a/src/mod_proxy65_redis.erl +++ b/src/mod_proxy65_redis.erl @@ -3,7 +3,7 @@ %%% Created : 31 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index 742649268..692ad146d 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -5,7 +5,7 @@ %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl index 67ceeac21..c05f055b7 100644 --- a/src/mod_proxy65_sql.erl +++ b/src/mod_proxy65_sql.erl @@ -3,7 +3,7 @@ %%% Created : 30 Mar 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl index d21b483a8..c12c67b63 100644 --- a/src/mod_proxy65_stream.erl +++ b/src/mod_proxy65_stream.erl @@ -4,7 +4,7 @@ %%% Purpose : Bytestream process. %%% Created : 12 Oct 2006 by Evgeniy Khramtsov %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 767d10215..3ebc69fa4 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub_mnesia.erl b/src/mod_pubsub_mnesia.erl index e0d62d7c1..a1dbc2ff3 100644 --- a/src/mod_pubsub_mnesia.erl +++ b/src/mod_pubsub_mnesia.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl index f0b65a6b2..59b22c110 100644 --- a/src/mod_pubsub_sql.erl +++ b/src/mod_pubsub_sql.erl @@ -1,5 +1,5 @@ %%%---------------------------------------------------------------------- -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push.erl b/src/mod_push.erl index fca89fceb..20cb6394c 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 03cdc1e02..2c43282cd 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_mnesia.erl b/src/mod_push_mnesia.erl index 0fbfae10e..6a5f068b9 100644 --- a/src/mod_push_mnesia.erl +++ b/src/mod_push_mnesia.erl @@ -5,7 +5,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index 0b4d84afd..a36e50f8e 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -5,7 +5,7 @@ %%% Created : 26 Oct 2017 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2017-2024 ProcessOne +%%% ejabberd, Copyright (C) 2017-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_register.erl b/src/mod_register.erl index 08b461b28..fed56b6ea 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -5,7 +5,7 @@ %%% Created : 8 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 76d84d068..b91b4637b 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -5,7 +5,7 @@ %%% Created : 4 May 2008 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster.erl b/src/mod_roster.erl index cc0838812..7765fc255 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -5,7 +5,7 @@ %%% Created : 11 Dec 2002 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl index 75d1a7668..d8d4bd1c9 100644 --- a/src/mod_roster_mnesia.erl +++ b/src/mod_roster_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 2f159d681..44d507e5e 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl index 5d0ba699d..06a6b92aa 100644 --- a/src/mod_s2s_bidi.erl +++ b/src/mod_s2s_bidi.erl @@ -2,7 +2,7 @@ %%% Created : 20 Oct 2024 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl index 5a3387390..f6128b573 100644 --- a/src/mod_s2s_dialback.erl +++ b/src/mod_s2s_dialback.erl @@ -2,7 +2,7 @@ %%% Created : 16 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index d18e95f66..e0ffabc70 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -2,7 +2,7 @@ %%% Created : 20 Oct 2024 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl index 434798fd9..533ff517f 100644 --- a/src/mod_service_log.erl +++ b/src/mod_service_log.erl @@ -5,7 +5,7 @@ %%% Created : 24 Aug 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index c0c8c4b49..3c454b0df 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -5,7 +5,7 @@ %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index ee3ee5823..509334be1 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -7,7 +7,7 @@ %%% Created : 5 Mar 2005 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl index e4a487c94..028584459 100644 --- a/src/mod_shared_roster_mnesia.erl +++ b/src/mod_shared_roster_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl index 0bdd531d9..4d582a1bf 100644 --- a/src/mod_shared_roster_sql.erl +++ b/src/mod_shared_roster_sql.erl @@ -4,7 +4,7 @@ %%% Created : 14 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sic.erl b/src/mod_sic.erl index 09b752bda..f781c1690 100644 --- a/src/mod_sic.erl +++ b/src/mod_sic.erl @@ -5,7 +5,7 @@ %%% Created : 6 Mar 2010 by Karim Gemayel %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip.erl b/src/mod_sip.erl index cb77227bf..aa98be2cc 100644 --- a/src/mod_sip.erl +++ b/src/mod_sip.erl @@ -5,7 +5,7 @@ %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2024 ProcessOne +%%% ejabberd, Copyright (C) 2014-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl index 8fae2f545..8c5d8348c 100644 --- a/src/mod_sip_proxy.erl +++ b/src/mod_sip_proxy.erl @@ -5,7 +5,7 @@ %%% Created : 21 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2024 ProcessOne +%%% ejabberd, Copyright (C) 2014-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl index 6bd8bad26..ade4c0be0 100644 --- a/src/mod_sip_registrar.erl +++ b/src/mod_sip_registrar.erl @@ -5,7 +5,7 @@ %%% Created : 23 Apr 2014 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2014-2024 ProcessOne +%%% ejabberd, Copyright (C) 2014-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 17235fa8f..184407e8c 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -5,7 +5,7 @@ %%% Created : 11 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 4a58e1d93..359d2601e 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -3,7 +3,7 @@ %%% Created : 25 Dec 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index d70651a3a..c210868e7 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -5,7 +5,7 @@ %%% Created : 18 Apr 2020 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2020-2024 ProcessOne +%%% ejabberd, Copyright (C) 2020-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_time.erl b/src/mod_time.erl index 1dfdd201b..fbebf03a7 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -6,7 +6,7 @@ %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index cce0ab225..a225ba23b 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -5,7 +5,7 @@ %%% Created : 2 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 0c32923fc..3ecf39ba1 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -4,7 +4,7 @@ %%% Created : 29 Jul 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl index a8e3ed24e..e70b13fc0 100644 --- a/src/mod_vcard_mnesia.erl +++ b/src/mod_vcard_mnesia.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index 64e411ab8..18456f402 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -4,7 +4,7 @@ %%% Created : 13 Apr 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl index 0b6367684..70ed707b2 100644 --- a/src/mod_vcard_xupdate.erl +++ b/src/mod_vcard_xupdate.erl @@ -5,7 +5,7 @@ %%% Created : 9 Mar 2007 by Igor Goryachev %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mod_version.erl b/src/mod_version.erl index f8a8dadcf..e10168c84 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -5,7 +5,7 @@ %%% Created : 18 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/mqtt_codec.erl b/src/mqtt_codec.erl index 7e0d31bff..1ef474dd5 100644 --- a/src/mqtt_codec.erl +++ b/src/mqtt_codec.erl @@ -1,6 +1,6 @@ %%%------------------------------------------------------------------- %%% @author Evgeny Khramtsov -%%% @copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. +%%% @copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. diff --git a/src/node_flat.erl b/src/node_flat.erl index b385e66e1..7093d4beb 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 6db0dd06b..acfdf3331 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_pep.erl b/src/node_pep.erl index 22846e3a6..3d208c73b 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl index 26b458e36..5d35c7bfe 100644 --- a/src/node_pep_sql.erl +++ b/src/node_pep_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl index 32841fcc6..facb4fd74 100644 --- a/src/nodetree_tree.erl +++ b/src/nodetree_tree.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index 64af45b5d..f7a31b8ae 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/nodetree_virtual.erl b/src/nodetree_virtual.erl index 455db2619..18eb9ed30 100644 --- a/src/nodetree_virtual.erl +++ b/src/nodetree_virtual.erl @@ -5,7 +5,7 @@ %%% Created : 1 Dec 2007 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 193314f50..cbfb49cd4 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -4,7 +4,7 @@ %%% Created : 20 Jan 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/proxy_protocol.erl b/src/proxy_protocol.erl index 8405cb037..4ce0d31b4 100644 --- a/src/proxy_protocol.erl +++ b/src/proxy_protocol.erl @@ -5,7 +5,7 @@ %%% Created : 27 Nov 2018 by Paweł Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl index e3e16fb45..a67654c7c 100644 --- a/src/pubsub_db_sql.erl +++ b/src/pubsub_db_sql.erl @@ -5,7 +5,7 @@ %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_index.erl b/src/pubsub_index.erl index ffa11adc4..0c34ea63b 100644 --- a/src/pubsub_index.erl +++ b/src/pubsub_index.erl @@ -5,7 +5,7 @@ %%% Created : 30 Apr 2009 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl index 9834c3022..8d9fc6198 100644 --- a/src/pubsub_migrate.erl +++ b/src/pubsub_migrate.erl @@ -5,7 +5,7 @@ %%% Created : 26 Jul 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl index 74766d1fb..6db643af6 100644 --- a/src/pubsub_subscription.erl +++ b/src/pubsub_subscription.erl @@ -5,7 +5,7 @@ %%% Created : 29 May 2009 by Brian Cully %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl index 64207b696..8f1361b47 100644 --- a/src/pubsub_subscription_sql.erl +++ b/src/pubsub_subscription_sql.erl @@ -6,7 +6,7 @@ %%% Created : 7 Aug 2009 by Pablo Polvorin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/rest.erl b/src/rest.erl index 7f4883295..7fde9e710 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -5,7 +5,7 @@ %%% Created : 16 Oct 2014 by Christophe Romain %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/str.erl b/src/str.erl index e69e8693d..6dafcfda6 100644 --- a/src/str.erl +++ b/src/str.erl @@ -5,7 +5,7 @@ %%% Created : 23 Feb 2012 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/translate.erl b/src/translate.erl index e5d6d8b09..5adde6e24 100644 --- a/src/translate.erl +++ b/src/translate.erl @@ -5,7 +5,7 @@ %%% Created : 6 Jan 2003 by Alexey Shchepin %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/src/win32_dns.erl b/src/win32_dns.erl index 3569e0217..f26f4cd34 100644 --- a/src/win32_dns.erl +++ b/src/win32_dns.erl @@ -5,7 +5,7 @@ %%% Created : 5 Mar 2009 by Geoff Cant %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/announce_tests.erl b/test/announce_tests.erl index 570f67df5..724baba27 100644 --- a/test/announce_tests.erl +++ b/test/announce_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/carbons_tests.erl b/test/carbons_tests.erl index 2d9c4b606..eabd3af2a 100644 --- a/test/carbons_tests.erl +++ b/test/carbons_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/commands_tests.erl b/test/commands_tests.erl index 7e77136b9..e98e288ed 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -3,7 +3,7 @@ %%% Created : 2 Jul 2024 by Badlop %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/csi_tests.erl b/test/csi_tests.erl index 8886bb519..f2b61abff 100644 --- a/test/csi_tests.erl +++ b/test/csi_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index b7c72a02b..935ccbba0 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -3,7 +3,7 @@ %%% Created : 2 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/example_tests.erl b/test/example_tests.erl index ce699208b..5fd0a86ff 100644 --- a/test/example_tests.erl +++ b/test/example_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/jidprep_tests.erl b/test/jidprep_tests.erl index 2091c4fc0..f18e150fb 100644 --- a/test/jidprep_tests.erl +++ b/test/jidprep_tests.erl @@ -3,7 +3,7 @@ %%% Created : 11 Sep 2019 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2019-2024 ProcessOne +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/ldap_srv.erl b/test/ldap_srv.erl index 077c3de4b..2d0cea988 100644 --- a/test/ldap_srv.erl +++ b/test/ldap_srv.erl @@ -3,7 +3,7 @@ %%% Created : 21 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/mam_tests.erl b/test/mam_tests.erl index ed384cfa1..27988bf5e 100644 --- a/test/mam_tests.erl +++ b/test/mam_tests.erl @@ -3,7 +3,7 @@ %%% Created : 14 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/muc_tests.erl b/test/muc_tests.erl index a63567481..7a5d1d28f 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -3,7 +3,7 @@ %%% Created : 15 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/offline_tests.erl b/test/offline_tests.erl index 4ab366f05..d859da622 100644 --- a/test/offline_tests.erl +++ b/test/offline_tests.erl @@ -3,7 +3,7 @@ %%% Created : 7 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/privacy_tests.erl b/test/privacy_tests.erl index 7faf59f5d..51782dcf4 100644 --- a/test/privacy_tests.erl +++ b/test/privacy_tests.erl @@ -3,7 +3,7 @@ %%% Created : 18 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/private_tests.erl b/test/private_tests.erl index 5bb8b3a50..e7077f4ba 100644 --- a/test/private_tests.erl +++ b/test/private_tests.erl @@ -3,7 +3,7 @@ %%% Created : 23 Nov 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/proxy65_tests.erl b/test/proxy65_tests.erl index 6de5c4fea..612a926fb 100644 --- a/test/proxy65_tests.erl +++ b/test/proxy65_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/pubsub_tests.erl b/test/pubsub_tests.erl index 427893e1a..1cb02f020 100644 --- a/test/pubsub_tests.erl +++ b/test/pubsub_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/push_tests.erl b/test/push_tests.erl index 931471353..9e400cccc 100644 --- a/test/push_tests.erl +++ b/test/push_tests.erl @@ -3,7 +3,7 @@ %%% Created : 15 Jul 2017 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/replaced_tests.erl b/test/replaced_tests.erl index c8a81c9d3..37e22b3ac 100644 --- a/test/replaced_tests.erl +++ b/test/replaced_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/roster_tests.erl b/test/roster_tests.erl index f218a85d6..8d096eea3 100644 --- a/test/roster_tests.erl +++ b/test/roster_tests.erl @@ -3,7 +3,7 @@ %%% Created : 22 Oct 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/sm_tests.erl b/test/sm_tests.erl index f013ccfff..a55957856 100644 --- a/test/sm_tests.erl +++ b/test/sm_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/stundisco_tests.erl b/test/stundisco_tests.erl index f892805b2..ca941983f 100644 --- a/test/stundisco_tests.erl +++ b/test/stundisco_tests.erl @@ -3,7 +3,7 @@ %%% Created : 22 Apr 2020 by Holger Weiss %%% %%% -%%% ejabberd, Copyright (C) 2020-2024 ProcessOne +%%% ejabberd, Copyright (C) 2020-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/suite.erl b/test/suite.erl index 198a4b596..0066b5116 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -3,7 +3,7 @@ %%% Created : 27 Jun 2013 by Evgeniy Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/upload_tests.erl b/test/upload_tests.erl index 0fb22dc29..7e2d89958 100644 --- a/test/upload_tests.erl +++ b/test/upload_tests.erl @@ -3,7 +3,7 @@ %%% Created : 17 May 2018 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/vcard_tests.erl b/test/vcard_tests.erl index 4ee94ddcc..fc3adb611 100644 --- a/test/vcard_tests.erl +++ b/test/vcard_tests.erl @@ -3,7 +3,7 @@ %%% Created : 16 Nov 2016 by Evgeny Khramtsov %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/test/webadmin_tests.erl b/test/webadmin_tests.erl index 753f87930..6a4ff7cac 100644 --- a/test/webadmin_tests.erl +++ b/test/webadmin_tests.erl @@ -3,7 +3,7 @@ %%% Created : 23 Mar 2020 by Pawel Chmielowski %%% %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as diff --git a/tools/xml_compress_gen.erl b/tools/xml_compress_gen.erl index 8d9e770b0..63101ced2 100644 --- a/tools/xml_compress_gen.erl +++ b/tools/xml_compress_gen.erl @@ -4,7 +4,7 @@ %% Created : 14 Sep 2018 Pawel Chmielowski %% %% -%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%% ejabberd, Copyright (C) 2002-2025 ProcessOne %% %% This program is free software; you can redistribute it and/or %% modify it under the terms of the GNU General Public License as diff --git a/vars.config.in b/vars.config.in index 69e51f07a..ded059b3e 100644 --- a/vars.config.in +++ b/vars.config.in @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% -%%% ejabberd, Copyright (C) 2002-2024 ProcessOne +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as From 457d8fc6ccd927304dad5abbd984b170914d2dae Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Jan 2025 11:24:22 +0100 Subject: [PATCH 0914/1302] Container: Bump versions to Erlang/OTP 27.2 and Elixir 1.18.1 --- .github/container/Dockerfile | 4 ++-- CONTAINER.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index f9a97e0af..2a44b9035 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,7 +1,7 @@ #' Define default build variables ## specifc ARGs for METHOD='direct' -ARG OTP_VSN='26.2' -ARG ELIXIR_VSN='1.16.2' +ARG OTP_VSN='27.2' +ARG ELIXIR_VSN='1.18.1' ## specifc ARGs for METHOD='package' ARG ALPINE_VSN='3.19' ## general ARGs diff --git a/CONTAINER.md b/CONTAINER.md index 5284afd7f..95115a6c2 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -19,7 +19,7 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. This document explains how to use the `ejabberd` container image available in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), built using the files in `.github/container/`. -This image is based in Alpine 3.19, includes Erlang/OTP 26.2 and Elixir 1.16.1. +This image is based in Alpine 3.19, includes Erlang/OTP 27.2 and Elixir 1.18.1. Alternatively, there is also the `ecs` container image available in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), From 85b660fb4b0ecb558180f0c0e6d9f0ebff210286 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Jan 2025 14:57:16 +0100 Subject: [PATCH 0915/1302] rebar.config: Bump provider_asn1 version to 0.4.1 --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index f367477cf..bc6a258c5 100644 --- a/rebar.config +++ b/rebar.config @@ -154,7 +154,7 @@ {if_rebar3, {plugins, [{if_version_below, "21", {rebar3_hex, "7.0.7"}}, {if_version_above, "20", {rebar3_hex, "~> 7.0.8"}}, - {provider_asn1, "0.2.0"}, + {provider_asn1, "0.4.1"}, %% Protocol consolidation doesn't work correctly in upstream rebar_mix, see %% https://github.com/Supersonido/rebar_mix/issues/27#issuecomment-894873335 %% Let's use this fixed rebar_mix fork, see its PR: From 9d87193d80f9bdbb8d4dd70d4143a3a4d3a22d7f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 7 Jan 2025 10:39:12 +0100 Subject: [PATCH 0916/1302] mix.lock: Update to recent versions --- mix.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.lock b/mix.lock index eeceba2ab..9842b9c91 100644 --- a/mix.lock +++ b/mix.lock @@ -2,19 +2,19 @@ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, - "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, + "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, "fast_xml": {:hex, :fast_xml, "1.1.55", "ace020f2521f2a484ac8467d2822af85534a346e2aae03ffcbc34f29318befaf", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "83f3e23a780ed5f567cdec73953f06c95b838d709dbfa86b59a98a8d23c99f85"}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, - "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, + "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, @@ -27,7 +27,7 @@ "p1_acme": {:hex, :p1_acme, "1.0.25", "db91f0d6c193cd1d5c0b0fa3939a898dbf56a6075db4347cde26e802715de50c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a7b55b47495ddb4f98a15e65451ec3ad43f4637b955c74cd695d98e6a645d08c"}, "p1_mysql": {:hex, :p1_mysql, "1.0.25", "875d4cbdc7c9990270df3292cce2514e4c18a9fdfd19bef258cb4d0c45b4f243", [:rebar3], [], "hexpm", "e6187ffae95b726098e88f3ee6f2344ac259ce2c26e0ee403b05feef341ae434"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.29", "fae0c90cbc5931865958150f1b667fb0d20b063f6a46a17770a4e5232ded632c", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "a6ff58e8b174993f3895da3ea6211a9f9d0c54d1a6e28bb321da3b3cd68b38c1"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.31", "8339beac1f0f4a45f476ff5306be5135020f02979a61df0d8cf7b1c67e85e2fd", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b7fc45dfb2549187347871b7fd0573638ad5ea337f6263fba1b3840efab2ff49"}, "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, From bc6c868c8a958b0f01e723fac33bae2b5b72d300 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 12 Jan 2025 19:38:08 +0100 Subject: [PATCH 0917/1302] Docs: Don't use backtick quotes for ejabberd name This change improves sentences legibility. In the Introduction page, use **strong** instead. Notice backtick quotes are for raw code in markdown, for example: - `ejabberd` container image - `ejabberd` script generated by OTP Release - `ejabberd` username in system, for example for MySQL or container host --- README.md | 2 +- src/ejabberd_options_doc.erl | 2 +- src/mod_register.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e2f3ef13..76eae7b55 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ There are several places to get in touch with other ejabberd developers and admi License ------- -- `ejabberd` is released under the __GNU General Public License v2__ (see [COPYING](COPYING)) +- ejabberd is released under the __GNU General Public License v2__ (see [COPYING](COPYING)) - [ejabberd translations](https://github.com/processone/ejabberd-po/) under __MIT License__. [discussions]: https://github.com/processone/ejabberd/discussions diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 380b2b605..189debef4 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -32,7 +32,7 @@ doc() -> desc => ?T("List of one or more " "_`../configuration/basic.md#host-names|host names`_ " - "(or domains) that 'ejabberd' will serve. This is a " + "(or domains) that ejabberd will serve. This is a " "**mandatory** option.")}}, {listen, #{value => "[Options, ...]", diff --git a/src/mod_register.erl b/src/mod_register.erl index fed56b6ea..b811dcff0 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -661,7 +661,7 @@ mod_doc() -> {access_from, #{value => ?T("AccessName"), desc => - ?T("By default, 'ejabberd' doesn't allow the client to register new accounts " + ?T("By default, ejabberd doesn't allow the client to register new accounts " "from s2s or existing c2s sessions. You can change it by defining " "access rule in this option. Use with care: allowing registration " "from s2s leads to uncontrolled massive accounts creation by rogue users.")}}, From 29e6204bde8abb6e82ba05c97c752a03eba6b265 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 12 Jan 2025 19:44:07 +0100 Subject: [PATCH 0918/1302] Docs: Reword explanation about ACL names and definitions --- src/ejabberd_options_doc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 189debef4..19e7773da 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -222,7 +222,7 @@ doc() -> "specified 'Pattern' according to the rules used by the " "Unix shell.")}}]}, {access_rules, - #{value => "{AccessName: {allow|deny: ACLRules|ACLName}}", + #{value => "{AccessName: {allow|deny: ACLName|ACLDefinition}}", desc => ?T("This option defines " "_`basic.md#access-rules|Access Rules`_. " @@ -1322,7 +1322,7 @@ doc() -> " normal: 1000", " fast: 50000"]}}, {shaper_rules, - #{value => "{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}", + #{value => "{ShaperRuleName: {Number|ShaperName: ACLName|ACLDefinition}}", desc => ?T("This option defines " "_`../configuration/basic.md#shaper-rules|shaper rules`_ " From 862cacabcb4ee79bf5482fd5bf0df5f94e9d1928 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 12 Jan 2025 23:48:31 +0100 Subject: [PATCH 0919/1302] ejabberdctl.template: Handle erts versions 9 o lower --- .github/container/ejabberdctl.template | 2 +- ejabberdctl.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index af2874fff..a27a14587 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -265,7 +265,7 @@ help() # dynamic node name helper uid() { - ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.*\([0-9][0-9]\).*|\1|g')" + ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.* \([0-9]*[0-9]\).*|\1|g')" if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 # Erlang/OTP lower than 23, which doesn's support dynamic node code N=1 diff --git a/ejabberdctl.template b/ejabberdctl.template index 21be6430f..b9f01535b 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -253,7 +253,7 @@ help() # dynamic node name helper uid() { - ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.*\([0-9][0-9]\).*|\1|g')" + ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.* \([0-9]*[0-9]\).*|\1|g')" if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0 # Erlang/OTP lower than 23, which doesn's support dynamic node code N=1 From 4a363b6e76786f047b3dfd61a77500a6a629141c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Jan 2025 00:00:40 +0100 Subject: [PATCH 0920/1302] Test: Fixes to handle re-running test after update_sql Enable some modules so ejabberd creates their SQL tables and later those tables can be updated without errors by mod_admin_update_sql when ci.yml calls "make test" to check update_sql. However, mod_shared_roster should be stopped before running the actual tests, as it introduces undesired IQ queries On the other hand, a few SQL tables are global RAM, and cannot be created in SQL just for a vhost that is not the first one defined. --- test/ejabberd_SUITE.erl | 19 ++++++++++++++++++- test/ejabberd_SUITE_data/ejabberd.mssql.yml | 8 ++++++++ test/ejabberd_SUITE_data/ejabberd.mysql.yml | 8 ++++++++ test/ejabberd_SUITE_data/ejabberd.pgsql.yml | 8 ++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 935ccbba0..e240de59b 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -100,6 +100,7 @@ do_init_per_group(mysql, Config) -> {selected, _, _} -> mod_muc:shutdown_rooms(?MYSQL_VHOST), update_sql(?MYSQL_VHOST, Config), + stop_temporary_modules(?MYSQL_VHOST), set_opt(server, ?MYSQL_VHOST, Config); Err -> {skip, {mysql_not_available, Err}} @@ -109,6 +110,7 @@ do_init_per_group(mssql, Config) -> {selected, _, _} -> mod_muc:shutdown_rooms(?MSSQL_VHOST), update_sql(?MSSQL_VHOST, Config), + stop_temporary_modules(?MSSQL_VHOST), set_opt(server, ?MSSQL_VHOST, Config); Err -> {skip, {mssql_not_available, Err}} @@ -118,6 +120,7 @@ do_init_per_group(pgsql, Config) -> {selected, _, _} -> mod_muc:shutdown_rooms(?PGSQL_VHOST), update_sql(?PGSQL_VHOST, Config), + stop_temporary_modules(?PGSQL_VHOST), set_opt(server, ?PGSQL_VHOST, Config); Err -> {skip, {pgsql_not_available, Err}} @@ -161,6 +164,10 @@ do_init_per_group(GroupName, Config) -> _ -> NewConfig end. +stop_temporary_modules(Host) -> + Modules = [mod_shared_roster], + [gen_mod:stop_module(Host, M) || M <- Modules]. + end_per_group(mnesia, _Config) -> ok; end_per_group(redis, _Config) -> @@ -1094,7 +1101,17 @@ clear_table_queries(Queries) -> fun(Query, Acc) -> case split(str:to_lower(Query)) of [<<"create">>, <<"table">>, Table|_] -> - [<<"DELETE FROM ", Table/binary, ";">>|Acc]; + GlobalRamTables = [<<"bosh">>, + <<"oauth_client">>, + <<"oauth_token">>, + <<"proxy65">>, + <<"route">>], + case lists:member(Table, GlobalRamTables) of + true -> + Acc; + false -> + [<<"DELETE FROM ", Table/binary, ";">>|Acc] + end; _ -> Acc end diff --git a/test/ejabberd_SUITE_data/ejabberd.mssql.yml b/test/ejabberd_SUITE_data/ejabberd.mssql.yml index 1ecf77ba8..66bfee3f9 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mssql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mssql.yml @@ -69,3 +69,11 @@ Welcome to this XMPP server." mod_stats: [] mod_time: [] mod_version: [] + mod_mix: + db_type: sql + mod_mix_pam: + db_type: sql + mod_mqtt: + db_type: sql + mod_shared_roster: + db_type: sql diff --git a/test/ejabberd_SUITE_data/ejabberd.mysql.yml b/test/ejabberd_SUITE_data/ejabberd.mysql.yml index 411901976..abb0a6937 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mysql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mysql.yml @@ -70,3 +70,11 @@ Welcome to this XMPP server." mod_stats: [] mod_time: [] mod_version: [] + mod_mix: + db_type: sql + mod_mix_pam: + db_type: sql + mod_mqtt: + db_type: sql + mod_shared_roster: + db_type: sql diff --git a/test/ejabberd_SUITE_data/ejabberd.pgsql.yml b/test/ejabberd_SUITE_data/ejabberd.pgsql.yml index c0cd0b0d6..014e1c058 100644 --- a/test/ejabberd_SUITE_data/ejabberd.pgsql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.pgsql.yml @@ -70,3 +70,11 @@ Welcome to this XMPP server." mod_stats: [] mod_time: [] mod_version: [] + mod_mix: + db_type: sql + mod_mix_pam: + db_type: sql + mod_mqtt: + db_type: sql + mod_shared_roster: + db_type: sql From 6959447c2ce47c90cae1be7b557165aff9b78300 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 12 Jan 2025 22:33:33 +0100 Subject: [PATCH 0921/1302] mod_admin_update_sql: Fix update_sql when using tables created by ejabberd internally --- src/mod_admin_update_sql.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 917c8d2bc..b6594851a 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -286,6 +286,7 @@ update_tables(State) -> true -> drop_index(State, "private_storage", "i_private_storage_username"), drop_index(State, "private_storage", "i_private_storage_username_namespace"), + drop_pkey(State, "private_storage"), add_pkey(State, "private_storage", ["server_host", "username", "namespace"]), drop_sh_default(State, "private_storage"); false -> @@ -342,6 +343,7 @@ update_tables(State) -> true -> drop_index(State, "sm", "i_sm_sid"), drop_index(State, "sm", "i_sm_username"), + drop_pkey(State, "sm"), add_pkey(State, "sm", ["usec", "pid"]), create_index(State, "sm", "i_sm_sh_username", ["server_host", "username"]), drop_sh_default(State, "sm"); From 2e754a5557ecfa25868aca9757738a3387e3c827 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Jan 2025 12:49:44 +0100 Subject: [PATCH 0922/1302] mod_admin_update_sql: Fix mysql support --- src/mod_admin_update_sql.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index b6594851a..0c3f9095f 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -594,8 +594,22 @@ mssql_clustered(_) -> "CLUSTERED ". mysql_keylen(_, "bare_peer") -> "(191)"; mysql_keylen(_, "channel") -> "(191)"; mysql_keylen(_, "domain") -> "(75)"; +mysql_keylen(_, "grp") -> "(191)"; %% in mysql*.sql this is text, not varchar(191) mysql_keylen(_, "jid") -> "(75)"; +mysql_keylen(_, "lbday") -> "(191)"; +mysql_keylen(_, "lctry") -> "(191)"; +mysql_keylen(_, "lemail") -> "(191)"; +mysql_keylen(_, "lfamily") -> "(191)"; +mysql_keylen(_, "lfn") -> "(191)"; +mysql_keylen(_, "lgiven") -> "(191)"; +mysql_keylen(_, "llocality") -> "(191)"; +mysql_keylen(_, "lmiddle") -> "(191)"; +mysql_keylen(_, "lnickname") -> "(191)"; +mysql_keylen(_, "lorgname") -> "(191)"; +mysql_keylen(_, "lorgunit") -> "(191)"; +mysql_keylen(_, "lusername") -> "(191)"; mysql_keylen(_, "name") -> "(75)"; +mysql_keylen(_, "namespace") -> "(191)"; mysql_keylen(_, "node") -> "(75)"; mysql_keylen(_, "peer") -> "(191)"; mysql_keylen(_, "pid") -> "(75)"; From 056635119c8b9f169f1c59cccbf81faab88a6712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 14 Jan 2025 10:03:31 +0100 Subject: [PATCH 0923/1302] Fix json version of json_encode_with_kv_list for nested kv lists This should fix error reported in issue #4338 --- src/misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.erl b/src/misc.erl index 2bfb6ded8..46877538b 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -141,7 +141,7 @@ json_decode(Bin) -> -else. json_encode_with_kv_lists(Term) -> iolist_to_binary(json:encode(Term, - fun([{_, _} | _] = Val, Encoder) -> + fun({Val}, Encoder) when is_list(Val) -> json:encode_key_value_list(Val, Encoder); (Val, Encoder) -> json:encode_value(Val, Encoder) From f72cfa9a1327e01aed229fc4d41b2a7658d9841f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 09:20:09 +0100 Subject: [PATCH 0924/1302] Test: Uninstall mod_example when the tests has finished --- test/commands_tests.erl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/commands_tests.erl b/test/commands_tests.erl index e98e288ed..166415e89 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -48,11 +48,12 @@ single_cases() -> single_test(http_list), single_test(http_tuple), single_test(http_list_tuple), - single_test(http_list_tuple_map)]}. + single_test(http_list_tuple_map), + single_test(clean)]}. setup(_Config) -> M = <<"mod_example">>, - execute(module_uninstall, [M]), + clean(_Config), case execute(module_install, [M]) of ok -> ok; @@ -63,6 +64,12 @@ setup(_Config) -> Installed = execute(modules_installed, []), ?match(true, lists:keymember(mod_example, 1, Installed)). +clean(_Config) -> + M = <<"mod_example">>, + execute(module_uninstall, [M]), + Installed = execute(modules_installed, []), + ?match(false, lists:keymember(mod_example, 1, Installed)). + %%%================================== %%%% ejabberdctl From ad1b577ca9ffe42c24d8616daec7700cf1869b31 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 09:12:56 +0100 Subject: [PATCH 0925/1302] mix.exs: The ex_doc dependency is only relevant for the edoc Mix environment --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 4f46caf86..97487b84c 100644 --- a/mix.exs +++ b/mix.exs @@ -133,7 +133,7 @@ defmodule Ejabberd.MixProject do [{:cache_tab, "~> 1.0"}, {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, - {:ex_doc, "~> 0.31", only: [:dev, :edoc], runtime: false}, + {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, {:fast_tls, "~> 1.1.22"}, {:fast_xml, "~> 1.1.53"}, {:fast_yaml, "~> 1.0"}, From e0bb90065155ca5a6fc2fab7f4d474c9754d03f7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 20:17:56 +0100 Subject: [PATCH 0926/1302] mod_muc_admin: Add forgotten support to set enable_hats room option --- src/mod_muc_admin.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 6c4c9d3a4..f9592d539 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1860,6 +1860,7 @@ change_option(Option, Value, Config) -> anonymous -> Config#config{anonymous = Value}; captcha_protected -> Config#config{captcha_protected = Value}; description -> Config#config{description = Value}; + enable_hats -> Config#config{enable_hats = Value}; lang -> Config#config{lang = Value}; logging -> Config#config{logging = Value}; mam -> Config#config{mam = Value}; From 9827ad43e4338c40f51699705b5ff74bd12aba2c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 20:15:40 +0100 Subject: [PATCH 0927/1302] mod_muc_admin: Verify room option value before setting it (#4337) --- src/mod_muc_admin.erl | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index f9592d539..36d37b899 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1722,9 +1722,8 @@ format_room_option(OptionString, ValueString) -> password -> ValueString; subject ->ValueString; subject_author ->ValueString; - presence_broadcast ->misc:expr_to_term(ValueString); - max_users -> binary_to_integer(ValueString); - voice_request_min_interval -> binary_to_integer(ValueString); + max_users -> try_convert_integer(Option, ValueString); + voice_request_min_interval -> try_convert_integer(Option, ValueString); vcard -> ValueString; vcard_xupdate when ValueString /= <<"undefined">>, ValueString /= <<"external">> -> @@ -1735,10 +1734,35 @@ format_room_option(OptionString, ValueString) -> [parse_affiliation_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; subscribers -> [parse_subscription_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; - _ -> misc:binary_to_atom(ValueString) + allow_private_messages_from_visitors when + (ValueString == <<"anyone">>) or + (ValueString == <<"moderators">>) or + (ValueString == <<"nobody">>) -> binary_to_existing_atom(ValueString); + allowpm when + (ValueString == <<"anyone">>) or + (ValueString == <<"participants">>) or + (ValueString == <<"moderators">>) or + (ValueString == <<"none">>) -> binary_to_existing_atom(ValueString); + presence_broadcast when + (ValueString == <<"participant">>) or + (ValueString == <<"moderator">>) or + (ValueString == <<"visitor">>) -> binary_to_existing_atom(ValueString); + _ when ValueString == <<"true">> -> true; + _ when ValueString == <<"false">> -> false; + _ -> throw_error(Option, ValueString) end, {Option, Value}. +try_convert_integer(Option, ValueString) -> + try binary_to_integer(ValueString) of + I when is_integer(I) -> I + catch _:badarg -> + throw_error(Option, ValueString) + end. + +throw_error(O, V) -> + throw({error, "Invalid value for that option", O, V}). + parse_affiliation_string(String) -> {Type, JidS} = case String of <<"owner:", Jid/binary>> -> {owner, Jid}; From aa612463cc93e48134ebce6a66f8374618cdcc39 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 20:16:43 +0100 Subject: [PATCH 0928/1302] mod_muc: Document MUC room option vcard_xupdate --- src/mod_muc.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ca9d21800..278b72014 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1692,6 +1692,11 @@ mod_doc() -> " -", " work: true", " street: Elm Street"]}}, + {vcard_xupdate, + #{value => "undefined | external | AvatarHash", + desc => + ?T("Set the hash of the avatar image. " + "The default value is 'undefined'.")}}, {cleanup_affiliations_on_start, #{value => "true | false", note => "added in 22.05", From 6e4ac0c501ca4b5d0f7918980d5910dccb96252d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 15 Jan 2025 20:49:53 +0100 Subject: [PATCH 0929/1302] Fix recent commit to work with Erlang/OTP 20 --- src/mod_muc_admin.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 36d37b899..a260d43b2 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1737,16 +1737,16 @@ format_room_option(OptionString, ValueString) -> allow_private_messages_from_visitors when (ValueString == <<"anyone">>) or (ValueString == <<"moderators">>) or - (ValueString == <<"nobody">>) -> binary_to_existing_atom(ValueString); + (ValueString == <<"nobody">>) -> binary_to_existing_atom(ValueString, utf8); allowpm when (ValueString == <<"anyone">>) or (ValueString == <<"participants">>) or (ValueString == <<"moderators">>) or - (ValueString == <<"none">>) -> binary_to_existing_atom(ValueString); + (ValueString == <<"none">>) -> binary_to_existing_atom(ValueString, utf8); presence_broadcast when (ValueString == <<"participant">>) or (ValueString == <<"moderator">>) or - (ValueString == <<"visitor">>) -> binary_to_existing_atom(ValueString); + (ValueString == <<"visitor">>) -> binary_to_existing_atom(ValueString, utf8); _ when ValueString == <<"true">> -> true; _ when ValueString == <<"false">> -> false; _ -> throw_error(Option, ValueString) From e7035f3235605a0b5ab07b90ebf0ec711a3ad910 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 20 Jan 2025 09:10:57 +0300 Subject: [PATCH 0930/1302] Fix handling of 3PI events in mod_matrix_gw_room --- src/mod_matrix_gw_room.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 90ecfec3f..07b5a74b0 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -1232,8 +1232,14 @@ check_event_auth(Event, StateMap, Data) -> <<"join">>}}}} -> case Event#event.type of ?ROOM_3PI -> - %% TODO - {todo, Event}; + SenderLevel = get_user_power_level(Event#event.sender, StateMap, Data), + InviteLevel = + case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + {ok, #event{json = #{<<"content">> := #{<<"invite">> := S}}}} -> + get_int(S); + _ -> 0 + end, + SenderLevel >= InviteLevel; _ -> case check_event_power_level( Event, StateMap, Data) of From 9be76cce9ec51c7c93a6baa19bb14fcf95eef091 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 20 Jan 2025 18:18:22 +0100 Subject: [PATCH 0931/1302] Fix support for compiling in VSCode --- .vscode/launch.json | 2 +- .vscode/relive.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6725568c3..78cdf45ae 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,7 +19,7 @@ "cwd": "." }, { - "name": "Relive", + "name": "Relive (VSCode)", "type": "erlang", "request": "launch", "runinterminal": [ diff --git a/.vscode/relive.sh b/.vscode/relive.sh index c7db2f74e..b10b83fb0 100755 --- a/.vscode/relive.sh +++ b/.vscode/relive.sh @@ -1,6 +1,6 @@ [ ! -f Makefile ] \ && ./autogen.sh \ && ./configure --with-rebar=rebar3 \ - && make deps + && make make relive From cab96d21565ab57f080fccb31312b5a65071ce0b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Jan 2025 11:36:38 +0100 Subject: [PATCH 0932/1302] mod_shared_roster: Remove unnecesary double call to split_grouphost which was added in 5b0f0d8 --- src/mod_shared_roster.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 3c454b0df..479956d59 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -439,8 +439,7 @@ get_group_opts(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), get_group_opts_int(Host, Group). -get_group_opts_int(Host1, Group1) -> - {Host, Group} = split_grouphost(Host1, Group1), +get_group_opts_int(Host, Group) -> Mod = gen_mod:db_mod(Host, ?MODULE), Res = case use_cache(Mod, Host) of true -> From 20a77cb9c7f4851505f209027b000c817dd0c6b4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Jan 2025 11:18:50 +0100 Subject: [PATCH 0933/1302] acl: Fixed bug matching the acl "shared_group: NAME" This config triggered a crash at client login: acl: tech: shared_group: techteam access_rules: announce: allow: tech configure: allow: tech --- src/acl.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/acl.erl b/src/acl.erl index 9daa373b3..eaa0aa50f 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -106,8 +106,8 @@ match_acl(_Host, {shared_group, {G, H}}, #{usr := {U, S, _}}) -> undefined -> false; Mod -> Mod:is_user_in_group({U, S}, G, H) end; -match_acl(Host, {shared_group, G}, Map) -> - match_acl(Host, {shared_group, {G, Host}}, Map); +match_acl(Host, {shared_group, G}, #{usr := {_, S, _}} = Map) -> + match_acl(Host, {shared_group, {G, S}}, Map); match_acl(_Host, {user_regexp, {UR, S1}}, #{usr := {U, S2, _}}) -> S1 == S2 andalso match_regexp(U, UR); match_acl(_Host, {user_regexp, UR}, #{usr := {U, S, _}}) -> From 133d52d04023d603283a7796c46bc40ffc7cd3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 22 Jan 2025 14:12:32 +0100 Subject: [PATCH 0934/1302] Stop processing other handlers in mod_s2s_bidi:s2s_in_handle_info This should fix issue reported in #4344 --- src/mod_s2s_bidi.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_s2s_bidi.erl b/src/mod_s2s_bidi.erl index 06a6b92aa..7b9556028 100644 --- a/src/mod_s2s_bidi.erl +++ b/src/mod_s2s_bidi.erl @@ -125,7 +125,7 @@ s2s_out_packet(State, _Pkt) -> State. s2s_in_handle_info(State, {route, Pkt}) when ?is_stanza(Pkt) -> - ejabberd_s2s_in:send(State, Pkt); + {stop, ejabberd_s2s_in:send(State, Pkt)}; s2s_in_handle_info(State, _Info) -> State. From a19ab9f4e3586d6298f739b88b9e624bc282f815 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 25 Jan 2025 22:21:43 +0100 Subject: [PATCH 0935/1302] Update xmpp to bring SSDP to XEP version 0.4 --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 97487b84c..fb9a31dbb 100644 --- a/mix.exs +++ b/mix.exs @@ -144,7 +144,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, "~> 1.9.3"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", override: true}, {:yconf, "~> 1.0.17"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 9842b9c91..8ce6a75c1 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.9.3", "fa4f9eda52d789f4b9f5ff11fed17da2c43fa8d01ed7c4fadcb09ab602be9c18", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "d5c96fcb2cf8a8da43a5f81935b2e43fa9b959e1626fcecf082f4af9139bdbd2"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", [ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"]}, "yconf": {:hex, :yconf, "1.0.17", "dcf242e27f3fc5d0743d6b8175dd39bc14a1f4ed7e6ea986366a44a6ff3b2a3a", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "dd2892923241449a46cc8457b9ec0fb14030700735a5885955677c735c341a25"}, } diff --git a/rebar.config b/rebar.config index bc6a258c5..0e0e3154f 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", {tag, "1.9.3"}}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 7a9e810bf..e96263cb9 100644 --- a/rebar.lock +++ b/rebar.lock @@ -24,7 +24,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.9.3">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, + 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.17">>},0}]}. [ {pkg_hash,[ @@ -53,7 +56,6 @@ {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"FA4F9EDA52D789F4B9F5FF11FED17DA2C43FA8D01ED7C4FADCB09AB602BE9C18">>}, {<<"yconf">>, <<"DCF242E27F3FC5D0743D6B8175DD39BC14A1F4ED7E6EA986366A44A6FF3B2A3A">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -81,6 +83,5 @@ {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"D5C96FCB2CF8A8DA43A5F81935B2E43FA9B959E1626FCECF082F4AF9139BDBD2">>}, {<<"yconf">>, <<"DD2892923241449A46CC8457B9EC0FB14030700735A5885955677C735C341A25">>}]} ]. From eca3204e824a687b6f941b05fca61ee6125cee3b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 28 Jan 2025 19:37:49 +0100 Subject: [PATCH 0936/1302] mod_private: Don't crash on invalid bookmarks Catch failures while decoding the conference bookmark element. --- src/mod_private.erl | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 3f73a3bca..950b7d818 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -447,17 +447,20 @@ pubsub_delete_item(_, _, _, _, _) -> -spec pubsub_item_to_storage_bookmark(#pubsub_item{}) -> {true, bookmark_conference()} | false. pubsub_item_to_storage_bookmark(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}) -> - case xmpp:decode(B) of + try #pep_bookmarks_conference{name = Name, autojoin = AutoJoin, nick = Nick, - password = Password} -> - try jid:decode(Id) of - #jid{} = Jid -> - {true, #bookmark_conference{jid = Jid, name = Name, autojoin = AutoJoin, nick = Nick, - password = Password}} - catch _:_ -> - false - end; - _ -> + password = Password} = xmpp:decode(B), + #jid{} = Jid = jid:decode(Id), + {true, #bookmark_conference{jid = Jid, name = Name, + autojoin = AutoJoin, nick = Nick, + password = Password}} + catch + _:{xmpp_codec, Why} -> + ?DEBUG("Failed to decode bookmark element (~ts): ~ts", + [Id, xmpp:format_error(Why)]), + false; + _:{bad_jid, _} -> + ?DEBUG("Failed to decode bookmark ID (~ts)", [Id]), false end; pubsub_item_to_storage_bookmark(_) -> From 20a0051578df86e4a7b30aaca9c738871cd8a30b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 31 Jan 2025 13:49:22 +0100 Subject: [PATCH 0937/1302] mod_private: Handle invalid PEP-native bookmarks Don't crash while attempting to convert invalid XEP-0402 conference bookmark elements. --- src/mod_private.erl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 950b7d818..ab8b0aa2c 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -490,15 +490,19 @@ storage_bookmark_to_xmpp_bookmark(#bookmark_conference{name = Name, autojoin = A -spec pubsub_item_to_map(#pubsub_item{}, map()) -> map(). pubsub_item_to_map(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}, Map) -> - case xmpp:decode(B) of - #pep_bookmarks_conference{} = B2 -> - try jid:decode(Id) of - #jid{} = Jid -> - maps:put(jid:tolower(Jid), B2#pep_bookmarks_conference{extensions = undefined}, Map) - catch _:_ -> - Map - end; - _ -> + try {xmpp:decode(B), jid:decode(Id)} of + {#pep_bookmarks_conference{} = B1, #jid{} = Jid} -> + B2 = B1#pep_bookmarks_conference{extensions = undefined}, + maps:put(jid:tolower(Jid), B2, Map); + {_, _} -> + Map + catch + _:{xmpp_codec, Why} -> + ?DEBUG("Failed to decode bookmark element (~ts): ~ts", + [Id, xmpp:format_error(Why)]), + Map; + _:{bad_jid, _} -> + ?DEBUG("Failed to decode bookmark ID (~ts)", [Id]), Map end; pubsub_item_to_map(_, Map) -> From e134d7f0b42888ee09edfee0d74820b29bc80b2b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 31 Jan 2025 13:51:37 +0100 Subject: [PATCH 0938/1302] mod_private: Don't warn on conversion errors Clients publish invalid bookmark elements in practice (e.g., bookmarks with an empty element). The server admin can't address that issue, so don't spam the log with warnings. --- src/mod_private.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index ab8b0aa2c..3244f3515 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -340,8 +340,8 @@ publish_pep_native_bookmarks(JID, Data) -> #bookmark_storage{conference = C} -> C; _ -> [] catch _:{xmpp_codec, Why} -> - ?WARNING_MSG("Failed to decode bookmarks of ~ts: ~ts", - [jid:encode(JID), xmpp:format_error(Why)]), + ?DEBUG("Failed to decode bookmarks of ~ts: ~ts", + [jid:encode(JID), xmpp:format_error(Why)]), [] end, PubOpts = [{persist_items, true}, {access_model, whitelist}, {max_items, max}, {notify_retract,true}, {notify_delete,true}, {send_last_published_item, never}], From 76baf58d5d1158a1f0d02484db807a041a6f96fc Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 31 Jan 2025 14:23:45 +0100 Subject: [PATCH 0939/1302] mod_private: Improve exception handling Properly isolate the code that should be subject to exception handling. --- src/mod_private.erl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/mod_private.erl b/src/mod_private.erl index 3244f3515..145edefcf 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -447,13 +447,15 @@ pubsub_delete_item(_, _, _, _, _) -> -spec pubsub_item_to_storage_bookmark(#pubsub_item{}) -> {true, bookmark_conference()} | false. pubsub_item_to_storage_bookmark(#pubsub_item{itemid = {Id, _}, payload = [#xmlel{} = B | _]}) -> - try - #pep_bookmarks_conference{name = Name, autojoin = AutoJoin, nick = Nick, - password = Password} = xmpp:decode(B), - #jid{} = Jid = jid:decode(Id), - {true, #bookmark_conference{jid = Jid, name = Name, - autojoin = AutoJoin, nick = Nick, - password = Password}} + try {xmpp:decode(B), jid:decode(Id)} of + {#pep_bookmarks_conference{name = Name, autojoin = AutoJoin, + nick = Nick, password = Password}, + #jid{} = Jid} -> + {true, #bookmark_conference{jid = Jid, name = Name, + autojoin = AutoJoin, nick = Nick, + password = Password}}; + {_, _} -> + false catch _:{xmpp_codec, Why} -> ?DEBUG("Failed to decode bookmark element (~ts): ~ts", From a4062f6ac0f07f788bbd1dd45753da2362c7a41c Mon Sep 17 00:00:00 2001 From: Matthew Stickney Date: Sat, 18 Jan 2025 13:57:26 -0500 Subject: [PATCH 0940/1302] mod_privilege: Accept non-privileged IQs from privileged components. mod_privilege current drops any non-privileged IQ received from a component with an error about it not being properly wrapped. While this might represent a mistake on the part of the component, it means that well- behaved components can no longer send non-privileged IQs (something they normally can do if mod_privilege isn't enabled). Since mod_privilege is intended to grant additional permissions, and not remove existing ones, route non-privileged IQs received from the component normally. This also removes the special-case for roster-query IQ stanzas, since those are also non-privileged and will be routed along with any other non-privileged IQ packet. This mirrors the privileged-IQ/everything-else structure of the XEP, which defined the handling of privileged IQ stanzas and leaves all other IQ stanzas as defined in their own specs. To make this clearer, the predicate function now returns distinct results indicating privileged IQs, non-privileged IQs, and error conditions, rather than treating non-privilege IQs as an error that gets handled by routing the packet normally. --- src/mod_privilege.erl | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index bdf512f34..862f0c0be 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -272,7 +272,7 @@ component_send_packet({#iq{from = From, Permissions = get_permissions(ServerHost), Result = case {maps:find(Host, Permissions), get_iq_encapsulated_details(IQ)} of - {{ok, Access}, {ok, EncapType, EncapNs, EncapFrom, EncIq}} + {{ok, Access}, {privileged_iq, EncapType, EncapNs, EncapFrom, EncIq}} when (EncapType == Type) and ((EncapFrom == undefined) or (EncapFrom == To)) -> NsPermissions = proplists:get_value(iq, Access, []), Permission = @@ -297,20 +297,18 @@ component_send_packet({#iq{from = From, %% Component is disconnected ?INFO_MSG("IQ not forwarded: Component seems disconnected", []), drop; - {_, {ok, E, _, _, _}} when E /= Type -> + {_, {privileged_iq, E, _, _, _}} when E /= Type -> ?INFO_MSG("IQ not forwarded: The encapsulated IQ stanza type=~p " "does not match the top-level IQ stanza type=~p", [E, Type]), drop; - {_, {ok, _, _, EF, _}} when (EF /= undefined) and (EF /= To) -> + {_, {privileged_iq, _, _, EF, _}} when (EF /= undefined) and (EF /= To) -> ?INFO_MSG("IQ not forwarded: The FROM attribute in the encapsulated " "IQ stanza and the TO in top-level IQ stanza do not match", []), drop; - {_, {error, no_privileged_iq, _Err}} -> - ?INFO_MSG("IQ not forwarded: Component tried to send not wrapped IQ stanza.", []), - drop; - {_, {error, roster_query, _Err}} -> + {_, {unprivileged_iq}} -> + ?DEBUG("Component ~ts sent a not-wrapped IQ stanza, routing it as-is.", [From#jid.lserver]), IQ; {_, {error, ErrType, _Err}} -> ?INFO_MSG("IQ not forwarded: Component tried to send not valid IQ stanza: ~p.", @@ -566,7 +564,8 @@ forward_message(#message{to = To} = Msg) -> %% @format-begin -spec get_iq_encapsulated_details(iq()) -> - {ok, iq_type(), binary(), jid(), iq()} | + {privileged_iq, iq_type(), binary(), jid(), iq()} | + {unprivileged_iq} | {error, Why :: atom(), stanza_error()}. get_iq_encapsulated_details(#iq{sub_els = [IqSub]} = Msg) -> Lang = xmpp:get_lang(Msg), @@ -575,21 +574,9 @@ get_iq_encapsulated_details(#iq{sub_els = [IqSub]} = Msg) -> [IqSubSub] = xmpp:get_els(IqSub), [Element] = xmpp:get_els(IqSubSub), Ns = xmpp:get_ns(Element), - {ok, EncapsulatedType, Ns, From, EncIq}; + {privileged_iq, EncapsulatedType, Ns, From, EncIq}; _ -> - try xmpp:try_subtag(Msg, #roster_query{}) of - #roster_query{} -> - {error, roster_query, xmpp:err_bad_request()}; - _ -> - Txt = ?T("No element found"), - Err = xmpp:err_bad_request(Txt, Lang), - {error, no_privileged_iq, Err} - catch - _:{xmpp_codec, Why} -> - Txt = xmpp:io_format_error(Why), - Err = xmpp:err_bad_request(Txt, Lang), - {error, codec_error, Err} - end + {unprivileged_iq} catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), From 64142de4fee88bc4c48289f8433cf3bd323e24b6 Mon Sep 17 00:00:00 2001 From: Matthew Stickney Date: Tue, 28 Jan 2025 14:50:44 -0500 Subject: [PATCH 0941/1302] Don't rewrite "self-addressed" privileged IQs as results. process_privilege_iq is meant to rewrite the result of a privileged IQ into the forwarded form required by XEP-0356 so it can be routed back to the original privileged requester. It checks whether the impersonated JID (`ReplacedJid`) of the original request matches the recipient of the IQ being processed to determine if this is a response to a a privileged IQ (assuming it has privileged-IQ metadata attached). Unfortunately, it doesn't check the packet type, and this check will also match a privileged-IQ _request_ that is being sent to the same user that's being impersonated. This results in the request itself being rewritten and forwarded back to the sending component, instead of being processed and having the result send back. Instead, just check for IQ results (either a regular result or an error), and as long as it is marked as being a response to a privileged-IQ, always rewrite it and forward it to the sending component. There's no circumstance under which we _shouldn't_ forward a privileged-IQ response, so we don't need to be tricky about checking whether impersonated-user and recipient match. --- src/ejabberd_router.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index 2d80314c7..de088d8ba 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -408,9 +408,9 @@ do_route(OrigPacket1) -> %% @format-begin process_privilege_iq(Packet) -> - To = xmpp:get_to(Packet), + Type = xmpp:get_type(Packet), case xmpp:get_meta(Packet, privilege_iq, none) of - {OriginalId, OriginalHost, ReplacedJid} when ReplacedJid == To -> + {OriginalId, OriginalHost, ReplacedJid} when (Type == result) or (Type == error) -> Privilege = #privilege{forwarded = #forwarded{sub_els = [Packet]}}, #iq{type = xmpp:get_type(Packet), id = OriginalId, From e34b6f4204e67d55967b460ebf944a11410ec90b Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 Jan 2025 23:37:07 +0100 Subject: [PATCH 0942/1302] Update odbc:connection_reference mention to fix dialyzer with Erlang/OTP 28 --- mix.exs | 16 +--------------- rebar.config | 2 +- src/ejabberd_sql.erl | 19 +++++++++++++++---- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/mix.exs b/mix.exs index fb9a31dbb..af72070c9 100644 --- a/mix.exs +++ b/mix.exs @@ -79,20 +79,6 @@ defmodule Ejabberd.MixProject do end end - defp if_type_exported(module, typeDef, okResult) do - try do - {:ok, concrete} = :dialyzer_utils.get_core_from_beam(:code.which(module)) - {:ok, types} = :dialyzer_utils.get_record_and_type_info(concrete) - if Map.has_key?(types, typeDef) do - okResult - else - [] - end - rescue - _ -> [] - end - end - defp erlc_options do # Use our own includes + includes from all dependencies includes = ["include", deps_include()] @@ -113,7 +99,7 @@ defmodule Ejabberd.MixProject do if_version_below(~c"25", [{:d, :OTP_BELOW_25}]) ++ if_version_below(~c"26", [{:d, :OTP_BELOW_26}]) ++ if_version_below(~c"27", [{:d, :OTP_BELOW_27}]) ++ - if_type_exported(:odbc, {:opaque, :connection_reference, 0}, [{:d, :ODBC_HAS_TYPES}]) + if_version_below(~c"28", [{:d, :OTP_BELOW_28}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] end diff --git a/rebar.config b/rebar.config index 0e0e3154f..3b0942c87 100644 --- a/rebar.config +++ b/rebar.config @@ -140,6 +140,7 @@ {if_version_below, "25", {d, 'OTP_BELOW_25'}}, {if_version_below, "26", {d, 'OTP_BELOW_26'}}, {if_version_below, "27", {d, 'OTP_BELOW_27'}}, + {if_version_below, "28", {d, 'OTP_BELOW_28'}}, {if_var_false, debug, no_debug_info}, {if_var_true, debug, debug_info}, {if_var_true, elixir, {d, 'ELIXIR_ENABLED'}}, @@ -147,7 +148,6 @@ {if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATEWAY_WORKAROUND'}}, {if_var_true, sip, {d, 'SIP'}}, {if_var_true, stun, {d, 'STUN'}}, - {if_type_exported, {odbc, {opaque, connection_reference, 0}}, {d, 'ODBC_HAS_TYPES'}}, {src_dirs, [src, {if_rebar3, sql}, {if_var_true, tools, tools}]}]}. diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index e6e06bc40..36db77fee 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -70,10 +70,21 @@ -export([connecting/2, connecting/3, session_established/2, session_established/3]). --ifdef(ODBC_HAS_TYPES). - -type(odbc_connection_reference() :: odbc:connection_reference()). +-ifdef(OTP_BELOW_28). +-ifdef(OTP_BELOW_26). +%% OTP 25 or lower +-type(odbc_connection_reference() :: pid()). +-type(db_ref_pid() :: pid()). -else. - -type(odbc_connection_reference() :: pid()). +%% OTP 26 or 27 +-type(odbc_connection_reference() :: odbc:connection_reference()). +-type(db_ref_pid() :: pid()). +-endif. +-else. +%% OTP 28 or higher +-nominal(odbc_connection_reference() :: odbc:connection_reference()). +-nominal(db_ref_pid() :: pid()). +-dialyzer([no_opaque_union]). -endif. -include("logger.hrl"). @@ -81,7 +92,7 @@ -include("ejabberd_stacktrace.hrl"). -record(state, - {db_ref :: undefined | pid() | odbc_connection_reference(), + {db_ref :: undefined | db_ref_pid() | odbc_connection_reference(), db_type = odbc :: pgsql | mysql | sqlite | odbc | mssql, db_version :: undefined | non_neg_integer() | {non_neg_integer(), atom(), non_neg_integer()}, reconnect_count = 0 :: non_neg_integer(), From 0732603a4e1374def9c9e74138942f30f460b2d6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 24 Jan 2025 18:50:48 +0100 Subject: [PATCH 0943/1302] Disable opaque_union dialyzer warnings as workaround for dialyzer with Erlang/OTP 28 See https://github.com/erlang/otp/commit/5dab31e9f991531f2e0de44b896229b686f753f9 --- src/ejabberd_config.erl | 4 ++++ src/ejabberd_shaper.erl | 4 ++++ src/ejabberd_websocket.erl | 5 +++++ src/mod_muc_room.erl | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 8b55be2db..c7849792b 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -68,6 +68,10 @@ -optional_callbacks([globals/0]). +-ifndef(OTP_BELOW_28). +-dialyzer([no_opaque_union]). +-endif. + %%%=================================================================== %%% API %%%=================================================================== diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl index c47d72dbd..af0b3faba 100644 --- a/src/ejabberd_shaper.erl +++ b/src/ejabberd_shaper.erl @@ -36,6 +36,10 @@ -export_type([shaper/0, shaper_rule/0, shaper_rate/0]). +-ifndef(OTP_BELOW_28). +-dialyzer([no_opaque_union]). +-endif. + %%%=================================================================== %%% API %%%=================================================================== diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 43b6673d4..dbcf8e2e0 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -62,6 +62,11 @@ ?AC_ALLOW_HEADERS, ?AC_MAX_AGE]). -define(HEADER, [?CT_XML, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]). +-ifndef(OTP_BELOW_28). +-dialyzer([no_opaque_union]). +-endif. + + is_valid_websocket_upgrade(_Path, Headers) -> HeadersToValidate = [{'Upgrade', <<"websocket">>}, {'Connection', ignore}, diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f9fb9f882..f48e58a94 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -116,6 +116,10 @@ -callback search_affiliation(binary(), binary(), binary(), affiliation()) -> {ok, [{ljid(), {affiliation(), binary()}}]} | {error, any()}. +-ifndef(OTP_BELOW_28). +-dialyzer([no_opaque_union]). +-endif. + %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- From 22b3d0e49f7b2d7d9974e66c41591c837922b465 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Jan 2025 12:22:31 +0100 Subject: [PATCH 0944/1302] get_auto_url: Don't build auto URL if port is unix domain socket (#4345) --- src/ejabberd_captcha.erl | 17 ++++++++++------- src/mod_host_meta.erl | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl index 794041a19..d1d62e59b 100644 --- a/src/ejabberd_captcha.erl +++ b/src/ejabberd_captcha.erl @@ -454,12 +454,15 @@ get_prog_name() -> maybe_warning_norequesthandler() -> Host = hd(ejabberd_option:hosts()), - URL = get_auto_url(any, ?MODULE, Host), - case URL of - undefined -> - ?WARNING_MSG("The option captcha_cmd is configured, " - "but there is NO request_handler in listen option " - "configured with ejabberd_captcha. Please check " + AutoURL = get_auto_url(any, ?MODULE, Host), + ManualURL = ejabberd_option:captcha_url(), + case (AutoURL == undefined) and not is_binary(ManualURL) of + true -> + ?CRITICAL_MSG("The option captcha_cmd is configured " + "and captcha_url is set to auto, " + "but I couldn't find a request_handler in listen option " + "configured with ejabberd_captcha and integer port. " + "Please setup the URL with option captcha_url, see " "https://docs.ejabberd.im/admin/configuration/basic/#captcha", []); _ -> @@ -523,7 +526,7 @@ find_handler_port_path(Tls, Module) -> fun({{Port, _, _}, ejabberd_http, #{tls := ThisTls, request_handlers := Handlers}}) - when (Tls == any) or (Tls == ThisTls) -> + when is_integer(Port) and ((Tls == any) or (Tls == ThisTls)) -> case lists:keyfind(Module, 2, Handlers) of false -> false; {Path, Module} -> {true, {ThisTls, Port, Path}} diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index 80b4dbe84..c99da63a2 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -168,7 +168,7 @@ find_handler_port_path(Tls, Module) -> fun({{Port, _, _}, ejabberd_http, #{tls := ThisTls, request_handlers := Handlers}}) - when (Tls == any) or (Tls == ThisTls) -> + when is_integer(Port) and ((Tls == any) or (Tls == ThisTls)) -> case lists:keyfind(Module, 2, Handlers) of false -> false; {Path, Module} -> {true, {ThisTls, Port, Path}} From 4f90d1a0d97f2194c7d191ea76f98c1a3b1c7ad5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Jan 2025 12:26:20 +0100 Subject: [PATCH 0945/1302] Document that XEP-0474 0.4.0 was recently upgraded Thanks to https://github.com/processone/xmpp/pull/99 --- src/ejabberd.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 579c9c628..89cbd229c 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -42,7 +42,7 @@ -protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 424, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). --protocol({xep, 474, '0.3.0', '24.02', "complete", ""}). +-protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.xx"}). -protocol({xep, 485, '0.2.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, From cae40c3f725dd0beb811e0e763e2a85590653c69 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Jan 2025 16:58:16 +0100 Subject: [PATCH 0946/1302] mod_http_api: Define the option type that opt_type.sh cannot derive itself --- src/mod_http_api.erl | 2 ++ src/mod_http_api_opt.erl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 219267ef9..b2fe7b7c0 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -571,6 +571,8 @@ mod_opt_type(default_version) -> end end)). +-spec mod_options(binary()) -> [{default_version, integer()}]. + mod_options(_) -> [{default_version, ?DEFAULT_API_VERSION}]. diff --git a/src/mod_http_api_opt.erl b/src/mod_http_api_opt.erl index 7ab489b72..326c53e02 100644 --- a/src/mod_http_api_opt.erl +++ b/src/mod_http_api_opt.erl @@ -5,7 +5,7 @@ -export([default_version/1]). --spec default_version(gen_mod:opts() | global | binary()) -> any(). +-spec default_version(gen_mod:opts() | global | binary()) -> integer(). default_version(Opts) when is_map(Opts) -> gen_mod:get_opt(default_version, Opts); default_version(Host) -> From a9c7bf97efc137795def1da6d4595c778c37dfda Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Feb 2025 13:38:03 +0100 Subject: [PATCH 0947/1302] Add define_macro to globals() because it's useless inside host_config --- src/ejabberd_options.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 673de29d7..6c0ed08f2 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -750,6 +750,7 @@ globals() -> certfiles, cluster_backend, cluster_nodes, + define_macro, domain_balancing, ext_api_path_oauth, fqdn, From 480e2442ee5313910533e8f7e0d8d10d48879d0c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 3 Feb 2025 12:07:02 +0100 Subject: [PATCH 0948/1302] Improve define_macro option validator --- src/ejabberd_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 6c0ed08f2..6caee7624 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -139,7 +139,7 @@ opt_type(default_db) -> opt_type(default_ram_db) -> econf:enum([mnesia, sql, redis]); opt_type(define_macro) -> - econf:any(); + econf:map(econf:binary(), econf:any(), [unique]); opt_type(disable_sasl_scram_downgrade_protection) -> econf:bool(); opt_type(disable_sasl_mechanisms) -> From 07b102bb806d4e8b43e7124b11f8562b4f53948b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Feb 2025 13:09:14 +0100 Subject: [PATCH 0949/1302] Docs: Fix markdown of some toplevel options --- src/ejabberd_options_doc.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 19e7773da..ae2d7538b 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -472,8 +472,8 @@ doc() -> note => "improved in 23.01", desc => ?T("Full path to a script that generates _`basic.md#captcha|CAPTCHA`_ images. " - "'@VERSION@' is replaced with ejabberd version number in 'XX.YY' format. " - "'@SEMVER@' is replaced with ejabberd version number in semver format " + "The keyword '@VERSION@' is replaced with ejabberd version number in 'XX.YY' format. " + "The keyword '@SEMVER@' is replaced with ejabberd version number in semver format " "when compiled with Elixir's mix, or XX.YY format otherwise. " "Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support. " "There is no default value: when this option is not " @@ -845,18 +845,18 @@ doc() -> "as alternatives for getting the JID, where 'Attr' is " "an LDAP attribute which holds the user's part of the JID and " "'AttrFormat' must contain one and only one pattern variable " - "\"%u\" which will be replaced by the user's part of the JID. " - "For example, \"%u@example.org\". If the value is in the form " - "of '[Attr]' then 'AttrFormat' is assumed to be \"%u\".")}}, + "'\"%u\"' which will be replaced by the user's part of the JID. " + "For example, '\"%u@example.org\"'. If the value is in the form " + "of '[Attr]' then 'AttrFormat' is assumed to be '\"%u\"'.")}}, {ldap_filter, #{value => ?T("Filter"), desc => ?T("An LDAP filter as defined in " "https://tools.ietf.org/html/rfc4515[RFC4515]. " "There is no default value. Example: " - "\"(&(objectClass=shadowAccount)(memberOf=XMPP Users))\". " + "'\"(&(objectClass=shadowAccount)(memberOf=XMPP Users))\"'. " "NOTE: don't forget to close brackets and don't use superfluous " - "whitespaces. Also you must not use \"uid\" attribute in the " + "whitespaces. Also you must not use '\"uid\"' attribute in the " "filter because this attribute will be appended to the filter " "automatically.")}}, {ldap_dn_filter, @@ -866,11 +866,11 @@ doc() -> "filter. The filter performs an additional LDAP lookup to make " "the complete result. This is useful when you are unable to " "define all filter rules in 'ldap_filter'. You can define " - "\"%u\", \"%d\", \"%s\" and \"%D\" pattern variables in 'Filter': " - "\"%u\" is replaced by a user's part of the JID, \"%d\" is " - "replaced by the corresponding domain (virtual host), all \"%s\" " + "'\"%u\"', '\"%d\"', '\"%s\"' and '\"%D\"' pattern variables in 'Filter: " + "\"%u\"' is replaced by a user's part of the JID, '\"%d\"' is " + "replaced by the corresponding domain (virtual host), all '\"%s\"' " "variables are consecutively replaced by values from the attributes " - "in 'FilterAttrs' and \"%D\" is replaced by Distinguished Name from " + "in 'FilterAttrs' and '\"%D\"' is replaced by Distinguished Name from " "the result set. There is no default value, which means the " "result is not filtered. WARNING: Since this filter makes " "additional LDAP lookups, use it only as the last resort: " From d834a9c1c1786678af4bb51e17ab750473f19164 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Feb 2025 13:36:56 +0100 Subject: [PATCH 0950/1302] Delete ejabberd.cfg which apparently isn't needed anymore --- test/ejabberd_SUITE_data/ejabberd.cfg | 181 -------------------------- 1 file changed, 181 deletions(-) delete mode 100644 test/ejabberd_SUITE_data/ejabberd.cfg diff --git a/test/ejabberd_SUITE_data/ejabberd.cfg b/test/ejabberd_SUITE_data/ejabberd.cfg deleted file mode 100644 index 251f23118..000000000 --- a/test/ejabberd_SUITE_data/ejabberd.cfg +++ /dev/null @@ -1,181 +0,0 @@ -{loglevel, 4}. -{hosts, ["localhost", - "mnesia.localhost", - "mysql.localhost", - "mssql.localhost", - "pgsql.localhost", - "sqlite.localhost", - "extauth.localhost", - "ldap.localhost"]}. -{define_macro, 'CERTFILE', "cert.pem"}. -{listen, - [ - {5222, ejabberd_c2s, [ - {access, c2s}, - {shaper, c2s_shaper}, - starttls, zlib, - {certfile, 'CERTFILE'}, - {max_stanza_size, 65536} - ]}, - {5269, ejabberd_s2s_in, [ - {shaper, s2s_shaper}, - {max_stanza_size, 131072} - ]}, - {5280, ejabberd_http, [ - captcha - ]} - ]}. -{shaper, normal, {maxrate, 1000}}. -{shaper, fast, {maxrate, 50000}}. -{max_fsm_queue, 1000}. -{acl, local, {user_regexp, ""}}. -{access, max_user_sessions, [{10, all}]}. -{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. -{access, local, [{allow, local}]}. -{access, c2s, [{deny, blocked}, - {allow, all}]}. -{access, c2s_shaper, [{none, admin}, - {normal, all}]}. -{access, s2s_shaper, [{fast, all}]}. -{access, announce, [{allow, admin}]}. -{access, configure, [{allow, admin}]}. -{access, muc_admin, [{allow, admin}]}. -{access, muc_create, [{allow, local}]}. -{access, muc, [{allow, all}]}. -{access, pubsub_createnode, [{allow, local}]}. -{access, register, [{allow, all}]}. -{registration_timeout, infinity}. -{language, "en"}. -{modules, - [ - {mod_adhoc, []}, - {mod_configure, []}, - {mod_disco, []}, - {mod_ping, []}, - {mod_proxy65, []}, - {mod_register, [ - {welcome_message, {"Welcome!", - "Hi.\nWelcome to this XMPP server."}} - ]}, - {mod_stats, []}, - {mod_time, []}, - {mod_version, []} -]}. -{host_config, "localhost", [{auth_method, internal}]}. -{host_config, "extauth.localhost", - [{auth_method, external}, - {extauth_program, "python3 extauth.py"}]}. -{host_config, "mnesia.localhost", - [{auth_method, internal}, - {{add, modules}, [{mod_announce, [{db_type, internal}]}, - {mod_blocking, [{db_type, internal}]}, - {mod_caps, [{db_type, internal}]}, - {mod_last, [{db_type, internal}]}, - {mod_muc, [{db_type, internal}]}, - {mod_offline, [{db_type, internal}]}, - {mod_privacy, [{db_type, internal}]}, - {mod_private, [{db_type, internal}]}, - {mod_pubsub, [{access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, ["flat", "hometree", "pep"]}]}, - {mod_roster, [{db_type, internal}]}, - {mod_vcard, [{db_type, internal}]}]} - ]}. -{host_config, "mysql.localhost", - [{auth_method, odbc}, - {odbc_pool_size, 1}, - {odbc_server, {mysql, "localhost", "ejabberd_test", - "ejabberd_test", "ejabberd_test"}}, - {{add, modules}, [{mod_announce, [{db_type, odbc}]}, - {mod_blocking, [{db_type, odbc}]}, - {mod_caps, [{db_type, odbc}]}, - {mod_last, [{db_type, odbc}]}, - {mod_muc, [{db_type, odbc}]}, - {mod_offline, [{db_type, odbc}]}, - {mod_privacy, [{db_type, odbc}]}, - {mod_private, [{db_type, odbc}]}, - {mod_pubsub, [{db_type, odbc}, - {access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, ["flat", "hometree", "pep"]}]}, - {mod_roster, [{db_type, odbc}]}, - {mod_vcard, [{db_type, odbc}]}]} - ]}. -{host_config, "mssql.localhost", - [{auth_method, odbc}, - {odbc_pool_size, 1}, - {odbc_server, {mssql, "localhost", "ejabberd_test", - "ejabberd_test", "ejabberd_Test1"}}, - {{add, modules}, [{mod_announce, [{db_type, odbc}]}, - {mod_blocking, [{db_type, odbc}]}, - {mod_caps, [{db_type, odbc}]}, - {mod_last, [{db_type, odbc}]}, - {mod_muc, [{db_type, odbc}]}, - {mod_offline, [{db_type, odbc}]}, - {mod_privacy, [{db_type, odbc}]}, - {mod_private, [{db_type, odbc}]}, - {mod_pubsub, [{db_type, odbc}, - {access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, ["flat", "hometree", "pep"]}]}, - {mod_roster, [{db_type, odbc}]}, - {mod_vcard, [{db_type, odbc}]}]} - ]}. -{host_config, "pgsql.localhost", - [{auth_method, odbc}, - {odbc_pool_size, 1}, - {odbc_server, {pgsql, "localhost", "ejabberd_test", - "ejabberd_test", "ejabberd_test"}}, - {{add, modules}, [{mod_announce, [{db_type, odbc}]}, - {mod_blocking, [{db_type, odbc}]}, - {mod_caps, [{db_type, odbc}]}, - {mod_last, [{db_type, odbc}]}, - {mod_muc, [{db_type, odbc}]}, - {mod_offline, [{db_type, odbc}]}, - {mod_privacy, [{db_type, odbc}]}, - {mod_private, [{db_type, odbc}]}, - {mod_pubsub, [{db_type, odbc}, - {access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, ["flat", "hometree", "pep"]}]}, - {mod_roster, [{db_type, odbc}]}, - {mod_vcard, [{db_type, odbc}]}]} - ]}. -{host_config, "sqlite.localhost", - [{auth_method, odbc}, - {odbc_pool_size, 1}, - {odbc_server, {sqlite, "/tmp/ejabberd_test.db"}}, - {{add, modules}, [{mod_announce, [{db_type, odbc}]}, - {mod_blocking, [{db_type, odbc}]}, - {mod_caps, [{db_type, odbc}]}, - {mod_last, [{db_type, odbc}]}, - {mod_muc, [{db_type, odbc}]}, - {mod_offline, [{db_type, odbc}]}, - {mod_privacy, [{db_type, odbc}]}, - {mod_private, [{db_type, odbc}]}, - {mod_pubsub, [{db_type, odbc}, - {access_createnode, pubsub_createnode}, - {ignore_pep_from_offline, true}, - {last_item_cache, false}, - {plugins, ["flat", "hometree", "pep"]}]}, - {mod_roster, [{db_type, odbc}]}, - {mod_vcard, [{db_type, odbc}]}]} - ]}. -{host_config, "ldap.localhost", - [{auth_method, ldap}, - {ldap_servers, ["localhost"]}, - {ldap_port, 1389}, - {ldap_rootdn, "cn=admin,dc=localhost"}, - {ldap_password, "password"}, - {ldap_base, "ou=users,dc=localhost"}, - {{add, modules}, [{mod_vcard_ldap, []}]} - ]}. - -%%% Local Variables: -%%% mode: erlang -%%% End: -%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: From 21ae72d02ecef9df116e95030afe3448d710b0ed Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 6 Feb 2025 11:00:32 +0100 Subject: [PATCH 0951/1302] Result of running "make doap options" --- ejabberd.doap | 4 ++-- src/ejabberd_option.erl | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index 28a68863c..c17c89ca7 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -767,10 +767,10 @@ - 0.3.0 + 0.4.0 24.02 complete - + , 0.4.0 since 25.xx diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 68e959288..2677cf42f 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -38,7 +38,7 @@ -export([cluster_nodes/0]). -export([default_db/0, default_db/1]). -export([default_ram_db/0, default_ram_db/1]). --export([define_macro/0, define_macro/1]). +-export([define_macro/0]). -export([disable_sasl_mechanisms/0, disable_sasl_mechanisms/1]). -export([disable_sasl_scram_downgrade_protection/0, disable_sasl_scram_downgrade_protection/1]). -export([domain_balancing/0]). @@ -374,10 +374,7 @@ default_ram_db(Host) -> -spec define_macro() -> any(). define_macro() -> - define_macro(global). --spec define_macro(global | binary()) -> any(). -define_macro(Host) -> - ejabberd_config:get_option({define_macro, Host}). + ejabberd_config:get_option({define_macro, global}). -spec disable_sasl_mechanisms() -> [binary()]. disable_sasl_mechanisms() -> From 4188c062e7a42786647bca53d83f7992596ffd5c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 7 Feb 2025 15:27:15 +0100 Subject: [PATCH 0952/1302] make-binaries: Bump crosstool-NG version to 1.27.0 --- .github/workflows/container.yml | 2 +- .github/workflows/installers.yml | 2 +- tools/make-binaries | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 33ae16960..7940c9b68 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -32,7 +32,7 @@ jobs: uses: actions/cache@v4 with: path: ~/build/ - key: ${{runner.os}}-ctr-ct-ng-1.26.0 + key: ${{runner.os}}-ctr-ct-ng-1.27.0 - name: Get erlang/OTP version for bootstrapping run: | diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index b820dd6d9..1e7f3e761 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -27,7 +27,7 @@ jobs: uses: actions/cache@v4 with: path: ~/build/ - key: ${{runner.os}}-ct-ng-1.26.0 + key: ${{runner.os}}-ct-ng-1.27.0 - name: Install prerequisites run: | sudo apt-get -qq update diff --git a/tools/make-binaries b/tools/make-binaries index 5c92b241b..434552332 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -65,7 +65,7 @@ fi rel_name='ejabberd' rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:]') mix_vsn=$(mix_version "$rel_vsn") -crosstool_vsn='1.26.0' +crosstool_vsn='1.27.0' termcap_vsn='1.3.1' expat_vsn='2.6.4' zlib_vsn='1.3.1' From d875e0280cd3c5d8b9355a20b2041b81be963a07 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 7 Feb 2025 15:45:59 +0100 Subject: [PATCH 0953/1302] make-binaries: Stick to Linux-PAM 1.6.1 Stick to Linux-PAM version 1.6.1 for the moment. Newer Linux-PAM versions are built using Meson instead of Autotools, so we need to add that to our toolchain before being able to update Linux-PAM. --- tools/make-binaries | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 434552332..d766002b4 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -73,7 +73,7 @@ yaml_vsn='0.2.5' ssl_vsn='3.4.0' otp_vsn='27.2' elixir_vsn='1.18.1' -pam_vsn='1.6.1' +pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.44' jpeg_vsn='9f' webp_vsn='1.5.0' @@ -199,9 +199,13 @@ check_configured_dep_vsns() check_vsn 'ODBC' "$odbc_vsn" \ 'http://www.unixodbc.org/download.html' \ 'unixODBC-\([1-9][0-9.]*\)\.tar\.gz' - check_vsn 'Linux-PAM' "$pam_vsn" \ - 'https://github.com/linux-pam/linux-pam/releases' \ - '[0-9]\]Linux-PAM \([1-9][0-9.]*\)' + # + # Linux-PAM uses Meson since version 1.7.0, we don't support that yet. + # + # check_vsn 'Linux-PAM' "$pam_vsn" \ + # 'https://github.com/linux-pam/linux-pam/releases' \ + # '[0-9]\]Linux-PAM \([1-9][0-9.]*\)' + # check_vsn 'libpng' "$png_vsn" \ 'http://www.libpng.org/pub/png/libpng.html' \ 'libpng-\([1-9][0-9.]*\)\.tar\.gz' From 5f849bdb3f03cb8a4a7b1c8ad03643b1c40f020d Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 7 Feb 2025 15:49:25 +0100 Subject: [PATCH 0954/1302] make-binaries: Bump dependency versions --- tools/make-binaries | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index d766002b4..edf56314f 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,15 +71,15 @@ expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.4.0' -otp_vsn='27.2' -elixir_vsn='1.18.1' +otp_vsn='27.2.2' +elixir_vsn='1.18.2' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. -png_vsn='1.6.44' +png_vsn='1.6.45' jpeg_vsn='9f' webp_vsn='1.5.0' gd_vsn='2.3.3' odbc_vsn='2.3.12' -sqlite_vsn='3470200' +sqlite_vsn='3490000' root_dir="${BUILD_DIR:-$HOME/build}" bootstrap_dir="$root_dir/bootstrap" ct_prefix_dir="$root_dir/x-tools" From a49ec4d583f7050a96fb23cd36c29b5022ee4db1 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Fri, 7 Feb 2025 20:08:26 +0100 Subject: [PATCH 0955/1302] make-binaries: Fix building Termcap and Linux-PAM Building GNU Termcap and Linux-PAM using the updated toolchain both failed, since crosstool-NG now uses GCC 14.x, which turns the "implicit-function-declaration" warning into an error. See: https://gcc.gnu.org/gcc-14/porting_to.html#warnings-as-errors Therefore, specify "CFLAGS=-Wno-error=implicit-function-declaration" to turn this error back into a warning. --- tools/make-binaries | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index edf56314f..d5876807e 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -547,6 +547,7 @@ build_deps() info "Building Termcap $termcap_vsn for $arch-$libc ..." cd "$target_src_dir/$termcap_dir" + sed -i 's/CFLAGS =/CFLAGS = -Wno-error=implicit-function-declaration/' 'Makefile.in' $configure --prefix="$prefix" cat >'config.h' <<-'EOF' #ifndef CONFIG_H @@ -630,7 +631,7 @@ build_deps() $configure --prefix="$prefix" --includedir="$prefix/include/security" \ --enable-static --disable-shared --disable-doc --disable-examples \ --enable-db=no \ - CFLAGS="$CFLAGS -O3 -fPIC" + CFLAGS="$CFLAGS -O3 -fPIC -Wno-error=implicit-function-declaration" make make install cd "$OLDPWD" From da61f3dfea03e0d9ab690b4774c76bc1a78abd8e Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 11 Feb 2025 16:46:45 +0100 Subject: [PATCH 0956/1302] make-binaries: Bump OpenSSL version to 3.4.1 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index d5876807e..e8b5cc75b 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,7 +70,7 @@ termcap_vsn='1.3.1' expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.4.0' +ssl_vsn='3.4.1' otp_vsn='27.2.2' elixir_vsn='1.18.2' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. From 80423d7e69c508c3f8c6891ef90c832577b9e62c Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 13 Feb 2025 16:21:05 +0300 Subject: [PATCH 0957/1302] Support Matrix room aliases --- src/mod_matrix_gw_room.erl | 106 +++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 07b5a74b0..01f9e8b52 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -77,7 +77,8 @@ client_state}). -record(multi_user, - {join_ts :: integer()}). + {join_ts :: integer(), + room_jid :: jid()}). -record(multi, {users :: #{{binary(), binary()} => #{binary() => #multi_user{}}}}). @@ -105,6 +106,9 @@ -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). -define(MAX_TXN_RETRIES, 5). +-define(MATRIX_ROOM_ALIAS_CACHE, matrix_room_alias_cache). +-define(MATRIX_ROOM_ALIAS_CACHE_ERROR_TIMEOUT, 60000). + %%%=================================================================== %%% API %%%=================================================================== @@ -140,6 +144,7 @@ create_db() -> [{ram_copies, [node()]}, {type, set}, {attributes, record_info(fields, matrix_direct)}]), + ets_cache:new(?MATRIX_ROOM_ALIAS_CACHE), ok. get_room_pid(Host, RoomID) -> @@ -170,11 +175,13 @@ join_direct(Host, MatrixServer, RoomID, Sender, UserID) -> Error end. -route(#presence{from = From, to = #jid{luser = <<$!, _/binary>>} = To, - type = Type} = Packet) -> - case room_id_from_xmpp(To#jid.luser) of +route(#presence{from = From, to = #jid{luser = <>} = To, + type = Type} = Packet) + when C == $!; + C == $# -> + Host = ejabberd_config:get_myname(), + case room_id_from_xmpp(Host, To#jid.luser) of {ok, RoomID} -> - Host = ejabberd_config:get_myname(), case From#jid.lserver of Host -> case Type of @@ -200,14 +207,20 @@ route(#presence{from = From, to = #jid{luser = <<$!, _/binary>>} = To, ok end; error -> + Lang = xmpp:get_lang(Packet), + Txt = <<"bad or non-existing room id">>, + Err = xmpp:err_not_acceptable(Txt, Lang), + ejabberd_router:route_error(Packet, Err), ok end; -route(#message{from = From, to = #jid{luser = <<$!, _/binary>>} = To, +route(#message{from = From, to = #jid{luser = <>} = To, type = groupchat, - body = Body}) -> - case room_id_from_xmpp(To#jid.luser) of + body = Body}) + when C == $!; + C == $# -> + Host = ejabberd_config:get_myname(), + case room_id_from_xmpp(Host, To#jid.luser) of {ok, RoomID} -> - Host = ejabberd_config:get_myname(), case From#jid.lserver of Host -> case user_id_from_jid(From, Host) of @@ -685,6 +698,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> #{{LUser, LServer} := Rs} -> Rs; _ -> #{} end, + RoomJID = jid:remove_resource(xmpp:get_to(Packet)), Data2 = Data#data{ kind = @@ -692,7 +706,8 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> users = Users#{{LUser, LServer} => Resources#{LResource => - #multi_user{join_ts = JoinTS}}}}}, + #multi_user{join_ts = JoinTS, + room_jid = RoomJID}}}}}, {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; error -> ?INFO_MSG("bad join user id: ~p", [UserJID]), @@ -758,6 +773,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> {timeout, 60000}], [{sync, true}, {body_format, binary}]), + RoomJID = jid:remove_resource(xmpp:get_to(Packet)), ?DEBUG("send_join ~p~n", [SendJoinRes]), process_send_join_res( MatrixServer, SendJoinRes, RoomVersion, @@ -765,7 +781,8 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> kind = #multi{users = #{{LUser, LServer} => - #{LResource => #multi_user{join_ts = JoinTS}}}}, + #{LResource => #multi_user{join_ts = JoinTS, + room_jid = RoomJID}}}}, room_version = RoomVersion}) end; _JSON -> @@ -2642,12 +2659,13 @@ notify_event_xmpp( case Sender of <<$@, SenderUser/binary>> -> ?DEBUG("notify xmpp ~p", [Users]), - From = jid:replace_resource(Data#data.room_jid, SenderUser), maps:fold( fun({LUser, LServer}, Resources, ok) -> maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) + fun(LResource, #multi_user{join_ts = JoinTS, + room_jid = RoomJID}, ok) when JoinTS =< OriginTS -> + From = jid:replace_resource(RoomJID, SenderUser), UserJID = jid:make(LUser, LServer, LResource), Msg = #message{from = From, to = UserJID, @@ -2673,16 +2691,17 @@ notify_event_xmpp( case user_id_to_jid(Sender, Data) of #jid{} = SenderJID -> <<$@, SenderUser/binary>> = Sender, - From = jid:replace_resource(Data#data.room_jid, SenderUser), maps:fold( fun({LUser, LServer}, Resources, ok) -> maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) + fun(LResource, #multi_user{join_ts = JoinTS, + room_jid = RoomJID}, ok) when JoinTS =< OriginTS -> + From = jid:replace_resource(RoomJID, SenderUser), case jid:tolower(SenderJID) of {LUser, LServer, _} -> send_initial_presences( - SenderJID, Event, Data); + SenderJID, RoomJID, Event, Data); _ -> ok end, @@ -2709,12 +2728,13 @@ notify_event_xmpp( Membership == <<"ban">> -> case StateKey of <<$@, RUser/binary>> -> - From = jid:replace_resource(Data#data.room_jid, RUser), maps:fold( fun({LUser, LServer}, Resources, ok) -> maps:fold( - fun(LResource, #multi_user{join_ts = JoinTS}, ok) + fun(LResource, #multi_user{join_ts = JoinTS, + room_jid = RoomJID}, ok) when JoinTS =< OriginTS -> + From = jid:replace_resource(RoomJID, RUser), UserJID = jid:make(LUser, LServer, LResource), Pres = #presence{from = From, to = UserJID, @@ -2755,7 +2775,7 @@ notify_event_xmpp( notify_event_xmpp(_Event, Data) -> Data. -send_initial_presences(JID, Event, Data) -> +send_initial_presences(JID, RoomJID, Event, Data) -> ?DEBUG("send_initial_presences ~p", [{JID, Event}]), maps:fold( fun({?ROOM_MEMBER, _}, EID, ok) -> @@ -2764,7 +2784,7 @@ send_initial_presences(JID, Event, Data) -> sender = <<$@, SenderUser/binary>>, json = #{<<"content">> := #{<<"membership">> := <<"join">>}}}} -> - From = jid:replace_resource(Data#data.room_jid, SenderUser), + From = jid:replace_resource(RoomJID, SenderUser), Pres = #presence{from = From, to = JID, type = available @@ -3148,7 +3168,7 @@ room_id_to_xmpp(RoomID) -> error end. -room_id_from_xmpp(RID) -> +room_id_from_xmpp(Host, RID) -> case RID of <<$!, Parts/binary>> -> case binary:split(Parts, <<"%">>) of @@ -3159,10 +3179,54 @@ room_id_from_xmpp(RID) -> {ok, <<$!, RoomID/binary, $:, S/binary>>}; _ -> error end; + <<$#, Parts/binary>> -> + case binary:split(Parts, <<"%">>) of + [R, S] -> + Alias = <<$#, R/binary, $:, S/binary>>, + case resolve_alias(Host, S, Alias) of + {ok, <<$!, _/binary>> = RoomID} -> + {ok, RoomID}; + error -> + error + end; + _ -> error + end; _ -> error end. +resolve_alias(Host, Origin, Alias) -> + ets_cache:lookup( + ?MATRIX_ROOM_ALIAS_CACHE, Alias, + fun() -> + Res = + mod_matrix_gw:send_request( + Host, get, Origin, + [<<"_matrix">>, <<"federation">>, + <<"v1">>, <<"query">>, <<"directory">>], + [{<<"room_alias">>, Alias}], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + case Res of + {ok, {{_, 200, _}, _Headers, Body}} -> + try + case misc:json_decode(Body) of + #{<<"room_id">> := RoomID} -> + {ok, RoomID} + end + catch + Class:Reason:ST -> + ?DEBUG("failed resolve_alias: ~p", [{Class, Reason, ST}]), + {cache_with_timeout, error, ?MATRIX_ROOM_ALIAS_CACHE_ERROR_TIMEOUT} + end; + {ok, {{_, _Status, _Reason}, _Headers, _Body}} -> + {cache_with_timeout, error, ?MATRIX_ROOM_ALIAS_CACHE_ERROR_TIMEOUT}; + {error, _Reason} -> + {cache_with_timeout, error, ?MATRIX_ROOM_ALIAS_CACHE_ERROR_TIMEOUT} + end + end). escape(S) -> From 376f7b261e8e8b86757778fc10d8638f105736ae Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 7 Feb 2025 19:27:26 +0100 Subject: [PATCH 0958/1302] mod_mam: Mention in documentation that MAM should use some SQL storage --- src/mod_mam.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 1e2d13f3d..aa4bb3b84 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -1760,13 +1760,17 @@ mod_options(Host) -> mod_doc() -> #{desc => - ?T("This module implements " + [?T("This module implements " "https://xmpp.org/extensions/xep-0313.html" "[XEP-0313: Message Archive Management] and " "https://xmpp.org/extensions/xep-0441.html" "[XEP-0441: Message Archive Management Preferences]. " "Compatible XMPP clients can use it to store their " - "chat history on the server."), + "chat history on the server."), "", + ?T("NOTE: Mnesia backend for mod_mam is not recommended: it's limited " + "to 2GB and often gets corrupted when reaching this limit. " + "SQL backend is recommended. Namely, for small servers SQLite " + "is a preferred choice because it's very easy to configure.")], opts => [{access_preferences, #{value => ?T("AccessName"), From 8a7e9554536bf8407a3be33dae27c10b79483eb5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Feb 2025 14:55:37 +0100 Subject: [PATCH 0959/1302] Update yconf to support macro inside string --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 11 ++++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/mix.exs b/mix.exs index af72070c9..b3a26eb3f 100644 --- a/mix.exs +++ b/mix.exs @@ -131,7 +131,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:xmpp, git: "https://github.com/processone/xmpp", ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", override: true}, - {:yconf, "~> 1.0.17"}] + {:yconf, git: "https://github.com/processone/yconf", ref: "9682a6025ed543eedf34637e4cfcc66837074af6", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 8ce6a75c1..3f6fb7dfa 100644 --- a/mix.lock +++ b/mix.lock @@ -35,5 +35,5 @@ "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "xmpp": {:git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", [ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"]}, - "yconf": {:hex, :yconf, "1.0.17", "dcf242e27f3fc5d0743d6b8175dd39bc14a1f4ed7e6ea986366a44a6ff3b2a3a", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "dd2892923241449a46cc8457b9ec0fb14030700735a5885955677c735c341a25"}, + "yconf": {:git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6", [ref: "9682a6025ed543eedf34637e4cfcc66837074af6"]}, } diff --git a/rebar.config b/rebar.config index 3b0942c87..4a851c789 100644 --- a/rebar.config +++ b/rebar.config @@ -78,7 +78,7 @@ {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, - {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", {tag, "1.0.17"}}} + {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6"}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index e96263cb9..c699f3c36 100644 --- a/rebar.lock +++ b/rebar.lock @@ -28,7 +28,10 @@ {git,"https://github.com/processone/xmpp", {ref,"64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, 0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.17">>},0}]}. + {<<"yconf">>, + {git,"https://github.com/processone/yconf", + {ref,"9682a6025ed543eedf34637e4cfcc66837074af6"}}, + 0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -55,8 +58,7 @@ {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"yconf">>, <<"DCF242E27F3FC5D0743D6B8175DD39BC14A1F4ED7E6EA986366A44A6FF3B2A3A">>}]}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, @@ -82,6 +84,5 @@ {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"yconf">>, <<"DD2892923241449A46CC8457B9EC0FB14030700735A5885955677C735C341A25">>}]} + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} ]. From 983c016bbac0b211c57da85da06d3c2d56baf296 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 14 Feb 2025 12:50:48 +0100 Subject: [PATCH 0960/1302] Update xmpp to get XEP-0317 Hats namespaces version 0.2.0 --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 4 ---- src/mod_muc.erl | 2 ++ src/mod_muc_room.erl | 8 ++++---- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index b3a26eb3f..feb1666c2 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "e295055dd1cb3dafb4b3134bc7462a775ff16b5c", override: true}, {:yconf, git: "https://github.com/processone/yconf", ref: "9682a6025ed543eedf34637e4cfcc66837074af6", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 3f6fb7dfa..678274178 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0", [ref: "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "e295055dd1cb3dafb4b3134bc7462a775ff16b5c", [ref: "e295055dd1cb3dafb4b3134bc7462a775ff16b5c"]}, "yconf": {:git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6", [ref: "9682a6025ed543eedf34637e4cfcc66837074af6"]}, } diff --git a/rebar.config b/rebar.config index 4a851c789..ca2658b22 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "e295055dd1cb3dafb4b3134bc7462a775ff16b5c"}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6"}} ]}. diff --git a/rebar.lock b/rebar.lock index c699f3c36..2e867dd9d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -24,10 +24,6 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"64d6d292015a7ec4de0b9e963d20b7ac5b63c7f0"}}, - 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", {ref,"9682a6025ed543eedf34637e4cfcc66837074af6"}}, diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 278b72014..1c33d064b 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1770,8 +1770,10 @@ mod_doc() -> "The default value is an empty string.")}}, {enable_hats, #{value => "true | false", + note => "improved in 25.xx", desc => ?T("Allow extended roles as defined in XEP-0317 Hats. " + "Check the _`../../tutorials/muc-hats.md|MUC Hats`_ tutorial. " "The default value is 'false'.")}}, {lang, #{value => ?T("Language"), diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f48e58a94..973759b27 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 317, '0.1', '21.12', "complete", "conversejs/prosody compatible"}). +-protocol({xep, 317, '0.2.0', '25.xx', "complete", ""}). -protocol({xep, 410, '1.1.0', '18.12', "complete", ""}). -behaviour(p1_fsm). @@ -79,9 +79,9 @@ -define(MAX_USERS_DEFAULT_LIST, [5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]). --define(MUC_HAT_ADD_CMD, <<"http://prosody.im/protocol/hats#add">>). --define(MUC_HAT_REMOVE_CMD, <<"http://prosody.im/protocol/hats#remove">>). --define(MUC_HAT_LIST_CMD, <<"p1:hats#list">>). +-define(MUC_HAT_ADD_CMD, <<"urn:xmpp:hats:commands:don">>). +-define(MUC_HAT_REMOVE_CMD, <<"urn:xmpp:hats:commands:doff">>). +-define(MUC_HAT_LIST_CMD, <<"urn:xmpp:hats:commands:dlist">>). -define(MAX_HATS_USERS, 100). -define(MAX_HATS_PER_USER, 10). -define(CLEAN_ROOM_TIMEOUT, 30000). From ceee3d3be1fc1053e61bbc81881bd40dbbbc1e89 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 14 Feb 2025 17:10:57 +0100 Subject: [PATCH 0961/1302] Fix placement of vcard_xupdate documentation --- src/mod_muc.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 1c33d064b..c1ad7a8ba 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1692,11 +1692,6 @@ mod_doc() -> " -", " work: true", " street: Elm Street"]}}, - {vcard_xupdate, - #{value => "undefined | external | AvatarHash", - desc => - ?T("Set the hash of the avatar image. " - "The default value is 'undefined'.")}}, {cleanup_affiliations_on_start, #{value => "true | false", note => "added in 22.05", @@ -1840,6 +1835,11 @@ mod_doc() -> desc => ?T("A custom vCard for the room. See the equivalent mod_muc option." "The default value is an empty string.")}}, + {vcard_xupdate, + #{value => "undefined | external | AvatarHash", + desc => + ?T("Set the hash of the avatar image. " + "The default value is 'undefined'.")}}, {voice_request_min_interval, #{value => ?T("Number"), desc => From 44782001e20584029d997d476f2dadb40ce6ba96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 06:36:13 +0000 Subject: [PATCH 0962/1302] mix.lock: bump ex_doc from 0.36.1 to 0.37.1 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.36.1 to 0.37.1. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.36.1...v0.37.1) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- mix.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.lock b/mix.lock index 678274178..8eb5566d3 100644 --- a/mix.lock +++ b/mix.lock @@ -2,13 +2,13 @@ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, - "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"}, + "ex_doc": {:hex, :ex_doc, "0.37.1", "65ca30d242082b95aa852b3b73c9d9914279fff56db5dc7b3859be5504417980", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "6774f75477733ea88ce861476db031f9399c110640752ca2b400dbbb50491224"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, @@ -21,9 +21,9 @@ "luerl": {:hex, :luerl, "1.2.3", "df25f41944e57a7c4d9ef09d238bc3e850276c46039cfc12b8bb42eccf36fcb1", [:rebar3], [], "hexpm", "1b4b9d0ca5d7d280d1d2787a6a5ee9f5a212641b62bff91556baa53805df3aed"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "p1_acme": {:hex, :p1_acme, "1.0.25", "db91f0d6c193cd1d5c0b0fa3939a898dbf56a6075db4347cde26e802715de50c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a7b55b47495ddb4f98a15e65451ec3ad43f4637b955c74cd695d98e6a645d08c"}, "p1_mysql": {:hex, :p1_mysql, "1.0.25", "875d4cbdc7c9990270df3292cce2514e4c18a9fdfd19bef258cb4d0c45b4f243", [:rebar3], [], "hexpm", "e6187ffae95b726098e88f3ee6f2344ac259ce2c26e0ee403b05feef341ae434"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, From 78f7a9a244f6cdb3b3dfbeefc9d5aea51ae628dc Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Feb 2025 11:54:49 +0100 Subject: [PATCH 0963/1302] CI: Fix step name, remove obsolete step reference --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d95bcc155..28efecd1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - - name: Get compatible Rebar binaries + - name: Get old eredis for old Erlang if: matrix.otp < 21 run: ./rebar3 unlock eredis @@ -185,7 +185,7 @@ jobs: ./rebar3 cover - name: Check results - if: always() && (steps.ct.outcome != 'skipped' || steps.ct2.outcome != 'skipped') + if: always() && (steps.ct.outcome != 'skipped') id: ctresults run: | [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true From a32bfd1215450b35f88e19f0dbbef22483967c5a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Feb 2025 10:56:58 +0100 Subject: [PATCH 0964/1302] Show warning also when deprecated listener option is set as disabled (#4345) --- src/ejabberd_config_transformer.erl | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl index a04e8771f..025db6dc3 100644 --- a/src/ejabberd_config_transformer.erl +++ b/src/ejabberd_config_transformer.erl @@ -271,25 +271,25 @@ replace_request_handlers(Opts) -> Handlers = proplists:get_value(request_handlers, Opts, []), Handlers1 = lists:foldl( - fun({captcha, true}, Acc) -> + fun({captcha, IsEnabled}, Acc) -> Handler = {<<"/captcha">>, ejabberd_captcha}, - warn_replaced_handler(captcha, Handler), + warn_replaced_handler(captcha, Handler, IsEnabled), [Handler|Acc]; - ({register, true}, Acc) -> + ({register, IsEnabled}, Acc) -> Handler = {<<"/register">>, mod_register_web}, - warn_replaced_handler(register, Handler), + warn_replaced_handler(register, Handler, IsEnabled), [Handler|Acc]; - ({web_admin, true}, Acc) -> + ({web_admin, IsEnabled}, Acc) -> Handler = {<<"/admin">>, ejabberd_web_admin}, - warn_replaced_handler(web_admin, Handler), + warn_replaced_handler(web_admin, Handler, IsEnabled), [Handler|Acc]; - ({http_bind, true}, Acc) -> + ({http_bind, IsEnabled}, Acc) -> Handler = {<<"/bosh">>, mod_bosh}, - warn_replaced_handler(http_bind, Handler), + warn_replaced_handler(http_bind, Handler, IsEnabled), [Handler|Acc]; - ({xmlrpc, true}, Acc) -> + ({xmlrpc, IsEnabled}, Acc) -> Handler = {<<"/">>, ejabberd_xmlrpc}, - warn_replaced_handler(xmlrpc, Handler), + warn_replaced_handler(xmlrpc, Handler, IsEnabled), Acc ++ [Handler]; (_, Acc) -> Acc @@ -538,7 +538,12 @@ warn_removed_module(Mod) -> ?WARNING_MSG("Module ~ts is deprecated and was automatically " "removed from the configuration. ~ts", [Mod, adjust_hint()]). -warn_replaced_handler(Opt, {Path, Module}) -> +warn_replaced_handler(Opt, {Path, Module}, false) -> + ?WARNING_MSG("Listening option '~ts' is deprecated, " + "please use instead the " + "HTTP request handler: \"~ts\" -> ~ts. ~ts", + [Opt, Path, Module, adjust_hint()]); +warn_replaced_handler(Opt, {Path, Module}, true) -> ?WARNING_MSG("Listening option '~ts' is deprecated " "and was automatically replaced by " "HTTP request handler: \"~ts\" -> ~ts. ~ts", From 45dafbcdcc96292e0dbc171888f772b5a281491c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Feb 2025 11:03:56 +0100 Subject: [PATCH 0965/1302] Result of running "make format" --- src/mod_privilege.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 862f0c0be..d614c8a35 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -308,7 +308,8 @@ component_send_packet({#iq{from = From, []), drop; {_, {unprivileged_iq}} -> - ?DEBUG("Component ~ts sent a not-wrapped IQ stanza, routing it as-is.", [From#jid.lserver]), + ?DEBUG("Component ~ts sent a not-wrapped IQ stanza, routing it as-is.", + [From#jid.lserver]), IQ; {_, {error, ErrType, _Err}} -> ?INFO_MSG("IQ not forwarded: Component tried to send not valid IQ stanza: ~p.", From 6bd4399aeec7a733d1b3f8193b98b4cf6a09ade3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 20 Feb 2025 14:48:45 +0100 Subject: [PATCH 0966/1302] Bubble up db errors in nodetree_tree_sql:set_node --- src/nodetree_tree_sql.erl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl index f7a31b8ae..09959099e 100644 --- a/src/nodetree_tree_sql.erl +++ b/src/nodetree_tree_sql.erl @@ -82,17 +82,22 @@ set_node(Record) when is_record(Record, pubsub_node) -> " parent=%(Parent)s, plugin=%(Type)s " "where nodeid=%(OldNidx)d")), OldNidx; - _ -> + {error, not_found} -> catch ejabberd_sql:sql_query_t( ?SQL("insert into pubsub_node(host, node, parent, plugin) " "values(%(H)s, %(Node)s, %(Parent)s, %(Type)s)")), case nodeidx(Host, Node) of {result, NewNidx} -> NewNidx; - _ -> none % this should not happen - end + {error, not_found} -> none; % this should not happen + {error, _} -> db_error + end; + {error, _} -> + db_error end, case Nidx of + db_error -> + {error, xmpp:err_internal_server_error(?T("Database failure"), ejabberd_option:language())}; none -> Txt = ?T("Node index not found"), {error, xmpp:err_internal_server_error(Txt, ejabberd_option:language())}; From 41232ccb393ccaa3b83100e6d2e6c4ef1f313024 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 21 Feb 2025 04:15:08 +0300 Subject: [PATCH 0967/1302] Properly handle IQ requests in mod_matrix_gw --- src/mod_matrix_gw.erl | 49 +++++++++++++++++++++++++++++++++++++- src/mod_matrix_gw_room.erl | 36 ++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index bd7882117..312eced8c 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -46,6 +46,8 @@ prune_event/2, get_event_id/2, content_hash/1, sign_event/3, sign_pruned_event/2, sign_json/2, send_request/8, s2s_out_bounce_packet/2, user_receive_packet/1, + process_disco_info/1, + process_disco_items/1, route/1]). -include_lib("xmpp/include/xmpp.hrl"). @@ -516,6 +518,10 @@ init([Host]) -> Opts = gen_mod:get_module_opts(Host, ?MODULE), MyHost = gen_mod:get_opt(host, Opts), register_routes(Host, [MyHost]), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO, + ?MODULE, process_disco_info), + gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS, + ?MODULE, process_disco_items), {ok, #state{server_host = Host, host = MyHost}}. -spec handle_call(term(), {pid(), term()}, state()) -> @@ -536,7 +542,11 @@ handle_info(Info, State) -> -spec terminate(term(), state()) -> any(). terminate(_Reason, #state{host = Host}) -> - unregister_routes([Host]). + unregister_routes([Host]), + gen_iq_handler:del_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, + ?MODULE, process_disco_info), + gen_iq_handler:del_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, + ?MODULE, process_disco_items). -spec code_change(term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -873,9 +883,46 @@ user_receive_packet({Pkt, C2SState} = Acc) -> end end. +-spec route(stanza()) -> ok. +route(#iq{to = #jid{luser = <<"">>, lresource = <<"">>}} = IQ) -> + ejabberd_router:process_iq(IQ); route(Pkt) -> mod_matrix_gw_room:route(Pkt). +-spec process_disco_info(iq()) -> iq(). +process_disco_info(#iq{type = set, lang = Lang} = IQ) -> + Txt = ?T("Value 'set' of 'type' attribute is not allowed"), + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_disco_info(#iq{type = get, + sub_els = [#disco_info{node = <<"">>}]} = IQ) -> + Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC], + Identity = #identity{category = <<"gateway">>, + type = <<"matrix">>}, + xmpp:make_iq_result( + IQ, #disco_info{features = Features, + identities = [Identity]}); +process_disco_info(#iq{type = get, lang = Lang, + sub_els = [#disco_info{}]} = IQ) -> + xmpp:make_error(IQ, xmpp:err_item_not_found(?T("Node not found"), Lang)); +process_disco_info(#iq{lang = Lang} = IQ) -> + Txt = ?T("No module is handling this query"), + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_disco_items(iq()) -> iq(). +process_disco_items(#iq{type = set, lang = Lang} = IQ) -> + Txt = ?T("Value 'set' of 'type' attribute is not allowed"), + xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); +process_disco_items(#iq{type = get, + sub_els = [#disco_items{node = <<>>}]} = IQ) -> + xmpp:make_iq_result(IQ, #disco_items{}); +process_disco_items(#iq{type = get, lang = Lang, + sub_els = [#disco_items{}]} = IQ) -> + xmpp:make_error(IQ, xmpp:err_item_not_found(?T("Node not found"), Lang)); +process_disco_items(#iq{lang = Lang} = IQ) -> + Txt = ?T("No module is handling this query"), + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + + depends(_Host, _Opts) -> []. diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 01f9e8b52..91abf2573 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -356,6 +356,42 @@ route(#message{from = From, to = To, body = Body} = _Pkt) -> error -> ok end; +route(#iq{type = Type}) when Type == error; Type == result -> + ok; +route(#iq{type = Type, lang = Lang, sub_els = [_]} = IQ0) -> + try xmpp:decode_els(IQ0) of + #iq{sub_els = [SubEl]} = IQ -> + Result = + case {Type, SubEl} of + {set, _} -> + {error, xmpp:err_not_allowed()}; + {get, #disco_info{node = <<>>}} -> + {result, + #disco_info{identities = + [#identity{category = <<"conference">>, + type = <<"text">>}], + features = [?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS]}}; + {get, #disco_info{node = _}} -> + {error, xmpp:err_item_not_found()}; + {get, #disco_items{node = <<>>}} -> + {result, #disco_items{}}; + {get, #disco_items{node = _}} -> + {error, xmpp:err_item_not_found()}; + _ -> + {error, xmpp:err_service_unavailable()} + end, + case Result of + {result, Res} -> + ejabberd_router:route(xmpp:make_iq_result(IQ, Res)); + {error, Error} -> + ejabberd_router:route(xmpp:make_error(IQ, Error)) + end + catch _:{xmpp_codec, Why} -> + ErrTxt = xmpp:io_format_error(Why), + Err = xmpp:err_bad_request(ErrTxt, Lang), + ejabberd_router:route_error(IQ0, Err), + ok + end; route(_) -> ok. From 7018b8b164368f034cded92efd010e20829639d8 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 21 Feb 2025 12:05:34 +0300 Subject: [PATCH 0968/1302] Fix gen_iq_handler:remove_iq_handler call in mod_matrix_gw --- src/mod_matrix_gw.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 312eced8c..173e82356 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -543,10 +543,8 @@ handle_info(Info, State) -> -spec terminate(term(), state()) -> any(). terminate(_Reason, #state{host = Host}) -> unregister_routes([Host]), - gen_iq_handler:del_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, - ?MODULE, process_disco_info), - gen_iq_handler:del_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, - ?MODULE, process_disco_items). + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS). -spec code_change(term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, State, _Extra) -> {ok, State}. From 2a85c0a47466076e23d869f6ebb506b8c487f18b Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 24 Feb 2025 17:39:57 +0300 Subject: [PATCH 0969/1302] Add muc#user element to presences and an initial empty subject --- src/mod_matrix_gw_room.erl | 56 ++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 91abf2573..095b6fa52 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -2734,19 +2734,42 @@ notify_event_xmpp( room_jid = RoomJID}, ok) when JoinTS =< OriginTS -> From = jid:replace_resource(RoomJID, SenderUser), - case jid:tolower(SenderJID) of - {LUser, LServer, _} -> - send_initial_presences( - SenderJID, RoomJID, Event, Data); - _ -> - ok - end, + IsSelfPresence = + case jid:tolower(SenderJID) of + {LUser, LServer, _} -> + send_initial_presences( + SenderJID, RoomJID, Event, Data), + true; + _ -> + false + end, UserJID = jid:make(LUser, LServer, LResource), - Pres = #presence{from = From, - to = UserJID, - type = available - }, - ejabberd_router:route(Pres); + Item = #muc_item{affiliation = member, + role = participant}, + Status = case IsSelfPresence of + true -> [110]; + false -> [] + end, + Pres = #presence{ + from = From, + to = UserJID, + type = available, + sub_els = [#muc_user{items = [Item], + status_codes = Status}] + }, + ejabberd_router:route(Pres), + case IsSelfPresence of + true -> + Subject = + #message{ + from = RoomJID, + to = UserJID, + type = groupchat, + subject = [#text{}] + }, + ejabberd_router:route(Subject); + false -> ok + end; (_, _, _) -> ok end, ok, Resources) end, ok, Users), @@ -2772,9 +2795,12 @@ notify_event_xmpp( when JoinTS =< OriginTS -> From = jid:replace_resource(RoomJID, RUser), UserJID = jid:make(LUser, LServer, LResource), + Item = #muc_item{affiliation = member, + role = none}, Pres = #presence{from = From, to = UserJID, - type = unavailable + type = unavailable, + sub_els = [#muc_user{items = [Item]}] }, ejabberd_router:route(Pres); (_, _, _) -> ok @@ -2821,9 +2847,11 @@ send_initial_presences(JID, RoomJID, Event, Data) -> json = #{<<"content">> := #{<<"membership">> := <<"join">>}}}} -> From = jid:replace_resource(RoomJID, SenderUser), + Item = #muc_item{affiliation = member, role = participant}, Pres = #presence{from = From, to = JID, - type = available + type = available, + sub_els = [#muc_user{items = [Item]}] }, ejabberd_router:route(Pres), ok; From 051093f4f871cb0d5f300dd57fadcb4391bf3c6a Mon Sep 17 00:00:00 2001 From: p Date: Tue, 25 Feb 2025 14:11:02 +0330 Subject: [PATCH 0970/1302] ref: c2s_handle_bind hook --- src/ejabberd_c2s.erl | 93 ++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index c68e6ad30..f00755625 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -49,8 +49,9 @@ get_fast_tokens_fun/2, fast_mechanisms/1]). %% Hooks -export([handle_unexpected_cast/2, handle_unexpected_call/3, - process_auth_result/3, reject_unauthenticated_packet/2, - process_closed/2, process_terminated/2, process_info/2]). + process_auth_result/3, c2s_handle_bind/1, + reject_unauthenticated_packet/2, process_closed/2, + process_terminated/2, process_info/2]). %% API -export([get_presence/1, set_presence/2, resend_presence/1, resend_presence/2, open_session/1, call/3, cast/2, send/2, close/1, close/2, stop_async/1, @@ -165,6 +166,7 @@ host_up(Host) -> ejabberd_hooks:add(c2s_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:add(c2s_terminated, Host, ?MODULE, process_terminated, 100), + ejabberd_hooks:add(c2s_handle_bind, Host, ?MODULE, c2s_handle_bind, 100), ejabberd_hooks:add(c2s_unauthenticated_packet, Host, ?MODULE, reject_unauthenticated_packet, 100), ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, @@ -181,6 +183,7 @@ host_down(Host) -> ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, process_closed, 100), ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, process_terminated, 100), + ejabberd_hooks:delete(c2s_handle_bind, Host, ?MODULE, c2s_handle_bind, 100), ejabberd_hooks:delete(c2s_unauthenticated_packet, Host, ?MODULE, reject_unauthenticated_packet, 100), ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, @@ -285,6 +288,11 @@ handle_unexpected_cast(State, Msg) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), State. +c2s_handle_bind({<<"">>, {ok, State}}) -> + {new_uniq_id(), {ok, State}}; +c2s_handle_bind(Acc) -> + Acc. + reject_unauthenticated_packet(State, _Pkt) -> Err = xmpp:serr_not_authorized(), send(State, Err). @@ -480,33 +488,60 @@ fast_mechanisms(#{lserver := LServer}) -> _ -> mod_auth_fast:get_mechanisms(LServer) end. -bind(<<"">>, State) -> - bind(new_uniq_id(), State); -bind(R, #{user := U, server := S, access := Access, lang := Lang, - lserver := LServer, socket := Socket, - ip := IP} = State) -> - case resource_conflict_action(U, S, R) of - closenew -> - {error, xmpp:err_conflict(), State}; - {accept_resource, Resource} -> - JID = jid:make(U, S, Resource), - case acl:match_rule(LServer, Access, - #{usr => jid:split(JID), ip => IP}) of - allow -> - State1 = open_session(State#{resource => Resource, - sid => ejabberd_sm:make_sid()}), - State2 = ejabberd_hooks:run_fold( - c2s_session_opened, LServer, State1, []), - ?INFO_MSG("(~ts) Opened c2s session for ~ts", - [xmpp_socket:pp(Socket), jid:encode(JID)]), - {ok, State2}; - deny -> - ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]), - ?WARNING_MSG("(~ts) Forbidden c2s session for ~ts", - [xmpp_socket:pp(Socket), jid:encode(JID)]), - Txt = ?T("Access denied by service policy"), - {error, xmpp:err_not_allowed(Txt, Lang), State} - end +bind( + R, + #{ + user := U, + server := S, + lserver := LServer, + access := Access, + lang := Lang, + socket := Socket, + ip := IP + }=State +) -> + case ejabberd_hooks:run_fold(c2s_handle_bind, LServer, {R, {ok, State}}, []) of + {R2, {ok, State2}} -> + case resource_conflict_action(U, S, R2) of + closenew -> + {error, xmpp:err_conflict(), State2}; + {accept_resource, Resource} -> + JID = jid:make(U, S, Resource), + case acl:match_rule(LServer, Access, #{usr => jid:split(JID), ip => IP}) of + allow -> + State3 = open_session( + State2#{resource => Resource, sid => ejabberd_sm:make_sid()} + ), + State4 = ejabberd_hooks:run_fold( + c2s_session_opened, LServer, State3, [] + ), + ?INFO_MSG( + "(~ts) Opened c2s session for ~ts", [xmpp_socket:pp(Socket), jid:encode(JID)] + ), + {ok, State4}; + deny -> + ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]), + ?WARNING_MSG( + "(~ts) Forbidden c2s session for ~ts", + [xmpp_socket:pp(Socket), jid:encode(JID)] + ), + Txt = ?T("Access denied by service policy"), + {error, xmpp:err_not_allowed(Txt, Lang), State2} + end + end; + {R2, {error, XmppErr, _State2}=Err} -> + case XmppErr of + #stanza_error{reason = 'not-allowed'} -> + JID = jid:make(U, S, R2), + ejabberd_hooks:run(forbidden_session_hook, LServer, [JID]), + ?WARNING_MSG( + "(~ts) Forbidden c2s session for ~ts", + [xmpp_socket:pp(Socket), jid:encode(JID)] + ); + _ -> + ok + end, + Err end. handle_stream_start(StreamStart, #{lserver := LServer} = State) -> From b38d8618b269fee1d76f33d5c3b091eae0400f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 26 Feb 2025 11:17:55 +0100 Subject: [PATCH 0971/1302] Fix crashes when ouath is feed with invalid jid This should fix issue #4355 --- src/ejabberd_oauth.erl | 191 ++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 88 deletions(-) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 5275673fe..971f23402 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -595,61 +595,71 @@ process(_Handlers, RedirectURI = proplists:get_value(<<"redirect_uri">>, Q, <<"">>), SScope = proplists:get_value(<<"scope">>, Q, <<"">>), StringJID = proplists:get_value(<<"username">>, Q, <<"">>), - #jid{user = Username, server = Server} = jid:decode(StringJID), - Password = proplists:get_value(<<"password">>, Q, <<"">>), - State = proplists:get_value(<<"state">>, Q, <<"">>), - Scope = str:tokens(SScope, <<" ">>), - TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), - ExpiresIn = case TTL of - <<>> -> undefined; - _ -> binary_to_integer(TTL) - end, - case oauth2:authorize_password({Username, Server}, - ClientId, - RedirectURI, - Scope, - #oauth_ctx{password = Password}) of - {ok, {_AppContext, Authorization}} -> - {ok, {_AppContext2, Response}} = - oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), - {ok, AccessToken} = oauth2_response:access_token(Response), - {ok, Type} = oauth2_response:token_type(Response), - %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have - %%per-case expirity time. - Expires = case ExpiresIn of - undefined -> - {ok, Ex} = oauth2_response:expires_in(Response), - Ex; - _ -> - ExpiresIn - end, - {ok, VerifiedScope} = oauth2_response:scope(Response), - %oauth2_wrq:redirected_access_token_response(ReqData, - % RedirectURI, - % AccessToken, - % Type, - % Expires, - % VerifiedScope, - % State, - % Context); - {302, [{<<"Location">>, - <>))/binary, - "&state=", State/binary>> - }], - ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}; - {error, Error} when is_atom(Error) -> - %oauth2_wrq:redirected_error_response( - % ReqData, RedirectURI, Error, State, Context) - {302, [{<<"Location">>, - <>, <<"302 Found">>)])} + try jid:decode(StringJID) of + #jid{user = Username, server = Server} -> + Password = proplists:get_value(<<"password">>, Q, <<"">>), + State = proplists:get_value(<<"state">>, Q, <<"">>), + Scope = str:tokens(SScope, <<" ">>), + TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), + ExpiresIn = case TTL of + <<>> -> undefined; + _ -> binary_to_integer(TTL) + end, + case oauth2:authorize_password({Username, Server}, + ClientId, + RedirectURI, + Scope, + #oauth_ctx{password = Password}) of + {ok, {_AppContext, Authorization}} -> + {ok, {_AppContext2, Response}} = + oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined]), + {ok, AccessToken} = oauth2_response:access_token(Response), + {ok, Type} = oauth2_response:token_type(Response), + %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have + %%per-case expirity time. + Expires = case ExpiresIn of + undefined -> + {ok, Ex} = oauth2_response:expires_in(Response), + Ex; + _ -> + ExpiresIn + end, + {ok, VerifiedScope} = oauth2_response:scope(Response), + %oauth2_wrq:redirected_access_token_response(ReqData, + % RedirectURI, + % AccessToken, + % Type, + % Expires, + % VerifiedScope, + % State, + % Context); + {302, [{<<"Location">>, + <>))/binary, + "&state=", State/binary>> + }], + ejabberd_web:make_xhtml([?XC(<<"h1">>, <<"302 Found">>)])}; + {error, Error} when is_atom(Error) -> + %oauth2_wrq:redirected_error_response( + % ReqData, RedirectURI, Error, State, Context) + {302, [{<<"Location">>, + <>, <<"302 Found">>)])} + end + catch _:{bad_jid, _} -> + State = proplists:get_value(<<"state">>, Q, <<"">>), + {400, [{<<"Location">>, + <>, <<"400 Invalid request">>)])} end; process(_Handlers, #request{method = 'POST', q = Q, lang = _Lang, @@ -701,38 +711,42 @@ process(_Handlers, password -> SScope = proplists:get_value(<<"scope">>, Q, <<"">>), StringJID = proplists:get_value(<<"username">>, Q, <<"">>), - #jid{user = Username, server = Server} = jid:decode(StringJID), - Password = proplists:get_value(<<"password">>, Q, <<"">>), - Scope = str:tokens(SScope, <<" ">>), - TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), - ExpiresIn = case TTL of - <<>> -> undefined; - _ -> binary_to_integer(TTL) - end, - case oauth2:authorize_password({Username, Server}, - Scope, - #oauth_ctx{password = Password}) of - {ok, {_AppContext, Authorization}} -> - {ok, {_AppContext2, Response}} = - oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), - {ok, AccessToken} = oauth2_response:access_token(Response), - {ok, Type} = oauth2_response:token_type(Response), - %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have - %%per-case expirity time. - Expires = case ExpiresIn of - undefined -> - {ok, Ex} = oauth2_response:expires_in(Response), - Ex; - _ -> - ExpiresIn - end, - {ok, VerifiedScope} = oauth2_response:scope(Response), - json_response(200, #{<<"access_token">> => AccessToken, - <<"token_type">> => Type, - <<"scope">> => str:join(VerifiedScope, <<" ">>), - <<"expires_in">> => Expires}); - {error, Error} when is_atom(Error) -> - json_error(400, <<"invalid_grant">>, Error) + try jid:decode(StringJID) of + #jid{user = Username, server = Server} -> + Password = proplists:get_value(<<"password">>, Q, <<"">>), + Scope = str:tokens(SScope, <<" ">>), + TTL = proplists:get_value(<<"ttl">>, Q, <<"">>), + ExpiresIn = case TTL of + <<>> -> undefined; + _ -> binary_to_integer(TTL) + end, + case oauth2:authorize_password({Username, Server}, + Scope, + #oauth_ctx{password = Password}) of + {ok, {_AppContext, Authorization}} -> + {ok, {_AppContext2, Response}} = + oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined]), + {ok, AccessToken} = oauth2_response:access_token(Response), + {ok, Type} = oauth2_response:token_type(Response), + %%Ugly: workardound to return the correct expirity time, given than oauth2 lib doesn't really have + %%per-case expirity time. + Expires = case ExpiresIn of + undefined -> + {ok, Ex} = oauth2_response:expires_in(Response), + Ex; + _ -> + ExpiresIn + end, + {ok, VerifiedScope} = oauth2_response:scope(Response), + json_response(200, #{<<"access_token">> => AccessToken, + <<"token_type">> => Type, + <<"scope">> => str:join(VerifiedScope, <<" ">>), + <<"expires_in">> => Expires}); + {error, Error} when is_atom(Error) -> + json_error(400, <<"invalid_grant">>, Error) + end + catch _:{bad_jid, _} -> + json_error(400, <<"invalid_request">>, invalid_jid) end; unsupported_grant_type -> json_error(400, <<"unsupported_grant_type">>, @@ -774,7 +788,8 @@ json_error(Code, Error, Reason) -> json_error_desc(access_denied) -> <<"Access denied">>; json_error_desc(badpass) -> <<"Bad password">>; json_error_desc(unsupported_grant_type) -> <<"Unsupported grant type">>; -json_error_desc(invalid_scope) -> <<"Invalid scope">>. +json_error_desc(invalid_scope) -> <<"Invalid scope">>; +json_error_desc(invalid_jid) -> <<"Invalid JID">>. web_head() -> [?XA(<<"meta">>, [{<<"http-equiv">>, <<"X-UA-Compatible">>}, From 54f89e75687333113c4f55a42d571bfba97eb1df Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Feb 2025 12:16:51 +0100 Subject: [PATCH 0972/1302] Update XEP-0280 supported version to 1.0.1 --- src/mod_carboncopy.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index dfd723c99..b2e1ff544 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -27,7 +27,7 @@ -module (mod_carboncopy). -author ('ecestari@process-one.net'). --protocol({xep, 280, '0.13.2', '13.06', "complete", ""}). +-protocol({xep, 280, '1.0.1', '13.06', "complete", ""}). -behaviour(gen_mod). From 6af2a65e53b8cc7e7b74a937c060c2ba04d41e4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:31:59 +0000 Subject: [PATCH 0973/1302] build(deps): bump stringprep from 1.0.30 to 1.0.31 Bumps [stringprep](https://github.com/processone/stringprep) from 1.0.30 to 1.0.31. - [Changelog](https://github.com/processone/stringprep/blob/master/CHANGELOG.md) - [Commits](https://github.com/processone/stringprep/compare/1.0.30...1.0.31) --- updated-dependencies: - dependency-name: stringprep dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 8eb5566d3..28d2412ce 100644 --- a/mix.lock +++ b/mix.lock @@ -31,7 +31,7 @@ "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, - "stringprep": {:hex, :stringprep, "1.0.30", "46cf0ff631b3e7328f61f20b454d59428d87738f25d709798b5dcbb9b83c23f1", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6fc9b3384a03877830f89b2f38580caf3f4a27448a4a333d6a8c3975c220b9a"}, + "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "xmpp": {:git, "https://github.com/processone/xmpp", "e295055dd1cb3dafb4b3134bc7462a775ff16b5c", [ref: "e295055dd1cb3dafb4b3134bc7462a775ff16b5c"]}, From c124dbdd6af30d99613487d8c0b299908135ccec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:32:10 +0000 Subject: [PATCH 0974/1302] build(deps-dev): bump ex_doc from 0.37.1 to 0.37.2 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.37.1 to 0.37.2. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.37.1...v0.37.2) --- updated-dependencies: - dependency-name: ex_doc dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 28d2412ce..2b1065bf8 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, - "ex_doc": {:hex, :ex_doc, "0.37.1", "65ca30d242082b95aa852b3b73c9d9914279fff56db5dc7b3859be5504417980", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "6774f75477733ea88ce861476db031f9399c110640752ca2b400dbbb50491224"}, + "ex_doc": {:hex, :ex_doc, "0.37.2", "2a3aa7014094f0e4e286a82aa5194a34dd17057160988b8509b15aa6c292720c", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "4dfa56075ce4887e4e8b1dcc121cd5fcb0f02b00391fd367ff5336d98fa49049"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, From 70980efe399080fb94e777c9aa8d4b009de2aa52 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 23:22:11 +0100 Subject: [PATCH 0975/1302] make-binaries: Bump versions to Erlang/OTP 27.3 and Elixir 1.18.3 --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index e8b5cc75b..eaf0b1557 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,8 +71,8 @@ expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.4.1' -otp_vsn='27.2.2' -elixir_vsn='1.18.2' +otp_vsn='27.3' +elixir_vsn='1.18.3' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' jpeg_vsn='9f' From 178b09f5bb65a147a5733a00c2b858337471c8a9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 15:57:57 +0100 Subject: [PATCH 0976/1302] ejabberd_admin: Fix crash in list_cluster_detailed when a node is down --- src/ejabberd_admin.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 2452e86b7..c6e955ab2 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -931,14 +931,14 @@ get_cluster_node_details(Node, RunningNodes) -> get_cluster_node_details2(Node, lists:member(Node, RunningNodes)). get_cluster_node_details2(Node, false) -> - {Node, "false", "", -1, -1, -1, "unknown"}; + {Node, "false", "", -1, -1, -1, unknown}; get_cluster_node_details2(Node, true) -> try ejabberd_cluster:call(Node, ejabberd_admin, get_cluster_node_details3, []) of Result -> Result catch E:R -> Status = io_lib:format("~p: ~p", [E, R]), - {Node, "true", Status, -1, -1, -1, "unknown"} + {Node, "true", Status, -1, -1, -1, unknown} end. get_cluster_node_details3() -> From b75c3257bd8bc125c5b72070521cadf74c65c748 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Feb 2025 16:37:33 +0100 Subject: [PATCH 0977/1302] ejabberd_oauth: Commands description should be plain text The desc field is displayed in "ejabberdctl help", which has no markdown processing. If adding links is important, they can be added in the longdesc field. --- src/ejabberd_oauth.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 971f23402..a18596d46 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -83,7 +83,7 @@ get_commands_spec() -> [ #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an _`oauth.md|OAuth`_ token for the given jid", + desc = "Issue an OAuth token for the given jid", module = ?MODULE, function = oauth_issue_token, args = [{jid, string},{ttl, integer}, {scopes, string}], policy = restricted, @@ -94,7 +94,7 @@ get_commands_spec() -> result = {result, {tuple, [{token, string}, {scopes, string}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_issue_token, tags = [oauth], - desc = "Issue an _`oauth.md|OAuth`_ optionredir token for the given jid", + desc = "Issue an OAuth token for the given jid", module = ?MODULE, function = oauth_issue_token, version = 1, note = "updated in 24.02", @@ -107,15 +107,15 @@ get_commands_spec() -> result = {result, {tuple, [{token, string}, {scopes, {list, {scope, string}}}, {expires_in, string}]}} }, #ejabberd_commands{name = oauth_list_tokens, tags = [oauth], - desc = "List _`oauth.md|OAuth`_ tokens, user, scope, and seconds to expire (only Mnesia)", - longdesc = "List OAuth tokens, their user and scope, and how many seconds remain until expirity", + desc = "List OAuth tokens, user, scope, and seconds to expire (only Mnesia)", + longdesc = "List _`oauth.md|OAuth`_ tokens, their user and scope, and how many seconds remain until expiry", module = ?MODULE, function = oauth_list_tokens, args = [], policy = restricted, result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}} }, #ejabberd_commands{name = oauth_revoke_token, tags = [oauth], - desc = "Revoke authorization for an _`oauth.md|OAuth`_ token", + desc = "Revoke authorization for an OAuth token", note = "changed in 22.05", module = ?MODULE, function = oauth_revoke_token, args = [{token, binary}], @@ -124,7 +124,7 @@ get_commands_spec() -> result_desc = "Result code" }, #ejabberd_commands{name = oauth_add_client_password, tags = [oauth], - desc = "Add _`oauth.md|OAuth`_ client_id with password grant type", + desc = "Add OAuth client_id with password grant type", module = ?MODULE, function = oauth_add_client_password, args = [{client_id, binary}, {client_name, binary}, @@ -133,7 +133,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_add_client_implicit, tags = [oauth], - desc = "Add _`oauth.md|OAuth`_ client_id with implicit grant type", + desc = "Add OAuth client_id with implicit grant type", module = ?MODULE, function = oauth_add_client_implicit, args = [{client_id, binary}, {client_name, binary}, @@ -142,7 +142,7 @@ get_commands_spec() -> result = {res, restuple} }, #ejabberd_commands{name = oauth_remove_client, tags = [oauth], - desc = "Remove _`oauth.md|OAuth`_ client_id", + desc = "Remove OAuth client_id", module = ?MODULE, function = oauth_remove_client, args = [{client_id, binary}], policy = restricted, From 90c4fa2a842522b7708cce06ba655d13db58fab2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 22:01:22 +0100 Subject: [PATCH 0978/1302] mod_admin_extra: If policy=user, ejabberd adds the user/host args --- src/mod_admin_extra.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index d5149708f..11b0fbb95 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -729,7 +729,6 @@ get_commands_spec() -> policy = user, module = ?MODULE, function = get_roster_count, args = [], - args_example = [<<"sun">>, <<"localhost">>], args_rename = [{server, host}], result_example = 5, result_desc = "Number", From 6bf59307033b04df1e05a13df4faf1bde9c9550d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 6 Mar 2025 18:28:53 +0100 Subject: [PATCH 0979/1302] mod_announce: Improve documentation syntax --- src/mod_announce.erl | 49 ++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 366a81e42..b382dc3a6 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -920,38 +920,43 @@ mod_doc() -> [?T("This module enables configured users to broadcast " "announcements and to set the message of the day (MOTD). " "Configured users can perform these actions with an XMPP " - "client either using Ad-hoc Commands or sending messages " + "client either using Ad-Hoc Commands or sending messages " "to specific JIDs."), "", - ?T("Note that this module can be resource intensive on large " + ?T("NOTE: This module can be resource intensive on large " "deployments as it may broadcast a lot of messages. This module " "should be disabled for instances of ejabberd with hundreds of " "thousands users."), "", - ?T("The Ad-hoc Commands are listed in the Server Discovery. " - "For this feature to work, _`mod_adhoc`_ must be enabled."), "", - ?T("The specific JIDs where messages can be sent are listed below. " - "The first JID in each entry will apply only to the specified " - "virtual host example.org, while the JID between brackets " - "will apply to all virtual hosts in ejabberd:"), "", - "- example.org/announce/all (example.org/announce/all-hosts/all)::", - ?T("The message is sent to all registered users. If the user is " + ?T("To send announcements using " + "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands], " + "this module requires _`mod_adhoc`_ (to execute the commands), " + "and recommends _`mod_disco`_ (to discover the commands)."), "", + ?T("To send announcements by sending messages to specific JIDs, these are the destination JIDs:"), "", + "- 'example.org/announce/all':", + ?T("Send the message to all registered users in that vhost. If the user is " "online and connected to several resources, only the resource " "with the highest priority will receive the message. " - "If the registered user is not connected, the message will be " + "If the registered user is not connected, the message is " "stored offline in assumption that offline storage (see _`mod_offline`_) " "is enabled."), - "- example.org/announce/online (example.org/announce/all-hosts/online)::", - ?T("The message is sent to all connected users. If the user is " + "- 'example.org/announce/online':", + ?T("Send the message to all connected users. If the user is " "online and connected to several resources, all resources will " "receive the message."), - "- example.org/announce/motd (example.org/announce/all-hosts/motd)::", - ?T("The message is set as the message of the day (MOTD) and is sent " - "to users when they login. In addition the message is sent to all " - "connected users (similar to announce/online)."), - "- example.org/announce/motd/update (example.org/announce/all-hosts/motd/update)::", - ?T("The message is set as message of the day (MOTD) and is sent to users " - "when they login. The message is not sent to any currently connected user."), - "- example.org/announce/motd/delete (example.org/announce/all-hosts/motd/delete)::", - ?T("Any message sent to this JID removes the existing message of the day (MOTD).")], + "- 'example.org/announce/motd':", + ?T("Set the message of the day (MOTD) that is sent " + "to users when they login. Also sends the message to all " + "connected users (similar to 'announce/online')."), + "- 'example.org/announce/motd/update':", + ?T("Set the message of the day (MOTD) that is sent to users " + "when they login. This does not send the message to any currently connected user."), + "- 'example.org/announce/motd/delete':", + ?T("Remove the existing message of the day (MOTD) by sending a message to this JID."), "", + ?T("There are similar destination JIDs to apply to all virtual hosts in ejabberd:"), "", + "- 'example.org/announce/all-hosts/all': send to all registered accounts", + "- 'example.org/announce/all-hosts/online': send to online sessions", + "- 'example.org/announce/all-hosts/motd': set MOTD and send to online", + "- 'example.org/announce/all-hosts/motd/update': update MOTD", + "- 'example.org/announce/all-hosts/motd/delete': delete MOTD"], opts => [{access, #{value => ?T("AccessName"), From 087718c47a1da1871e3eda1bdee80be0eb9d350a Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Tue, 11 Mar 2025 15:05:54 +0100 Subject: [PATCH 0980/1302] fix greedy include path (fixes #4359) --- src/ext_mod.erl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 451467c66..387ef1bd4 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -573,7 +573,7 @@ compile_and_install(Module, Spec, Config) -> true -> case compile_deps(SrcDir) of ok -> - case compile(SrcDir) of + case compile(SrcDir, filename:join(SrcDir, "deps")) of ok -> install(Module, Spec, SrcDir, LibDir, Config); Error -> Error end; @@ -589,25 +589,28 @@ compile_and_install(Module, Spec, Config) -> end. compile_deps(LibDir) -> - Deps = filename:join(LibDir, "deps"), - case filelib:is_dir(Deps) of + DepsDir = filename:join(LibDir, "deps"), + case filelib:is_dir(DepsDir) of true -> ok; % assume deps are included false -> fetch_rebar_deps(LibDir) end, - Rs = [compile(Dep) || Dep <- filelib:wildcard(filename:join(Deps, "*"))], + Rs = [compile(Dep, DepsDir) || Dep <- filelib:wildcard(filename:join(DepsDir, "*"))], compile_result(Rs). -compile(LibDir) -> +compile(LibDir, DepsDir) -> Bin = filename:join(LibDir, "ebin"), Lib = filename:join(LibDir, "lib"), Src = filename:join(LibDir, "src"), - Includes = [{i, Inc} || Inc <- filelib:wildcard(LibDir++"/../../**/include")], + Includes = [{i, Inc} || Inc <- filelib:wildcard(DepsDir++"/**/include")], Options = [{outdir, Bin}, {i, LibDir++"/.."} | Includes ++ compile_options()], + ?DEBUG("compile options: ~p", [Options]), filelib:ensure_dir(filename:join(Bin, ".")), [copy(App, filename:join(Bin, filename:basename(App, ".src"))) || App <- filelib:wildcard(Src++"/*.app*")], compile_c_files(LibDir), + ErlFiles = filelib:wildcard(Src++"/**/*.erl"), + ?DEBUG("erl files to compile: ~p", [ErlFiles]), Er = [compile_erlang_file(Bin, File, Options) - || File <- filelib:wildcard(Src++"/**/*.erl")], + || File <- ErlFiles], Ex = compile_elixir_files(Bin, filelib:wildcard(Lib ++ "/**/*.ex")), compile_result(lists:flatten([Er, Ex])). From 0145594adcefe09c6de35d67d5d05edc656f07ce Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Wed, 12 Mar 2025 08:11:07 +0100 Subject: [PATCH 0981/1302] add $libdir/include to include path --- src/ext_mod.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 387ef1bd4..e6ee3fa76 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -601,8 +601,11 @@ compile(LibDir, DepsDir) -> Bin = filename:join(LibDir, "ebin"), Lib = filename:join(LibDir, "lib"), Src = filename:join(LibDir, "src"), - Includes = [{i, Inc} || Inc <- filelib:wildcard(DepsDir++"/**/include")], - Options = [{outdir, Bin}, {i, LibDir++"/.."} | Includes ++ compile_options()], + Includes = [ {i, Inc} || Inc <- filelib:wildcard(DepsDir++"/**/include") ], + Options = [ {outdir, Bin}, + {i, LibDir++"/.."}, + {i, filename:join(LibDir, "include")} + | Includes ++ compile_options()], ?DEBUG("compile options: ~p", [Options]), filelib:ensure_dir(filename:join(Bin, ".")), [copy(App, filename:join(Bin, filename:basename(App, ".src"))) || App <- filelib:wildcard(Src++"/*.app*")], From 27f98f50d309ea999c9ee4f3d2c4a47feb24152c Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Wed, 12 Mar 2025 08:11:25 +0100 Subject: [PATCH 0982/1302] fix typo --- src/ejabberd_hooks.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 39a8c812c..0fedc1dfc 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -152,7 +152,7 @@ unsubscribe(Hook, Host, Module, Function, InitArg) -> -spec run(atom(), list()) -> ok. -%% @doc Run the calls (and subscibers) of this hook in order, don't care about function results. +%% @doc Run the calls (and subscribers) of this hook in order, don't care about function results. %% If a call returns stop, no more calls are performed. run(Hook, Args) -> run(Hook, global, Args). @@ -475,7 +475,7 @@ call_subscriber_list([], _Host, _Hook, _CallbackOrArgs, _Event, Result) -> lists:reverse(Result); call_subscriber_list([{Mod, Func, InitArg} | SubscriberList], Host, Hook, CallbackOrArgs, Event, Result) -> SubscriberArgs = [InitArg, Event, Host, Hook, CallbackOrArgs], - ?DEBUG("Running hook subsciber ~p: ~p:~p/~B with event ~p", + ?DEBUG("Running hook subscriber ~p: ~p:~p/~B with event ~p", [Hook, Mod, Func, length(SubscriberArgs), Event]), try apply(Mod, Func, SubscriberArgs) of State -> From afc54aeb20b600d5b22fde6750c8f454504ca9db Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 12 Mar 2025 12:42:42 +0100 Subject: [PATCH 0983/1302] ext_mod: Provide output path when mix unpacks dependency from hex When installing a module with mix available in the system, mix downloads and uncompresses the dependency. By default the output dir is - [1], and that breaks compilation of the prometheus library: .../sources/ejabberd-contrib/mod_prometheus/deps/prometheus-4.11.0/src/metrics/prometheus_quantile_summary.erl:67:14: can't find include lib "quantile_estimator/include/quantile_estimator.hrl" % 67| -include_lib("quantile_estimator/include/quantile_estimator.hrl"). [1] https://hexdocs.pm/hex/Mix.Tasks.Hex.Package.html#module-command-line-options --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index e6ee3fa76..2d34a91f9 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -798,7 +798,7 @@ rebar_dep({App, Version, Git}) when Version /= ".*" -> Help = os:cmd("mix hex.package"), case string:find(Help, "mix hex.package fetch") /= nomatch of true -> - {App, "mix hex.package fetch "++AppS++" "++Version++" --unpack"}; + {App, "mix hex.package fetch "++AppS++" "++Version++" --unpack --output "++AppS}; false -> io:format("I'll download ~p using git because I can't use Mix " "to fetch from hex.pm:~n~s", [AppS, Help]), From bb2f398fa21cdd9b710b46f5a5d2f7ad01289587 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 Jan 2025 12:21:44 +0100 Subject: [PATCH 0984/1302] ejabberd_listener: Handle unix socket when logging remote client --- src/ejabberd_listener.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 2412eb545..1e822992f 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -562,10 +562,12 @@ format_error(Reason) -> end. -spec format_endpoint(endpoint()) -> string(). -format_endpoint({Port, IP, _Transport}) -> +format_endpoint({Port, IP, Transport}) -> case Port of <<"unix:", _/binary>> -> Port; + <<>> when (IP == local) and (Transport == tcp) -> + "local-unix-socket-domain"; Unix when is_binary(Unix) -> Def = get_definitive_udsocket_path(Unix), <<"unix:", Def/binary>>; From c9a9585573ef49e1d87653225256078545abd592 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Jan 2025 12:30:38 +0100 Subject: [PATCH 0985/1302] ejabberd_listener: Add support for socket relative path If the 'port' option is set to "unix:some-filename" without absolute path, then the file is created in the mnesia spool directory --- src/ejabberd_listener.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 1e822992f..639c58153 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -236,7 +236,7 @@ get_definitive_udsocket_path(<<"unix", _>> = Unix) -> get_definitive_udsocket_path(ProvisionalPath) -> PathBase64 = filename:basename(ProvisionalPath), {term, Path} = misc:base64_to_term(PathBase64), - Path. + relative_socket_to_mnesia(Path). set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> Prov = get_provisional_udsocket_path(Path), @@ -268,10 +268,19 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> throw({error_setting_socket_group, Group, Prov}) end end, - file:rename(Prov, Path); + file:rename(Prov, relative_socket_to_mnesia(Path)); set_definitive_udsocket(_Port, _Opts) -> ok. +relative_socket_to_mnesia(Path1) -> + case filename:pathtype(Path1) of + absolute -> + Path1; + relative -> + MnesiaDir = mnesia:system_info(directory), + filename:join(MnesiaDir, Path1) + end. + %%% %%% %%% From 71dbbc1b5a9373326c4f0e8a34d90ff9fb7d83a5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 7 Jan 2025 13:53:26 +0100 Subject: [PATCH 0986/1302] ejabberd_listener: Use /tmp for temporary socket, as path is restricted to 107 chars --- src/ejabberd_listener.erl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 639c58153..bac69f19b 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -216,18 +216,13 @@ listen_tcp(Port, SockOpts) -> setup_provisional_udsocket_dir(DefinitivePath) -> ProvisionalPath = get_provisional_udsocket_path(DefinitivePath), - SocketDir = filename:dirname(ProvisionalPath), - file:make_dir(SocketDir), - file:change_mode(SocketDir, 8#00700), ?DEBUG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", [ProvisionalPath, DefinitivePath]), ProvisionalPath. get_provisional_udsocket_path(Path) -> - MnesiaDir = mnesia:system_info(directory), - SocketDir = filename:join(MnesiaDir, "socket"), PathBase64 = misc:term_to_base64(Path), - PathBuild = filename:join(SocketDir, PathBase64), + PathBuild = filename:join(os:getenv("HOME"), PathBase64), %% Shorthen the path, a long path produces a crash when opening the socket. binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}). @@ -268,7 +263,15 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> throw({error_setting_socket_group, Group, Prov}) end end, - file:rename(Prov, relative_socket_to_mnesia(Path)); + FinalPath = relative_socket_to_mnesia(Path), + FinalPathDir = filename:dirname(FinalPath), + case file:make_dir(FinalPathDir) of + ok -> + file:change_mode(FinalPathDir, 8#00700); + _ -> + ok + end, + file:rename(Prov, FinalPath); set_definitive_udsocket(_Port, _Opts) -> ok. From c5abe5d882d9495a20dc89bf6dea7377cb06c5ae Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Jan 2025 14:52:16 +0100 Subject: [PATCH 0987/1302] ejabberd_listener: Apparently the sleep 5 seconds isn't needed anymore --- src/ejabberd_listener.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index bac69f19b..be7d91914 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -235,7 +235,6 @@ get_definitive_udsocket_path(ProvisionalPath) -> set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> Prov = get_provisional_udsocket_path(Path), - timer:sleep(5000), Usd = maps:get(unix_socket, Opts), case maps:get(mode, Usd, undefined) of undefined -> ok; From d4fd987e84ef9b1ff4fa7c4f94bad4b936b2c019 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Jan 2025 13:26:03 +0100 Subject: [PATCH 0988/1302] ejabberd_listener: When stopping listener, delete Unix Domain Socket file --- src/ejabberd_listener.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index be7d91914..5b5cdeb20 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -283,6 +283,12 @@ relative_socket_to_mnesia(Path1) -> filename:join(MnesiaDir, Path1) end. +maybe_delete_udsocket_file(<<"unix:", Path/binary>>) -> + PathAbsolute = relative_socket_to_mnesia(Path), + file:delete(PathAbsolute); +maybe_delete_udsocket_file(_Port) -> + ok. + %%% %%% %%% @@ -481,12 +487,13 @@ stop_listeners() -> Ports). -spec stop_listener(endpoint(), module(), opts()) -> ok | {error, any()}. -stop_listener({_, _, Transport} = EndPoint, Module, Opts) -> +stop_listener({Port, _, Transport} = EndPoint, Module, Opts) -> case supervisor:terminate_child(?MODULE, EndPoint) of ok -> ?INFO_MSG("Stop accepting ~ts connections at ~ts for ~p", [format_transport(Transport, Opts), format_endpoint(EndPoint), Module]), + maybe_delete_udsocket_file(Port), ets:delete(?MODULE, EndPoint), supervisor:delete_child(?MODULE, EndPoint); Err -> From 630301a7bac6632359ad5ebce3b0deb4edee83ad Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 16 Jan 2025 17:51:57 +0100 Subject: [PATCH 0989/1302] ejabberdctl: Document the --auth option The expected placement of --auth is not arbitrary, it should be provided immediately before the command+args --- src/ejabberd_ctl.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 6fcd46e1c..1504c2609 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -550,7 +550,8 @@ print_usage(HelpMode, MaxC, ShCode, Version) -> AllCommands = get_list_commands(Version), print( - ["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("nodename"), "] [--version ", ?A("api_version"), "] ", + ["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("name"), "] [--version ", ?A("apiv"), "] ", + "[--auth ", ?A("user host pass"), "] ", ?C("command"), " [", ?A("arguments"), "]\n" "\n" "Available commands in this ejabberd node:\n"], []), From ab8a39e71fdc893c4bd21aae5e3d446b0fe78bee Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Jan 2025 13:25:04 +0100 Subject: [PATCH 0990/1302] ejabberdctl: Improve explanation how to stop ejabberd in live mode --- ejabberdctl.template | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index b9f01535b..37e018b08 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -179,7 +179,9 @@ livewarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To exit and detach this shell from ejabberd, press:" + echo "To stop ejabberd gracefully:" + echo " ejabberd:stop()." + echo "To quit erlang immediately, press:" echo " control+g and then q" echo "" echo "--------------------------------------------------------------------" From 46a64c0f68007370f478ccd5d6b93adfb2fc14cd Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 Jan 2025 19:05:58 +0100 Subject: [PATCH 0991/1302] New ejabberdctl option CTL_OVER_HTTP This uses an HTTP connection to execute the command, which is way faster than starting an erlang node --- ejabberdctl.cfg.example | 11 +++++++++ ejabberdctl.template | 52 +++++++++++++++++++++++++++++++++-------- src/ejabberd_ctl.erl | 49 +++++++++++++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/ejabberdctl.cfg.example b/ejabberdctl.cfg.example index 6887fb4cc..88f99cd78 100644 --- a/ejabberdctl.cfg.example +++ b/ejabberdctl.cfg.example @@ -198,6 +198,17 @@ # #CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules +#. +#' CTL_OVER_HTTP: Path to ejabberdctl HTTP listener socket +# +# To speedup ejabberdctl execution time for ejabberd commands, +# you can setup an ejabberd_http listener with ejabberd_ctl handling requests, +# listening in a unix domain socket. +# +# Default: disabled +# +#CTL_OVER_HTTP=sockets/ctl_over_http.sock + #. #' # vim: foldmarker=#',#. foldmethod=marker: diff --git a/ejabberdctl.template b/ejabberdctl.template index 37e018b08..bf590acba 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -334,6 +334,47 @@ wait_status() [ $timeout -gt 0 ] } +exec_other_command() +{ + if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ + || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ + || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then + exec_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" + result=$? + case $result in + 3) help;; + *) :;; + esac + exit $result + else + exec_ctl_over_http_socket "$@" + fi +} + +exec_ctl_over_http_socket() +{ + CARGS='{"ctl-command-line": "'${*}'"}' + TEMPHEADERS=temp-headers.log + curl \ + --unix-socket ${CTL_OVER_HTTP} \ + --header "Content-Type: application/json" \ + --header "Accept: application/json" \ + --data "${CARGS}" \ + --dump-header ${TEMPHEADERS} \ + --no-progress-meter \ + "http://localhost/ctl/${1}" + result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS) + rm ${TEMPHEADERS} + case $result in + 2|3) exec_other_command help ${1};; + *) :;; + esac + exit $result +} + # ensure we can change current directory to SPOOL_DIR [ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR" cd "$SPOOL_DIR" || { @@ -402,15 +443,6 @@ case $1 in ;; *) set_dist_client - exec_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ - -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" - result=$? - case $result in - 2|3) help;; - *) :;; - esac - exit $result + exec_other_command "$@" ;; esac diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 1504c2609..9533632f8 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -28,7 +28,7 @@ -behaviour(gen_server). -author('alexey@process-one.net'). --export([start/0, start_link/0, process/1, process2/2]). +-export([start/0, start_link/0, process/1, process/2, process2/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -37,6 +37,7 @@ -include("ejabberd_ctl.hrl"). -include("ejabberd_commands.hrl"). +-include("ejabberd_http.hrl"). -include("logger.hrl"). -include("ejabberd_stacktrace.hrl"). @@ -115,15 +116,51 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%----------------------------- -%% Process +%% Process http +%%----------------------------- + +-spec process_http([binary()], tuple()) -> {non_neg_integer(), [{binary(), binary()}], string()}. + +process_http([Call], #request{data = Data} = Request) when is_binary(Call) and is_record(Request, request) -> + [{<<"ctl-command-line">>, LineBin}] = extract_args(Data), + LineStrings = string:split(binary_to_list(LineBin), " ", all), + process_http2(LineStrings, ?DEFAULT_VERSION). + +process_http2(["--version", Arg | Args], _) -> + Version = + try + list_to_integer(Arg) + catch _:_ -> + throw({invalid_version, Arg}) + end, + process_http2(Args, Version); + +process_http2(Args, Version) -> + {String, Code} = process2(Args, [], Version), + String2 = case String of + [] -> String; + _ -> [String, "\n"] + end, + {200, [{<<"status-code">>, integer_to_binary(Code)}], String2}. + +%% Be tolerant to make API more easily usable from command-line pipe. +extract_args(<<"\n">>) -> []; +extract_args(Data) -> + Maps = misc:json_decode(Data), + maps:to_list(Maps). + +%%----------------------------- +%% Process command line %%----------------------------- -spec process([string()]) -> non_neg_integer(). process(Args) -> process(Args, ?DEFAULT_VERSION). +-spec process([string() | binary()], non_neg_integer() | tuple()) -> non_neg_integer(). --spec process([string()], non_neg_integer()) -> non_neg_integer(). +process([Call], Request) when is_binary(Call) and is_record(Request, request) -> + process_http([Call], Request); %% The commands status, stop and restart are defined here to ensure %% they are usable even if ejabberd is completely stopped. @@ -232,7 +269,7 @@ process2(Args, AccessCommands, Auth, Version) -> io:format(lists:flatten(["\n" | String]++["\n"])), [CommandString | _] = Args, process(["help" | [CommandString]], Version), - {lists:flatten(String), ?STATUS_ERROR}; + {lists:flatten(String), ?STATUS_USAGE}; {String, Code} when is_list(String) and is_integer(Code) -> {lists:flatten(String), Code}; @@ -271,7 +308,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) -> try_call_command(Args, Auth, AccessCommands, Version); false -> print_usage(Version), - {"", ?STATUS_USAGE}; + {"", ?STATUS_BADRPC}; Status -> {"", Status} catch @@ -288,7 +325,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) -> try_call_command(Args, Auth, AccessCommands, Version) -> try call_command(Args, Auth, AccessCommands, Version) of {Reason, wrong_command_arguments} -> - {Reason, ?STATUS_ERROR}; + {Reason, ?STATUS_USAGE}; Res -> Res catch From f789495c3957d28537840ff092767c95f01c9987 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 16 Jan 2025 13:17:01 +0100 Subject: [PATCH 0992/1302] ejabberdctl: Improve method to pass command arguments --- ejabberdctl.template | 13 ++++++++++--- src/ejabberd_ctl.erl | 13 +++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index bf590acba..7858067d1 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -356,7 +356,14 @@ exec_other_command() exec_ctl_over_http_socket() { - CARGS='{"ctl-command-line": "'${*}'"}' + COMMAND=${1} + CARGS="" + while [ $# -gt 0 ]; do + [ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, " + CARGS="${CARGS}\"$1\"" + shift + done + CARGS="${CARGS}]" TEMPHEADERS=temp-headers.log curl \ --unix-socket ${CTL_OVER_HTTP} \ @@ -365,11 +372,11 @@ exec_ctl_over_http_socket() --data "${CARGS}" \ --dump-header ${TEMPHEADERS} \ --no-progress-meter \ - "http://localhost/ctl/${1}" + "http://localhost/ctl/${COMMAND}" result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS) rm ${TEMPHEADERS} case $result in - 2|3) exec_other_command help ${1};; + 2|3) exec_other_command help ${COMMAND};; *) :;; esac exit $result diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 9533632f8..72aac3780 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -121,10 +121,9 @@ code_change(_OldVsn, State, _Extra) -> -spec process_http([binary()], tuple()) -> {non_neg_integer(), [{binary(), binary()}], string()}. -process_http([Call], #request{data = Data} = Request) when is_binary(Call) and is_record(Request, request) -> - [{<<"ctl-command-line">>, LineBin}] = extract_args(Data), - LineStrings = string:split(binary_to_list(LineBin), " ", all), - process_http2(LineStrings, ?DEFAULT_VERSION). +process_http([_Call], #request{data = Data, path = [<<"ctl">> | _]}) -> + Args = [binary_to_list(E) || E <- misc:json_decode(Data)], + process_http2(Args, ?DEFAULT_VERSION). process_http2(["--version", Arg | Args], _) -> Version = @@ -143,12 +142,6 @@ process_http2(Args, Version) -> end, {200, [{<<"status-code">>, integer_to_binary(Code)}], String2}. -%% Be tolerant to make API more easily usable from command-line pipe. -extract_args(<<"\n">>) -> []; -extract_args(Data) -> - Maps = misc:json_decode(Data), - maps:to_list(Maps). - %%----------------------------- %% Process command line %%----------------------------- From 4d62f545c51b4a26b881de59031e08d79362980a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 Jan 2025 20:49:49 +0100 Subject: [PATCH 0993/1302] ejabberd_admin: Separate Status command result with newline This is useful for CTL_OVER_HTTP --- src/ejabberd_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index c6e955ab2..259831d7f 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -663,7 +663,7 @@ status() -> {value, {_, _, Version}} -> {ok, io_lib:format("ejabberd ~s is running in that node", [Version])} end, - {Is_running, String1 ++ " " ++String2}. + {Is_running, String1 ++ "\n" ++String2}. stop() -> _ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm), From e761b22c611ba310d1af2760e0e0f7ea182d8012 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Jan 2025 14:52:45 +0100 Subject: [PATCH 0994/1302] ejabberd_listener: When opening ctl_over_http connection, log in DEBUG --- src/ejabberd_listener.erl | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 5b5cdeb20..67f3db2de 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -358,11 +358,20 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) -> gen_tcp:close(Socket), none end, - ?INFO_MSG("(~p) Accepted connection ~ts -> ~ts", - [Receiver, - ejabberd_config:may_hide_data( - format_endpoint({PPort, PAddr, tcp})), - format_endpoint({Port, Addr, tcp})]); + case is_ctl_over_http(State) of + false -> + ?INFO_MSG("(~p) Accepted connection ~ts -> ~ts", + [Receiver, + ejabberd_config:may_hide_data( + format_endpoint({PPort, PAddr, tcp})), + format_endpoint({Port, Addr, tcp})]); + true -> + ?DEBUG("(~p) Accepted connection ~ts -> ~ts", + [Receiver, + ejabberd_config:may_hide_data( + format_endpoint({PPort, PAddr, tcp})), + format_endpoint({Port, Addr, tcp})]) + end; _ -> gen_tcp:close(Socket) end, @@ -373,6 +382,16 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) -> accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity) end. +is_ctl_over_http(State) -> + case lists:keyfind(request_handlers, 1, State) of + {request_handlers, Handlers} -> + case lists:keyfind(ejabberd_ctl, 2, Handlers) of + {_, ejabberd_ctl} -> true; + _ -> false + end; + _ -> false + end. + -spec udp_recv(inet:socket(), module(), state()) -> no_return(). udp_recv(Socket, Module, State) -> case gen_udp:recv(Socket, 0) of From 9f28098d04a87b0db232e2c4dec3aeb2c76b6da9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Dec 2024 18:26:24 +0100 Subject: [PATCH 0995/1302] Container: Copy files to stable path, add ecs backwards compatibility Copy captcha scripts to stable path for referencing in compose files: /usr/local/bin/ which is included in $PATH For backwards compatibility with ecs, link: /opt/ -> /home/ /usr/local/bin/ -> /opt/ejabberd/bin/ Copy sql files to stable path for referencing: /opt/ejabberd/sql/ For backwards compatibility with ecs, copy also to /opt/ejabberd/database/ ecs image implemented this in ejabberdctl since 2019: edb0373fd0ae0b24807a41ba2c3bf04b5b514844 Keep SQL init scripts in container (#42) --- .github/container/Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 2a44b9035..840ac2944 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -137,6 +137,11 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ ARG UID RUN chown -R $UID:$UID $HOME +ARG VERSION +RUN cp /rootfs/$HOME-$VERSION/lib/captcha*.sh usr/local/bin/ +RUN mkdir $HOME/sql \ + && find /rootfs/$HOME-$VERSION/lib/ -name *.sql -exec cp {} $HOME/sql \; -exec cp {} $HOME/database \; + ################################################################################ #' METHOD='direct' - Remove erlang/OTP & rebar3 FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-direct @@ -167,6 +172,10 @@ ARG HOME RUN addgroup $USER -g $UID \ && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER +RUN ln -fs /usr/local/bin/ /opt/ejabberd/bin +RUN rm -rf /home \ + && ln -fs /opt /home + ################################################################################ #' Build together production image FROM scratch AS prod From 3d49bed0cf500ae828367eb1585b1c511416651e Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Dec 2024 21:49:49 +0100 Subject: [PATCH 0996/1302] Container: Copy main example configuration file, will be customized --- .github/container/Dockerfile | 1 + .github/container/ejabberd.yml.example | 244 +++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 .github/container/ejabberd.yml.example diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 840ac2944..90a10ec38 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -50,6 +50,7 @@ COPY / $BUILD_DIR/ WORKDIR $BUILD_DIR RUN mv .github/container/ejabberdctl.template . \ + && mv .github/container/ejabberd.yml.example . \ && ./autogen.sh \ && ./configure --with-rebar=mix --enable-all \ && make deps \ diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example new file mode 100644 index 000000000..39e423a64 --- /dev/null +++ b/.github/container/ejabberd.yml.example @@ -0,0 +1,244 @@ +### +### ejabberd configuration file +### +### The parameters used in this configuration file are explained at +### +### https://docs.ejabberd.im/admin/configuration +### +### The configuration file is written in YAML. +### ******************************************************* +### ******* !!! WARNING !!! ******* +### ******* YAML IS INDENTATION SENSITIVE ******* +### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY ******* +### ******************************************************* +### Refer to http://en.wikipedia.org/wiki/YAML for the brief description. +### + +hosts: + - localhost + +loglevel: info + +## If you already have certificates, list them here +# certfiles: +# - /etc/letsencrypt/live/domain.tld/fullchain.pem +# - /etc/letsencrypt/live/domain.tld/privkey.pem + +listen: + - + port: 5222 + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + starttls_required: true + - + port: 5223 + ip: "::" + module: ejabberd_c2s + max_stanza_size: 262144 + shaper: c2s_shaper + access: c2s + tls: true + - + port: 5269 + ip: "::" + module: ejabberd_s2s_in + max_stanza_size: 524288 + shaper: s2s_shaper + - + port: 5443 + ip: "::" + module: ejabberd_http + tls: true + request_handlers: + /admin: ejabberd_web_admin + /api: mod_http_api + /bosh: mod_bosh + /captcha: ejabberd_captcha + /upload: mod_http_upload + /ws: ejabberd_http_ws + - + port: 5280 + ip: "::" + module: ejabberd_http + request_handlers: + /admin: ejabberd_web_admin + /.well-known/acme-challenge: ejabberd_acme + - + port: 5478 + ip: "::" + transport: udp + module: ejabberd_stun + use_turn: true + ## The server's public IPv4 address: + # turn_ipv4_address: "203.0.113.3" + ## The server's public IPv6 address: + # turn_ipv6_address: "2001:db8::3" + - + port: 1883 + ip: "::" + module: mod_mqtt + backlog: 1000 + +s2s_use_starttls: optional + +acl: + local: + user_regexp: "" + loopback: + ip: + - 127.0.0.0/8 + - ::1/128 + +access_rules: + local: + allow: local + c2s: + deny: blocked + allow: all + announce: + allow: admin + configure: + allow: admin + muc_create: + allow: local + pubsub_createnode: + allow: local + trusted_network: + allow: loopback + +api_permissions: + "console commands": + from: ejabberd_ctl + who: all + what: "*" + "webadmin commands": + from: ejabberd_web_admin + who: admin + what: "*" + "admin access": + who: + access: + allow: + - acl: loopback + - acl: admin + oauth: + scope: "ejabberd:admin" + access: + allow: + - acl: loopback + - acl: admin + what: + - "*" + - "!stop" + - "!start" + "public commands": + who: + ip: 127.0.0.1/8 + what: + - status + - connected_users_number + +shaper: + normal: + rate: 3000 + burst_size: 20000 + fast: 100000 + +shaper_rules: + max_user_sessions: 10 + max_user_offline_messages: + 5000: admin + 100: all + c2s_shaper: + none: admin + normal: all + s2s_shaper: fast + +modules: + mod_adhoc: {} + mod_admin_extra: {} + mod_announce: + access: announce + mod_avatar: {} + mod_blocking: {} + mod_bosh: {} + mod_caps: {} + mod_carboncopy: {} + mod_client_state: {} + mod_configure: {} + mod_disco: {} + mod_fail2ban: {} + mod_http_api: {} + mod_http_upload: + put_url: https://@HOST@:5443/upload + custom_headers: + "Access-Control-Allow-Origin": "https://@HOST@" + "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" + "Access-Control-Allow-Headers": "Content-Type" + mod_last: {} + mod_mam: + ## Mnesia is limited to 2GB, better to use an SQL backend + ## For small servers SQLite is a good fit and is very easy + ## to configure. Uncomment this when you have SQL configured: + ## db_type: sql + assume_mam_usage: true + default: always + mod_mqtt: {} + mod_muc: + access: + - allow + access_admin: + - allow: admin + access_create: muc_create + access_persistent: muc_create + access_mam: + - allow + default_room_options: + mam: true + mod_muc_admin: {} + mod_offline: + access_max_user_messages: max_user_offline_messages + mod_ping: {} + mod_privacy: {} + mod_private: {} + mod_proxy65: + access: local + max_connections: 5 + mod_pubsub: + access_createnode: pubsub_createnode + plugins: + - flat + - pep + force_node_config: + ## Avoid buggy clients to make their bookmarks public + storage:bookmarks: + access_model: whitelist + mod_push: {} + mod_push_keepalive: {} + mod_register: + ## Only accept registration requests from the "trusted" + ## network (see access_rules section above). + ## Think twice before enabling registration from any + ## address. See the Jabber SPAM Manifesto for details: + ## https://github.com/ge0rg/jabber-spam-fighting-manifesto + ip_access: trusted_network + mod_roster: + versioning: true + mod_s2s_bidi: {} + mod_s2s_dialback: {} + mod_shared_roster: {} + mod_stream_mgmt: + resend_on_timeout: if_offline + mod_stun_disco: {} + mod_vcard: {} + mod_vcard_xupdate: {} + mod_version: + show_os: false + +### Local Variables: +### mode: yaml +### End: +### vim: set filetype=yaml tabstop=8 From 090a7e664ecd60b9f7f6150fcd02f982bb08b7f2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Dec 2024 21:51:49 +0100 Subject: [PATCH 0997/1302] Container: Apply customizations directly in the configuration file --- .github/container/Dockerfile | 6 +----- .github/container/ejabberd.yml.example | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 90a10ec38..1cb2ace6b 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -68,11 +68,7 @@ RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete -RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \ - && sed -i '/^loglevel:/a \ \ - \nca_file: /opt/ejabberd/conf/cacert.pem \ - \ncertfiles: \ - \n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml" +RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' ################################################################################ #' METHOD='package' - install ejabberd from binary tarball package diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index 39e423a64..9aeab643a 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -24,6 +24,10 @@ loglevel: info # - /etc/letsencrypt/live/domain.tld/fullchain.pem # - /etc/letsencrypt/live/domain.tld/privkey.pem +ca_file: /opt/ejabberd/conf/cacert.pem +certfiles: + - /opt/ejabberd/conf/server.pem + listen: - port: 5222 From 7df7daa0507d5d6e786775ec8369615b720e0577 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Dec 2024 22:01:25 +0100 Subject: [PATCH 0998/1302] Container: Define and use macros in the configuration file --- .github/container/ejabberd.yml.example | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index 9aeab643a..613f38d41 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -14,8 +14,20 @@ ### Refer to http://en.wikipedia.org/wiki/YAML for the brief description. ### +define_macro: + HOST: localhost + ADMIN: "admin@localhost" + PORT_C2S: 5222 + PORT_C2S_TLS: 5223 + PORT_S2S: 5269 + PORT_HTTP_TLS: 5443 + PORT_HTTP: 5280 + PORT_STUN: 5478 + PORT_MQTT: 1883 + PORT_PROXY65: 7777 + hosts: - - localhost + - HOST loglevel: info @@ -30,7 +42,7 @@ certfiles: listen: - - port: 5222 + port: PORT_C2S ip: "::" module: ejabberd_c2s max_stanza_size: 262144 @@ -38,7 +50,7 @@ listen: access: c2s starttls_required: true - - port: 5223 + port: PORT_C2S_TLS ip: "::" module: ejabberd_c2s max_stanza_size: 262144 @@ -46,13 +58,13 @@ listen: access: c2s tls: true - - port: 5269 + port: PORT_S2S ip: "::" module: ejabberd_s2s_in max_stanza_size: 524288 shaper: s2s_shaper - - port: 5443 + port: PORT_HTTP_TLS ip: "::" module: ejabberd_http tls: true @@ -64,14 +76,14 @@ listen: /upload: mod_http_upload /ws: ejabberd_http_ws - - port: 5280 + port: PORT_HTTP ip: "::" module: ejabberd_http request_handlers: /admin: ejabberd_web_admin /.well-known/acme-challenge: ejabberd_acme - - port: 5478 + port: PORT_STUN ip: "::" transport: udp module: ejabberd_stun @@ -81,7 +93,7 @@ listen: ## The server's public IPv6 address: # turn_ipv6_address: "2001:db8::3" - - port: 1883 + port: PORT_MQTT ip: "::" module: mod_mqtt backlog: 1000 @@ -95,6 +107,9 @@ acl: ip: - 127.0.0.0/8 - ::1/128 + admin: + user: + - ADMIN access_rules: local: @@ -211,6 +226,7 @@ modules: mod_proxy65: access: local max_connections: 5 + port: PORT_PROXY65 mod_pubsub: access_createnode: pubsub_createnode plugins: From dd7550dbad7399a1e2895630ca7338fde4f26c91 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Dec 2024 22:19:32 +0100 Subject: [PATCH 0999/1302] Container: Listen for webadmin in a port number lower than any other In the docker-desktop and podman-desktop, when user clicks their "Open Browser" buttons, those apps open a browser with / URL and the lowest exposed port number. --- .github/container/Dockerfile | 2 +- .github/container/ejabberd.yml.example | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 1cb2ace6b..2847fad89 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -192,7 +192,7 @@ HEALTHCHECK \ WORKDIR /$HOME USER $USER VOLUME ["/$HOME"] -EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443 +EXPOSE 1880 1883 4369-4399 5210 5222 5269 5280 5443 ENTRYPOINT ["/sbin/tini","--","ejabberdctl"] CMD ["foreground"] diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index 613f38d41..ba6e08fbb 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -22,6 +22,7 @@ define_macro: PORT_S2S: 5269 PORT_HTTP_TLS: 5443 PORT_HTTP: 5280 + PORT_BROWSER: 1880 PORT_STUN: 5478 PORT_MQTT: 1883 PORT_PROXY65: 7777 @@ -82,6 +83,12 @@ listen: request_handlers: /admin: ejabberd_web_admin /.well-known/acme-challenge: ejabberd_acme + - + port: PORT_BROWSER + ip: "::" + module: ejabberd_http + request_handlers: + /: ejabberd_web_admin - port: PORT_STUN ip: "::" From 1d42d55064f5eb443df2aa4c729faab53d6efb53 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 30 Dec 2024 10:39:13 +0100 Subject: [PATCH 1000/1302] Container: Compile ejabberdapi during build Code written originally by sando38 for ecs's Dockerfile. --- .github/container/Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 2847fad89..d389251c7 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -81,6 +81,13 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ && ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \ && tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir +################################################################################ +#' Compile ejabberdapi +FROM docker.io/golang:1.23-alpine AS api +RUN go install -v \ + github.com/processone/ejabberd-api/cmd/ejabberd@master \ + && mv bin/ejabberd bin/ejabberdapi + ################################################################################ #' Prepare ejabberd for runtime FROM ${METHOD} AS ejabberd @@ -131,6 +138,8 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ | sed -e "s|so:libc.so|so:libc.musl-$(uname -m).so.1|" \ > /tmp/runDeps +COPY --from=api /go/bin/ejabberdapi usr/local/bin/ + ARG UID RUN chown -R $UID:$UID $HOME From c924a47188a1afb195cb98a8a69ca6ac4db857e1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Dec 2024 21:46:03 +0100 Subject: [PATCH 1001/1302] Container: Improve entrypoint script: register account, or set random If password variable is set, register that account. Example kubernetes yaml file in podman: env: - name: EJABBERD_MACRO_ADMIN value: administrator@example.org - name: REGISTER_ADMIN_PASSWORD value: somePass0rd If admin and password are not set, grant admin rights only to a random account name. Notice that admin rights are granted to that variable in the default ejabberd.yml, so if the account is not created, somebody else could do. --- .github/container/Dockerfile | 22 ++++++++++++++++++++++ .github/container/ejabberd.yml.example | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index d389251c7..bb8bdf9e5 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -125,6 +125,28 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ && echo -e \ "#!/bin/sh \ \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \ + \nexport EMA=\"\$EJABBERD_MACRO_ADMIN\" \ + \nexport HOST=\"\${EJABBERD_MACRO_HOST:-localhost}\" \ + \nif [ -n \"\$EMA\" ] \ + \nthen \ + \n if [ \"\$EMA\" != \"\${EMA%%@*}\" ] \ + \n then \ + \n export USERNAME=\"\${EMA%%@*}\" \ + \n export HOST=\"\${EMA##*@}\" \ + \n else \ + \n export USERNAME=\"\$EMA\" \ + \n export SHOW_WARNING=\"true\" \ + \n fi \ + \nelif [ -n \"\$REGISTER_ADMIN_PASSWORD\" ] \ + \nthen \ + \n export USERNAME=\"admin\" \ + \nelse \ + \n export USERNAME=\"\$(od -A n -N 8 -t x8 /dev/urandom)\" \ + \nfi \ + \nexport EJABBERD_MACRO_ADMIN=\"\$USERNAME@\$HOST\" \ + \n[ -n \"\$SHOW_WARNING\" ] && echo \"WARNING: The EJABBERD_MACRO_ADMIN environment variable was set to '\$EMA', but it should include the host... I'll overwrite it to become '\$EJABBERD_MACRO_ADMIN'.\" \ + \n[ -n \"\$CTL_ON_CREATE\" ] && export SEPARATOR=\";\" \ + \n[ -n \"\$REGISTER_ADMIN_PASSWORD\" ] && export CTL_ON_CREATE=\"register \${EJABBERD_MACRO_ADMIN%%@*} \${EJABBERD_MACRO_ADMIN##*@} \$REGISTER_ADMIN_PASSWORD \$SEPARATOR \$CTL_ON_CREATE\" \ \nexport CONFIG_DIR=/$HOME/conf \ \nexport LOGS_DIR=/$HOME/logs \ \nexport SPOOL_DIR=/$HOME/database \ diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index ba6e08fbb..72ac292aa 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -16,7 +16,7 @@ define_macro: HOST: localhost - ADMIN: "admin@localhost" + ## ADMIN: ... # set by /usr/local/bin/ejabberdctl PORT_C2S: 5222 PORT_C2S_TLS: 5223 PORT_S2S: 5269 From 7832a6342a67665d6e31ef8ed565706cc1ac3387 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 2 Jan 2025 13:18:15 +0100 Subject: [PATCH 1002/1302] Container: Link path to mnesia spool dir for backwards compatibility The ejabberdctl script in ecs image sets mnesia spool dir as: : "${SPOOL_DIR:="$HOME_DIR/database/$ERLANG_NODE"}" --- .github/container/ejabberdctl.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index a27a14587..d4b37cdac 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -305,6 +305,8 @@ stop_epmd() # if all ok, ensure runtime directory exists and make it current directory check_start() { + ECSIMAGE_DBPATH=$HOME/database/$ERLANG_NODE + [ ! -d "$ECSIMAGE_DBPATH" ] && ln -s $HOME/database $HOME/database/$ERLANG_NODE [ -n "$ERL_DIST_PORT" ] && return "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && { pgrep -f "$ERLANG_NODE" >/dev/null && { From 9305232f8c0490fd7f34260dcfadad2d573ec992 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 10 Jan 2025 21:58:01 +0100 Subject: [PATCH 1003/1302] Container: Remove runDeps file once it's used --- .github/container/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index bb8bdf9e5..c70061dc0 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -192,6 +192,7 @@ RUN apk -U upgrade --available --no-cache \ so:libcap.so.2 \ so:libtdsodbc.so.0 \ tini \ + && rm /tmp/runDeps \ && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so ARG USER From e887546c27121f9c90db22a3d43281b3335ec5f5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 7 Jan 2025 12:12:08 +0100 Subject: [PATCH 1004/1302] Container: Copy support for CTL_OVER_HTTP --- .github/container/Dockerfile | 1 + .github/container/ejabberdctl.template | 59 +++++++++++++++++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index c70061dc0..4c7d65af7 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -191,6 +191,7 @@ RUN apk -U upgrade --available --no-cache \ $(cat /tmp/runDeps) \ so:libcap.so.2 \ so:libtdsodbc.so.0 \ + curl \ tini \ && rm /tmp/runDeps \ && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index d4b37cdac..2353040d5 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -379,6 +379,54 @@ wait_status() [ $timeout -gt 0 ] } +exec_other_command() +{ + if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ + || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ + || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then + run_erl "$(uid ctl)" -hidden -noinput \ + -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -s ejabberd_ctl \ + -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" + result=$? + case $result in + 3) help;; + *) :;; + esac + exit $result + else + exec_ctl_over_http_socket "$@" + fi +} + +exec_ctl_over_http_socket() +{ + COMMAND=${1} + CARGS="" + while [ $# -gt 0 ]; do + [ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, " + CARGS="${CARGS}\"$1\"" + shift + done + CARGS="${CARGS}]" + TEMPHEADERS=temp-headers.log + curl \ + --unix-socket ${CTL_OVER_HTTP} \ + --header "Content-Type: application/json" \ + --header "Accept: application/json" \ + --data "${CARGS}" \ + --dump-header ${TEMPHEADERS} \ + --no-progress-meter \ + "http://localhost/ctl/${COMMAND}" + result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS) + rm ${TEMPHEADERS} + case $result in + 2|3) exec_other_command help ${COMMAND};; + *) :;; + esac + exit $result +} + # ensure we can change current directory to SPOOL_DIR [ -f "$SPOOL_DIR/schema.DAT" ] || FIRST_RUN=true [ -d "$SPOOL_DIR" ] || run_cmd mkdir -p "$SPOOL_DIR" @@ -454,15 +502,6 @@ case $1 in ;; *) set_dist_client - run_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ - -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" - result=$? - case $result in - 2|3) help;; - *) :;; - esac - exit $result + exec_other_command "$@" ;; esac From 105a0c2029d485b8671814b302f62a7144ad066c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Jan 2025 13:46:42 +0100 Subject: [PATCH 1005/1302] Container: Enable CTL_OVER_HTTP by default --- .github/container/Dockerfile | 2 ++ .github/container/ejabberd.yml.example | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 4c7d65af7..ed0dfe504 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -120,6 +120,8 @@ RUN export PEM=$HOME/conf/server.pem \ -days 3650 \ -subj "/CN=localhost" +RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=|' "$HOME/conf/ejabberdctl.cfg" + RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \ && echo -e \ diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index 72ac292aa..b8a84fc92 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -89,6 +89,13 @@ listen: module: ejabberd_http request_handlers: /: ejabberd_web_admin + - + port: "unix:sockets/ctl_over_http.sock" + module: ejabberd_http + unix_socket: + mode: '0600' + request_handlers: + /ctl: ejabberd_ctl - port: PORT_STUN ip: "::" From 8070a656fe99f7522893ab1e5e3bcbc40e1a3937 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Mar 2025 17:41:26 +0100 Subject: [PATCH 1006/1302] Container: Use again direct METHOD, qemu got fixed (3983)(4280) Partially revert d15cf99: Container: Add METHOD to build container using packages (3983) --- .github/container/Dockerfile | 56 +++++++++------------------------ .github/workflows/container.yml | 49 ----------------------------- 2 files changed, 14 insertions(+), 91 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index ed0dfe504..7b502f2c4 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,20 +1,22 @@ #' Define default build variables -## specifc ARGs for METHOD='direct' ARG OTP_VSN='27.2' ARG ELIXIR_VSN='1.18.1' -## specifc ARGs for METHOD='package' -ARG ALPINE_VSN='3.19' -## general ARGs ARG UID='9000' ARG USER='ejabberd' ARG HOME="opt/$USER" -ARG METHOD='direct' ARG BUILD_DIR="/$USER" ARG VERSION='master' ################################################################################ -#' METHOD='direct' - build and install ejabberd directly from source -FROM docker.io/erlang:${OTP_VSN}-alpine AS direct +#' Compile ejabberdapi +FROM docker.io/golang:1.23-alpine AS api +RUN go install -v \ + github.com/processone/ejabberd-api/cmd/ejabberd@master \ + && mv bin/ejabberd bin/ejabberdapi + +################################################################################ +#' build and install ejabberd directly from source +FROM docker.io/erlang:${OTP_VSN}-alpine AS ejabberd RUN apk -U add --no-cache \ autoconf \ @@ -70,37 +72,16 @@ RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \ RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' -################################################################################ -#' METHOD='package' - install ejabberd from binary tarball package -FROM docker.io/alpine:${ALPINE_VSN} AS package -COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/ -WORKDIR /rootfs -ARG HOME -RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ - && mkdir -p $home_root_dir \ - && ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \ - && tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir - -################################################################################ -#' Compile ejabberdapi -FROM docker.io/golang:1.23-alpine AS api -RUN go install -v \ - github.com/processone/ejabberd-api/cmd/ejabberd@master \ - && mv bin/ejabberd bin/ejabberdapi - -################################################################################ #' Prepare ejabberd for runtime -FROM ${METHOD} AS ejabberd RUN apk -U add --no-cache \ git \ libcap \ openssl -WORKDIR /rootfs -ARG HOME RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload -ARG BUILD_DIR +COPY --from=api /go/bin/ejabberdapi usr/local/bin/ + RUN if [ ! -d $HOME/.ejabberd-modules ]; \ then \ if [ -d $BUILD_DIR/.ejabberd-modules ]; \ @@ -162,31 +143,22 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ | sed -e "s|so:libc.so|so:libc.musl-$(uname -m).so.1|" \ > /tmp/runDeps -COPY --from=api /go/bin/ejabberdapi usr/local/bin/ - ARG UID RUN chown -R $UID:$UID $HOME -ARG VERSION RUN cp /rootfs/$HOME-$VERSION/lib/captcha*.sh usr/local/bin/ RUN mkdir $HOME/sql \ && find /rootfs/$HOME-$VERSION/lib/ -name *.sql -exec cp {} $HOME/sql \; -exec cp {} $HOME/database \; ################################################################################ -#' METHOD='direct' - Remove erlang/OTP & rebar3 -FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-direct +#' Remove erlang/OTP & rebar3 +FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime RUN apk del .erlang-rundeps \ && rm -f $(which rebar3) \ && find /usr -type d -name 'erlang' -exec rm -rf {} + \ && find /usr -type l -exec test ! -e {} \; -delete -################################################################################ -#' METHOD='package' - define runtime base image -FROM docker.io/alpine:${ALPINE_VSN} AS runtime-package - -################################################################################ #' Update alpine, finalize runtime environment -FROM runtime-${METHOD} AS runtime COPY --from=ejabberd /tmp/runDeps /tmp/runDeps RUN apk -U upgrade --available --no-cache \ && apk add --no-cache \ @@ -210,7 +182,7 @@ RUN rm -rf /home \ ################################################################################ #' Build together production image -FROM scratch AS prod +FROM scratch ARG USER ARG HOME diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 7940c9b68..567a2c845 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,8 +1,6 @@ name: Container on: - schedule: - - cron: '22 2 */6 * *' # every 6 days to avoid gha cache being evicted push: paths-ignore: - '.devcontainer/**' @@ -28,52 +26,6 @@ jobs: with: fetch-depth: 0 - - name: Cache build directory - uses: actions/cache@v4 - with: - path: ~/build/ - key: ${{runner.os}}-ctr-ct-ng-1.27.0 - - - name: Get erlang/OTP version for bootstrapping - run: | - echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.rc-]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV - echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV - - - name: Install prerequisites - run: | - sudo apt-get -qq update - sudo apt-get -qq install makeself - # https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile - sudo apt-get -qq install build-essential autoconf bison flex gawk - sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin - sudo apt-get -qq install python3-dev texinfo unzip - - - name: Install erlang/OTP - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ env.OTP_VSN }} - elixir-version: ${{ env.ELIXIR_VSN }} - version-type: strict - - - name: Remove Elixir Matchers - run: | - echo "::remove-matcher owner=elixir-mixCompileWarning::" - echo "::remove-matcher owner=elixir-credoOutputDefault::" - echo "::remove-matcher owner=elixir-mixCompileError::" - echo "::remove-matcher owner=elixir-mixTestFailure::" - echo "::remove-matcher owner=elixir-dialyzerOutputDefault::" - - - name: Build musl-libc based binary archives - run: | - sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries - mv .github/container/ejabberdctl.template . - CHECK_DEPS=false tools/make-binaries - - - name: Collect packages - run: | - mkdir tarballs - mv ejabberd-*.tar.gz tarballs - - name: Checkout ejabberd-contrib uses: actions/checkout@v4 with: @@ -111,7 +63,6 @@ jobs: uses: docker/build-push-action@v6 with: build-args: | - METHOD=package VERSION=${{ steps.gitdescribe.outputs.ver }} cache-from: type=gha cache-to: type=gha,mode=max From 74d6d53ac64e0802fa95f55e8601456fb26016e3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 10 Jan 2025 21:05:42 +0100 Subject: [PATCH 1007/1302] Container: Add ERL_FLAGS to compile elixir on qemu cross-platform Without this, compiling Elixir on arm64 using QEMU fails with: <<"could not call Module.put_attribute/3 because the module ExUnit.DocTest is already compiled">> Solution found in: https://elixirforum.com/t/elixir-docker-image-wont-build-for-linux-arm64-v8-using-github-actions/56383/13 --- .github/container/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 7b502f2c4..f5b5b6d12 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -42,6 +42,7 @@ RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz | tar -xzf - WORKDIR elixir-$ELIXIR_VSN +ENV ERL_FLAGS="+JPperf true" RUN make install clean RUN mix local.hex --force \ From fa4a93c4651eae48e2644b737039c9cb9f2c302b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 16 Jan 2025 11:44:37 +0100 Subject: [PATCH 1008/1302] Container: Place sockets/ outside database/ The socket file is useless outside the container, and also database/ may get mounted as volume, and can't handle socket file --- .github/container/Dockerfile | 2 +- .github/container/ejabberd.yml.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index f5b5b6d12..2a47766aa 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -102,7 +102,7 @@ RUN export PEM=$HOME/conf/server.pem \ -days 3650 \ -subj "/CN=localhost" -RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=|' "$HOME/conf/ejabberdctl.cfg" +RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=../|' "$HOME/conf/ejabberdctl.cfg" RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \ && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \ diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index b8a84fc92..62dff50c9 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -90,7 +90,7 @@ listen: request_handlers: /: ejabberd_web_admin - - port: "unix:sockets/ctl_over_http.sock" + port: "unix:../sockets/ctl_over_http.sock" module: ejabberd_http unix_socket: mode: '0600' From 3b01e4e4e0f9c509c01a4530fe4422949cd33d71 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 17 Jan 2025 11:27:44 +0100 Subject: [PATCH 1009/1302] Container: Fix warning about relative workdir --- .github/container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 2a47766aa..ae2cc7311 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -41,7 +41,7 @@ ARG ELIXIR_VSN RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz \ | tar -xzf - -WORKDIR elixir-$ELIXIR_VSN +WORKDIR /elixir-$ELIXIR_VSN ENV ERL_FLAGS="+JPperf true" RUN make install clean From cfa6575b4fd0d216c34e66daab45710bf3a59a5e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 30 Dec 2024 13:11:28 +0100 Subject: [PATCH 1010/1302] CONTAINER.md: Update with all the recent improvements --- CONTAINER.md | 60 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 95115a6c2..1f2588cb4 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -96,20 +96,30 @@ Next steps ### Register the administrator account -The default ejabberd configuration does not grant admin privileges -to any account, -you may want to register a new account in ejabberd -and grant it admin rights. +If you set the `REGISTER_ADMIN_PASSWORD` environment variable, +an account is registered with that password, +and admin privileges are granted to it. +The account created may be: -Register an account using the `ejabberdctl` script: +- `EJABBERD_MACRO_ADMIN=juliet@example.org` --> `juliet@example.org` +- `EJABBERD_MACRO_HOST=example.org` --> `admin@example.org` +- None of those variables are set --> `admin@localhost` + +The account registration is shown in the container log: + +``` +:> ejabberdctl register admin example.org somePassw0rd +User admin@example.org successfully registered +``` + +Alternatively, you can register the account manually yourself +and edit conf/ejabberd.yml and add the ACL as explained in +[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account): ```bash docker exec -it ejabberd ejabberdctl register admin localhost passw0rd ``` -Then edit conf/ejabberd.yml and add the ACL as explained in -[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account) - ### Check ejabberd log files @@ -185,7 +195,8 @@ This container image exposes the ports: - `5222`: The default port for XMPP clients. - `5269`: For XMPP federation. Only needed if you want to communicate with users on other servers. -- `5280`: For admin interface. +- `5280`: For admin interface (URL is `admin/`). +- `1880`: For admin interface (URL is `/`, useful for podman-desktop and docker-desktop) - `5443`: With encryption, used for admin interface, API, CAPTCHA, OAuth, Websockets and XMPP BOSH. - `1883`: Used for MQTT - `4369-4399`: EPMD and Erlang connectivity, used for `ejabberdctl` and clustering @@ -299,8 +310,8 @@ Example using environment variables (see full example [docker-compose.yml](https ``` -Build a Container Image ------------------------ +Build `ejabberd` Container Image +-------------------------------- This container image includes ejabberd as a standalone OTP release built using Elixir. That OTP release is configured with: @@ -370,6 +381,33 @@ docker buildx build \ . ``` +Build `ecs` Container Image +--------------------------- + +### Configuration + +Image is built by embedding an ejabberd Erlang/OTP standalone release in the image. + +The configuration of ejabberd Erlang/OTP release is customized with: + +- `rel/config.exs`: Customize ejabberd release +- `rel/dev.exs`: ejabberd environment configuration for development release +- `rel/prod.exs`: ejabberd environment configuration for production release +- `vars.config`: ejabberd compilation configuration options +- `conf/ejabberd.yml`: ejabberd default config file + +Build ejabberd Community Server base image from ejabberd master on Github: + +```bash +docker build -t docker.io/ejabberd/ecs . +``` + +Build ejabberd Community Server base image for a given ejabberd version: + +```bash +./build.sh 18.03 +``` + Composer Examples ----------------- From 60324d4b7ad3d860bf3ea2c76bdec8a106e2a4d6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Jan 2025 12:28:20 +0100 Subject: [PATCH 1011/1302] CONTAINER.md: Include documentation for ecs container image --- CONTAINER.md | 574 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 367 insertions(+), 207 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 1f2588cb4..b8a483e07 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1,10 +1,10 @@ [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&label=&color=49c0c4)](https://github.com/processone/ejabberd/tags) -[![ejabberd Container on GitHub](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=docker)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) +[![ejabberd Container on GitHub](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) [![ecs Container on Docker](https://img.shields.io/docker/v/ejabberd/ecs?label=ecs&sort=semver&logo=docker)](https://hub.docker.com/r/ejabberd/ecs/) -`ejabberd` Container Image -========================== +ejabberd Container Images +========================= [ejabberd][home] is an open-source, robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], @@ -16,26 +16,29 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. [mqtt]: https://mqtt.org/ [sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol -This document explains how to use the `ejabberd` container image available in -[ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), -built using the files in `.github/container/`. -This image is based in Alpine 3.19, includes Erlang/OTP 27.2 and Elixir 1.18.1. +This page documents those container images ([images comparison](#images-comparison)): -Alternatively, there is also the `ecs` container image available in -[docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), -built using the -[docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) -repository. -Check the [differences between `ejabberd` and `ecs` images](https://github.com/processone/docker-ejabberd/blob/master/ecs/HUB-README.md#alternative-image-in-github). +- [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) + published in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), + built using [ejabberd](https://github.com/processone/ejabberd/tree/master/.github/container) repository, + both for stable ejabberd releases and the `master` branch, in x64 and arm64 architectures. -If you are using a Windows operating system, check the tutorials mentioned in -[ejabberd Docs > Docker Image](https://docs.ejabberd.im/admin/install/container/#ejabberd-container-image). +- [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) + published in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), + built using [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) repository + for ejabberd stable releases in x64 architectures. + +For Microsoft Windows, see +[Docker Desktop for Windows 10](https://www.process-one.net/blog/install-ejabberd-on-windows-10-using-docker-desktop/), +and [Docker Toolbox for Windows 7](https://www.process-one.net/blog/install-ejabberd-on-windows-7-using-docker-toolbox/). + +For Kubernetes Helm, see [help-ejabberd](https://github.com/sando38/helm-ejabberd). Start ejabberd -------------- -### With default configuration +### daemon Start ejabberd in a new container: @@ -46,22 +49,28 @@ docker run --name ejabberd -d -p 5222:5222 ghcr.io/processone/ejabberd That runs the container as a daemon, using ejabberd default configuration file and XMPP domain `localhost`. -Stop the running container: - -```bash -docker stop ejabberd -``` - Restart the stopped ejabberd container: ```bash docker restart ejabberd ``` +Stop the running container: -### Start with Erlang console attached +```bash +docker stop ejabberd +``` -Start ejabberd with an Erlang console attached using the `live` command: +Remove the ejabberd container: + +```bash +docker rm ejabberd +``` + + +### with Erlang console + +Start ejabberd with an interactive Erlang console attached using the `live` command: ```bash docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live @@ -70,40 +79,42 @@ docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live That uses the default configuration file and XMPP domain `localhost`. -### Start with your configuration and database +### with your data Pass a configuration file as a volume and share the local directory to store database: ```bash -mkdir database -chown ejabberd database +mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml -cp ejabberd.yml.example ejabberd.yml +mkdir database && chown ejabberd database docker run --name ejabberd -it \ - -v $(pwd)/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \ + -v $(pwd)/conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \ -v $(pwd)/database:/opt/ejabberd/database \ -p 5222:5222 ghcr.io/processone/ejabberd live ``` -Notice that ejabberd runs in the container with an account named `ejabberd`, +Notice that ejabberd runs in the container with an account named `ejabberd` +with UID 9000 and group `ejabberd` with GID 9000, and the volumes you mount must grant proper rights to that account. Next steps ---------- -### Register the administrator account +### Register admin account + +#### [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) [:orange_circle:](#images-comparison) If you set the `REGISTER_ADMIN_PASSWORD` environment variable, -an account is registered with that password, +an account is automatically registered with that password, and admin privileges are granted to it. -The account created may be: +The account created depends on what variables you have set: -- `EJABBERD_MACRO_ADMIN=juliet@example.org` --> `juliet@example.org` -- `EJABBERD_MACRO_HOST=example.org` --> `admin@example.org` -- None of those variables are set --> `admin@localhost` +- `EJABBERD_MACRO_ADMIN=juliet@example.org` -> `juliet@example.org` +- `EJABBERD_MACRO_HOST=example.org` -> `admin@example.org` +- None of those variables are set -> `admin@localhost` The account registration is shown in the container log: @@ -113,15 +124,22 @@ User admin@example.org successfully registered ``` Alternatively, you can register the account manually yourself -and edit conf/ejabberd.yml and add the ACL as explained in -[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account): +and edit `conf/ejabberd.yml` and add the ACL as explained in +[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account). + +--- + +#### [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) + +The default ejabberd configuration has already granted admin privilege +to an account that would be called `admin@localhost`, +so you just need to register it, for example: ```bash docker exec -it ejabberd ejabberdctl register admin localhost passw0rd ``` - -### Check ejabberd log files +### Check ejabberd log Check the content of the log files inside the container, even if you do not put it on a shared persistent drive: @@ -131,7 +149,7 @@ docker exec -it ejabberd tail -f logs/ejabberd.log ``` -### Inspect the container files +### Inspect container files The container uses Alpine Linux. Start a shell inside the container: @@ -140,7 +158,7 @@ docker exec -it ejabberd sh ``` -### Open ejabberd debug console +### Open debug console Open an interactive debug Erlang console attached to a running ejabberd in a running container: @@ -165,7 +183,7 @@ docker exec -it ejabberd vi conf/ejabberd.yml and add this option: ```yaml -captcha_cmd: /opt/ejabberd-22.04/lib/captcha.sh +captcha_cmd: "$HOME/bin/captcha.sh" ``` Finally, reload the configuration file or restart the container: @@ -186,21 +204,22 @@ For more details about CAPTCHA options, please check the documentation section. -Advanced Container Configuration --------------------------------- +Advanced +-------- ### Ports -This container image exposes the ports: +The container image exposes several ports +(check also [Docs: Firewall Settings](https://docs.ejabberd.im/admin/guide/security/#firewall-settings)): - `5222`: The default port for XMPP clients. - `5269`: For XMPP federation. Only needed if you want to communicate with users on other servers. - `5280`: For admin interface (URL is `admin/`). -- `1880`: For admin interface (URL is `/`, useful for podman-desktop and docker-desktop) +- `1880`: For admin interface (URL is `/`, useful for [podman-desktop](https://podman-desktop.io/) and [docker-desktop](https://www.docker.com/products/docker-desktop/)) [:orange_circle:](#images-comparison) - `5443`: With encryption, used for admin interface, API, CAPTCHA, OAuth, Websockets and XMPP BOSH. - `1883`: Used for MQTT - `4369-4399`: EPMD and Erlang connectivity, used for `ejabberdctl` and clustering -- `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD +- `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD [:orange_circle:](#images-comparison) ### Volumes @@ -217,11 +236,26 @@ You should back up or export the content of the directory to persistent storage - `/opt/ejabberd/logs/`: Directory containing log files - `/opt/ejabberd/upload/`: Directory containing uploaded files. This should also be backed up. -All these files are owned by `ejabberd` user inside the container. +All these files are owned by an account named `ejabberd` with group `ejabberd` in the container. +Its corresponding `UID:GID` is `9000:9000`. +If you prefer bind mounts instead of volumes, then +you need to map this to valid `UID:GID` on your host to get read/write access on +mounted directories. -It's possible to install additional ejabberd modules using volumes, -[this comment](https://github.com/processone/docker-ejabberd/issues/81#issuecomment-1036115146) -explains how to install an additional module using docker-compose. +If using Docker, try: +```bash +mkdir database +sudo chown 9000:9000 database +``` + +If using Podman, try: +```bash +mkdir database +podman unshare chown 9000:9000 database +``` + +It's possible to install additional ejabberd modules using volumes, check +[this Docs tutorial](http://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container). ### Commands on start @@ -245,10 +279,10 @@ Example usage (or check the [full example](#customized-example)): ``` -### Macros in environment +### Macros in environment [:high_brightness:](#images-comparison) ejabberd reads `EJABBERD_MACRO_*` environment variables -and uses them to define the `*` +and uses them to define the corresponding [macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file), overwriting the corresponding macro definition if it was set in the configuration file. This is supported since ejabberd 24.12. @@ -258,18 +292,61 @@ For example, if you configure this in `ejabberd.yml`: ```yaml acl: admin: - user: ADMINJID + user: ADMIN ``` now you can define the admin account JID using an environment variable: ```yaml environment: - - EJABBERD_MACRO_ADMINJID=admin@localhost + - EJABBERD_MACRO_ADMIN=admin@localhost ``` Check the [full example](#customized-example) for other example. +### ejabberdapi + +When the container is running (and thus ejabberd), you can exec commands inside the container +using `ejabberdctl` or any other of the available interfaces, see +[Understanding ejabberd "commands"](https://docs.ejabberd.im/developer/ejabberd-api/#understanding-ejabberd-commands) + +Additionally, the container image includes the `ejabberdapi` executable. +Please check the [ejabberd-api homepage](https://github.com/processone/ejabberd-api) +for configuration and usage details. + +For example, if you configure ejabberd like this: +```yaml +listen: + - + port: 5282 + module: ejabberd_http + request_handlers: + "/api": mod_http_api + +acl: + loopback: + ip: + - 127.0.0.0/8 + - ::1/128 + - ::FFFF:127.0.0.1/128 + +api_permissions: + "admin access": + who: + access: + allow: + acl: loopback + what: + - "register" +``` + +Then you could register new accounts with this query: + +```bash +docker exec -it ejabberd ejabberdapi register --endpoint=http://127.0.0.1:5282/ --jid=admin@localhost --password=passw0rd +``` + + ### Clustering When setting several containers to form a @@ -284,6 +361,8 @@ For this you can either: - edit `conf/ejabberdctl.cfg` and set variables `ERLANG_NODE` and `ERLANG_COOKIE` - set the environment variables `ERLANG_NODE_ARG` and `ERLANG_COOKIE` +--- + Example to connect a local `ejabberdctl` to a containerized ejabberd: 1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: @@ -293,7 +372,7 @@ Example to connect a local `ejabberdctl` to a containerized ejabberd: -p 5210:5210 -p 5222:5222 \ ghcr.io/processone/ejabberd ``` -2. Set `ERL_DIST_PORT=5210` in ejabberdctl.cfg of container and local ejabberd +2. Set `ERL_DIST_PORT=5210` in `ejabberdctl.cfg` of container and local ejabberd 3. Restart the container 4. Now use `ejabberdctl` in your local ejabberd deployment @@ -309,19 +388,175 @@ Example using environment variables (see full example [docker-compose.yml](https - ERLANG_COOKIE=dummycookie123 ``` +--- -Build `ejabberd` Container Image --------------------------------- +Once you have the ejabberd nodes properly set and running, +you can tell the secondary nodes to join the master node using the +[`join_cluster`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join-cluster) +API call. -This container image includes ejabberd as a standalone OTP release built using Elixir. -That OTP release is configured with: +Example using environment variables (see the full +[`docker-compose.yml` clustering example](#clustering-example)): +```yaml +environment: + - ERLANG_NODE_ARG=ejabberd@replica + - ERLANG_COOKIE=dummycookie123 + - CTL_ON_CREATE=join_cluster ejabberd@main +``` + +### Change Mnesia Node Name + +To use the same Mnesia database in a container with a different hostname, +it is necessary to change the old hostname stored in Mnesia. + +This section is equivalent to the ejabberd Documentation +[Change Computer Hostname](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname), +but particularized to containers that use this +ecs container image from ejabberd 23.01 or older. + +#### Setup Old Container + +Let's assume a container running ejabberd 23.01 (or older) from +this ecs container image, with the database directory binded +and one registered account. +This can be produced with: +```bash +OLDCONTAINER=ejaold +NEWCONTAINER=ejanew + +mkdir database +sudo chown 9000:9000 database +docker run -d --name $OLDCONTAINER -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + ghcr.io/processone/ejabberd:23.01 +docker exec -it $OLDCONTAINER ejabberdctl started +docker exec -it $OLDCONTAINER ejabberdctl register user1 localhost somepass +docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost +``` + +Methods to know the Erlang node name: +```bash +ls database/ | grep ejabberd@ +docker exec -it $OLDCONTAINER ejabberdctl status +docker exec -it $OLDCONTAINER grep "started in the node" logs/ejabberd.log +``` + +#### Change Mnesia Node + +First of all let's store the Erlang node names and paths in variables. +In this example they would be: +```bash +OLDCONTAINER=ejaold +NEWCONTAINER=ejanew +OLDNODE=ejabberd@95145ddee27c +NEWNODE=ejabberd@localhost +OLDFILE=/opt/ejabberd/database/old.backup +NEWFILE=/opt/ejabberd/database/new.backup +``` + +1. Start your old container that can still read the Mnesia database correctly. +If you have the Mnesia spool files, +but don't have access to the old container anymore, go to +[Create Temporary Container](#create-temporary-container) +and later come back here. + +2. Generate a backup file and check it was created: +```bash +docker exec -it $OLDCONTAINER ejabberdctl backup $OLDFILE +ls -l database/*.backup +``` + +3. Stop ejabberd: +```bash +docker stop $OLDCONTAINER +``` + +4. Create the new container. For example: +```bash +docker run \ + --name $NEWCONTAINER \ + -d \ + -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + ghcr.io/processone/ejabberd:latest +``` + +5. Convert the backup file to new node name: +```bash +docker exec -it $NEWCONTAINER ejabberdctl mnesia_change_nodename $OLDNODE $NEWNODE $OLDFILE $NEWFILE +``` + +6. Install the backup file as a fallback: +```bash +docker exec -it $NEWCONTAINER ejabberdctl install_fallback $NEWFILE +``` + +7. Restart the container: +```bash +docker restart $NEWCONTAINER +``` + +8. Check that the information of the old database is available. +In this example, it should show that the account `user1` is registered: +```bash +docker exec -it $NEWCONTAINER ejabberdctl registered_users localhost +``` + +9. When the new container is working perfectly with the converted Mnesia database, +you may want to remove the unneeded files: +the old container, the old Mnesia spool files, and the backup files. + +#### Create Temporary Container + +In case the old container that used the Mnesia database is not available anymore, +a temporary container can be created just to read the Mnesia database +and make a backup of it, as explained in the previous section. + +This method uses `--hostname` command line argument for docker, +and `ERLANG_NODE_ARG` environment variable for ejabberd. +Their values must be the hostname of your old container +and the Erlang node name of your old ejabberd node. +To know the Erlang node name please check +[Setup Old Container](#setup-old-container). + +Command line example: +```bash +OLDHOST=${OLDNODE#*@} +docker run \ + -d \ + --name $OLDCONTAINER \ + --hostname $OLDHOST \ + -p 5222:5222 \ + -v $(pwd)/database:/opt/ejabberd/database \ + -e ERLANG_NODE_ARG=$OLDNODE \ + ghcr.io/processone/ejabberd:latest +``` + +Check the old database content is available: +```bash +docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost +``` + +Now that you have ejabberd running with access to the Mnesia database, +you can continue with step 2 of previous section +[Change Mnesia Node](#change-mnesia-node). + + +Build Container Image +---------------- + +The container image includes ejabberd as a standalone OTP release built using Elixir. + +### Build `ejabberd` [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) + +The ejabberd Erlang/OTP release is configured with: - `mix.exs`: Customize ejabberd release - `vars.config`: ejabberd compilation configuration options - `config/runtime.exs`: Customize ejabberd paths - `ejabberd.yml.template`: ejabberd default config file -### Direct build +#### Direct build Build ejabberd Community Server container image from ejabberd master git repository: @@ -332,7 +567,7 @@ docker buildx build \ . ``` -### Podman build +#### Podman build To build the image using Podman, please notice: @@ -357,38 +592,9 @@ podman stop eja1 podman run --name eja1 -it -e EJABBERD_BYPASS_WARNINGS=true -p 5222:5222 localhost/ejabberd live ``` -### Package build for `arm64` +### Build `ecs` [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) -By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd, -it is a fast and direct method. -However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25 -for the `arm64` architecture. - -Providing `--build-arg METHOD=package` is an alternate method to build the container -used by the Github Actions workflow that provides `amd64` and `arm64` container images. -It first builds an ejabberd binary package, and later installs it in the image. -That method avoids using QEMU, so it can build `arm64` container images, but is extremely -slow the first time it's used, and consequently not recommended for general use. - -In this case, to build the ejabberd container image for arm64 architecture: - -```bash -docker buildx build \ - --build-arg METHOD=package \ - --platform linux/arm64 \ - -t personal/ejabberd:$VERSION \ - -f .github/container/Dockerfile \ - . -``` - -Build `ecs` Container Image ---------------------------- - -### Configuration - -Image is built by embedding an ejabberd Erlang/OTP standalone release in the image. - -The configuration of ejabberd Erlang/OTP release is customized with: +The ejabberd Erlang/OTP release is configured with: - `rel/config.exs`: Customize ejabberd release - `rel/dev.exs`: ejabberd environment configuration for development release @@ -399,7 +605,7 @@ The configuration of ejabberd Erlang/OTP release is customized with: Build ejabberd Community Server base image from ejabberd master on Github: ```bash -docker build -t docker.io/ejabberd/ecs . +docker build -t personal/ejabberd . ``` Build ejabberd Community Server base image for a given ejabberd version: @@ -408,7 +614,6 @@ Build ejabberd Community Server base image for a given ejabberd version: ./build.sh 18.03 ``` - Composer Examples ----------------- @@ -468,26 +673,21 @@ stores the mnesia database in a local path, registers an account when it's created, and checks the number of registered accounts every time it's started. -Download or copy the ejabberd configuration file: +Prepare an ejabberd configuration file: ```bash -wget https://raw.githubusercontent.com/processone/ejabberd/master/ejabberd.yml.example -mv ejabberd.yml.example ejabberd.yml -``` - -Use a macro in `ejabberd.yml` to set the served vhost, with `localhost` as default value: -```yaml -define_macro: - XMPPHOST: localhost - -hosts: - - XMPPHOST +mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml ``` Create the database directory and allow the container access to it: -```bash -mkdir database -sudo chown 9000:9000 database -``` + +- Docker: + ```bash + mkdir database && sudo chown 9000:9000 database + ``` +- Podman: + ```bash + mkdir database && podman unshare chown 9000:9000 database + ``` If using Docker, write this `docker-compose.yml` file and start it with `docker-compose up`: @@ -501,8 +701,9 @@ services: image: ghcr.io/processone/ejabberd container_name: ejabberd environment: - - EJABBERD_MACRO_XMPPHOST=example.com - - CTL_ON_CREATE=register admin example.com asd + - EJABBERD_MACRO_HOST=example.com + - EJABBERD_MACRO_ADMIN=admin@example.com + - REGISTER_ADMIN_PASSWORD=somePassw0rd - CTL_ON_START=registered_users example.com ; status ports: @@ -511,7 +712,7 @@ services: - "5280:5280" - "5443:5443" volumes: - - ./ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro + - ./conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro - ./database:/opt/ejabberd/database ``` @@ -532,8 +733,12 @@ spec: - name: ejabberd image: ghcr.io/processone/ejabberd env: - - name: CTL_ON_CREATE - value: register admin example.com asd + - name: EJABBERD_MACRO_HOST + value: example.com + - name: EJABBERD_MACRO_ADMIN + value: admin@example.com + - name: REGISTER_ADMIN_PASSWORD + value: somePassw0rd - name: CTL_ON_START value: registered_users example.com ; status @@ -556,7 +761,7 @@ spec: volumes: - name: config hostPath: - path: ./ejabberd.yml + path: ./conf/ejabberd.yml type: File - name: db hostPath: @@ -589,11 +794,17 @@ services: main: image: ghcr.io/processone/ejabberd - container_name: ejabberd + container_name: main environment: - ERLANG_NODE_ARG=ejabberd@main - ERLANG_COOKIE=dummycookie123 - CTL_ON_CREATE=! register admin localhost asd + healthcheck: + test: netstat -nl | grep -q 5222 + start_period: 5s + interval: 5s + timeout: 5s + retries: 120 replica: image: ghcr.io/processone/ejabberd @@ -692,92 +903,41 @@ spec: ``` -Your configuration file should use those macros to allow each ejabberd node -use different listening port numbers: -```diff -diff --git a/ejabberd.yml.example b/ejabberd.yml.example -index 39e423a64..6e875b48f 100644 ---- a/ejabberd.yml.example -+++ b/ejabberd.yml.example -@@ -24,9 +24,19 @@ loglevel: info - # - /etc/letsencrypt/live/domain.tld/fullchain.pem - # - /etc/letsencrypt/live/domain.tld/privkey.pem - -+define_macro: -+ PORT_C2S: 5222 -+ PORT_C2S_TLS: 5223 -+ PORT_S2S: 5269 -+ PORT_HTTP_TLS: 5443 -+ PORT_HTTP: 5280 -+ PORT_STUN: 5478 -+ PORT_MQTT: 1883 -+ PORT_PROXY65: 7777 -+ - listen: - - -- port: 5222 -+ port: PORT_C2S - ip: "::" - module: ejabberd_c2s - max_stanza_size: 262144 -@@ -34,7 +44,7 @@ listen: - access: c2s - starttls_required: true - - -- port: 5223 -+ port: PORT_C2S_TLS - ip: "::" - module: ejabberd_c2s - max_stanza_size: 262144 -@@ -42,13 +52,13 @@ listen: - access: c2s - tls: true - - -- port: 5269 -+ port: PORT_S2S - ip: "::" - module: ejabberd_s2s_in - max_stanza_size: 524288 - shaper: s2s_shaper - - -- port: 5443 -+ port: PORT_HTTP_TLS - ip: "::" - module: ejabberd_http - tls: true -@@ -60,14 +70,14 @@ listen: - /upload: mod_http_upload - /ws: ejabberd_http_ws - - -- port: 5280 -+ port: PORT_HTTP - ip: "::" - module: ejabberd_http - request_handlers: - /admin: ejabberd_web_admin - /.well-known/acme-challenge: ejabberd_acme - - -- port: 5478 -+ port: PORT_STUN - ip: "::" - transport: udp - module: ejabberd_stun -@@ -77,7 +87,7 @@ listen: - ## The server's public IPv6 address: - # turn_ipv6_address: "2001:db8::3" - - -- port: 1883 -+ port: PORT_MQTT - ip: "::" - module: mod_mqtt - backlog: 1000 -@@ -207,6 +217,7 @@ modules: - mod_proxy65: - access: local - max_connections: 5 -+ port: PORT_PROXY65 - mod_pubsub: - access_createnode: pubsub_createnode - plugins: -``` +Images Comparison +----------------- + +Let's summarize the differences between both container images. Legend: + +- :sparkle: is the recommended alternative +- :orange_circle: added in the latest release (ejabberd 25.xx) +- :high_brightness: added in the previous release (ejabberd 24.12) +- :low_brightness: added in the pre-previous release (ejabberd 24.10) + +| | [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) | +|:----------------------|:------------------|:-----------------------| +| Source code | [ejabberd/.github/container](https://github.com/processone/ejabberd/tree/master/.github/container) | [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) | +| Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | +| Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | +| Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | +| Software | Erlang/OTP 27.2-alpine
Elixir 1.18.1 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | +| :black_square_button: **Additional content** | +| [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included | +| [ejabberdapi](#ejabberdapi) | included :orange_circle: | included | +| :black_square_button: **Ports** | +| [1880](#ports) for WebAdmin | yes :orange_circle: | yes :orange_circle: | +| [5210](#ports) for `ERL_DIST_PORT` | supported | supported :orange_circle: | +| :black_square_button: **Paths** | +| `$HOME` | `/opt/ejabberd/` | `/home/ejabberd/` | +| User data | `$HOME` :sparkle:
`/home/ejabberd/` :orange_circle: | `$HOME`
`/opt/ejabberd/` :sparkle: :low_brightness: | +| `ejabberdctl` | `ejabberdctl` :sparkle:
`bin/ejabberdctl` :orange_circle: | `bin/ejabberdctl`
`ejabberdctl` :sparkle: :low_brightness: | +| [`captcha.sh`](#captcha) | `$HOME/bin/captcha.sh` :orange_circle: | `$HOME/bin/captcha.sh` :orange_circle: | +| `*.sql` files | `$HOME/sql/*.sql` :sparkle: :orange_circle:
`$HOME/database/*.sql` :orange_circle: | `$HOME/database/*.sql`
`$HOME/sql/*.sql` :sparkle: :orange_circle: | +| Mnesia spool files | `$HOME/database/` :sparkle:
`$HOME/database/NODENAME/` :orange_circle: | `$HOME/database/NODENAME/`
`$HOME/database/` :sparkle: :orange_circle: | +| :black_square_button: **Variables** | +| [`EJABBERD_MACRO_*`](#macros-in-environment) | supported :high_brightness: | supported :high_brightness: | +| Macros used in `ejabberd.yml` | yes :orange_circle: | yes :orange_circle: | +| [`EJABBERD_MACRO_ADMIN`](#register-admin-account) | Grant admin rights :orange_circle:
(default `admin@localhost`)
| Hardcoded `admin@localhost` | +| [`REGISTER_ADMIN_PASSWORD`](#register-admin-account) | Register admin account :orange_circle: | unsupported | +| `CTL_OVER_HTTP` | enabled :orange_circle: | unsupported | From 62a165e4cf599c031794d91fe99d499df0348611 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Mar 2025 16:30:47 +0100 Subject: [PATCH 1012/1302] ejabberd_web_admin: Support commands with tuple arguments; fix list indent --- src/ejabberd_web_admin.erl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 02cd3c13d..96b232184 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -2359,11 +2359,21 @@ format_result([], {_Name, {list, _ElementsDef}}) -> ""; format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> Separator = ",", - [format_result(FirstElement, ElementsDef) | lists:map(fun(Element) -> - [Separator | format_result(Element, - ElementsDef)] - end, - Elements)]; + Head = format_result(FirstElement, ElementsDef), + Tail = + lists:map(fun(Element) -> [Separator | format_result(Element, ElementsDef)] end, + Elements), + [Head | Tail]; +format_result([], {_Name, {tuple, _ElementsDef}}) -> + ""; +format_result(Value, {_Name, {tuple, [FirstDef | ElementsDef]}}) -> + [FirstElement | Elements] = tuple_to_list(Value), + Separator = ":", + Head = format_result(FirstElement, FirstDef), + Tail = + lists:map(fun(Element) -> [Separator | format_result(Element, ElementsDef)] end, + Elements), + [Head | Tail]; format_result(Value, _ResultFormat) when is_atom(Value) -> misc:atom_to_binary(Value); format_result(Value, _ResultFormat) when is_list(Value) -> From b901a69f5c56e6ce33654934ff6ffb2317eecef1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Mar 2025 12:30:19 +0100 Subject: [PATCH 1013/1302] mix.exs: Keep debug info when building dev release (thanks to Stefan Strigler) --- mix.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/mix.exs b/mix.exs index feb1666c2..3f63e90f8 100644 --- a/mix.exs +++ b/mix.exs @@ -267,6 +267,7 @@ defmodule Ejabberd.MixProject do ejabberd: [ include_executables_for: [:unix], # applications: [runtime_tools: :permanent] + strip_beams: Mix.env() != :dev, steps: [©_extra_files/1, :assemble | maybe_tar] ] ] From ad8e32513918700643f143ec0b706ba5c228ece3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Mar 2025 12:02:17 +0100 Subject: [PATCH 1014/1302] Disable commands tests for old Erlang/OTP versions Since recently, this test fails with Erlang/OTP 22 and lower: =result failed: {test_case_failed, "Received input: [{error,{compilation_failed, \"/home/runner/.ejabberd-modules/sources/ejabberd-contrib/mod_example/src/mod_example.erl\"}}] don't match expected patterns:ok"}, --- test/commands_tests.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/commands_tests.erl b/test/commands_tests.erl index 166415e89..52933e409 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -34,6 +34,13 @@ %%%================================== %%%% setup +-ifdef(OTP_BELOW_24). + +single_cases() -> + {commands_single, [sequence], []}. + +-else. + single_cases() -> {commands_single, [sequence], @@ -51,6 +58,8 @@ single_cases() -> single_test(http_list_tuple_map), single_test(clean)]}. +-endif. + setup(_Config) -> M = <<"mod_example">>, clean(_Config), From 19482529eea11a0fd240860e12d9641be681d7ca Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Jan 2025 13:06:39 +0100 Subject: [PATCH 1015/1302] Inform that define_macro cannot be used inside host_config --- src/ejabberd_config_transformer.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl index 025db6dc3..362bbecea 100644 --- a/src/ejabberd_config_transformer.erl +++ b/src/ejabberd_config_transformer.erl @@ -128,6 +128,11 @@ transform(Host, s2s_use_starttls, required_trusted, Acc) -> Hosts = maps:get(remove_s2s_dialback, Acc, []), Acc1 = maps:put(remove_s2s_dialback, [Host|Hosts], Acc), {{true, {s2s_use_starttls, required}}, Acc1}; +transform(Host, define_macro, Macro, Acc) when is_binary(Host) -> + ?WARNING_MSG("The option 'define_macro' is not supported inside 'host_config'. " + "Consequently those macro definitions for host '~ts' are unused: ~ts", + [Host, io_lib:format("~p", [Macro])]), + {true, Acc}; transform(_Host, _Opt, _Val, Acc) -> {true, Acc}. From b769de0690b94011a8aed5222ac42fd3a13e6705 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 6 Feb 2025 11:07:11 +0100 Subject: [PATCH 1016/1302] New option define_keyword --- src/ejabberd_option.erl | 8 ++++++++ src/ejabberd_options.erl | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 2677cf42f..cb3765b58 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -38,6 +38,7 @@ -export([cluster_nodes/0]). -export([default_db/0, default_db/1]). -export([default_ram_db/0, default_ram_db/1]). +-export([define_keyword/0, define_keyword/1]). -export([define_macro/0]). -export([disable_sasl_mechanisms/0, disable_sasl_mechanisms/1]). -export([disable_sasl_scram_downgrade_protection/0, disable_sasl_scram_downgrade_protection/1]). @@ -372,6 +373,13 @@ default_ram_db() -> default_ram_db(Host) -> ejabberd_config:get_option({default_ram_db, Host}). +-spec define_keyword() -> any(). +define_keyword() -> + define_keyword(global). +-spec define_keyword(global | binary()) -> any(). +define_keyword(Host) -> + ejabberd_config:get_option({define_keyword, Host}). + -spec define_macro() -> any(). define_macro() -> ejabberd_config:get_option({define_macro, global}). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 6caee7624..bcd697733 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -138,6 +138,8 @@ opt_type(default_db) -> econf:enum([mnesia, sql]); opt_type(default_ram_db) -> econf:enum([mnesia, sql, redis]); +opt_type(define_keyword) -> + econf:map(econf:binary(), econf:any(), [unique]); opt_type(define_macro) -> econf:map(econf:binary(), econf:any(), [unique]); opt_type(disable_sasl_scram_downgrade_protection) -> @@ -510,6 +512,7 @@ opt_type(jwt_auth_only_rule) -> {jwt_key, jose_jwk:key() | undefined} | {append_host_config, [{binary(), any()}]} | {host_config, [{binary(), any()}]} | + {define_keyword, any()} | {define_macro, any()} | {include_config_file, any()} | {atom(), any()}]. @@ -567,6 +570,7 @@ options() -> {certfiles, undefined}, {cluster_backend, mnesia}, {cluster_nodes, []}, + {define_keyword, []}, {define_macro, []}, {disable_sasl_scram_downgrade_protection, false}, {disable_sasl_mechanisms, []}, From 69b190775c6baded248ea514c2529a166e7c2bfb Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 10 Feb 2025 16:35:41 +0100 Subject: [PATCH 1017/1302] Implement internal functions get_predefined and replace keywords --- src/ejabberd_config.erl | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index c7849792b..f13235c5b 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -38,6 +38,7 @@ -export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]). -export([callback_modules/1]). -export([set_option/2]). +-export([get_defined_keywords/1, get_predefined_keywords/1, replace_keywords/2, replace_keywords/3]). %% Deprecated functions -export([get_option/2]). @@ -404,6 +405,93 @@ format_error({error, {exception, Class, Reason, St}}) -> "file attached and the following stacktrace included:~n** ~ts", [misc:format_exception(2, Class, Reason, St)])). +%% @format-begin + +replace_keywords(Host, Value) -> + Keywords = get_defined_keywords(Host) ++ get_predefined_keywords(Host), + replace_keywords(Host, Value, Keywords). + +replace_keywords(Host, List, Keywords) when is_list(List) -> + [replace_keywords(Host, Element, Keywords) || Element <- List]; +replace_keywords(_Host, Atom, Keywords) when is_atom(Atom) -> + Str = atom_to_list(Atom), + case Str == string:uppercase(Str) of + false -> + Atom; + true -> + MacroName = iolist_to_binary(Str), + case proplists:get_value(MacroName, Keywords) of + undefined -> + Atom; + Replacement -> + Replacement + end + end; +replace_keywords(_Host, Binary, Keywords) when is_binary(Binary) -> + lists:foldl(fun ({Key, Replacement}, V) when is_binary(Replacement) -> + misc:expand_keyword(<<"@", Key/binary, "@">>, V, Replacement); + ({_, _}, V) -> + V + end, + Binary, + Keywords); +replace_keywords(Host, {Element1, Element2}, Keywords) -> + {Element1, replace_keywords(Host, Element2, Keywords)}; +replace_keywords(_Host, Value, _DK) -> + Value. + +get_defined_keywords(Host) -> + Tab = case get_tmp_config() of + undefined -> + ejabberd_options; + T -> + T + end, + get_defined_keywords(Tab, Host). + +get_defined_keywords(Tab, Host) -> + KeysHost = + case ets:lookup(Tab, {define_keyword, Host}) of + [{_, List}] -> + List; + _ -> + [] + end, + KeysGlobal = + case Host /= global andalso ets:lookup(Tab, {define_keyword, global}) of + [{_, ListG}] -> + ListG; + _ -> + [] + end, + %% Trying to get defined keywords in host_config when starting ejabberd, + %% the options are not yet stored in ets + KeysTemp = case not is_atom(Tab) andalso KeysHost == [] andalso KeysGlobal == [] of + true -> + get_defined_keywords_yaml_config(ets:lookup_element(Tab, {yaml_config, global}, 2)); + false -> + [] + end, + lists:reverse(KeysTemp ++ KeysGlobal ++ KeysHost). + +get_defined_keywords_yaml_config(Y) -> + [{erlang:atom_to_binary(KwAtom, latin1), KwValue} + || {KwAtom, KwValue} <- proplists:get_value(define_keyword, Y, [])]. + +get_predefined_keywords(Host) -> + HostList = case Host of + global -> []; + _ -> [{<<"HOST">>, Host}] + end, + {ok, [[Home]]} = init:get_argument(home), + HostList ++ + [{<<"HOME">>, list_to_binary(Home)}, + {<<"SEMVER">>, ejabberd_option:version()}, + {<<"VERSION">>, + misc:semver_to_xxyy( + ejabberd_option:version())}]. +%% @format-end + %%%=================================================================== %%% Internal functions %%%=================================================================== From 896b7c75590835cc49b6d2413b6b2bd0c09dfc8b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 10 Feb 2025 16:34:04 +0100 Subject: [PATCH 1018/1302] Add support to replace keywords in toplevel options --- src/ejabberd_config.erl | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index f13235c5b..e48006afb 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -343,12 +343,20 @@ env_binary_to_list(Application, Parameter) -> Other end. +%% ejabberd_options calls this function when parsing options inside host_config -spec validators([atom()]) -> {econf:validators(), [atom()]}. validators(Disallowed) -> + Host = global, + DefinedKeywords = get_defined_keywords(Host), + validators(Disallowed, DefinedKeywords). + +%% validate/1 calls this function when parsing toplevel options +-spec validators([atom()], [any()]) -> {econf:validators(), [atom()]}. +validators(Disallowed, DK) -> Modules = callback_modules(all), Validators = lists:foldl( fun(M, Vs) -> - maps:merge(Vs, validators(M, Disallowed)) + maps:merge(Vs, validators(M, Disallowed, DK)) end, #{}, Modules), Required = lists:flatmap( fun(M) -> @@ -560,19 +568,27 @@ callback_modules(external) -> callback_modules(all) -> callback_modules(local) ++ callback_modules(external). --spec validators(module(), [atom()]) -> econf:validators(). -validators(Mod, Disallowed) -> +-spec validators(module(), [atom()], [any()]) -> econf:validators(). +validators(Mod, Disallowed, DK) -> + Keywords = DK ++ get_predefined_keywords(global), maps:from_list( lists:filtermap( fun(O) -> case lists:member(O, Disallowed) of true -> false; false -> - {true, - try {O, Mod:opt_type(O)} + Type = + try Mod:opt_type(O) catch _:_ -> - {O, ejabberd_options:opt_type(O)} - end} + ejabberd_options:opt_type(O) + end, + TypeProcessed = + econf:and_then( + fun(B) -> + replace_keywords(global, B, Keywords) + end, + Type), + {true, {O, TypeProcessed}} end end, proplists:get_keys(Mod:options()))). @@ -665,12 +681,13 @@ validate(Y1) -> {ok, Y3} -> Hosts = proplists:get_value(hosts, Y3), Version = proplists:get_value(version, Y3, version()), + DK = get_defined_keywords_yaml_config(Y3), create_tmp_config(), set_option(hosts, Hosts), set_option(host, hd(Hosts)), set_option(version, Version), set_option(yaml_config, Y3), - {Validators, Required} = validators([]), + {Validators, Required} = validators([], DK), Validator = econf:options(Validators, [{required, Required}, unique]), From 6e68c2ec020c8d705b804db3a37403989d13db6b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 12 Feb 2025 22:47:41 +0100 Subject: [PATCH 1019/1302] Add support to replace keywords in listener options --- src/ejabberd_listener.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 67f3db2de..4fd7115a7 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -677,13 +677,21 @@ validator(M, T) -> true -> [] end, + Keywords = ejabberd_config:get_defined_keywords(global) ++ ejabberd_config:get_predefined_keywords(global), Validator = maps:from_list( lists:map( fun(Opt) -> - try {Opt, M:listen_opt_type(Opt)} + Type = try M:listen_opt_type(Opt) catch _:_ when M /= ?MODULE -> - {Opt, listen_opt_type(Opt)} - end + listen_opt_type(Opt) + end, + TypeProcessed = + econf:and_then( + fun(B) -> + ejabberd_config:replace_keywords(global, B, Keywords) + end, + Type), + {Opt, TypeProcessed} end, proplists:get_keys(Options))), econf:options( Validator, From 998690f58ceec058ce1dea874b16e0891d1a495d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 10 Feb 2025 16:34:38 +0100 Subject: [PATCH 1020/1302] Add support to replace keywords in modules options --- src/gen_mod.erl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 9d40e0c17..be815606d 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -551,10 +551,10 @@ validator(Host, Module, Opts) -> lists:mapfoldl( fun({Opt, Def}, {DAcc1, VAcc1}) -> {[], {DAcc1#{Opt => Def}, - VAcc1#{Opt => get_opt_type(Module, M, Opt)}}}; + VAcc1#{Opt => get_opt_type(Host, Module, M, Opt)}}}; (Opt, {DAcc1, VAcc1}) -> {[Opt], {DAcc1, - VAcc1#{Opt => get_opt_type(Module, M, Opt)}}} + VAcc1#{Opt => get_opt_type(Host, Module, M, Opt)}}} end, {DAcc, VAcc}, DefOpts) end, {#{}, #{}}, get_defaults(Host, Module, Opts)), econf:and_then( @@ -604,11 +604,16 @@ get_defaults(Host, Module, Opts) -> false end, DefaultOpts)]. --spec get_opt_type(module(), module(), atom()) -> econf:validator(). -get_opt_type(Mod, SubMod, Opt) -> - try SubMod:mod_opt_type(Opt) +-spec get_opt_type(binary(), module(), module(), atom()) -> econf:validator(). +get_opt_type(Host, Mod, SubMod, Opt) -> + Type = try SubMod:mod_opt_type(Opt) catch _:_ -> Mod:mod_opt_type(Opt) - end. + end, + econf:and_then( + fun(B) -> + ejabberd_config:replace_keywords(Host, B) + end, + Type). -spec sort_modules(binary(), [{module(), opts()}]) -> {ok, [{module(), opts(), integer()}]}. sort_modules(Host, ModOpts) -> From c8abff33c1c376061202c7e3289035e9ff415ff4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Feb 2025 19:58:02 +0100 Subject: [PATCH 1021/1302] Remove some options keyword expansion, as they are now predefined --- src/econf.erl | 10 +++------- src/ejabberd_http.erl | 7 +------ src/ejabberd_options.erl | 10 +--------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/econf.erl b/src/econf.erl index a2a884e76..a6abdb647 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -546,14 +546,10 @@ sip_uri() -> -spec host() -> yconf:validator(binary()). host() -> fun(Domain) -> - Host = ejabberd_config:get_myname(), Hosts = ejabberd_config:get_option(hosts), - Domain1 = (binary())(Domain), - Domain2 = misc:expand_keyword(<<"@HOST@">>, Domain1, Host), - Domain3 = (domain())(Domain2), - case lists:member(Domain3, Hosts) of - true -> fail({route_conflict, Domain3}); - false -> Domain3 + case lists:member(Domain, Hosts) of + true -> fail({route_conflict, Domain}); + false -> Domain end end. diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 76fd80c40..8a3a45f54 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -932,12 +932,7 @@ listen_opt_type(default_host) -> listen_opt_type(custom_headers) -> econf:map( econf:binary(), - econf:and_then( - econf:binary(), - fun(V) -> - misc:expand_keyword(<<"@VERSION@">>, V, - ejabberd_option:version()) - end)). + econf:binary()). listen_options() -> [{ciphers, undefined}, diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index bcd697733..0e471059e 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -112,14 +112,7 @@ opt_type(cache_missed) -> opt_type(cache_size) -> econf:pos_int(infinity); opt_type(captcha_cmd) -> - econf:and_then( - econf:binary(), - fun(V) -> - V2 = misc:expand_keyword(<<"@SEMVER@">>, V, - ejabberd_option:version()), - misc:expand_keyword(<<"@VERSION@">>, V2, - misc:semver_to_xxyy(ejabberd_option:version())) - end); + econf:binary(); opt_type(captcha_host) -> econf:binary(); opt_type(captcha_limit) -> @@ -493,7 +486,6 @@ opt_type(jwt_auth_only_rule) -> {c2s_protocol_options, undefined | binary()} | {s2s_ciphers, undefined | binary()} | {c2s_ciphers, undefined | binary()} | - {captcha_cmd, undefined | binary()} | {websocket_origin, [binary()]} | {disable_sasl_mechanisms, [binary()]} | {s2s_zlib, boolean()} | From 352ee3a318a3cda428b00a9aaac3e4d3601eb01b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Feb 2025 19:52:49 +0100 Subject: [PATCH 1022/1302] Docs: Document define_keyword and simplify define_macro --- src/ejabberd_options_doc.erl | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index ae2d7538b..84c2ae992 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -534,18 +534,26 @@ doc() -> ?T("A list of Erlang nodes to connect on ejabberd startup. " "This option is mostly intended for ejabberd customization " "and sophisticated setups. The default value is an empty list.")}}, - {define_macro, - #{value => "{MacroName: MacroValue}", + {define_keyword, + #{value => "{NAME: Value}", desc => - ?T("Defines a " - "_`../configuration/file-format.md#macros-in-configuration-file|macro`_. " - "The value can be any valid arbitrary " - "YAML value. For convenience, it's recommended to define " - "a 'MacroName' in capital letters. Duplicated macros are not allowed. " - "Macros are processed after additional configuration files have " - "been included, so it is possible to use macros that are defined " - "in configuration files included before the usage. " - "It is possible to use a 'MacroValue' in the definition of another macro."), + ?T("Allows to define configuration " + "_`../configuration/file-format.md#keywords|keywords`_. "), + example => + ["define_keyword:", + " SQL_USERNAME: \"eja.global\"", + "", + "host_config:", + " localhost:", + " define_keyword:", + " SQL_USERNAME: \"eja.localhost\"", + "", + "sql_username: \"prefix.@SQL_USERNAME@\""]}}, + {define_macro, + #{value => "{NAME: Value}", + desc => + ?T("Allows to define configuration " + "_`../configuration/file-format.md#macros|macros`_. "), example => ["define_macro:", " DEBUG: debug", From 4dcf97c4c1644123754751d09f6d11f89768e4cc Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 12 Feb 2025 15:35:36 +0100 Subject: [PATCH 1023/1302] Now, when running tests, external may contain duplicate module --- src/ejabberd_config.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index e48006afb..d744616f5 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -566,7 +566,15 @@ callback_modules(external) -> end end, beams(external)); callback_modules(all) -> - callback_modules(local) ++ callback_modules(external). + lists_uniq(callback_modules(local) ++ callback_modules(external)). + +-ifdef(OTP_BELOW_25). +lists_uniq(List) -> + lists:usort(List). +-else. +lists_uniq(List) -> + lists:uniq(List). +-endif. -spec validators(module(), [atom()], [any()]) -> econf:validators(). validators(Mod, Disallowed, DK) -> From 888c335c2e7403f7bc4bb22b6da9b4ab0dd9e013 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Feb 2025 19:58:35 +0100 Subject: [PATCH 1024/1302] Add tests for config features define_macro and define_keyword --- test/configtest_tests.erl | 187 ++++++++++++++++++++++++ test/ejabberd_SUITE.erl | 2 + test/ejabberd_SUITE_data/configtest.yml | 119 +++++++++++++++ test/ejabberd_SUITE_data/ejabberd.yml | 3 + test/ejabberd_test_options.erl | 114 +++++++++++++++ test/mod_configtest.erl | 45 ++++++ test/suite.erl | 24 ++- 7 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 test/configtest_tests.erl create mode 100644 test/ejabberd_SUITE_data/configtest.yml create mode 100644 test/ejabberd_test_options.erl create mode 100644 test/mod_configtest.erl diff --git a/test/configtest_tests.erl b/test/configtest_tests.erl new file mode 100644 index 000000000..3763b8645 --- /dev/null +++ b/test/configtest_tests.erl @@ -0,0 +1,187 @@ +%%%------------------------------------------------------------------- +%%% Author : Badlop +%%% Created : 5 Feb 2025 by Badlop +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%------------------------------------------------------------------- + +-module(configtest_tests). + +-compile(export_all). + +-include("suite.hrl"). + +%%%================================== + +single_cases() -> + {configtest_single, + [sequence], + [single_test(macro_over_keyword), + single_test(keyword_inside_macro), + single_test(macro_and_keyword), + single_test(macro_double), + single_test(keyword_double), + + single_test(macro_toplevel_global_atom), + single_test(macro_toplevel_global_string), + single_test(macro_toplevel_global_string_inside), + single_test(macro_toplevel_local_atom), + single_test(macro_toplevel_local_string), + single_test(macro_toplevel_local_string_inside), + + single_test(keyword_toplevel_global_atom), + single_test(keyword_toplevel_global_string), + single_test(keyword_toplevel_global_string_inside), + single_test(keyword_toplevel_local_atom), + single_test(keyword_toplevel_local_string), + single_test(keyword_toplevel_local_string_inside), + + single_test(macro_module_atom), + single_test(macro_module_string), + single_test(macro_module_string_inside), + + single_test(keyword_module_atom), + single_test(keyword_module_string), + single_test(keyword_module_string_inside), + + single_test(toplevel_global_predefined), + single_test(toplevel_local_predefined), + single_test(module_predefined)]}. + +%% Interactions + +macro_over_keyword(_) -> + toplevel_global(macro, macro_over_keyword). + +keyword_inside_macro(_) -> + toplevel_global(<<"+macro+/-keyword-">>, keyword_inside_macro). + +macro_and_keyword(_) -> + toplevel_global(<<"+macro+&-keyword-">>, macro_and_keyword). + +macro_double(_) -> + toplevel_global(<<"macro--macro">>, macro_double). + +keyword_double(_) -> + toplevel_global(<<"keyword--keyword">>, keyword_double). + +%% Macro Toplevel + +macro_toplevel_global_atom(_) -> + toplevel_global(mtga, mtga). + +macro_toplevel_global_string(_) -> + toplevel_global(<<"Mtgs">>, mtgs). + +macro_toplevel_global_string_inside(_) -> + toplevel_global(<<"Mtgsi">>, mtgsi). + +macro_toplevel_local_atom(_) -> + toplevel_local(mtla, mtla). + +macro_toplevel_local_string(_) -> + toplevel_local(<<"Mtls">>, mtls). + +macro_toplevel_local_string_inside(_) -> + toplevel_local(<<"Mtlsi">>, mtlsi). + +%% Keyword Toplevel + +keyword_toplevel_global_atom(_) -> + toplevel_global(ktga, ktga). + +keyword_toplevel_global_string(_) -> + toplevel_global(<<"Ktgs">>, ktgs). + +keyword_toplevel_global_string_inside(_) -> + toplevel_global(<<"Ktgsi">>, ktgsi). + +keyword_toplevel_local_atom(_) -> + toplevel_local(ktla, ktla). + +keyword_toplevel_local_string(_) -> + toplevel_local(<<"Ktls">>, ktls). + +keyword_toplevel_local_string_inside(_) -> + toplevel_local(<<"Ktlsi">>, ktlsi). + +%% Macro Module + +macro_module_atom(_) -> + module(mma, mma). + +macro_module_string(_) -> + module(<<"Mms">>, mms). + +macro_module_string_inside(_) -> + module(<<"Mmsi">>, mmsi). + +%% Keyword Module + +keyword_module_atom(_) -> + module(kma, kma). + +keyword_module_string(_) -> + module(<<"Kms">>, kms). + +keyword_module_string_inside(_) -> + module(<<"Kmsi">>, kmsi). + +%% Predefined + +toplevel_global_predefined(_) -> + Semver = ejabberd_option:version(), + Version = misc:semver_to_xxyy(Semver), + String = <<"tgp - semver: ", Semver/binary, ", version: ", Version/binary>>, + toplevel_global(String, tgp). + +toplevel_local_predefined(_) -> + Semver = ejabberd_option:version(), + Version = misc:semver_to_xxyy(Semver), + String = <<"tlp - semver: ", Semver/binary, ", version: ", Version/binary>>, + toplevel_local(String, tlp). + +module_predefined(_) -> + Host = <<"configtest.localhost">>, + Semver = ejabberd_option:version(), + Version = misc:semver_to_xxyy(Semver), + String = <<"mp - host: ", Host/binary, ", semver: ", Semver/binary, ", version: ", Version/binary>>, + module(String, predefined_keywords). + +%%%================================== +%%%% internal functions + +single_test(T) -> + list_to_atom("configtest_" ++ atom_to_list(T)). + +toplevel_global(Result, Option) -> + ?match(Result, ejabberd_config:get_option(Option)). + +toplevel_local(Result, Option) -> + Host = <<"configtest.localhost">>, + ?match(Result, ejabberd_config:get_option({Option, Host})). + +module(Result, Option) -> + Host = <<"configtest.localhost">>, + Module = mod_configtest, + ?match(Result, gen_mod:get_module_opt(Host, Module, Option)). + +%%%================================== + +%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index e240de59b..0a9e73d4d 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -60,6 +60,7 @@ init_per_suite(Config) -> NewConfig. start_ejabberd(_) -> + application:set_env(ejabberd, external_beams, "../../lib/ejabberd/test/"), {ok, _} = application:ensure_all_started(ejabberd, transient). end_per_suite(_Config) -> @@ -398,6 +399,7 @@ no_db_tests() -> auth_external_wrong_server, auth_external_invalid_cert, commands_tests:single_cases(), + configtest_tests:single_cases(), jidprep_tests:single_cases(), sm_tests:single_cases(), sm_tests:master_slave_cases(), diff --git a/test/ejabberd_SUITE_data/configtest.yml b/test/ejabberd_SUITE_data/configtest.yml new file mode 100644 index 000000000..cdd8b0023 --- /dev/null +++ b/test/ejabberd_SUITE_data/configtest.yml @@ -0,0 +1,119 @@ + +define_macro: + CONFIGTEST_CONFIG: + modules: + mod_mam: {} + mod_muc: {} + +## Interactions + +define_macro: + MOK: macro + +define_keyword: + MOK: keyword + +macro_over_keyword: MOK + +## + +define_keyword: + KIM_KEYWORD: "-keyword-" + +define_macro: + KIM_MACRO: "+macro+/@KIM_KEYWORD@" + +keyword_inside_macro: KIM_MACRO + +## + +define_macro: + MAK_MACRO: "+macro+" + +define_keyword: + MAK_KEYWORD: "-keyword-" + +macro_and_keyword: "@MAK_MACRO@&@MAK_KEYWORD@" + +## + +define_macro: + MD: "macro" + +macro_double: "@MD@--@MD@" + +## + +define_keyword: + KD: "keyword" + +keyword_double: "@KD@--@KD@" + +## Macro Toplevel + +define_macro: + MTGA: mtga + MTGS: "Mtgs" + MTLA: mtla + MTLS: "Mtls" + +mtga: MTGA +mtgs: MTGS +mtgsi: "@MTGS@i" + +host_config: + configtest.localhost: + mtla: MTLA + mtls: MTLS + mtlsi: "@MTLS@i" + +## Keyword Toplevel + +define_keyword: + KTGA: ktga + KTLA: ktla + KTGS: "Ktgs" + KTLS: "Ktls" + +ktga: KTGA +ktgs: KTGS +ktgsi: "@KTGS@i" + +host_config: + configtest.localhost: + ktla: KTLA + ktls: KTLS + ktlsi: "@KTLS@i" + +## Macro Module +## Keyword Module +## Predefined Module + +define_macro: + MMA: mma + MMS: "Mms" + +define_keyword: + KMA: kma + KMS: "Kms" + +append_host_config: + configtest.localhost: + modules: + mod_configtest: + mma: MMA + mms: MMS + mmsi: "@MMS@i" + kma: KMA + kms: KMS + kmsi: "@KMS@i" + predefined_keywords: "mp - host: @HOST@, semver: @SEMVER@, version: @VERSION@" + +## Predefined + +tgp: "tgp - semver: @SEMVER@, version: @VERSION@" + +host_config: + configtest.localhost: + tlp: "tlp - semver: @SEMVER@, version: @VERSION@" + diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 0155e8f90..e3aa43c3d 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -1,5 +1,6 @@ include_config_file: - macros.yml + - configtest.yml - ejabberd.extauth.yml - ejabberd.ldap.yml - ejabberd.mnesia.yml @@ -10,6 +11,7 @@ include_config_file: - ejabberd.sqlite.yml host_config: + configtest.localhost: CONFIGTEST_CONFIG pgsql.localhost: PGSQL_CONFIG sqlite.localhost: SQLITE_CONFIG mysql.localhost: MYSQL_CONFIG @@ -25,6 +27,7 @@ host_config: hosts: - localhost + - configtest.localhost - mnesia.localhost - redis.localhost - mysql.localhost diff --git a/test/ejabberd_test_options.erl b/test/ejabberd_test_options.erl new file mode 100644 index 000000000..10cfcab3e --- /dev/null +++ b/test/ejabberd_test_options.erl @@ -0,0 +1,114 @@ +%%%---------------------------------------------------------------------- +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(ejabberd_test_options). +-behaviour(ejabberd_config). + +-export([opt_type/1, options/0, globals/0, doc/0]). + +%%%=================================================================== +%%% API +%%%=================================================================== +-spec opt_type(atom()) -> econf:validator(). + +opt_type(macro_over_keyword) -> + econf:atom(); + +opt_type(keyword_inside_macro) -> + econf:binary(); + +opt_type(macro_and_keyword) -> + econf:binary(); + +opt_type(macro_double) -> + econf:binary(); + +opt_type(keyword_double) -> + econf:binary(); + +opt_type(mtga) -> + econf:atom(); + +opt_type(mtgs) -> + econf:binary(); + +opt_type(mtgsi) -> + econf:binary(); + +opt_type(mtla) -> + econf:atom(); + +opt_type(mtls) -> + econf:binary(); + +opt_type(mtlsi) -> + econf:binary(); + +opt_type(ktga) -> + econf:atom(); + +opt_type(ktgs) -> + econf:binary(); + +opt_type(ktgsi) -> + econf:binary(); + +opt_type(ktla) -> + econf:atom(); + +opt_type(ktls) -> + econf:binary(); + +opt_type(ktlsi) -> + econf:binary(); + +opt_type(tgp) -> + econf:binary(); + +opt_type(tlp) -> + econf:binary(). + +options() -> + [{macro_over_keyword, undefined}, + {keyword_inside_macro, undefined}, + {macro_and_keyword, undefined}, + {macro_double, undefined}, + {keyword_double, undefined}, + {mtga, undefined}, + {mtgs, undefined}, + {mtgsi, undefined}, + {mtla, undefined}, + {mtls, undefined}, + {mtlsi, undefined}, + {ktga, undefined}, + {ktgs, undefined}, + {ktgsi, undefined}, + {ktla, undefined}, + {ktls, undefined}, + {ktlsi, undefined}, + {tgp, undefined}, + {tlp, undefined} + ]. + +-spec globals() -> [atom()]. +globals() -> + []. + +doc() -> + ejabberd_options_doc:doc(). + diff --git a/test/mod_configtest.erl b/test/mod_configtest.erl new file mode 100644 index 000000000..a4ca36f33 --- /dev/null +++ b/test/mod_configtest.erl @@ -0,0 +1,45 @@ +-module(mod_configtest). +-behaviour(gen_mod). + +-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]). + +start(_Host, _Opts) -> + ok. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +depends(_Host, _Opts) -> + []. + +mod_opt_type(mma) -> + econf:atom(); +mod_opt_type(mms) -> + econf:binary(); +mod_opt_type(mmsi) -> + econf:binary(); + +mod_opt_type(kma) -> + econf:atom(); +mod_opt_type(kms) -> + econf:binary(); +mod_opt_type(kmsi) -> + econf:binary(); + +mod_opt_type(predefined_keywords) -> + econf:binary(). + +mod_options(_) -> + [{mma, undefined}, + {mms, undefined}, + {mmsi, undefined}, + {kma, undefined}, + {kms, undefined}, + {kmsi, undefined}, + {predefined_keywords, undefined}]. + +mod_doc() -> + #{}. diff --git a/test/suite.erl b/test/suite.erl index 0066b5116..28825b1d1 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -84,6 +84,7 @@ init_config(Config) -> {priv_dir, PrivDir}]), MacrosPath = filename:join([CWD, "macros.yml"]), ok = file:write_file(MacrosPath, MacrosContent), + copy_configtest_yml(DataDir, CWD), copy_backend_configs(DataDir, CWD, Backends), setup_ejabberd_lib_path(Config), case application:load(sasl) of @@ -137,11 +138,32 @@ init_config(Config) -> {backends, Backends} |Config]. +copy_configtest_yml(DataDir, CWD) -> + Files = filelib:wildcard(filename:join([DataDir, "configtest.yml"])), + lists:foreach( + fun(Src) -> + ct:pal("copying ~p", [Src]), + File = filename:basename(Src), + case string:tokens(File, ".") of + ["configtest", "yml"] -> + Dst = filename:join([CWD, File]), + case true of + true -> + {ok, _} = file:copy(Src, Dst); + false -> + ok + end; + _ -> + ok + end + end, Files). + + copy_backend_configs(DataDir, CWD, Backends) -> Files = filelib:wildcard(filename:join([DataDir, "ejabberd.*.yml"])), lists:foreach( fun(Src) -> - io:format("copying ~p", [Src]), + ct:pal("copying ~p", [Src]), File = filename:basename(Src), case string:tokens(File, ".") of ["ejabberd", SBackend, "yml"] -> From c87ba45a869ec25ef8b64451d802436653a68632 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Mar 2025 19:57:38 +0100 Subject: [PATCH 1025/1302] Result of running "make format" --- src/ejabberd_config.erl | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index d744616f5..7b08c4469 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -474,12 +474,13 @@ get_defined_keywords(Tab, Host) -> end, %% Trying to get defined keywords in host_config when starting ejabberd, %% the options are not yet stored in ets - KeysTemp = case not is_atom(Tab) andalso KeysHost == [] andalso KeysGlobal == [] of - true -> - get_defined_keywords_yaml_config(ets:lookup_element(Tab, {yaml_config, global}, 2)); - false -> - [] - end, + KeysTemp = + case not is_atom(Tab) andalso KeysHost == [] andalso KeysGlobal == [] of + true -> + get_defined_keywords_yaml_config(ets:lookup_element(Tab, {yaml_config, global}, 2)); + false -> + [] + end, lists:reverse(KeysTemp ++ KeysGlobal ++ KeysHost). get_defined_keywords_yaml_config(Y) -> @@ -487,17 +488,20 @@ get_defined_keywords_yaml_config(Y) -> || {KwAtom, KwValue} <- proplists:get_value(define_keyword, Y, [])]. get_predefined_keywords(Host) -> - HostList = case Host of - global -> []; - _ -> [{<<"HOST">>, Host}] - end, + HostList = + case Host of + global -> + []; + _ -> + [{<<"HOST">>, Host}] + end, {ok, [[Home]]} = init:get_argument(home), - HostList ++ - [{<<"HOME">>, list_to_binary(Home)}, - {<<"SEMVER">>, ejabberd_option:version()}, - {<<"VERSION">>, - misc:semver_to_xxyy( - ejabberd_option:version())}]. + HostList + ++ [{<<"HOME">>, list_to_binary(Home)}, + {<<"SEMVER">>, ejabberd_option:version()}, + {<<"VERSION">>, + misc:semver_to_xxyy( + ejabberd_option:version())}]. %% @format-end %%%=================================================================== From 53dea7b6d79ac35eb6ae5a488499fd7714febea4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Mar 2025 19:01:13 +0100 Subject: [PATCH 1026/1302] Fix email addresses in modules headers --- src/ejabberd_regexp.erl | 4 ++-- src/mod_muc_admin.erl | 6 +++--- src/mod_muc_log.erl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl index 7644810cd..0d18deac6 100644 --- a/src/ejabberd_regexp.erl +++ b/src/ejabberd_regexp.erl @@ -1,8 +1,8 @@ %%%---------------------------------------------------------------------- %%% File : ejabberd_regexp.erl -%%% Author : Badlop +%%% Author : Badlop %%% Purpose : Frontend to Re OTP module -%%% Created : 8 Dec 2011 by Badlop +%%% Created : 8 Dec 2011 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2025 ProcessOne diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index a260d43b2..35a0f2f94 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1,8 +1,8 @@ %%%---------------------------------------------------------------------- %%% File : mod_muc_admin.erl -%%% Author : Badlop +%%% Author : Badlop %%% Purpose : Tools for additional MUC administration -%%% Created : 8 Sep 2007 by Badlop +%%% Created : 8 Sep 2007 by Badlop %%% %%% %%% ejabberd, Copyright (C) 2002-2025 ProcessOne @@ -24,7 +24,7 @@ %%%---------------------------------------------------------------------- -module(mod_muc_admin). --author('badlop@ono.com'). +-author('badlop@process-one.net'). -behaviour(gen_mod). diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index c075b954c..57a975b0b 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -1,6 +1,6 @@ %%%---------------------------------------------------------------------- %%% File : mod_muc_log.erl -%%% Author : Badlop@process-one.net +%%% Author : Badlop %%% Purpose : MUC room logging %%% Created : 12 Mar 2006 by Alexey Shchepin %%% From 1ea0dde417d1b96782f8f13e2b9c6616cb5d084b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 18:10:01 +0100 Subject: [PATCH 1027/1302] ejabberd_admin: Allow using mnesia_list_tables and mnesia_table_change_storage --- src/ejabberd_admin.erl | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 259831d7f..9f2cc84b8 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -598,8 +598,9 @@ get_commands_spec() -> args = [{node, atom}, {table, binary}, {page, integer}], result = {res, any}}, - #ejabberd_commands{name = mnesia_list_tables, tags = [internal, mnesia], + #ejabberd_commands{name = mnesia_list_tables, tags = [mnesia], desc = "List of Mnesia tables", + note = "added in 25.xx", module = ?MODULE, function = mnesia_list_tables, result = {tables, {list, {table, {tuple, [{name, atom}, {storage_type, binary}, @@ -615,8 +616,10 @@ get_commands_spec() -> {value, binary} ]}}}}}, - #ejabberd_commands{name = mnesia_table_change_storage, tags = [internal, mnesia], - desc = "Change storage type of a Mnesia table to: ram_copies, disc_copies, or disc_only_copies.", + #ejabberd_commands{name = mnesia_table_change_storage, tags = [mnesia], + desc = "Change storage type of a Mnesia table", + note = "added in 25.xx", + longdesc = "Storage type can be: `ram_copies`, `disc_copies`, `disc_only_copies`, `remote_copy`.", module = ?MODULE, function = mnesia_table_change_storage, args = [{table, binary}, {storage_type, binary}], result = {res, restuple}}, @@ -1281,13 +1284,12 @@ is_my_host(Host) -> %% @format-begin -%% mnesia:del_table_copy(Table, Node); -%% mnesia:change_table_copy_type(Table, Node, Type); - mnesia_table_change_storage(STable, SType) -> Table = binary_to_existing_atom(STable, latin1), Type = case SType of + <<"remote_copy">> -> + remote_copy; <<"ram_copies">> -> ram_copies; <<"disc_copies">> -> @@ -1297,7 +1299,24 @@ mnesia_table_change_storage(STable, SType) -> _ -> false end, - mnesia:add_table_copy(Table, node(), Type). + Node = node(), + Result = + case Type of + false -> + "Nothing to do"; + remote_copy -> + mnesia:del_table_copy(Table, Node), + "Deleted table copy"; + _ -> + case mnesia:add_table_copy(Table, Node, Type) of + {aborted, _} -> + mnesia:change_table_copy_type(Table, Node, Type), + "Changed table copy type"; + _ -> + "Added table copy" + end + end, + {ok, Result}. mnesia_table_clear(STable) -> Table = binary_to_existing_atom(STable, latin1), From 6151674e64a595b66d98bcd83582b9ae1f547a75 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 21:10:19 +0100 Subject: [PATCH 1028/1302] ejabberd_commands: Show warning when registering command with an existing name In ejabberd modules, only register/unregister commands if module is not running for any other vhost. --- src/ejabberd_commands.erl | 35 +++++++++++++++++++++++++++++++---- src/mod_admin_extra.erl | 11 +++-------- src/mod_admin_update_sql.erl | 8 ++++---- src/mod_fail2ban.erl | 9 ++------- src/mod_mam.erl | 9 ++------- src/mod_muc_admin.erl | 11 +++-------- src/mod_private.erl | 9 ++------- src/mod_pubsub.erl | 9 ++------- src/mod_push.erl | 9 ++------- 9 files changed, 51 insertions(+), 59 deletions(-) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 5028f05fc..2318564ba 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -42,7 +42,9 @@ get_tags_commands/1, register_commands/1, register_commands/2, + register_commands/3, unregister_commands/1, + unregister_commands/3, get_commands_spec/0, get_commands_definition/0, get_commands_definition/1, @@ -142,19 +144,34 @@ code_change(_OldVsn, State, _Extra) -> register_commands(Commands) -> register_commands(unknown, Commands). +-spec register_commands(atom(), [ejabberd_commands()]) -> ok. + register_commands(Definer, Commands) -> + ExistingCommands = list_commands(), lists:foreach( fun(Command) -> - %% XXX check if command exists - mnesia:dirty_write(register_command_prepare(Command, Definer)) - %% ?DEBUG("This command is already defined:~n~p", [Command]) + Name = Command#ejabberd_commands.name, + case lists:keyfind(Name, 1, ExistingCommands) of + false -> + mnesia:dirty_write(register_command_prepare(Command, Definer)); + _ -> + OtherCommandDef = get_command_definition(Name), + ?CRITICAL_MSG("Error trying to define a command: another one already exists with the same name:~n Existing: ~p~n New: ~p", [OtherCommandDef, Command]) + end end, Commands), ejabberd_access_permissions:invalidate(), ok. +-spec register_commands(binary(), atom(), [ejabberd_commands()]) -> ok. - +register_commands(Host, Definer, Commands) -> + case gen_mod:is_loaded_elsewhere(Host, Definer) of + false -> + register_commands(Definer, Commands); + true -> + ok + end. register_command_prepare(Command, Definer) -> Tags1 = Command#ejabberd_commands.tags, @@ -175,6 +192,16 @@ unregister_commands(Commands) -> Commands), ejabberd_access_permissions:invalidate(). +-spec unregister_commands(binary(), atom(), [ejabberd_commands()]) -> ok. + +unregister_commands(Host, Definer, Commands) -> + case gen_mod:is_loaded_elsewhere(Host, Definer) of + false -> + unregister_commands(Commands); + true -> + ok + end. + -spec list_commands() -> [{atom(), [aterm()], string()}]. list_commands() -> diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 11b0fbb95..d461edc89 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -104,8 +104,8 @@ %%% gen_mod %%% -start(_Host, _Opts) -> - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), +start(Host, _Opts) -> + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, {hook, webadmin_page_main, web_page_main, 50, global}, {hook, webadmin_menu_host, web_menu_host, 50}, @@ -118,12 +118,7 @@ start(_Host, _Opts) -> {hook, webadmin_page_node, web_page_node, 50, global}]}. stop(Host) -> - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 0c3f9095f..c5b4ab865 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -47,11 +47,11 @@ %%% gen_mod %%% -start(_Host, _Opts) -> - ejabberd_commands:register_commands(?MODULE, get_commands_spec()). +start(Host, _Opts) -> + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()). -stop(_Host) -> - ejabberd_commands:unregister_commands(get_commands_spec()). +stop(Host) -> + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl index 2680248e5..2dbd8575c 100644 --- a/src/mod_fail2ban.erl +++ b/src/mod_fail2ban.erl @@ -107,16 +107,11 @@ c2s_stream_started(#{ip := {Addr, _}} = State, _) -> start(Host, Opts) -> catch ets:new(failed_auth, [named_table, public, {heir, erlang:group_leader(), none}]), - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), gen_mod:start_child(?MODULE, Host, Opts). stop(Host) -> - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end, + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()), gen_mod:stop_child(?MODULE, Host). reload(_Host, _NewOpts, _OldOpts) -> diff --git a/src/mod_mam.erl b/src/mod_mam.erl index aa4bb3b84..a7b47fe91 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -182,7 +182,7 @@ start(Host, Opts) -> ejabberd_hooks:add(check_create_room, Host, ?MODULE, check_create_room, 50) end, - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), ok; Err -> Err @@ -263,12 +263,7 @@ stop(Host) -> ejabberd_hooks:delete(check_create_room, Host, ?MODULE, check_create_room, 50) end, - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 35a0f2f94..c1c6ccf54 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -68,8 +68,8 @@ %% gen_mod %%---------------------------- -start(_Host, _Opts) -> - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), +start(Host, _Opts) -> + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, {hook, webadmin_page_main, web_page_main, 50, global}, {hook, webadmin_menu_host, web_menu_host, 50}, @@ -79,12 +79,7 @@ start(_Host, _Opts) -> ]}. stop(Host) -> - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_private.erl b/src/mod_private.erl index 145edefcf..3bb93ed77 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -71,7 +71,7 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), {ok, [{hook, remove_user, remove_user, 50}, {hook, disco_sm_features, get_sm_features, 50}, {hook, pubsub_publish_item, pubsub_publish_item, 50}, @@ -82,12 +82,7 @@ start(Host, Opts) -> {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. stop(Host) -> - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 3ebc69fa4..059fa9518 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -338,7 +338,7 @@ init([ServerHost|_]) -> false -> ok end, - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + ejabberd_commands:register_commands(ServerHost, ?MODULE, get_commands_spec()), NodeTree = config(ServerHost, nodetree), Plugins = config(ServerHost, plugins), PepMapping = config(ServerHost, pep_mapping), @@ -809,12 +809,7 @@ terminate(_Reason, terminate_plugins(Host, ServerHost, Plugins, TreePlugin), ejabberd_router:unregister_route(Host) end, Hosts), - case gen_mod:is_loaded_elsewhere(ServerHost, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(ServerHost, ?MODULE, get_commands_spec()). %%-------------------------------------------------------------------- %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} diff --git a/src/mod_push.erl b/src/mod_push.erl index 20cb6394c..7d2792eb4 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -96,7 +96,7 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_commands:register_commands(?MODULE, get_commands_spec()), + ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), {ok, [{iq_handler, ejabberd_sm, ?NS_PUSH_0, process_iq}, {hook, disco_sm_features, disco_sm_features, 50}, {hook, c2s_session_pending, c2s_session_pending, 50}, @@ -111,12 +111,7 @@ start(Host, Opts) -> -spec stop(binary()) -> ok. stop(Host) -> - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok - end. + ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(Host, NewOpts, OldOpts) -> From aa78362c7f5bfabc56183837233e24cf2a45ad09 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Feb 2025 13:45:20 +0100 Subject: [PATCH 1029/1302] mod_configure: Add option 'access' to let configure the access name --- src/mod_configure.erl | 85 +++++++++++++++++++++++++++------------ src/mod_configure_opt.erl | 13 ++++++ 2 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 src/mod_configure_opt.erl diff --git a/src/mod_configure.erl b/src/mod_configure.erl index d213ae9d9..08380a16a 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : mod_configure.erl %%% Author : Alexey Shchepin -%%% Purpose : Support for online configuration of ejabberd +%%% Purpose : Support for online configuration of ejabberd using XEP-0050 %%% Created : 19 Jan 2003 by Alexey Shchepin %%% %%% @@ -36,6 +36,7 @@ adhoc_local_items/4, adhoc_local_commands/4, get_sm_identity/5, get_sm_features/5, get_sm_items/5, adhoc_sm_items/4, adhoc_sm_commands/4, mod_options/1, + mod_opt_type/1, depends/2, mod_doc/0]). -include("logger.hrl"). @@ -92,6 +93,10 @@ depends(_Host, _Opts) -> -spec tokenize(binary()) -> [binary()]. tokenize(Node) -> str:tokens(Node, <<"/#">>). +acl_match_rule(Host, From) -> + Access = mod_configure_opt:access(Host), + acl:match_rule(Host, Access, From). + -spec get_sm_identity([identity()], jid(), jid(), binary(), binary()) -> [identity()]. get_sm_identity(Acc, _From, _To, Node, Lang) -> case Node of @@ -167,7 +172,7 @@ get_sm_features(Acc, From, case gen_mod:is_loaded(LServer, mod_adhoc) of false -> Acc; _ -> - Allow = acl:match_rule(LServer, configure, From), + Allow = acl_match_rule(LServer, From), case Node of <<"config">> -> ?INFO_RESULT(Allow, [?NS_COMMANDS], Lang); _ -> Acc @@ -182,7 +187,7 @@ get_local_features(Acc, From, false -> Acc; _ -> LNode = tokenize(Node), - Allow = acl:match_rule(LServer, configure, From), + Allow = acl_match_rule(LServer, From), case LNode of [<<"config">>] -> ?INFO_RESULT(Allow, [], Lang); [<<"user">>] -> ?INFO_RESULT(Allow, [], Lang); @@ -240,7 +245,7 @@ get_local_features(Acc, From, jid(), jid(), binary()) -> mod_disco:items_acc(). adhoc_sm_items(Acc, From, #jid{lserver = LServer} = To, Lang) -> - case acl:match_rule(LServer, configure, From) of + case acl_match_rule(LServer, From) of allow -> Items = case Acc of {result, Its} -> Its; @@ -266,7 +271,7 @@ get_sm_items(Acc, From, {result, Its} -> Its; empty -> [] end, - case {acl:match_rule(LServer, configure, From), Node} of + case {acl_match_rule(LServer, From), Node} of {allow, <<"">>} -> Nodes = [?NODEJID(To, ?T("Configuration"), <<"config">>), @@ -295,13 +300,13 @@ get_user_resources(User, Server) -> jid(), jid(), binary()) -> mod_disco:items_acc(). adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To, Lang) -> - case acl:match_rule(LServer, configure, From) of + case acl_match_rule(LServer, From) of allow -> Items = case Acc of {result, Its} -> Its; empty -> [] end, - PermLev = get_permission_level(From), + PermLev = get_permission_level(From, LServer), Nodes = recursively_get_local_items(PermLev, LServer, <<"">>, Server, Lang), Nodes1 = lists:filter( @@ -348,9 +353,10 @@ recursively_get_local_items(PermLev, LServer, Node, end, Items)). --spec get_permission_level(jid()) -> global | vhost. -get_permission_level(JID) -> - case acl:match_rule(global, configure, JID) of +-spec get_permission_level(jid(), binary()) -> global | vhost. +get_permission_level(JID, Host) -> + Access = mod_configure_opt:access(Host), + case acl:match_rule(global, Access, JID) of allow -> global; deny -> vhost end. @@ -361,7 +367,7 @@ get_permission_level(JID) -> case Allow of deny -> Fallback; allow -> - PermLev = get_permission_level(From), + PermLev = get_permission_level(From, LServer), case get_local_items({PermLev, LServer}, LNode, jid:encode(To), Lang) of @@ -381,11 +387,11 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To, {result, Its} -> Its; empty -> [] end, - Allow = acl:match_rule(LServer, configure, From), + Allow = acl_match_rule(LServer, From), case Allow of deny -> {result, Items}; allow -> - PermLev = get_permission_level(From), + PermLev = get_permission_level(From, LServer), case get_local_items({PermLev, LServer}, [], jid:encode(To), Lang) of @@ -400,7 +406,7 @@ get_local_items(Acc, From, #jid{lserver = LServer} = To, false -> Acc; _ -> LNode = tokenize(Node), - Allow = acl:match_rule(LServer, configure, From), + Allow = acl_match_rule(LServer, From), Err = xmpp:err_forbidden(?T("Access denied by service policy"), Lang), case LNode of [<<"config">>] -> @@ -690,7 +696,7 @@ get_stopped_nodes(_Lang) -> -define(COMMANDS_RESULT(LServerOrGlobal, From, To, Request, Lang), - case acl:match_rule(LServerOrGlobal, configure, From) of + case acl_match_rule(LServerOrGlobal, From) of deny -> {error, xmpp:err_forbidden(?T("Access denied by service policy"), Lang)}; allow -> adhoc_local_commands(From, To, Request) end). @@ -1268,7 +1274,7 @@ set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang, Server = AccountJID#jid.lserver, true = lists:member(Server, ejabberd_option:hosts()), true = Server == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, case ejabberd_auth:try_register(User, Server, Password) of ok -> {result, undefined}; {error, exists} -> {error, xmpp:err_conflict()}; @@ -1284,7 +1290,7 @@ set_form(From, Host, ?NS_ADMINL(<<"delete-user">>), User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, true = ejabberd_auth:user_exists(User, Server), {User, Server} end, @@ -1298,7 +1304,7 @@ set_form(From, Host, ?NS_ADMINL(<<"end-user-session">>), JID = jid:decode(AccountString), LServer = JID#jid.lserver, true = LServer == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, case JID#jid.lresource of <<>> -> ejabberd_sm:kick_user(JID#jid.luser, JID#jid.lserver); @@ -1314,7 +1320,7 @@ set_form(From, Host, User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, true = ejabberd_auth:user_exists(User, Server), ejabberd_auth:set_password(User, Server, Password), {result, undefined}; @@ -1325,7 +1331,7 @@ set_form(From, Host, User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, FLast = case ejabberd_sm:get_user_resources(User, Server) of @@ -1357,7 +1363,7 @@ set_form(From, Host, ?NS_ADMINL(<<"user-stats">>), Lang, User = JID#jid.luser, Server = JID#jid.lserver, true = Server == Host orelse - get_permission_level(From) == global, + get_permission_level(From, Host) == global, Resources = ejabberd_sm:get_user_resources(User, Server), IPs1 = [ejabberd_sm:get_user_ip(User, Server, Resource) @@ -1448,7 +1454,7 @@ adhoc_sm_commands(_Acc, From, #jid{user = User, server = Server, lserver = LServer}, #adhoc_command{lang = Lang, node = <<"config">>, action = Action, xdata = XData} = Request) -> - case acl:match_rule(LServer, configure, From) of + case acl_match_rule(LServer, From) of deny -> {error, xmpp:err_forbidden(?T("Access denied by service policy"), Lang)}; allow -> @@ -1530,12 +1536,41 @@ set_sm_form(_User, _Server, _Node, _Request) -> tr(Lang, Text) -> translate:translate(Lang, Text). -mod_options(_) -> []. +-spec mod_opt_type(atom()) -> econf:validator(). +mod_opt_type(access) -> + econf:acl(). + +-spec mod_options(binary()) -> [{services, [tuple()]} | {atom(), any()}]. +mod_options(_Host) -> + [{access, configure}]. + +%% @format-begin mod_doc() -> #{desc => ?T("The module provides server configuration functionality via " "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " - "Implements many commands as defined in " + "It also implements many commands as defined in " "https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]. " - "This module requires _`mod_adhoc`_ to be loaded.")}. + "This module requires _`mod_adhoc`_ (to execute the commands), " + "and recommends _`mod_disco`_ (to discover the commands). "), + opts => + [{access, + #{value => ?T("AccessName"), + note => "added in 25.xx", + desc => + ?T("This option defines which access rule will be used to " + "control who is allowed to access the features provided by this module. " + "The default value is 'configure'.")}}], + example => + ["acl:", + " admin:", + " user: sun@localhost", + "", + "access_rules:", + " configure:", + " allow: admin", + "", + "modules:", + " mod_configure:", + " access: configure"]}. diff --git a/src/mod_configure_opt.erl b/src/mod_configure_opt.erl new file mode 100644 index 000000000..0a8c190fd --- /dev/null +++ b/src/mod_configure_opt.erl @@ -0,0 +1,13 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_configure_opt). + +-export([access/1]). + +-spec access(gen_mod:opts() | global | binary()) -> 'configure' | acl:acl(). +access(Opts) when is_map(Opts) -> + gen_mod:get_opt(access, Opts); +access(Host) -> + gen_mod:get_module_opt(Host, mod_configure, access). + From 6d77ace5c969e531f8793dbc878e98583ad777ea Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 5 Mar 2025 19:20:41 +0100 Subject: [PATCH 1030/1302] mod_http_api: Sort list elements in a command result --- src/mod_http_api.erl | 9 ++++++--- test/commands_tests.erl | 14 +++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index b2fe7b7c0..f5c9b3c2a 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -473,14 +473,17 @@ format_result(Code, {Name, restuple}) -> {[{<<"res">>, Code == true orelse Code == ok}, {<<"text">>, <<"">>}]}}; -format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) -> +format_result(Els1, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) -> + Els = lists:keysort(1, Els1), {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; -format_result(Els, {Name, {list, {_, {tuple, [{name, string}, {value, _}]}} = Fmt}}) -> +format_result(Els1, {Name, {list, {_, {tuple, [{name, string}, {value, _}]}} = Fmt}}) -> + Els = lists:keysort(1, Els1), {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; %% Covered by command_test_list and command_test_list_tuple -format_result(Els, {Name, {list, Def}}) -> +format_result(Els1, {Name, {list, Def}}) -> + Els = lists:sort(Els1), {misc:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]}; format_result(Tuple, {_Name, {tuple, [{_, atom}, ValFmt]}}) -> diff --git a/test/commands_tests.erl b/test/commands_tests.erl index 52933e409..a1c60c5e4 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -130,7 +130,7 @@ http_restuple(Config) -> http_list(Config) -> ListS = ["one", "first", "primary"], - ListB = [<<"one">>, <<"first">>, <<"primary">>], + ListB = lists:sort([<<"one">>, <<"first">>, <<"primary">>]), ?match(ListB, query(Config, "command_test_list", #{arg_list => ListS})), ?match(ListB, query(Config, "command_test_list", #{arg_list => ListB})). @@ -148,20 +148,20 @@ http_tuple(Config) -> http_list_tuple(Config) -> LTA = [#{element1 => "one", element2 => "uno"}, - #{element1 => "dos", element2 => "two"}, + #{element1 => "two", element2 => "dos"}, #{element1 => "three", element2 => "tres"}], - LTB = [#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, - #{<<"element1">> => <<"dos">>, <<"element2">> => <<"two">>}, - #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}], + LTB = lists:sort([#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, + #{<<"element1">> => <<"two">>, <<"element2">> => <<"dos">>}, + #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}]), ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTA})), ?match(LTB, query(Config, "command_test_list_tuple", #{arg_list => LTB})). http_list_tuple_map(Config) -> LTA = #{<<"one">> => <<"uno">>, - <<"dos">> => <<"two">>, + <<"two">> => <<"dos">>, <<"three">> => <<"tres">>}, LTB = lists:sort([#{<<"element1">> => <<"one">>, <<"element2">> => <<"uno">>}, - #{<<"element1">> => <<"dos">>, <<"element2">> => <<"two">>}, + #{<<"element1">> => <<"two">>, <<"element2">> => <<"dos">>}, #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}]), ?match(LTB, lists:sort(query(Config, "command_test_list_tuple", #{arg_list => LTA}))). From 573e06cc0cb2432b2eb69a446f41b37ce53d5a22 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 3 Mar 2025 13:07:46 +0100 Subject: [PATCH 1031/1302] mod_muc_admin: create_room_with_opts command recommends using ; and = separators --- src/mod_muc_admin.erl | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index c1c6ccf54..06995daed 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -189,14 +189,20 @@ get_commands_spec() -> #ejabberd_commands{name = create_room_with_opts, tags = [muc_room, muc_sub], desc = "Create a MUC room name@service in host with given options", longdesc = - "The syntax of `affiliations` is: `Type:JID,Type:JID`. " - "The syntax of `subscribers` is: `JID:Nick:Node:Node2:Node3,JID:Nick:Node`.", + "Options `affiliations` and `subscribers` are lists of tuples. " + "The tuples in the list are separated with `;` and " + "the elements in each tuple are separated with `=` " + "(until ejabberd 24.12 the separators were `,` and `:` respectively). " + "Each subscriber can have one or more nodes. " + "In summary, `affiliations` is like `Type1=JID1;Type2=JID2` " + "and `subscribers` is like `JID1=Nick1=Node1A=Node1B=Node1C;JID2=Nick2=Node2`.", + note = "modified in 25.xx", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], args_example = ["room1", "conference.example.com", "localhost", [{"members_only","true"}, - {"affiliations", "owner:bob@example.com,member:peter@example.com"}, - {"subscribers", "bob@example.com:Bob:messages:subject,anne@example.com:Anne:messages"}]], + {"affiliations", "owner=user1@localhost;member=user2@localhost"}, + {"subscribers", "user3@localhost=User3=messages=subject;user4@localhost=User4=messages"}]], args = [{room, binary}, {service, binary}, {host, binary}, {options, {list, @@ -1726,9 +1732,9 @@ format_room_option(OptionString, ValueString) -> lang -> ValueString; pubsub -> ValueString; affiliations -> - [parse_affiliation_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; + [parse_affiliation_string(Opt) || Opt <- str:tokens(ValueString, <<";,">>)]; subscribers -> - [parse_subscription_string(Opt) || Opt <- str:tokens(ValueString, <<",">>)]; + [parse_subscription_string(Opt) || Opt <- str:tokens(ValueString, <<";,">>)]; allow_private_messages_from_visitors when (ValueString == <<"anyone">>) or (ValueString == <<"moderators">>) or @@ -1760,10 +1766,16 @@ throw_error(O, V) -> parse_affiliation_string(String) -> {Type, JidS} = case String of + %% Old syntax <<"owner:", Jid/binary>> -> {owner, Jid}; <<"admin:", Jid/binary>> -> {admin, Jid}; <<"member:", Jid/binary>> -> {member, Jid}; <<"outcast:", Jid/binary>> -> {outcast, Jid}; + %% New syntax + <<"owner=", Jid/binary>> -> {owner, Jid}; + <<"admin=", Jid/binary>> -> {admin, Jid}; + <<"member=", Jid/binary>> -> {member, Jid}; + <<"outcast=", Jid/binary>> -> {outcast, Jid}; _ -> throw({error, "Invalid 'affiliation'"}) end, try jid:decode(JidS) of @@ -1774,7 +1786,7 @@ parse_affiliation_string(String) -> end. parse_subscription_string(String) -> - case str:tokens(String, <<":">>) of + case str:tokens(String, <<"=:">>) of [_] -> throw({error, "Invalid 'subscribers' - missing nick"}); [_, _] -> From 496daf9220e0916d77f1d607de7b208d5a0884bd Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Mar 2025 10:39:53 +0100 Subject: [PATCH 1032/1302] mod_adhoc_api: New module to execute API Commands using Ad-Hoc Commands --- ejabberd.yml.example | 8 +- src/ejabberd_access_permissions.erl | 14 +- src/ejabberd_commands.erl | 11 + src/mod_adhoc_api.erl | 729 ++++++++++++++++++++++++++ src/mod_adhoc_api_opt.erl | 13 + src/mod_http_api.erl | 9 +- test/commands_tests.erl | 280 +++++++++- test/ejabberd_SUITE_data/ejabberd.yml | 1 + 8 files changed, 1046 insertions(+), 19 deletions(-) create mode 100644 src/mod_adhoc_api.erl create mode 100644 src/mod_adhoc_api_opt.erl diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 0964afa06..070194412 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -118,7 +118,12 @@ api_permissions: from: ejabberd_web_admin who: admin what: "*" - "admin access": + "adhoc commands": + from: mod_adhoc_api + who: admin + what: "*" + "http access": + from: mod_http_api who: access: allow: @@ -159,6 +164,7 @@ shaper_rules: modules: mod_adhoc: {} + mod_adhoc_api: {} mod_admin_extra: {} mod_announce: access: announce diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl index 1889ca8df..57b3637e3 100644 --- a/src/ejabberd_access_permissions.erl +++ b/src/ejabberd_access_permissions.erl @@ -344,10 +344,20 @@ validator(from) -> fun(L) when is_list(L) -> lists:map( fun({K, V}) -> {(econf:enum([tag]))(K), (econf:binary())(V)}; - (A) -> (econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl, ejabberd_web_admin]))(A) + (A) -> (econf:enum([ejabberd_ctl, + ejabberd_web_admin, + ejabberd_xmlrpc, + mod_adhoc_api, + mod_cron, + mod_http_api]))(A) end, lists:flatten(L)); (A) -> - [(econf:enum([ejabberd_xmlrpc, mod_cron, mod_http_api, ejabberd_ctl, ejabberd_web_admin]))(A)] + [(econf:enum([ejabberd_ctl, + ejabberd_web_admin, + ejabberd_xmlrpc, + mod_adhoc_api, + mod_cron, + mod_http_api]))(A)] end; validator(what) -> econf:and_then( diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 2318564ba..243a594ed 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -33,6 +33,7 @@ -export([start_link/0, list_commands/0, list_commands/1, + list_commands/2, get_command_format/1, get_command_format/2, get_command_format/3, @@ -217,6 +218,16 @@ list_commands(Version) -> desc = Desc} <- Commands, not lists:member(internal, Tags)]. +-spec list_commands(integer(), map()) -> [{atom(), [aterm()], string()}]. + +list_commands(Version, CallerInfo) -> + lists:filter( + fun({Name, _Args, _Desc}) -> + allow == ejabberd_access_permissions:can_access(Name, CallerInfo) + end, + list_commands(Version) + ). + -spec get_command_format(atom()) -> {[aterm()], [{atom(),atom()}], rterm()}. get_command_format(Name) -> diff --git a/src/mod_adhoc_api.erl b/src/mod_adhoc_api.erl new file mode 100644 index 000000000..397f9be6c --- /dev/null +++ b/src/mod_adhoc_api.erl @@ -0,0 +1,729 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_adhoc_api.erl +%%% Author : Badlop +%%% Purpose : Frontend for ejabberd API Commands via XEP-0050 Ad-Hoc Commands +%%% Created : 21 Feb 2025 by Badlop +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +%%%% definitions +%% @format-begin + +-module(mod_adhoc_api). + +-behaviour(gen_mod). + +-author('badlop@process-one.net'). + +%% gen_mod callbacks +-export([start/2, stop/1, reload/3, mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]). +%% hooks +-export([adhoc_local_commands/4, adhoc_local_items/4, disco_local_features/5, + disco_local_identity/5, disco_local_items/5]). + +-include("ejabberd_commands.hrl"). +-include("ejabberd_sm.hrl"). +-include("logger.hrl"). +-include("translate.hrl"). + +-include_lib("stdlib/include/ms_transform.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). + +-define(DEFAULT_API_VERSION, 1000000). + +%%%================================== +%%%% gen_mod + +start(_Host, _Opts) -> + {ok, + [{hook, adhoc_local_commands, adhoc_local_commands, 40}, + {hook, adhoc_local_items, adhoc_local_items, 40}, + {hook, disco_local_features, disco_local_features, 40}, + {hook, disco_local_identity, disco_local_identity, 40}, + {hook, disco_local_items, disco_local_items, 40}]}. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +mod_opt_type(default_version) -> + econf:either( + econf:int(0, 3), + econf:and_then( + econf:binary(), + fun(Binary) -> + case binary_to_list(Binary) of + F when F >= "24.06" -> + 2; + F when (F > "23.10") and (F < "24.06") -> + 1; + F when F =< "23.10" -> + 0 + end + end)). + +-spec mod_options(binary()) -> [{default_version, integer()}]. +mod_options(_) -> + [{default_version, ?DEFAULT_API_VERSION}]. + +depends(_Host, _Opts) -> + [{mod_adhoc, hard}, {mod_last, soft}]. + +mod_doc() -> + #{desc => + ?T("Execute https://docs.ejabberd.im/developer/ejabberd-api/[API Commands] " + "in a XMPP client using " + "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " + "This module requires _`mod_adhoc`_ (to execute the commands), " + "and recommends _`mod_disco`_ (to discover the commands)."), + note => "added in 25.xx", + opts => + [{default_version, + #{value => "integer() | string()", + desc => + ?T("What API version to use. " + "If setting an ejabberd version, it will use the latest API " + "version that was available in that ejabberd version. " + "For example, setting '\"24.06\"' in this option implies '2'. " + "The default value is the latest version.")}}], + example => + ["acl:", + " admin:", + " user: jan@localhost", + "", + "api_permissions:", + " \"adhoc commands\":", + " from: mod_adhoc_api", + " who: admin", + " what:", + " - \"[tag:roster]\"", + " - \"[tag:session]\"", + " - stats", + " - status", + "", + "modules:", + " mod_adhoc_api:", + " default_version: 2"]}. + +%%%================================== +%%%% Ad-Hoc Commands (copied from mod_configure) + +-define(INFO_IDENTITY(Category, Type, Name, Lang), + [#identity{category = Category, + type = Type, + name = tr(Lang, Name)}]). +-define(INFO_COMMAND(Name, Lang), + ?INFO_IDENTITY(<<"automation">>, <<"command-node">>, Name, Lang)). +-define(NODE(Name, Node), + #disco_item{jid = jid:make(Server), + node = Node, + name = tr(Lang, Name)}). + +-spec tokenize(binary()) -> [binary()]. +tokenize(Node) -> + str:tokens(Node, <<"/#">>). + +-spec tr(binary(), binary()) -> binary(). +tr(Lang, Text) -> + translate:translate(Lang, Text). + +%%%================================== +%%%% - disco identity + +-spec disco_local_identity([identity()], jid(), jid(), binary(), binary()) -> + [identity()]. +disco_local_identity(Acc, _From, #jid{lserver = LServer} = _To, Node, Lang) -> + case tokenize(Node) of + [<<"api-commands">>] -> + ?INFO_COMMAND(?T("API Commands"), Lang); + [<<"api-commands">>, CommandName] -> + ?INFO_COMMAND(get_api_command_desc(CommandName, LServer), Lang); + _ -> + Acc + end. + +get_api_command_desc(NameAtom, Host) -> + iolist_to_binary((get_api_command(NameAtom, Host))#ejabberd_commands.desc). + +%%%================================== +%%%% - disco features + +-spec disco_local_features(mod_disco:features_acc(), jid(), jid(), binary(), binary()) -> + mod_disco:features_acc(). +disco_local_features(Acc, _From, #jid{lserver = LServer} = _To, Node, _Lang) -> + case gen_mod:is_loaded(LServer, mod_adhoc) of + false -> + Acc; + _ -> + case tokenize(Node) of + [<<"api-commands">>] -> + {result, []}; + [<<"api-commands">>, _] -> + {result, [?NS_COMMANDS]}; + _ -> + Acc + end + end. + +%%%================================== +%%%% - adhoc items + +-spec adhoc_local_items(mod_disco:items_acc(), jid(), jid(), binary()) -> + mod_disco:items_acc(). +adhoc_local_items(Acc, From, #jid{lserver = LServer, server = Server} = To, Lang) -> + Items = + case Acc of + {result, Its} -> + Its; + empty -> + [] + end, + Nodes = recursively_get_local_items(From, global, LServer, <<"">>, Server, Lang), + Nodes1 = + lists:filter(fun(#disco_item{node = Nd}) -> + F = disco_local_features(empty, From, To, Nd, Lang), + case F of + {result, [?NS_COMMANDS]} -> + true; + _ -> + false + end + end, + Nodes), + {result, Items ++ Nodes1}. + +-spec recursively_get_local_items(jid(), + global | vhost, + binary(), + binary(), + binary(), + binary()) -> + [disco_item()]. +recursively_get_local_items(From, PermLev, LServer, Node, Server, Lang) -> + Items = + case get_local_items2(From, {PermLev, LServer}, tokenize(Node), Server, Lang) of + {result, Res} -> + Res; + {error, _Error} -> + [] + end, + lists:flatten( + lists:map(fun(#disco_item{jid = #jid{server = S}, node = Nd} = Item) -> + if (S /= Server) or (Nd == <<"">>) -> + []; + true -> + [Item, + recursively_get_local_items(From, PermLev, LServer, Nd, Server, Lang)] + end + end, + Items)). + +%%%================================== +%%%% - disco items + +-spec disco_local_items(mod_disco:items_acc(), jid(), jid(), binary(), binary()) -> + mod_disco:items_acc(). +disco_local_items(Acc, From, #jid{lserver = LServer} = To, Node, Lang) -> + case gen_mod:is_loaded(LServer, mod_adhoc) of + false -> + Acc; + _ -> + Items = + case Acc of + {result, Its} -> + Its; + empty -> + []; + Other -> + Other + end, + case tokenize(Node) of + LNode when (LNode == [<<"api-commands">>]) or (LNode == []) -> + case get_local_items2(From, {global, LServer}, LNode, jid:encode(To), Lang) of + {result, Res} -> + {result, Res}; + {error, Error} -> + {error, Error} + end; + _ -> + {result, Items} + end + end. + +%%%================================== +%%%% - get_local_items2 + +-spec get_local_items2(jid(), + {global | vhost, binary()}, + [binary()], + binary(), + binary()) -> + {result, [disco_item()]} | {error, stanza_error()}. +get_local_items2(_From, _Host, [], Server, Lang) -> + {result, [?NODE(?T("API Commands"), <<"api-commands">>)]}; +get_local_items2(From, {_, Host}, [<<"api-commands">>], _Server, Lang) -> + {result, get_api_commands(From, Host, Lang)}; +get_local_items2(_From, {_, _Host}, [<<"api-commands">>, _], _Server, _Lang) -> + {result, []}; +get_local_items2(_From, _Host, _, _Server, _Lang) -> + {error, xmpp:err_item_not_found()}. + +-spec get_api_commands(jid(), binary(), binary()) -> [disco_item()]. +get_api_commands(From, Server, Lang) -> + ApiVersion = mod_adhoc_api_opt:default_version(Server), + lists:map(fun({Name, _Args, _Desc}) -> + NameBin = list_to_binary(atom_to_list(Name)), + ?NODE(NameBin, <<"api-commands/", NameBin/binary>>) + end, + ejabberd_commands:list_commands(ApiVersion, get_caller_info(From))). + +%%%================================== +%%%% - adhoc commands + +-define(COMMANDS_RESULT(LServerOrGlobal, From, To, Request, Lang), + adhoc_local_commands(From, To, Request)). + +-spec adhoc_local_commands(adhoc_command(), jid(), jid(), adhoc_command()) -> + adhoc_command() | {error, stanza_error()}. +adhoc_local_commands(Acc, From, To, #adhoc_command{node = Node} = Request) -> + case tokenize(Node) of + [<<"api-commands">>, _CommandName] -> + ?COMMANDS_RESULT(LServer, From, To, Request, Lang); + _ -> + Acc + end. + +-spec adhoc_local_commands(jid(), jid(), adhoc_command()) -> + adhoc_command() | {error, stanza_error()}. +adhoc_local_commands(From, + #jid{lserver = LServer} = _To, + #adhoc_command{lang = Lang, + node = Node, + sid = SessionID, + action = Action, + xdata = XData} = + Request) -> + LNode = tokenize(Node), + ActionIsExecute = Action == execute orelse Action == complete, + if Action == cancel -> + #adhoc_command{status = canceled, + lang = Lang, + node = Node, + sid = SessionID}; + XData == undefined, ActionIsExecute -> + case get_form(LServer, LNode, Lang) of + {result, Form} -> + xmpp_util:make_adhoc_response(Request, + #adhoc_command{status = executing, xdata = Form}); + {error, Error} -> + {error, Error} + end; + XData /= undefined, ActionIsExecute -> + case set_form(From, LServer, LNode, Lang, XData) of + {result, Res} -> + xmpp_util:make_adhoc_response(Request, + #adhoc_command{xdata = Res, status = completed}); + %%{'EXIT', _} -> {error, xmpp:err_bad_request()}; + {error, Error} -> + {error, Error} + end; + true -> + {error, xmpp:err_bad_request(?T("Unexpected action"), Lang)} + end. + +-spec get_form(binary(), [binary()], binary()) -> + {result, xdata()} | {error, stanza_error()}. +get_form(Host, [<<"api-commands">>, CommandName], Lang) -> + get_form_api_command(CommandName, Host, Lang); +get_form(_Host, _, _Lang) -> + {error, xmpp:err_service_unavailable()}. + +-spec set_form(jid(), binary(), [binary()], binary(), xdata()) -> + {result, xdata() | undefined} | {error, stanza_error()}. +set_form(From, Host, [<<"api-commands">>, Command], Lang, XData) -> + set_form_api_command(From, Host, Command, XData, Lang); +set_form(_From, _Host, _, _Lang, _XData) -> + {error, xmpp:err_service_unavailable()}. + +%%%================================== +%%%% API Commands + +get_api_command(Name, Host) when is_binary(Name) -> + get_api_command(binary_to_existing_atom(Name, latin1), Host); +get_api_command(Name, Host) when is_atom(Name) -> + ApiVersion = mod_adhoc_api_opt:default_version(Host), + ejabberd_commands:get_command_definition(Name, ApiVersion). + +get_caller_info(#jid{user = User, server = Server} = From) -> + #{tag => <<>>, + usr => {User, Server, <<"">>}, + caller_server => Server, + ip => get_ip_address(From), + caller_module => ?MODULE}. + +get_ip_address(#jid{user = User, + server = Server, + resource = Resource}) -> + case ejabberd_sm:get_user_ip(User, Server, Resource) of + {IP, _Port} when is_tuple(IP) -> + IP; + _ -> + error_ip_address + end. + +%%%================================== +%%%% - get form + +get_form_api_command(NameBin, Host, _Lang) -> + Def = get_api_command(NameBin, Host), + Title = list_to_binary(atom_to_list(Def#ejabberd_commands.name)), + Instructions = get_instructions(Def), + FieldsArgs = + build_fields(Def#ejabberd_commands.args, + Def#ejabberd_commands.args_desc, + Def#ejabberd_commands.args_example, + Def#ejabberd_commands.policy, + get_replacements(Host), + true), + FieldsArgsWithHeads = + case FieldsArgs of + [] -> + []; + _ -> + [#xdata_field{type = fixed, label = ?T("Arguments")} | FieldsArgs] + end, + NodeFields = build_node_fields(), + {result, + #xdata{title = Title, + type = form, + instructions = Instructions, + fields = FieldsArgsWithHeads ++ NodeFields}}. + +get_replacements(Host) -> + [{user, <<"">>}, + {localuser, <<"">>}, + {host, Host}, + {localhost, Host}, + {password, <<"">>}, + {newpass, <<"">>}, + {service, mod_muc_admin:find_hosts(Host)}]. + +build_node_fields() -> + build_node_fields([node() | nodes()]). + +build_node_fields([_ThisNode]) -> + []; +build_node_fields(AtomNodes) -> + [ThisNode | _] = Nodes = [atom_to_binary(Atom, latin1) || Atom <- AtomNodes], + Options = [#xdata_option{label = N, value = N} || N <- Nodes], + [#xdata_field{type = fixed, label = ?T("Clustering")}, + #xdata_field{type = 'list-single', + label = <<"ejabberd node">>, + var = <<"mod_adhoc_api_target_node">>, + values = [ThisNode], + options = Options}]. + +%%%================================== +%%%% - set form + +set_form_api_command(From, Host, CommandNameBin, XData, _Lang) -> + %% Description + Def = get_api_command(CommandNameBin, Host), + Title = list_to_binary(atom_to_list(Def#ejabberd_commands.name)), + Instructions = get_instructions(Def), + + %% Arguments + FieldsArgs1 = [Field || Field <- XData#xdata.fields, Field#xdata_field.type /= fixed], + + {Node, FieldsArgs} = + case lists:keytake(<<"mod_adhoc_api_target_node">>, #xdata_field.var, FieldsArgs1) of + {value, #xdata_field{values = [TargetNode]}, FAs} -> + {binary_to_existing_atom(TargetNode, latin1), FAs}; + false -> + {node(), FieldsArgs1} + end, + + FieldsArgsWithHeads = + case FieldsArgs of + [] -> + []; + _ -> + [#xdata_field{type = fixed, label = ?T("Arguments")} | FieldsArgs] + end, + + %% Execute + Arguments = api_extract_fields(FieldsArgs, Def#ejabberd_commands.args), + ApiVersion = mod_adhoc_api_opt:default_version(Host), + CallResult = + ejabberd_cluster:call(Node, + mod_http_api, + handle, + [binary_to_existing_atom(CommandNameBin, latin1), + get_caller_info(From), + Arguments, + ApiVersion]), + + %% Command result + FieldsResult2 = + case CallResult of + {200, RR} -> + build_fields([Def#ejabberd_commands.result], + [Def#ejabberd_commands.result_desc], + [RR], + restricted, + [{host, Host}], + false); + {Code, _ApiErrorCode, MessageBin} -> + [#xdata_field{type = 'text-single', + label = <<"Error ", (integer_to_binary(Code))/binary>>, + values = encode(MessageBin, irrelevat_type), + var = <<"error">>}]; + {Code, MessageBin} -> + [#xdata_field{type = 'text-single', + label = <<"Error ", (integer_to_binary(Code))/binary>>, + values = encode(MessageBin, irrelevat_type), + var = <<"error">>}] + end, + FieldsResultWithHeads = + [#xdata_field{type = fixed, label = ?T("")}, + #xdata_field{type = fixed, label = ?T("Result")} + | FieldsResult2], + + %% Result stanza + {result, + #xdata{title = Title, + type = result, + instructions = Instructions, + fields = FieldsArgsWithHeads ++ FieldsResultWithHeads}}. + +api_extract_fields(Fields, ArgsDef) -> + lists:map(fun(#xdata_field{values = Values, var = ANameBin}) -> + ArgDef = proplists:get_value(binary_to_existing_atom(ANameBin, latin1), ArgsDef), + V = case {Values, ArgDef} of + {Values, {list, {_ElementName, {tuple, ElementsDef}}}} -> + [parse_tuple(ElementsDef, Value) || Value <- Values]; + {[Value], {tuple, ElementsDef}} -> + parse_tuple(ElementsDef, Value); + {[Value], _} -> + Value; + _ -> + Values + end, + {ANameBin, V} + end, + Fields). + +parse_tuple(ElementsDef, Value) -> + Values = str:tokens(Value, <<":">>), + List1 = + [{atom_to_binary(Name, latin1), Val} + || {{Name, _Type}, Val} <- lists:zip(ElementsDef, Values)], + maps:from_list(List1). + +%%%================================== +%%%% - get instructions + +get_instructions(Def) -> + Note2 = + case Def#ejabberd_commands.note of + [] -> + []; + Note -> + N = iolist_to_binary(Note), + [<<"Note: ", N/binary>>] + end, + Tags2 = + case Def#ejabberd_commands.tags of + [] -> + []; + Tags -> + T = str:join([atom_to_binary(Tag, latin1) || Tag <- Tags], <<", ">>), + [<<"Tags: ", T/binary>>] + end, + Module2 = + case Def#ejabberd_commands.definer of + unknown -> + []; + DefinerAtom -> + D = atom_to_binary(DefinerAtom, latin1), + [<<"Module: ", D/binary>>] + end, + Version2 = + case Def#ejabberd_commands.version of + 0 -> + []; + Version -> + V = integer_to_binary(Version), + [<<"API version: ", V/binary>>] + end, + get_instructions2([Def#ejabberd_commands.desc, Def#ejabberd_commands.longdesc] + ++ Note2 + ++ Tags2 + ++ Module2 + ++ Version2). + +get_instructions2(ListStrings) -> + [re:replace(String, "[\t]*[ ]+", " ", [{return, binary}, global]) + || String <- ListStrings, String /= ""]. + +%%%================================== +%%%% - build fields + +build_fields(NameTypes, none, Examples, Policy, Replacements, Required) -> + build_fields(NameTypes, [], Examples, Policy, Replacements, Required); +build_fields(NameTypes, Descs, none, Policy, Replacements, Required) -> + build_fields(NameTypes, Descs, [], Policy, Replacements, Required); +build_fields(NameTypes, [none], Examples, Policy, Replacements, Required) -> + build_fields(NameTypes, [], Examples, Policy, Replacements, Required); +build_fields(NameTypes, Descs, [none], Policy, Replacements, Required) -> + build_fields(NameTypes, Descs, [], Policy, Replacements, Required); +build_fields(NameTypes, Descs, Examples, Policy, Replacements, Required) -> + {NameTypes2, Descs2, Examples2} = + case Policy of + user -> + {[{user, binary}, {host, binary} | NameTypes], + ["Username", "Server host" | Descs], + ["tom", "example.com" | Examples]}; + _ -> + {NameTypes, Descs, Examples} + end, + build_fields2(NameTypes2, Descs2, Examples2, Replacements, Required). + +build_fields2([{_ArgName, {list, _ArgNameType}}] = NameTypes, + Descs, + Examples, + _Replacements, + Required) -> + Args = lists_zip3_pad(NameTypes, Descs, Examples), + lists:map(fun({{AName, AType}, ADesc, AExample}) -> + ANameBin = list_to_binary(atom_to_list(AName)), + #xdata_field{type = 'text-multi', + label = ANameBin, + desc = list_to_binary(ADesc), + values = encode(AExample, AType), + required = Required, + var = ANameBin} + end, + Args); +build_fields2(NameTypes, Descs, Examples, Replacements, Required) -> + Args = lists_zip3_pad(NameTypes, Descs, Examples), + lists:map(fun({{AName, AType}, ADesc, AExample}) -> + ANameBin = list_to_binary(atom_to_list(AName)), + AValue = proplists:get_value(AName, Replacements, AExample), + Values = encode(AValue, AType), + Type = + case {AType, Values} of + {{list, _}, _} -> + 'text-multi'; + {string, [_, _ | _]} -> + 'text-multi'; + _ -> + 'text-single' + end, + #xdata_field{type = Type, + label = ANameBin, + desc = make_desc(ADesc, AValue), + values = Values, + required = Required, + var = ANameBin} + end, + Args). + +-ifdef(OTP_BELOW_26). + +lists_zip3_pad(As, Bs, Cs) -> + lists_zip3_pad(As, Bs, Cs, []). + +lists_zip3_pad([A | As], [B | Bs], [C | Cs], Xs) -> + lists_zip3_pad(As, Bs, Cs, [{A, B, C} | Xs]); +lists_zip3_pad([A | As], [B | Bs], Nil, Xs) when (Nil == none) or (Nil == []) -> + lists_zip3_pad(As, Bs, [], [{A, B, ""} | Xs]); +lists_zip3_pad([A | As], Nil, [C | Cs], Xs) when (Nil == none) or (Nil == []) -> + lists_zip3_pad(As, [], Cs, [{A, "", C} | Xs]); +lists_zip3_pad([A | As], Nil, Nil, Xs) when (Nil == none) or (Nil == []) -> + lists_zip3_pad(As, [], [], [{A, "", ""} | Xs]); +lists_zip3_pad([], Nil, Nil, Xs) when (Nil == none) or (Nil == []) -> + lists:reverse(Xs). + +-else. + +lists_zip3_pad(As, Bs, Cs) -> + lists:zip3(As, Bs, Cs, {pad, {error_missing_args_def, "", ""}}). + +-endif. + +make_desc(ADesc, T) when is_tuple(T) -> + T3 = string:join(tuple_to_list(T), " : "), + iolist_to_binary([ADesc, " {", T3, "}"]); +make_desc(ADesc, M) when is_map(M) -> + M2 = [binary_to_list(V) || V <- maps:keys(M)], + M3 = string:join(M2, " : "), + iolist_to_binary([ADesc, " {", M3, "}"]); +make_desc(ADesc, _M) -> + iolist_to_binary(ADesc). + +%%%================================== +%%%% - encode + +encode({[T | _] = List}, Type) when is_tuple(T) -> + encode(List, Type); +encode([T | _] = List, Type) when is_tuple(T) -> + [encode(Element, Type) || Element <- List]; +encode(T, _Type) when is_tuple(T) -> + T2 = [x_to_binary(E) || E <- tuple_to_list(T)], + T3 = str:join(T2, <<":">>), + [T3]; +encode(M, {tuple, Types}) when is_map(M) -> + M2 = [x_to_list(maps:get(atom_to_binary(Key, latin1), M)) + || {Key, _ElementType} <- Types], + M3 = string:join(M2, " : "), + [iolist_to_binary(M3)]; +encode([S | _] = SList, _Type) when is_list(S) -> + [iolist_to_binary(A) || A <- SList]; +encode([B | _] = BList, _Type) when is_binary(B) -> + BList; +encode(I, _Type) when is_integer(I) -> + [integer_to_binary(I)]; +encode([M | _] = List, {list, {_Name, TupleType}}) when is_map(M) -> + [encode(M1, TupleType) || M1 <- List]; +encode(S, _Type) when is_list(S) -> + [iolist_to_binary(S)]; +encode(B, _Type) when is_binary(B) -> + str:tokens(B, <<"\n">>). + +x_to_list(B) when is_binary(B) -> + binary_to_list(B); +x_to_list(I) when is_integer(I) -> + integer_to_list(I); +x_to_list(L) when is_list(L) -> + L. + +x_to_binary(B) when is_binary(B) -> + B; +x_to_binary(I) when is_integer(I) -> + integer_to_binary(I); +x_to_binary(L) when is_list(L) -> + iolist_to_binary(L). + +%%%================================== + +%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: diff --git a/src/mod_adhoc_api_opt.erl b/src/mod_adhoc_api_opt.erl new file mode 100644 index 000000000..bd7cdce42 --- /dev/null +++ b/src/mod_adhoc_api_opt.erl @@ -0,0 +1,13 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_adhoc_api_opt). + +-export([default_version/1]). + +-spec default_version(gen_mod:opts() | global | binary()) -> integer(). +default_version(Opts) when is_map(Opts) -> + gen_mod:get_opt(default_version, Opts); +default_version(Host) -> + gen_mod:get_module_opt(Host, mod_adhoc_api, default_version). + diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index f5c9b3c2a..68d5c4181 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -30,7 +30,7 @@ -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process/2, depends/2, - format_arg/2, + format_arg/2, handle/4, mod_opt_type/1, mod_options/1, mod_doc/0]). -include_lib("xmpp/include/xmpp.hrl"). @@ -353,6 +353,9 @@ format_arg(Elements, || Element <- Elements]; %% Covered by command_test_list and command_test_list_tuple +format_arg(Element, {list, Def}) + when not is_list(Element) -> + format_arg([Element], {list, Def}); format_arg(Elements, {list, {_ElementDefName, ElementDefFormat}}) when is_list(Elements) -> @@ -395,6 +398,7 @@ format_arg(Elements, {list, ElementsDef}) || Element <- Elements]; format_arg(Arg, integer) when is_integer(Arg) -> Arg; +format_arg(Arg, integer) when is_binary(Arg) -> binary_to_integer(Arg); format_arg(Arg, binary) when is_list(Arg) -> process_unicode_codepoints(Arg); format_arg(Arg, binary) when is_binary(Arg) -> Arg; format_arg(Arg, string) when is_list(Arg) -> Arg; @@ -460,6 +464,9 @@ format_result([String | _] = StringList, {Name, string}) when is_list(String) -> format_result(String, {Name, string}) -> {misc:atom_to_binary(Name), iolist_to_binary(String)}; +format_result(Binary, {Name, binary}) -> + {misc:atom_to_binary(Name), Binary}; + format_result(Code, {Name, rescode}) -> {misc:atom_to_binary(Name), Code == true orelse Code == ok}; diff --git a/test/commands_tests.erl b/test/commands_tests.erl index a1c60c5e4..7b0675c3f 100644 --- a/test/commands_tests.erl +++ b/test/commands_tests.erl @@ -23,8 +23,6 @@ %%%% definitions -%% @format-begin - -module(commands_tests). -compile(export_all). @@ -56,10 +54,29 @@ single_cases() -> single_test(http_tuple), single_test(http_list_tuple), single_test(http_list_tuple_map), + single_test(adhoc_list_commands), + single_test(adhoc_apiversion), + single_test(adhoc_apizero), + single_test(adhoc_apione), + single_test(adhoc_integer), + single_test(adhoc_string), + single_test(adhoc_binary), + single_test(adhoc_tuple), + single_test(adhoc_list), + single_test(adhoc_list_tuple), + single_test(adhoc_atom), + single_test(adhoc_rescode), + single_test(adhoc_restuple), + %%single_test(adhoc_all), single_test(clean)]}. -endif. +%% @format-begin + +single_test(T) -> + list_to_atom("commands_" ++ atom_to_list(T)). + setup(_Config) -> M = <<"mod_example">>, clean(_Config), @@ -90,6 +107,9 @@ ejabberdctl(_Config) -> Installed = execute(modules_installed, []), ?match(true, lists:keymember(mod_example, 1, Installed)). +execute(Name, Args) -> + ejabberd_commands:execute_command2(Name, Args, #{caller_module => ejabberd_ctl}, 1000000). + %%%================================== %%%% mod_http_api @@ -165,19 +185,7 @@ http_list_tuple_map(Config) -> #{<<"element1">> => <<"three">>, <<"element2">> => <<"tres">>}]), ?match(LTB, lists:sort(query(Config, "command_test_list_tuple", #{arg_list => LTA}))). -%%%================================== -%%%% internal functions - -single_test(T) -> - list_to_atom("commands_" ++ atom_to_list(T)). - -execute(Name, Args) -> - ejabberd_commands:execute_command2(Name, Args, #{caller_module => ejabberd_ctl}, 1000000). - -page(Config, Tail) -> - Server = ?config(server_host, Config), - Port = ct:get_config(web_port, 5280), - "http://" ++ Server ++ ":" ++ integer_to_list(Port) ++ "/api/" ++ Tail. +%%% internal functions query(Config, Tail, Map) -> BodyQ = misc:json_encode(Map), @@ -192,6 +200,248 @@ make_query(Config, Tail, BodyQ) -> [{body_format, binary}]), Body). +page(Config, Tail) -> + Server = ?config(server_host, Config), + Port = ct:get_config(web_port, 5280), + "http://" ++ Server ++ ":" ++ integer_to_list(Port) ++ "/api/" ++ Tail. + +%%%================================== +%%%% ad-hoc + +%%% list commands + +adhoc_list_commands(Config) -> + {ok, Result} = get_items(Config, <<"api-commands">>), + {value, #disco_item{name = <<"command_test_binary">>}} = + lists:keysearch(<<"command_test_binary">>, #disco_item.name, Result), + suite:disconnect(Config). + +get_items(Config, Node) -> + case suite:send_recv(Config, + #iq{type = get, + to = server_jid(Config), + sub_els = [#disco_items{node = Node}]}) + of + #iq{type = result, sub_els = [#disco_items{node = Node, items = Items}]} -> + {ok, Items}; + #iq{type = result, sub_els = []} -> + {empty, []}; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +%%% apiversion + +adhoc_apiversion(Config) -> + Node = <<"api-commands/command_test_apiversion">>, + ArgFields = make_fields_args([]), + ResFields = make_fields_res([{<<"apiversion">>, <<"2">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% apizero + +adhoc_apizero(Config) -> + Node = <<"api-commands/command_test_apizero">>, + ArgFields = make_fields_args([]), + ResFields = make_fields_res([{<<"apiversion">>, <<"0">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% apione + +adhoc_apione(Config) -> + Node = <<"api-commands/command_test_apione">>, + ArgFields = make_fields_args([]), + ResFields = make_fields_res([{<<"apiversion">>, <<"1">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% integer + +adhoc_integer(Config) -> + Node = <<"api-commands/command_test_integer">>, + ArgFields = make_fields_args([{<<"arg_integer">>, <<"12345">>}]), + ResFields = make_fields_res([{<<"res_integer">>, <<"12345">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% string + +adhoc_string(Config) -> + Node = <<"api-commands/command_test_string">>, + ArgFields = make_fields_args([{<<"arg_string">>, <<"Some string.">>}]), + ResFields = make_fields_res([{<<"res_string">>, <<"Some string.">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% binary + +adhoc_binary(Config) -> + Node = <<"api-commands/command_test_binary">>, + ArgFields = make_fields_args([{<<"arg_binary">>, <<"Some binary.">>}]), + ResFields = make_fields_res([{<<"res_string">>, <<"Some binary.">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% tuple + +adhoc_tuple(Config) -> + Node = <<"api-commands/command_test_tuple">>, + ArgFields = make_fields_args([{<<"arg_tuple">>, <<"one:two:three">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, + [{xdata_field, + <<"res_tuple">>, + 'text-single', + <<"res_tuple">>, + false, + <<" {element1 : element2 : element3}">>, + [<<"one : two : three">>], + [], + []}]}, + set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% list + +adhoc_list(Config) -> + Node = <<"api-commands/command_test_list">>, + ArgFields = make_fields_args([{<<"arg_list">>, [<<"one">>, <<"first">>, <<"primary">>]}]), + ResFields = + make_fields_res([{<<"res_list">>, lists:sort([<<"one">>, <<"first">>, <<"primary">>])}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% list_tuple + +adhoc_list_tuple(Config) -> + Node = <<"api-commands/command_test_list_tuple">>, + ArgFields = + make_fields_args([{<<"arg_list">>, [<<"one:uno">>, <<"two:dos">>, <<"three:tres">>]}]), + ResFields = + make_fields_res([{<<"res_list">>, + lists:sort([<<"one : uno">>, <<"two : dos">>, <<"three : tres">>])}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% atom + +adhoc_atom(Config) -> + Node = <<"api-commands/command_test_atom">>, + ArgFields = make_fields_args([{<<"arg_string">>, <<"a_test_atom">>}]), + ResFields = make_fields_res([{<<"res_atom">>, <<"a_test_atom">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% rescode + +adhoc_rescode(Config) -> + Node = <<"api-commands/command_test_rescode">>, + ArgFields = make_fields_args([{<<"code">>, <<"ok">>}]), + ResFields = make_fields_res([{<<"res_atom">>, <<"0">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% restuple + +adhoc_restuple(Config) -> + Node = <<"api-commands/command_test_restuple">>, + ArgFields = + make_fields_args([{<<"code">>, <<"ok">>}, {<<"text">>, <<"Just a result text">>}]), + ResFields = make_fields_res([{<<"res_atom">>, <<"Just a result text">>}]), + {ok, Sid, _FormFields} = get_form(Config, Node), + ?match({ok, ResFields}, set_form(Config, Node, Sid, ArgFields)), + suite:disconnect(Config). + +%%% internal functions + +server_jid(Config) -> + jid:make(<<>>, ?config(server, Config), <<>>). + +make_fields_args(Fields) -> + lists:map(fun ({Var, Values}) when is_list(Values) -> + #xdata_field{label = Var, + var = Var, + required = true, + type = 'text-multi', + values = Values}; + ({Var, Value}) -> + #xdata_field{label = Var, + var = Var, + required = true, + type = 'text-single', + values = [Value]} + end, + Fields). + +make_fields_res(Fields) -> + lists:map(fun ({Var, Values}) when is_list(Values) -> + #xdata_field{label = Var, + var = Var, + type = 'text-multi', + values = Values}; + ({Var, Value}) -> + #xdata_field{label = Var, + var = Var, + type = 'text-single', + values = [Value]} + end, + Fields). + +get_form(Config, Node) -> + case suite:send_recv(Config, + #iq{type = set, + to = server_jid(Config), + sub_els = [#adhoc_command{node = Node}]}) + of + #iq{type = result, + sub_els = + [#adhoc_command{node = Node, + action = execute, + status = executing, + sid = Sid, + actions = #adhoc_actions{execute = complete, complete = true}, + xdata = #xdata{fields = Fields}}]} -> + {ok, Sid, [F || F <- Fields, F#xdata_field.type /= fixed]}; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + +set_form(Config, Node, Sid, ArgFields) -> + Xdata = #xdata{type = submit, fields = ArgFields}, + case suite:send_recv(Config, + #iq{type = set, + to = server_jid(Config), + sub_els = + [#adhoc_command{node = Node, + action = complete, + sid = Sid, + xdata = Xdata}]}) + of + #iq{type = result, + sub_els = + [#adhoc_command{node = Node, + action = execute, + status = completed, + sid = Sid, + xdata = #xdata{fields = ResFields}}]} -> + ResFields2 = [F || F <- ResFields, F#xdata_field.type /= fixed], + {ok, ResFields2 -- ArgFields}; + #iq{type = error} = Err -> + xmpp:get_error(Err) + end. + %%%================================== %%% vim: set foldmethod=marker foldmarker=%%%%,%%%=: diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index e3aa43c3d..0abc35c2a 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -111,6 +111,7 @@ max_fsm_queue: 1000 queue_type: file modules: mod_adhoc: [] + mod_adhoc_api: [] mod_admin_extra: [] mod_admin_update_sql: [] mod_announce: [] From 9bf2d6ce5bea39734599e00df308aa83ae6af3e6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 6 Mar 2025 20:11:36 +0100 Subject: [PATCH 1033/1302] mod_configure: Document available alternative API commands --- src/mod_configure.erl | 47 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 08380a16a..8e4c88405 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1546,14 +1546,49 @@ mod_options(_Host) -> %% @format-begin +%% All ad-hoc commands implemented by mod_configure are available as API Commands: +%% - add-user -> register +%% - delete-user -> unregister +%% - end-user-session -> kick_session / kick_user +%% - change-user-password -> change_password +%% - get-user-lastlogin -> get_last +%% - user-stats -> user_sessions_info +%% - get-registered-users-list -> registered_users +%% - get-registered-users-num -> stats +%% - get-online-users-list -> connected_users +%% - get-online-users-num -> stats +%% - stopped nodes -> list_cluster_detailed +%% - DB -> mnesia_list_tables and mnesia_table_change_storage +%% - restart -> stop_kindly / restart +%% - shutdown -> stop_kindly +%% - backup -> backup +%% - restore -> restore +%% - textfile -> dump +%% - import/file -> import_file +%% - import/dir -> import_dir +%% +%% An exclusive feature available only in this module is to list items and discover them: +%% - outgoing s2s +%% - online users +%% - all users + mod_doc() -> #{desc => - ?T("The module provides server configuration functionality via " - "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " - "It also implements many commands as defined in " - "https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]. " - "This module requires _`mod_adhoc`_ (to execute the commands), " - "and recommends _`mod_disco`_ (to discover the commands). "), + [?T("The module provides server configuration functionalities using " + "https://xmpp.org/extensions/xep-0030.html[XEP-0030: Service Discovery] and " + "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]:"), + "", + "- List and discover outgoing s2s, online client sessions and all registered accounts", + "- Most of the ad-hoc commands defined in https://xmpp.org/extensions/xep-0133.html[XEP-0133: Service Administration]", + "- Additional custom ad-hoc commands specific to ejabberd", + "", + ?T("This module requires _`mod_adhoc`_ (to execute the commands), " + "and recommends _`mod_disco`_ (to discover the commands). "), + "", + ?T("Please notice that all the ad-hoc commands implemented by this module " + "have an equivalent " + "https://docs.ejabberd.im/developer/ejabberd-api/[API Command] " + "that you can execute using _`mod_adhoc_api`_ or any other API frontend.")], opts => [{access, #{value => ?T("AccessName"), From 6d8e588b78054f3b3fef31e88ba810f3457077e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 19 Mar 2025 10:40:49 +0100 Subject: [PATCH 1034/1302] Allows test to be run using ct_run --- test/ejabberd_SUITE.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 0a9e73d4d..eb5f724f4 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -60,7 +60,11 @@ init_per_suite(Config) -> NewConfig. start_ejabberd(_) -> - application:set_env(ejabberd, external_beams, "../../lib/ejabberd/test/"), + TestBeams = case filelib:is_dir("../../test/") of + true -> "../../test/"; + _ -> "../../lib/ejabberd/test/" + end, + application:set_env(ejabberd, external_beams, TestBeams), {ok, _} = application:ensure_all_started(ejabberd, transient). end_per_suite(_Config) -> From 1668cde93fcf0f3a1a7360f3777ec6d15754d4e5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Mar 2025 20:00:15 +0100 Subject: [PATCH 1035/1302] Update xmpp to get support for webchat_url (#3041) --- mix.exs | 2 +- mix.lock | 6 +++--- rebar.config | 2 +- rebar.lock | 16 ++++++++++------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index 3f63e90f8..dc42f28d4 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "e295055dd1cb3dafb4b3134bc7462a775ff16b5c", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "be24923968261c2661a9e116650dce5ac95c9d23", override: true}, {:yconf, git: "https://github.com/processone/yconf", ref: "9682a6025ed543eedf34637e4cfcc66837074af6", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 2b1065bf8..e8ef48a9c 100644 --- a/mix.lock +++ b/mix.lock @@ -2,13 +2,13 @@ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, - "ex_doc": {:hex, :ex_doc, "0.37.2", "2a3aa7014094f0e4e286a82aa5194a34dd17057160988b8509b15aa6c292720c", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "4dfa56075ce4887e4e8b1dcc121cd5fcb0f02b00391fd367ff5336d98fa49049"}, + "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "e295055dd1cb3dafb4b3134bc7462a775ff16b5c", [ref: "e295055dd1cb3dafb4b3134bc7462a775ff16b5c"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "be24923968261c2661a9e116650dce5ac95c9d23", [ref: "be24923968261c2661a9e116650dce5ac95c9d23"]}, "yconf": {:git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6", [ref: "9682a6025ed543eedf34637e4cfcc66837074af6"]}, } diff --git a/rebar.config b/rebar.config index ca2658b22..81cc3ec7f 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "e295055dd1cb3dafb4b3134bc7462a775ff16b5c"}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "be24923968261c2661a9e116650dce5ac95c9d23"}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6"}} ]}. diff --git a/rebar.lock b/rebar.lock index 2e867dd9d..01d179d78 100644 --- a/rebar.lock +++ b/rebar.lock @@ -18,12 +18,16 @@ {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.25">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.31">>},0}, - {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.26">>},0}, + {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, - {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.30">>},0}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"be24923968261c2661a9e116650dce5ac95c9d23"}}, + 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", {ref,"9682a6025ed543eedf34637e4cfcc66837074af6"}}, @@ -49,10 +53,10 @@ {<<"p1_mysql">>, <<"875D4CBDC7C9990270DF3292CCE2514E4C18A9FDFD19BEF258CB4D0C45B4F243">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_pgsql">>, <<"8339BEAC1F0F4A45F476FF5306BE5135020F02979A61DF0D8CF7B1C67E85E2FD">>}, - {<<"p1_utils">>, <<"67B0C4AC9FA3BA3EF563B31AA111B0A004439A37FAC85E027F1C3617E1C7EC6C">>}, + {<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, - {<<"stringprep">>, <<"46CF0FF631B3E7328F61F20B454D59428D87738F25D709798B5DCBB9B83C23F1">>}, + {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, {pkg_hash_ext,[ @@ -75,10 +79,10 @@ {<<"p1_mysql">>, <<"E6187FFAE95B726098E88F3EE6F2344AC259CE2C26E0EE403B05FEEF341AE434">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_pgsql">>, <<"B7FC45DFB2549187347871B7FD0573638AD5EA337F6263FBA1B3840EFAB2FF49">>}, - {<<"p1_utils">>, <<"D0379E8C1156B98BD64F8129C1DE022FCCA4F2FDB7486CE73BF0ED2C3376B04C">>}, + {<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, - {<<"stringprep">>, <<"F6FC9B3384A03877830F89B2F38580CAF3F4A27448A4A333D6A8C3975C220B9A">>}, + {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} ]. From 7723951c05c49b9e677ec4a8d55acca9961e4df5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Mar 2025 17:35:58 +0100 Subject: [PATCH 1036/1302] mod_muc_room: New muc_disco_info_extras event, useful for mod_muc_webchat_url (#3041) --- src/mod_muc_room.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 973759b27..81254ec91 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4588,8 +4588,12 @@ iq_disco_info_extras(Lang, StateData, Static) -> _ -> Fs4 end, + Fs6 = ejabberd_hooks:run_fold(muc_disco_info_extras, + StateData#state.server_host, + Fs5, + [StateData]), #xdata{type = result, - fields = muc_roominfo:encode(Fs5, Lang)}. + fields = muc_roominfo:encode(Fs6, Lang)}. -spec process_iq_disco_items(jid(), iq(), state()) -> {error, stanza_error()} | {result, disco_items()}. From 16af90648ed4ff6f8257f7b3b673c018677fdd65 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 19 Mar 2025 12:40:38 +0100 Subject: [PATCH 1037/1302] Update moved or broken URLs in documentation --- CODE_OF_CONDUCT.md | 6 +++--- CONTAINER.md | 4 ++-- CONTRIBUTING.md | 2 +- README.md | 2 +- src/mod_admin_extra.erl | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 099c1be0b..e8855889e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://www.contributor-covenant.org/ +[version]: https://www.contributor-covenant.org/version/1/4/ diff --git a/CONTAINER.md b/CONTAINER.md index b8a483e07..ea29c2f4f 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -10,7 +10,7 @@ ejabberd Container Images robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. -[home]: https://ejabberd.im/ +[home]: https://www.ejabberd.im/ [erlang]: https://www.erlang.org/ [xmpp]: https://xmpp.org/ [mqtt]: https://mqtt.org/ @@ -255,7 +255,7 @@ podman unshare chown 9000:9000 database ``` It's possible to install additional ejabberd modules using volumes, check -[this Docs tutorial](http://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container). +[this Docs tutorial](https://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container). ### Commands on start diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5d4beda8..819921ee5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,7 +138,7 @@ gives us the option to relicense the code with a more permissive license in the [coc]: https://github.com/processone/ejabberd/blob/master/CODE_OF_CONDUCT.md [stackoverflow]: https://stackoverflow.com/questions/tagged/ejabberd?sort=newest -[list]: https://lists.jabber.ru/mailman/listinfo/ejabberd +[list]: https://web.archive.org/web/20230319174915/http://lists.jabber.ru/mailman/listinfo/ejabberd [muc]: xmpp:ejabberd@conference.process-one.net [github]: https://github.com/processone/ejabberd [github-issues]: https://github.com/processone/ejabberd/issues diff --git a/README.md b/README.md index 76eae7b55..646cc4a17 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ License [fluux]: https://fluux.io/ [homebrew]: https://docs.ejabberd.im/admin/install/homebrew/ [hubecs]: https://hub.docker.com/r/ejabberd/ecs/ -[im]: https://ejabberd.im/ +[im]: https://www.ejabberd.im/ [issues]: https://github.com/processone/ejabberd/issues [localization]: https://docs.ejabberd.im/developer/extending-ejabberd/localization/ [mqtt]: https://mqtt.org/ diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index d461edc89..38e29ec47 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -235,7 +235,7 @@ get_commands_spec() -> #ejabberd_commands{name = check_password_hash, tags = [accounts], desc = "Check if the password hash is correct", longdesc = "Allows hash methods from the Erlang/OTP " - "[crypto](https://www.erlang.org/doc/man/crypto) application.", + "[crypto](https://www.erlang.org/doc/apps/crypto/crypto.html) application.", module = ?MODULE, function = check_password_hash, args = [{user, binary}, {host, binary}, {passwordhash, binary}, {hashmethod, binary}], From 30a7b0ef3ba3c31975cbc492f06e715be6d281c8 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Thu, 20 Mar 2025 17:33:53 +0300 Subject: [PATCH 1038/1302] Better Matrix room topic and room roles to MUC conversion, support room aliases in invites --- src/mod_matrix_gw.erl | 8 +++- src/mod_matrix_gw_room.erl | 95 ++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 173e82356..f0767ebe2 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -148,7 +148,7 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], <<"room_id">> := RoomID, <<"sender">> := Sender, <<"state_key">> := UserID} = Event, - <<"room_version">> := RoomVer}, + <<"room_version">> := RoomVer} = JSON, Origin} -> case mod_matrix_gw_room:binary_to_room_version(RoomVer) of #room_version{} = RoomVersion -> @@ -167,7 +167,11 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], #{<<"is_direct">> := true} -> mod_matrix_gw_room:join_direct(Host, Origin, RoomID, Sender, UserID); _ -> - mod_matrix_gw_room:send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) + IRS = case JSON of + #{<<"invite_room_state">> := IRS1} -> IRS1; + _ -> [] + end, + mod_matrix_gw_room:send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event, IRS) end, ?DEBUG("res ~s~n", [misc:json_encode(ResJSON)]), {200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], misc:json_encode(ResJSON)}; diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 095b6fa52..380c67e06 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -36,7 +36,7 @@ create_new_room/3, room_add_event/3, binary_to_room_version/1, parse_user_id/1, - send_muc_invite/6, + send_muc_invite/7, escape/1, unescape/1, route/1]). @@ -102,6 +102,7 @@ -define(ROOM_3PI, <<"m.room.third_party_invite">>). -define(ROOM_MESSAGE, <<"m.room.message">>). -define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>). +-define(ROOM_TOPIC, <<"m.room.topic">>). -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). -define(MAX_TXN_RETRIES, 5). @@ -1206,6 +1207,16 @@ do_auth_and_store_external_events(EventList, Data) -> auth_and_store_external_events(Pid, EventList) -> gen_statem:call(Pid, {auth_and_store_external_events, EventList}). +statemap_find(Key, StateMap, Data) -> + case maps:find(Key, StateMap) of + {ok, #event{}} = Res -> + Res; + {ok, EventID} when is_binary(EventID) -> + maps:find(EventID, Data#data.events); + error -> + error + end. + check_event_auth(Event, Data) -> StateMap = maps:from_list( @@ -1572,7 +1583,7 @@ get_event_power_level(Type, PL) -> get_user_power_level(User, StateMap, Data) -> RoomVersion = Data#data.room_version, PL = - case maps:find({?ROOM_POWER_LEVELS, <<"">>}, StateMap) of + case statemap_find({?ROOM_POWER_LEVELS, <<"">>}, StateMap, Data) of {ok, #event{json = #{<<"content">> := C}}} -> C; _ -> #{} end, @@ -1580,12 +1591,12 @@ get_user_power_level(User, StateMap, Data) -> #{<<"users">> := #{User := Level}} -> get_int(Level); #{<<"users_default">> := Level} -> get_int(Level); _ -> - case {RoomVersion#room_version.implicit_room_creator, StateMap} of + case {RoomVersion#room_version.implicit_room_creator, + statemap_find({?ROOM_CREATE, <<"">>}, StateMap, Data)} of {false, - #{{?ROOM_CREATE, <<"">>} := - #event{json = #{<<"content">> := #{<<"creator">> := User}}}}} -> + {ok, #event{json = #{<<"content">> := #{<<"creator">> := User}}}}} -> 100; - {true, #{{?ROOM_CREATE, <<"">>} := #event{sender = User}}} -> + {true, {ok, #event{sender = User}}} -> 100; _ -> 0 @@ -2744,8 +2755,9 @@ notify_event_xmpp( false end, UserJID = jid:make(LUser, LServer, LResource), - Item = #muc_item{affiliation = member, - role = participant}, + Item = + get_user_muc_item( + Sender, Event#event.state_map, Data), Status = case IsSelfPresence of true -> [110]; false -> [] @@ -2760,12 +2772,24 @@ notify_event_xmpp( ejabberd_router:route(Pres), case IsSelfPresence of true -> + Topic = + case Event#event.state_map of + #{{?ROOM_TOPIC, <<"">>} := TEID} -> + case maps:find(TEID, Data#data.events) of + {ok, #event{json = #{<<"content">> := #{<<"topic">> := T}}}} when is_binary(T) -> + T; + _ -> + <<"">> + end; + _ -> + <<"">> + end, Subject = #message{ from = RoomJID, to = UserJID, type = groupchat, - subject = [#text{}] + subject = [#text{data = Topic}] }, ejabberd_router:route(Subject); false -> ok @@ -2795,7 +2819,7 @@ notify_event_xmpp( when JoinTS =< OriginTS -> From = jid:replace_resource(RoomJID, RUser), UserJID = jid:make(LUser, LServer, LResource), - Item = #muc_item{affiliation = member, + Item = #muc_item{affiliation = none, role = none}, Pres = #presence{from = From, to = UserJID, @@ -2843,11 +2867,13 @@ send_initial_presences(JID, RoomJID, Event, Data) -> fun({?ROOM_MEMBER, _}, EID, ok) -> case maps:find(EID, Data#data.events) of {ok, #event{ - sender = <<$@, SenderUser/binary>>, + sender = <<$@, SenderUser/binary>> = Sender, json = #{<<"content">> := #{<<"membership">> := <<"join">>}}}} -> From = jid:replace_resource(RoomJID, SenderUser), - Item = #muc_item{affiliation = member, role = participant}, + Item = + get_user_muc_item( + Sender, Event#event.state_map, Data), Pres = #presence{from = From, to = JID, type = available, @@ -2862,6 +2888,23 @@ send_initial_presences(JID, RoomJID, Event, Data) -> ok end, ok, Event#event.state_map). +get_user_muc_item(User, StateMap, Data) -> + SenderLevel = get_user_power_level(User, StateMap, Data), + BanLevel = + case statemap_find({?ROOM_POWER_LEVELS, <<"">>}, StateMap, Data) of + {ok, #event{json = #{<<"content">> := #{<<"ban">> := S}}}} -> + get_int(S); + _ -> 50 + end, + if + SenderLevel >= BanLevel -> + #muc_item{affiliation = admin, + role = moderator}; + true -> + #muc_item{affiliation = member, + role = participant} + end. + send_new_txn(Events, Server, Data) -> TxnID = p1_rand:get_string(), @@ -3198,12 +3241,36 @@ update_client(#data{kind = #multi{users = Users}} = Data) -> end. -send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event) -> +send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event, IRS) -> case {user_id_to_jid(Sender, Host), user_id_to_jid(UserID, Host)} of {#jid{} = SenderJID, #jid{lserver = Host} = UserJID} -> process_pdu(Host, Origin, Event), ServiceHost = mod_matrix_gw_opt:host(Host), - {ok, EscRoomID} = room_id_to_xmpp(RoomID), + Alias = + lists:foldl( + fun(#{<<"type">> := <<"m.room.canonical_alias">>, + <<"content">> := #{<<"alias">> := A}}, _) + when is_binary(A) -> A; + (_, Acc) -> Acc + end, none, IRS), + {ok, EscRoomID} = + case Alias of + <<$#, Parts/binary>> -> + case binary:split(Parts, <<":">>) of + [R, S] -> + User = <<$#, R/binary, $%, S/binary>>, + case jid:nodeprep(User) of + error -> + room_id_to_xmpp(RoomID); + _ -> + {ok, User} + end; + _ -> + room_id_to_xmpp(RoomID) + end; + _ -> + room_id_to_xmpp(RoomID) + end, RoomJID = jid:make(EscRoomID, ServiceHost), Invite = #muc_invite{to = undefined, from = SenderJID}, XUser = #muc_user{invites = [Invite]}, From 999ede59ce6164af9dbcadba386dbfbaf0ac3967 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 20 Mar 2025 19:59:22 +0100 Subject: [PATCH 1039/1302] Update a pair more URLs in documentation --- src/ejabberd_options_doc.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 84c2ae992..312e00f0e 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -538,7 +538,7 @@ doc() -> #{value => "{NAME: Value}", desc => ?T("Allows to define configuration " - "_`../configuration/file-format.md#keywords|keywords`_. "), + "_`../configuration/file-format.md#macros-and-keywords|keywords`_. "), example => ["define_keyword:", " SQL_USERNAME: \"eja.global\"", @@ -553,7 +553,7 @@ doc() -> #{value => "{NAME: Value}", desc => ?T("Allows to define configuration " - "_`../configuration/file-format.md#macros|macros`_. "), + "_`../configuration/file-format.md#macros-and-keywords|macros`_. "), example => ["define_macro:", " DEBUG: debug", From a6fec278c399814998d1eaac440ea92afe59d5e2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Mar 2025 12:02:46 +0100 Subject: [PATCH 1040/1302] ejabberd_commands: Fix command unregistration --- src/ejabberd_commands.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 243a594ed..f1e724da3 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -188,7 +188,7 @@ register_command_prepare(Command, Definer) -> unregister_commands(Commands) -> lists:foreach( fun(Command) -> - mnesia:dirty_delete_object(Command) + mnesia:dirty_delete(ejabberd_commands, Command#ejabberd_commands.name) end, Commands), ejabberd_access_permissions:invalidate(). From c180349fc65a02fad8bf97002400f5a8b56aa413 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Mar 2025 10:50:10 +0100 Subject: [PATCH 1041/1302] gen_mod: Support registering commands and hook_subscribe in start/2 result --- src/gen_mod.erl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index be815606d..c07e1d691 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -45,6 +45,7 @@ -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include("ejabberd_stacktrace.hrl"). +-include("ejabberd_commands.hrl"). -record(ejabberd_module, {module_host = {undefined, <<"">>} :: {atom(), binary()}, @@ -63,6 +64,11 @@ {hook, atom(), atom(), integer()} | {hook, atom(), atom(), integer(), binary() | global} | {hook, atom(), module(), atom(), integer()} | + {hook_subscribe, atom(), atom(), [any()]} | + {hook_subscribe, atom(), atom(), [any()], binary() | global} | + {hook_subscribe, atom(), module(), atom(), [any()]} | + {hook_subscribe, atom(), module(), atom(), [any()], binary() | global} | + {commands, [ejabberd_commands()]} | {iq_handler, component(), binary(), atom()} | {iq_handler, component(), binary(), module(), atom()}. -export_type([registration/0]). @@ -346,6 +352,16 @@ add_registrations(Host, Module, Registrations) -> ejabberd_hooks:add(Hook, Host1, Module, Function, Seq); ({hook, Hook, Module1, Function, Seq}) when is_integer(Seq) -> ejabberd_hooks:add(Hook, Host, Module1, Function, Seq); + ({hook_subscribe, Hook, Function, InitArg}) -> + ejabberd_hooks:subscribe(Hook, Host, Module, Function, InitArg); + ({hook_subscribe, Hook, Function, InitArg, Host1}) when is_binary(Host1) or (Host1 == global) -> + ejabberd_hooks:subscribe(Hook, Host1, Module, Function, InitArg); + ({hook_subscribe, Hook, Module1, Function, InitArg}) -> + ejabberd_hooks:subscribe(Hook, Host, Module1, Function, InitArg); + ({hook_subscribe, Hook, Module1, Function, InitArg, Host1}) -> + ejabberd_hooks:subscribe(Hook, Host1, Module1, Function, InitArg); + ({commands, Commands}) -> + ejabberd_commands:register_commands(Host, Module, Commands); ({iq_handler, Component, NS, Function}) -> gen_iq_handler:add_iq_handler( Component, Host, NS, Module, Function); @@ -363,6 +379,16 @@ del_registrations(Host, Module, Registrations) -> ejabberd_hooks:delete(Hook, Host1, Module, Function, Seq); ({hook, Hook, Module1, Function, Seq}) when is_integer(Seq) -> ejabberd_hooks:delete(Hook, Host, Module1, Function, Seq); + ({hook_subscribe, Hook, Function, InitArg}) -> + ejabberd_hooks:unsubscribe(Hook, Host, Module, Function, InitArg); + ({hook_subscribe, Hook, Function, InitArg, Host1}) when is_binary(Host1) or (Host1 == global) -> + ejabberd_hooks:unsubscribe(Hook, Host1, Module, Function, InitArg); + ({hook_subscribe, Hook, Module1, Function, InitArg}) -> + ejabberd_hooks:unsubscribe(Hook, Host, Module1, Function, InitArg); + ({hook_subscribe, Hook, Module1, Function, InitArg, Host1}) -> + ejabberd_hooks:unsubscribe(Hook, Host1, Module1, Function, InitArg); + ({commands, Commands}) -> + ejabberd_commands:unregister_commands(Host, Module, Commands); ({iq_handler, Component, NS, _Function}) -> gen_iq_handler:remove_iq_handler(Component, Host, NS); ({iq_handler, Component, NS, _Module, _Function}) -> From b8cb1bbdcfdd3b197557d57b122556a628e5a766 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Mar 2025 12:23:43 +0100 Subject: [PATCH 1042/1302] Register commands using the new gen_mod support --- src/mod_admin_extra.erl | 10 +++++----- src/mod_admin_update_sql.erl | 8 ++++---- src/mod_muc_admin.erl | 10 +++++----- src/mod_private.erl | 8 ++++---- src/mod_push.erl | 8 ++++---- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 38e29ec47..15d65bb0b 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -104,9 +104,9 @@ %%% gen_mod %%% -start(Host, _Opts) -> - ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), - {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, +start(_Host, _Opts) -> + {ok, [{commands, get_commands_spec()}, + {hook, webadmin_menu_main, web_menu_main, 50, global}, {hook, webadmin_page_main, web_page_main, 50, global}, {hook, webadmin_menu_host, web_menu_host, 50}, {hook, webadmin_page_host, web_page_host, 50}, @@ -117,8 +117,8 @@ start(Host, _Opts) -> {hook, webadmin_menu_node, web_menu_node, 50, global}, {hook, webadmin_page_node, web_page_node, 50, global}]}. -stop(Host) -> - ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index c5b4ab865..ba33f7e50 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -47,11 +47,11 @@ %%% gen_mod %%% -start(Host, _Opts) -> - ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()). +start(_Host, _Opts) -> + {ok, [{commands, get_commands_spec()}]}. -stop(Host) -> - ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 06995daed..59c5a0e93 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -68,9 +68,9 @@ %% gen_mod %%---------------------------- -start(Host, _Opts) -> - ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), - {ok, [{hook, webadmin_menu_main, web_menu_main, 50, global}, +start(_Host, _Opts) -> + {ok, [{commands, get_commands_spec()}, + {hook, webadmin_menu_main, web_menu_main, 50, global}, {hook, webadmin_page_main, web_page_main, 50, global}, {hook, webadmin_menu_host, web_menu_host, 50}, {hook, webadmin_page_host, web_page_host, 50}, @@ -78,8 +78,8 @@ start(Host, _Opts) -> {hook, webadmin_page_hostuser, web_page_hostuser, 50} ]}. -stop(Host) -> - ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). +stop(_Host) -> + ok. reload(_Host, _NewOpts, _OldOpts) -> ok. diff --git a/src/mod_private.erl b/src/mod_private.erl index 3bb93ed77..6f70e1c9a 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -71,8 +71,8 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), - {ok, [{hook, remove_user, remove_user, 50}, + {ok, [{commands, get_commands_spec()}, + {hook, remove_user, remove_user, 50}, {hook, disco_sm_features, get_sm_features, 50}, {hook, pubsub_publish_item, pubsub_publish_item, 50}, {hook, pubsub_delete_item, pubsub_delete_item, 50}, @@ -81,8 +81,8 @@ start(Host, Opts) -> {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, {iq_handler, ejabberd_sm, ?NS_PRIVATE, process_sm_iq}]}. -stop(Host) -> - ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_push.erl b/src/mod_push.erl index 7d2792eb4..fb5ba1be4 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -96,8 +96,8 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), init_cache(Mod, Host, Opts), - ejabberd_commands:register_commands(Host, ?MODULE, get_commands_spec()), - {ok, [{iq_handler, ejabberd_sm, ?NS_PUSH_0, process_iq}, + {ok, [{commands, get_commands_spec()}, + {iq_handler, ejabberd_sm, ?NS_PUSH_0, process_iq}, {hook, disco_sm_features, disco_sm_features, 50}, {hook, c2s_session_pending, c2s_session_pending, 50}, {hook, c2s_copy_session, c2s_copy_session, 50}, @@ -110,8 +110,8 @@ start(Host, Opts) -> -spec stop(binary()) -> ok. -stop(Host) -> - ejabberd_commands:unregister_commands(Host, ?MODULE, get_commands_spec()). +stop(_Host) -> + ok. -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(Host, NewOpts, OldOpts) -> From d9e86600dc0c1a043cf5a3cbf5557a7ab1493e41 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 21 Mar 2025 12:46:05 +0100 Subject: [PATCH 1043/1302] Register hooks and iq_handler using the gen_mod support for registrations --- src/mod_mix_pam.erl | 31 ++++++++----------- src/mod_push_keepalive.erl | 62 +++++++++----------------------------- 2 files changed, 26 insertions(+), 67 deletions(-) diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl index 9738205de..bae6133fb 100644 --- a/src/mod_mix_pam.erl +++ b/src/mod_mix_pam.erl @@ -64,29 +64,22 @@ start(Host, Opts) -> case Mod:init(Host, Opts) of ok -> init_cache(Mod, Host, Opts), - ejabberd_hooks:add(bounce_sm_packet, Host, ?MODULE, bounce_sm_packet, 50), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), - ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:add(roster_get, Host, ?MODULE, get_mix_roster_items, 50), - ejabberd_hooks:add(webadmin_user, Host, ?MODULE, webadmin_user, 50), - ejabberd_hooks:add(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), - ejabberd_hooks:add(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0, ?MODULE, process_iq), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2, ?MODULE, process_iq); + {ok, + [{hook, bounce_sm_packet, bounce_sm_packet, 50}, + {hook, disco_sm_features, disco_sm_features, 50}, + {hook, remove_user, remove_user, 50}, + {hook, roster_get, get_mix_roster_items, 50}, + {hook, webadmin_user, webadmin_user, 50}, + {hook, webadmin_menu_hostuser, webadmin_menu_hostuser, 50}, + {hook, webadmin_page_hostuser, webadmin_page_hostuser, 50}, + {iq_handler, ejabberd_sm, ?NS_MIX_PAM_0, process_iq}, + {iq_handler, ejabberd_sm, ?NS_MIX_PAM_2, process_iq}]}; Err -> Err end. -stop(Host) -> - ejabberd_hooks:delete(bounce_sm_packet, Host, ?MODULE, bounce_sm_packet, 50), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_sm_features, 50), - ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), - ejabberd_hooks:delete(roster_get, Host, ?MODULE, get_mix_roster_items, 50), - ejabberd_hooks:delete(webadmin_user, Host, ?MODULE, webadmin_user, 50), - ejabberd_hooks:delete(webadmin_menu_hostuser, Host, ?MODULE, webadmin_menu_hostuser, 50), - ejabberd_hooks:delete(webadmin_page_hostuser, Host, ?MODULE, webadmin_page_hostuser, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_2). +stop(_Host) -> + ok. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(NewOpts, ?MODULE), diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl index 2c43282cd..33bd2b53e 100644 --- a/src/mod_push_keepalive.erl +++ b/src/mod_push_keepalive.erl @@ -47,13 +47,22 @@ %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- --spec start(binary(), gen_mod:opts()) -> ok. -start(Host, _Opts) -> - register_hooks(Host). +-spec start(binary(), gen_mod:opts()) -> {ok, [gen_mod:registration()]}. +start(_Host, _Opts) -> + {ok, + [{hook, c2s_session_pending, c2s_session_pending, 50}, + {hook, c2s_session_resumed, c2s_session_resumed, 50}, + {hook, c2s_copy_session, c2s_copy_session, 50}, + {hook, c2s_handle_cast, c2s_handle_cast, 40}, + {hook, c2s_handle_info, c2s_handle_info, 50}, + {hook, c2s_handle_send, c2s_stanza, 50}, + %% Wait for ejabberd_pkix before running our ejabberd_started/0, so that we + %% don't initiate s2s connections before certificates are loaded: + {hook, ejabberd_started, ejabberd_started, 90, global}]}. -spec stop(binary()) -> ok. -stop(Host) -> - unregister_hooks(Host). +stop(_Host) -> + ok. -spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. reload(_Host, _NewOpts, _OldOpts) -> @@ -119,49 +128,6 @@ mod_doc() -> "out as per the 'resume_timeout' option. " "The default value is 'true'.")}}]}. -%%-------------------------------------------------------------------- -%% Register/unregister hooks. -%%-------------------------------------------------------------------- --spec register_hooks(binary()) -> ok. -register_hooks(Host) -> - ejabberd_hooks:add(c2s_session_pending, Host, ?MODULE, - c2s_session_pending, 50), - ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), - ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:add(c2s_handle_cast, Host, ?MODULE, - c2s_handle_cast, 40), - ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, - c2s_handle_info, 50), - ejabberd_hooks:add(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50), - % Wait for ejabberd_pkix before running our ejabberd_started/0, so that we - % don't initiate s2s connections before certificates are loaded: - ejabberd_hooks:add(ejabberd_started, ?MODULE, ejabberd_started, 90). - --spec unregister_hooks(binary()) -> ok. -unregister_hooks(Host) -> - ejabberd_hooks:delete(c2s_session_pending, Host, ?MODULE, - c2s_session_pending, 50), - ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, - c2s_session_resumed, 50), - ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, - c2s_copy_session, 50), - ejabberd_hooks:delete(c2s_handle_cast, Host, ?MODULE, - c2s_handle_cast, 40), - ejabberd_hooks:delete(c2s_handle_info, Host, ?MODULE, - c2s_handle_info, 50), - ejabberd_hooks:delete(c2s_handle_send, Host, ?MODULE, - c2s_stanza, 50), - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_hooks:delete( - ejabberd_started, ?MODULE, ejabberd_started, 90); - true -> - ok - end. - %%-------------------------------------------------------------------- %% Hook callbacks. %%-------------------------------------------------------------------- From 79a4dd4a26023249fbeea69ac718721fd7f44cde Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Mar 2025 12:02:11 +0100 Subject: [PATCH 1044/1302] econf: Recover support for setting hosts as atoms in config file --- src/econf.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/econf.erl b/src/econf.erl index a6abdb647..bcee46224 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -547,9 +547,10 @@ sip_uri() -> host() -> fun(Domain) -> Hosts = ejabberd_config:get_option(hosts), - case lists:member(Domain, Hosts) of + Domain3 = (domain())(Domain), + case lists:member(Domain3, Hosts) of true -> fail({route_conflict, Domain}); - false -> Domain + false -> Domain3 end end. From 12274a969a7a89f05f7279e2096d59d653fc9c7d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Mar 2025 12:05:43 +0100 Subject: [PATCH 1045/1302] ejabberd_config: Recover support for keywords in atom option that will be converted to binary --- src/ejabberd_config.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 7b08c4469..fd4fac591 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -421,14 +421,15 @@ replace_keywords(Host, Value) -> replace_keywords(Host, List, Keywords) when is_list(List) -> [replace_keywords(Host, Element, Keywords) || Element <- List]; -replace_keywords(_Host, Atom, Keywords) when is_atom(Atom) -> +replace_keywords(Host, Atom, Keywords) when is_atom(Atom) -> Str = atom_to_list(Atom), + Bin = iolist_to_binary(Str), case Str == string:uppercase(Str) of false -> - Atom; + BinaryReplaced = replace_keywords(Host, Bin, Keywords), + binary_to_atom(BinaryReplaced, utf8); true -> - MacroName = iolist_to_binary(Str), - case proplists:get_value(MacroName, Keywords) of + case proplists:get_value(Bin, Keywords) of undefined -> Atom; Replacement -> From 51e96433eadd9bbaac89459c3c383a0ad4526881 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Mar 2025 10:55:06 +0100 Subject: [PATCH 1046/1302] Add XEPs that are indirectly supported and required by XEP-0479 --- src/mod_muc.erl | 1 + src/mod_pubsub.erl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index c1ad7a8ba..42be73a93 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -25,6 +25,7 @@ -module(mod_muc). -author('alexey@process-one.net'). -protocol({xep, 45, '1.25', '0.5.0', "complete", ""}). +-protocol({xep, 249, '1.2', '0.5.0', "complete", ""}). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 059fa9518..06f0af657 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -35,8 +35,10 @@ -behaviour(gen_mod). -behaviour(gen_server). -author('christophe.romain@process-one.net'). +-protocol({xep, 48, '1.2', '0.5.0', "complete", ""}). -protocol({xep, 60, '1.14', '0.5.0', "partial", ""}). -protocol({xep, 163, '1.2', '2.0.0', "complete", ""}). +-protocol({xep, 223, '1.1.1', '2.0.0', "complete", ""}). -protocol({xep, 248, '0.2', '2.1.0', "complete", ""}). -include("logger.hrl"). From 239d4a5bfb6b6ec5ca6ced781a2ce10a1371ab6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 06:04:06 +0000 Subject: [PATCH 1047/1302] build(deps): bump golang in /.github/container Bumps golang from 1.23-alpine to 1.24-alpine. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index ae2cc7311..8aa356949 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -9,7 +9,7 @@ ARG VERSION='master' ################################################################################ #' Compile ejabberdapi -FROM docker.io/golang:1.23-alpine AS api +FROM docker.io/golang:1.24-alpine AS api RUN go install -v \ github.com/processone/ejabberd-api/cmd/ejabberd@master \ && mv bin/ejabberd bin/ejabberdapi From cf13abdab62b321a071f6ca396af3d8c0f3542e5 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 24 Mar 2025 20:00:33 +0300 Subject: [PATCH 1048/1302] Preserve XMPP message IDs in Matrix rooms --- src/mod_matrix_gw_room.erl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 380c67e06..2fd1f3be6 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -216,7 +216,8 @@ route(#presence{from = From, to = #jid{luser = <>} = To, end; route(#message{from = From, to = #jid{luser = <>} = To, type = groupchat, - body = Body}) + body = Body, + id = MsgID}) when C == $!; C == $# -> Host = ejabberd_config:get_myname(), @@ -232,7 +233,8 @@ route(#message{from = From, to = #jid{luser = <>} = To, JSON = #{<<"content">> => #{<<"body">> => Text, - <<"msgtype">> => <<"m.text">>}, + <<"msgtype">> => <<"m.text">>, + <<"net.process-one.xmpp-id">> => MsgID}, <<"sender">> => UserID, <<"type">> => ?ROOM_MESSAGE}, gen_statem:cast(Pid, {add_event, JSON}), @@ -2700,7 +2702,7 @@ notify_event_xmpp( notify_event_xmpp( #event{type = ?ROOM_MESSAGE, sender = Sender, json = #{<<"content">> := #{<<"msgtype">> := <<"m.text">>, - <<"body">> := Body}, + <<"body">> := Body} = Content, <<"origin_server_ts">> := OriginTS}}, #data{kind = #multi{users = Users}} = Data) -> case Sender of @@ -2714,7 +2716,15 @@ notify_event_xmpp( when JoinTS =< OriginTS -> From = jid:replace_resource(RoomJID, SenderUser), UserJID = jid:make(LUser, LServer, LResource), - Msg = #message{from = From, + MsgID = + case Content of + #{<<"net.process-one.xmpp-id">> := MID} -> + MID; + _ -> + <<"">> + end, + Msg = #message{id = MsgID, + from = From, to = UserJID, type = groupchat, body = [#text{data = Body}] From ced72f4a89d202a2deee1a4d64f53fcfb3a72995 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 25 Mar 2025 17:42:31 +0300 Subject: [PATCH 1049/1302] Sanitize message ID coming from Matrix --- src/mod_matrix_gw_room.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 9ac49bf8c..002704f69 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -2718,7 +2718,8 @@ notify_event_xmpp( UserJID = jid:make(LUser, LServer, LResource), MsgID = case Content of - #{<<"net.process-one.xmpp-id">> := MID} -> + #{<<"net.process-one.xmpp-id">> := MID} + when is_binary(MID) -> MID; _ -> <<"">> From 7862c6a7db0687e2af3b21bc2ff0af1db4c4b393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 26 Mar 2025 10:27:39 +0100 Subject: [PATCH 1050/1302] Add support for storing multiple passwords formats per user This adds option 'auth_stored_password_types' that can be used to setup storage of multiple passwords for each user. When this is set, on each password set, database will now store password in each format specified. --- include/ejabberd_auth.hrl | 2 +- rebar.config | 2 +- rebar.lock | 2 +- sql/lite.new.sql | 3 +- sql/lite.sql | 6 +- sql/mssql.new.sql | 4 +- sql/mssql.sql | 4 +- sql/mysql.new.sql | 3 +- sql/mysql.sql | 6 +- sql/pg.new.sql | 3 +- sql/pg.sql | 6 +- src/ejabberd_auth.erl | 314 +++++++++++++++++++++++++---------- src/ejabberd_auth_mnesia.erl | 184 ++++++++++---------- src/ejabberd_auth_sql.erl | 238 ++++++++++++++++++-------- src/ejabberd_c2s.erl | 21 ++- src/ejabberd_option.erl | 8 + src/ejabberd_options.erl | 3 + src/ejabberd_options_doc.erl | 8 + src/mod_scram_upgrade.erl | 2 +- test/webadmin_tests.erl | 4 +- 20 files changed, 553 insertions(+), 270 deletions(-) diff --git a/include/ejabberd_auth.hrl b/include/ejabberd_auth.hrl index 1f9117efb..bf7660d3f 100644 --- a/include/ejabberd_auth.hrl +++ b/include/ejabberd_auth.hrl @@ -18,5 +18,5 @@ %%% %%%---------------------------------------------------------------------- --record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', +-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | {binary(), binary(), atom()} | '$1', password = <<"">> :: binary() | scram() | '_'}). diff --git a/rebar.config b/rebar.config index 81cc3ec7f..f47955841 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, {if_var_true, stun, {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "be24923968261c2661a9e116650dce5ac95c9d23"}}, + {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "8929be60aa0c56653b13f5fcada1e460337a016b"}}, {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6"}} ]}. diff --git a/rebar.lock b/rebar.lock index 01d179d78..68e7d5ae7 100644 --- a/rebar.lock +++ b/rebar.lock @@ -26,7 +26,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"be24923968261c2661a9e116650dce5ac95c9d23"}}, + {ref,"8929be60aa0c56653b13f5fcada1e460337a016b"}}, 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", diff --git a/sql/lite.new.sql b/sql/lite.new.sql index 7248d080b..42f289fb3 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -18,13 +18,14 @@ CREATE TABLE users ( username text NOT NULL, + type smallint, server_host text NOT NULL, password text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (server_host, username) + PRIMARY KEY (server_host, username, type) ); diff --git a/sql/lite.sql b/sql/lite.sql index 2958a9628..b31e02b79 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -17,12 +17,14 @@ -- CREATE TABLE users ( - username text PRIMARY KEY, + username text, + type smallint, password text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + primary key (username, type) ); diff --git a/sql/mssql.new.sql b/sql/mssql.new.sql index 3e6817012..f67033eed 100644 --- a/sql/mssql.new.sql +++ b/sql/mssql.new.sql @@ -403,6 +403,7 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[users] ( [username] [varchar] (250) NOT NULL, [server_host] [varchar] (250) NOT NULL, + [type] [smallint] NOT NULL, [password] [text] NOT NULL, [serverkey] [text] NOT NULL DEFAULT '', [salt] [text] NOT NULL DEFAULT '', @@ -411,7 +412,8 @@ CREATE TABLE [dbo].[users] ( CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED ( [server_host] ASC, - [username] ASC + [username] ASC, + [type] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; diff --git a/sql/mssql.sql b/sql/mssql.sql index 5ee11d880..ab5596d48 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -379,6 +379,7 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW CREATE TABLE [dbo].[users] ( [username] [varchar] (250) NOT NULL, + [type] [smallint] NOT NULL, [password] [text] NOT NULL, [serverkey] [text] NOT NULL DEFAULT '', [salt] [text] NOT NULL DEFAULT '', @@ -386,7 +387,8 @@ CREATE TABLE [dbo].[users] ( [created_at] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [users_PRIMARY] PRIMARY KEY CLUSTERED ( - [username] ASC + [username] ASC, + [type] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) TEXTIMAGE_ON [PRIMARY]; diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index c5a4675b0..cf818ad3d 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -18,13 +18,14 @@ CREATE TABLE users ( username varchar(191) NOT NULL, + type smallint NOT NULL, server_host varchar(191) NOT NULL, password text NOT NULL, serverkey varchar(128) NOT NULL DEFAULT '', salt varchar(128) NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (server_host(191), username) + PRIMARY KEY (server_host(191), username, type) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Add support for SCRAM auth to a database created before ejabberd 16.03: diff --git a/sql/mysql.sql b/sql/mysql.sql index 479ccb435..210f41713 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -17,12 +17,14 @@ -- CREATE TABLE users ( - username varchar(191) PRIMARY KEY, + username varchar(191) NOT NULL, + type smallint NOT NULL,, password text NOT NULL, serverkey varchar(128) NOT NULL DEFAULT '', salt varchar(128) NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (username, type) ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- Add support for SCRAM auth to a database created before ejabberd 16.03: diff --git a/sql/pg.new.sql b/sql/pg.new.sql index a7e79fee3..5bd107d97 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -172,12 +172,13 @@ CREATE TABLE users ( username text NOT NULL, server_host text NOT NULL, + "type" smallint NOT NULL, "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT now(), - PRIMARY KEY (server_host, username) + PRIMARY KEY (server_host, username, "type") ); -- Add support for SCRAM auth to a database created before ejabberd 16.03: diff --git a/sql/pg.sql b/sql/pg.sql index 2cc8d4c95..75674e3a8 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -17,12 +17,14 @@ -- CREATE TABLE users ( - username text PRIMARY KEY, + username text NOT NULL, + "type" smallint NOT NULL "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', iterationcount integer NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT now() + created_at TIMESTAMP NOT NULL DEFAULT now(), + PRIMARY KEY (username, "type") ); -- Add support for SCRAM auth to a database created before ejabberd 16.03: diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 4ead00f48..aadbcf631 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -48,7 +48,7 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --export([auth_modules/1, convert_to_scram/1]). +-export([auth_modules/1, convert_to_scram/1, drop_password_type/2, set_password_instance/3]). -include_lib("xmpp/include/scram.hrl"). -include("logger.hrl"). @@ -76,26 +76,38 @@ -callback store_type(binary()) -> plain | external | scram. -callback set_password(binary(), binary(), password()) -> {ets_cache:tag(), {ok, password()} | {error, db_failure | not_allowed}}. +-callback set_password_multiple(binary(), binary(), [password()]) -> + {ets_cache:tag(), {ok, [password()]} | {error, db_failure | not_allowed}}. +-callback set_password_instance(binary(), binary(), password()) -> + ok | {error, db_failure | not_allowed}. -callback remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}. -callback user_exists(binary(), binary()) -> {ets_cache:tag(), boolean() | {error, db_failure}}. -callback check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean() | {stop, boolean()}}. -callback try_register(binary(), binary(), password()) -> {ets_cache:tag(), {ok, password()} | {error, exists | db_failure | not_allowed}}. +-callback try_register_multiple(binary(), binary(), [password()]) -> + {ets_cache:tag(), {ok, [password()]} | {error, exists | db_failure | not_allowed}}. -callback get_users(binary(), opts()) -> [{binary(), binary()}]. -callback count_users(binary(), opts()) -> number(). --callback get_password(binary(), binary()) -> {ets_cache:tag(), {ok, password()} | error}. +-callback get_password(binary(), binary()) -> {ets_cache:tag(), {ok, password() | [password()]} | error}. +-callback drop_password_type(binary(), atom()) -> + ok | {error, db_failure | not_allowed}. -callback use_cache(binary()) -> boolean(). -callback cache_nodes(binary()) -> boolean(). -optional_callbacks([reload/1, set_password/3, + set_password_multiple/3, + set_password_instance/3, remove_user/2, user_exists/2, check_password/4, try_register/3, + try_register_multiple/3, get_users/2, count_users/2, get_password/2, + drop_password_type/2, use_cache/1, cache_nodes/1]). @@ -265,15 +277,41 @@ check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGe false end. +convert_password_for_storage(_Server, #scram{} = Password) -> + {Password, [Password]}; +convert_password_for_storage(Server, Password) -> + P = case ejabberd_option:auth_stored_password_types(Server) of + [] -> + case ejabberd_option:auth_password_format(Server) of + plain -> + [Password]; + _ -> + [password_to_scram(Server, Password)] + end; + M -> + lists:sort(lists:map( + fun(scram_sha1) -> + password_to_scram(Server, Password, sha, ?SCRAM_DEFAULT_ITERATION_COUNT); + (scram_sha256) -> + password_to_scram(Server, Password, sha256, ?SCRAM_DEFAULT_ITERATION_COUNT); + (scram_sha512) -> + password_to_scram(Server, Password, sha512, ?SCRAM_DEFAULT_ITERATION_COUNT); + (plain) -> + Password + end, M)) + end, + {Password, P}. + -spec set_password(binary(), binary(), password()) -> ok | {error, db_failure | not_allowed | invalid_jid | invalid_password}. set_password(User, Server, Password) -> case validate_credentials(User, Server, Password) of {ok, LUser, LServer} -> + {Plain, Passwords} = convert_password_for_storage(Server, Password), lists:foldl( fun(M, {error, _}) -> - db_set_password(LUser, LServer, Password, M); + db_set_password(LUser, LServer, Plain, Passwords, M); (_, ok) -> ok end, {error, not_allowed}, auth_modules(LServer)); @@ -281,6 +319,32 @@ set_password(User, Server, Password) -> Err end. +set_password_instance(User, Server, Password) -> + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + lists:foldl( + fun(Mod, {error, _} = Acc) -> + case erlang:function_exported(Mod, set_password_instance, 3) of + true -> + R = Mod:set_password_instance(LUser, LServer, Password), + case use_cache(Mod, LServer) of + true -> + ets_cache:delete(cache_tab(Mod), {LUser, LServer}, + cache_nodes(Mod, LServer)); + _ -> + ok + end, + R; + _ -> + Acc + end; + (_, ok) -> + ok + end, {error, not_allowed}, auth_modules(LServer)); + Err -> + Err + end. + -spec try_register(binary(), binary(), password()) -> ok | {error, db_failure | not_allowed | exists | invalid_jid | invalid_password}. @@ -293,22 +357,23 @@ try_register(User, Server, Password) -> false -> case ejabberd_router:is_my_host(LServer) of true -> - case ejabberd_hooks:run_fold(check_register_user, LServer, true, [User, Server, Password]) of + case ejabberd_hooks:run_fold(check_register_user, LServer, true, + [User, Server, Password]) of true -> - case lists:foldl( - fun(_, ok) -> - ok; - (Mod, _) -> - db_try_register( - LUser, LServer, Password, Mod) - end, {error, not_allowed}, - auth_modules(LServer)) of - ok -> - ejabberd_hooks:run( - register_user, LServer, [LUser, LServer]); - {error, _} = Err -> - Err - end; + {Plain, Passwords} = convert_password_for_storage(Server, Password), + case lists:foldl( + fun(_, ok) -> + ok; + (Mod, _) -> + db_try_register( + LUser, LServer, Plain, Passwords, Mod) + end, {error, not_allowed}, auth_modules(LServer)) of + ok -> + ejabberd_hooks:run( + register_user, LServer, [LUser, LServer]); + {error, _} = Err -> + Err + end; false -> {error, not_allowed} end; @@ -356,32 +421,27 @@ count_users(Server, Opts) -> auth_modules(LServer))) end. --spec get_password(binary(), binary()) -> false | password(). +-spec get_password(binary(), binary()) -> false | [password()]. get_password(User, Server) -> - case validate_credentials(User, Server) of - {ok, LUser, LServer} -> - case lists:foldl( - fun(M, error) -> db_get_password(LUser, LServer, M); - (_M, Acc) -> Acc - end, error, auth_modules(LServer)) of - {ok, Password} -> - Password; - error -> - false - end; - _ -> - false + case get_password_with_authmodule(User, Server) of + {Passwords, _} -> Passwords end. -spec get_password_s(binary(), binary()) -> password(). get_password_s(User, Server) -> case get_password(User, Server) of false -> <<"">>; - Password -> Password + Passwords -> + {_, Pass} = lists:foldl( + fun(Plain, _) when is_binary(Plain) -> {true, Plain}; + (Pass, {false, _}) -> {true, Pass}; + (_, Acc) -> Acc + end, {false, <<"">>}, Passwords), + Pass end. -spec get_password_with_authmodule(binary(), binary()) -> - {false | {false, atom(), binary()} | password(), module()}. + {false | {false, atom(), binary()} | [password()], module()}. get_password_with_authmodule(User, Server) -> case validate_credentials(User, Server) of {ok, LUser, LServer} -> @@ -395,8 +455,10 @@ get_password_with_authmodule(User, Server) -> (_M, Acc) -> Acc end, {error, undefined}, auth_modules(LServer)) of - {{ok, Password}, Module} -> + {{ok, Password}, Module} when is_list(Password) -> {Password, Module}; + {{ok, Password}, Module} -> + {[Password], Module}; {error, Module} -> {false, Module} end @@ -455,6 +517,30 @@ user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> maybe_exists end. +drop_password_type(LServer, Type) -> + Hash = case Type of + plain -> plain; + scram_sha1 -> sha; + scram_sha256 -> sha256; + scram_sha512 -> sha512 + end, + lists:foreach( + fun(M) -> + case erlang:function_exported(M, drop_password_type, 2) of + true -> + M:drop_password_type(LServer, Hash), + case use_cache(M, LServer) of + true -> + ets_cache:clear(cache_tab(M), + cache_nodes(M, LServer)); + false -> + ok + end; + _ -> + ok + end + end, auth_modules(LServer)). + -spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}). which_users_exists(USPairs) -> ByServer = lists:foldl( @@ -592,54 +678,86 @@ get_is_banned(User, Server) -> %%%---------------------------------------------------------------------- %%% Backend calls %%%---------------------------------------------------------------------- --spec db_try_register(binary(), binary(), password(), module()) -> ok | {error, exists | db_failure | not_allowed}. -db_try_register(User, Server, Password, Mod) -> - case erlang:function_exported(Mod, try_register, 3) of +-spec db_try_register(binary(), binary(), binary(), [password()], module()) -> ok | {error, exists | db_failure | not_allowed}. +db_try_register(User, Server, PlainPassword, Passwords, Mod) -> + Ret = case erlang:function_exported(Mod, try_register_multiple, 3) of true -> - Password1 = case Mod:store_type(Server) of - scram -> password_to_scram(Server, Password); - _ -> Password - end, - Ret = case use_cache(Mod, Server) of - true -> - ets_cache:update( - cache_tab(Mod), {User, Server}, {ok, Password}, - fun() -> Mod:try_register(User, Server, Password1) end, - cache_nodes(Mod, Server)); - false -> - ets_cache:untag(Mod:try_register(User, Server, Password1)) - end, - case Ret of - {ok, _} -> ok; - {error, _} = Err -> Err + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, Passwords}, + fun() -> Mod:try_register_multiple(User, Server, Passwords) end, + cache_nodes(Mod, Server)); + false -> + ets_cache:untag(Mod:try_register_multiple(User, Server, Passwords)) end; - false -> - {error, not_allowed} + _ -> + case erlang:function_exported(Mod, try_register, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, [PlainPassword]}, + fun() -> + case Mod:try_register(User, Server, PlainPassword) of + {Tag, {ok, Pass}} -> {Tag, {ok, [Pass]}}; + Other -> Other + end + end, cache_nodes(Mod, Server)); + false -> + case Mod:try_register(User, Server, PlainPassword) of + {_, {ok, Pass}} -> {ok, [Pass]}; + V -> ets_cache:untag(V) + end + end; + false -> + {error, not_allowed} + end + end, + case Ret of + {ok, _} -> ok; + {error, _} = Err -> Err end. --spec db_set_password(binary(), binary(), password(), module()) -> ok | {error, db_failure | not_allowed}. -db_set_password(User, Server, Password, Mod) -> - case erlang:function_exported(Mod, set_password, 3) of - true -> - Password1 = case Mod:store_type(Server) of - scram -> password_to_scram(Server, Password); - _ -> Password - end, - Ret = case use_cache(Mod, Server) of +-spec db_set_password(binary(), binary(), binary(), [password()], module()) -> ok | {error, db_failure | not_allowed}. +db_set_password(User, Server, PlainPassword, Passwords, Mod) -> + Ret = case erlang:function_exported(Mod, set_password_multiple, 3) of + true -> + case use_cache(Mod, Server) of true -> ets_cache:update( - cache_tab(Mod), {User, Server}, {ok, Password}, - fun() -> Mod:set_password(User, Server, Password1) end, - cache_nodes(Mod, Server)); + cache_tab(Mod), {User, Server}, {ok, Passwords}, + fun() -> Mod:set_password_multiple(User, Server, Passwords) end, + cache_nodes(Mod, Server)); false -> - ets_cache:untag(Mod:set_password(User, Server, Password1)) - end, - case Ret of - {ok, _} -> ok; - {error, _} = Err -> Err - end; - false -> - {error, not_allowed} + ets_cache:untag(Mod:set_password_multiple(User, Server, Passwords)) + end; + _ -> + case erlang:function_exported(Mod, set_password, 3) of + true -> + case use_cache(Mod, Server) of + true -> + ets_cache:update( + cache_tab(Mod), {User, Server}, {ok, [PlainPassword]}, + fun() -> + case Mod:set_password(User, Server, PlainPassword) of + {Tag, {ok, Pass}} -> {Tag, {ok, [Pass]}}; + Other -> Other + end + end, cache_nodes(Mod, Server)); + false -> + case Mod:set_password(User, Server, PlainPassword) of + {_, {ok, Pass}} -> {ok, [Pass]}; + V -> ets_cache:untag(V) + end + end; + false -> + {error, not_allowed} + end + end, + case Ret of + {ok, _} -> ok; + {error, _} = Err -> Err end. db_get_password(User, Server, Mod) -> @@ -655,10 +773,20 @@ db_get_password(User, Server, Mod) -> error; true when UseCache -> ets_cache:lookup( - cache_tab(Mod), {User, Server}, - fun() -> Mod:get_password(User, Server) end); + cache_tab(Mod), {User, Server}, + fun() -> + case Mod:get_password(User, Server) of + {_, {ok, List}} = V when is_list(List) -> V; + {Tag, {ok, Single}} -> {Tag, {ok, [Single]}}; + Other -> Other + end + end); true -> - ets_cache:untag(Mod:get_password(User, Server)) + case Mod:get_password(User, Server) of + {_, {ok, List}} when is_list(List) -> {ok, List}; + {_, {ok, Single}} -> {ok, [Single]}; + Other -> ets_cache:untag(Other) + end end. db_user_exists(User, Server, Mod) -> @@ -703,8 +831,8 @@ db_user_exists(User, Server, Mod) -> db_check_password(User, AuthzId, Server, ProvidedPassword, Digest, DigestFun, Mod) -> case db_get_password(User, Server, Mod) of - {ok, ValidPassword} -> - match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun); + {ok, ValidPasswords} -> + match_passwords(ProvidedPassword, ValidPasswords, Digest, DigestFun); error -> case {Mod:store_type(Server), use_cache(Mod, Server)} of {external, true} -> @@ -809,7 +937,9 @@ password_to_scram(Host, Password) -> password_to_scram(_Host, #scram{} = Password, _IterationCount) -> Password; password_to_scram(Host, Password, IterationCount) -> - Hash = ejabberd_option:auth_scram_hash(Host), + password_to_scram(Host, Password, ejabberd_option:auth_scram_hash(Host), IterationCount). + +password_to_scram(_Host, Password, Hash, IterationCount) -> Salt = p1_rand:bytes(?SALT_LENGTH), SaltedPassword = scram:salted_password(Hash, Password, Salt, IterationCount), StoredKey = scram:stored_key(Hash, scram:client_key(Hash, SaltedPassword)), @@ -897,11 +1027,19 @@ auth_modules(Server) -> misc:atom_to_binary(M)]) || M <- Methods]. --spec match_passwords(password(), password(), +-spec match_passwords(password(), [password()], binary(), digest_fun() | undefined) -> boolean(). -match_passwords(Password, #scram{} = Scram, <<"">>, undefined) -> +match_passwords(Provided, Passwords, Digest, DigestFun) -> + lists:any( + fun(Pass) -> + match_password(Provided, Pass, Digest, DigestFun) + end, Passwords). + +-spec match_password(password(), password(), + binary(), digest_fun() | undefined) -> boolean(). +match_password(Password, #scram{} = Scram, <<"">>, undefined) -> is_password_scram_valid(Password, Scram); -match_passwords(Password, #scram{} = Scram, Digest, DigestFun) -> +match_password(Password, #scram{} = Scram, Digest, DigestFun) -> StoredKey = base64:decode(Scram#scram.storedkey), DigRes = if Digest /= <<"">> -> Digest == DigestFun(StoredKey); @@ -912,9 +1050,9 @@ match_passwords(Password, #scram{} = Scram, Digest, DigestFun) -> true -> StoredKey == Password andalso Password /= <<"">> end; -match_passwords(ProvidedPassword, ValidPassword, <<"">>, undefined) -> +match_password(ProvidedPassword, ValidPassword, <<"">>, undefined) -> ProvidedPassword == ValidPassword andalso ProvidedPassword /= <<"">>; -match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun) -> +match_password(ProvidedPassword, ValidPassword, Digest, DigestFun) -> DigRes = if Digest /= <<"">> -> Digest == DigestFun(ValidPassword); true -> false @@ -983,7 +1121,7 @@ convert_to_scram(Server) -> lists:foreach( fun({U, S}) -> case get_password(U, S) of - Pass when is_binary(Pass) -> + [Pass] when is_binary(Pass) -> SPass = password_to_scram(Server, Pass), set_password(U, S, SPass); _ -> diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl index 6ed303fae..1fe57aca1 100644 --- a/src/ejabberd_auth_mnesia.erl +++ b/src/ejabberd_auth_mnesia.erl @@ -29,11 +29,11 @@ -behaviour(ejabberd_auth). --export([start/1, stop/1, set_password/3, try_register/3, +-export([start/1, stop/1, set_password_multiple/3, try_register_multiple/3, get_users/2, init_db/0, count_users/2, get_password/2, remove_user/2, store_type/1, import/2, - plain_password_required/1, use_cache/1]). + plain_password_required/1, use_cache/1, drop_password_type/2, set_password_instance/3]). -export([need_transform/1, transform/1]). -include("logger.hrl"). @@ -86,30 +86,58 @@ plain_password_required(Server) -> store_type(Server) -> ejabberd_auth:password_format(Server). -set_password(User, Server, Password) -> - US = {User, Server}, - F = fun () -> - mnesia:write(#passwd{us = US, password = Password}) +set_password_multiple(User, Server, Passwords) -> + F = fun() -> + lists:foreach( + fun(#scram{hash = Hash} = Password) -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + (Plain) -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end, Passwords) end, case mnesia:transaction(F) of {atomic, ok} -> - {cache, {ok, Password}}; + {cache, {ok, Passwords}}; {aborted, Reason} -> ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), {nocache, {error, db_failure}} end. -try_register(User, Server, Password) -> - US = {User, Server}, - F = fun () -> - case mnesia:read({passwd, US}) of - [] -> - mnesia:write(#passwd{us = US, password = Password}), - mnesia:dirty_update_counter(reg_users_counter, Server, 1), - {ok, Password}; - [_] -> - {error, exists} - end +set_password_instance(User, Server, Password) -> + F = fun() -> + case Password of + #scram{hash = Hash} = Password -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + Plain -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} + end. + +try_register_multiple(User, Server, Passwords) -> + F = fun() -> + case mnesia:select(passwd, [{{'_', {'$1', '$2', '_'}, '$3'}, + [{'==', '$1', User}, + {'==', '$2', Server}], + ['$3']}]) of + [] -> + lists:foreach( + fun(#scram{hash = Hash} = Password) -> + mnesia:write(#passwd{us = {User, Server, Hash}, password = Password}); + (Plain) -> + mnesia:write(#passwd{us = {User, Server, plain}, password = Plain}) + end, Passwords), + mnesia:dirty_update_counter(reg_users_counter, Server, 1), + {ok, Passwords}; + [_] -> + {error, exists} + end end, case mnesia:transaction(F) of {atomic, Res} -> @@ -120,9 +148,10 @@ try_register(User, Server, Password) -> end. get_users(Server, []) -> - mnesia:dirty_select(passwd, + Users = mnesia:dirty_select(passwd, [{#passwd{us = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, Server}], ['$1']}]); + [{'==', {element, 2, '$1'}, Server}], ['$1']}]), + lists:uniq(lists:map(fun({U, S, _}) -> {U, S} end, Users)); get_users(Server, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> get_users(Server, [{limit, End - Start + 1}, {offset, Start}]); @@ -179,22 +208,48 @@ count_users(Server, _) -> count_users(Server, []). get_password(User, Server) -> - case mnesia:dirty_read(passwd, {User, Server}) of - [{passwd, _, {scram, SK, SEK, Salt, IC}}] -> - {cache, {ok, #scram{storedkey = SK, serverkey = SEK, - salt = Salt, hash = sha, iterationcount = IC}}}; - [#passwd{password = Password}] -> - {cache, {ok, Password}}; + case mnesia:dirty_select(passwd, [{{'_', {'$1', '$2', '_'}, '$3'}, + [{'==', '$1', User}, + {'==', '$2', Server}], + ['$3']}]) of + [_|_] = List -> + List2 = lists:map( + fun({scram, SK, SEK, Salt, IC}) -> + #scram{storedkey = SK, serverkey = SEK, + salt = Salt, hash = sha, iterationcount = IC}; + (Other) -> Other + end, List), + {cache, {ok, List2}}; _ -> {cache, error} end. +drop_password_type(Server, Hash) -> + F = fun() -> + Keys = mnesia:select(passwd, [{{'_', '$1', '_'}, + [{'==', {element, 3, '$1'}, Hash}, + {'==', {element, 2, '$1'}, Server}], + ['$1']}]), + lists:foreach(fun(Key) -> mnesia:delete({passwd, Key}) end, Keys), + ok + end, + case mnesia:transaction(F) of + {atomic, ok} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]), + {error, db_failure} + end. + remove_user(User, Server) -> - US = {User, Server}, F = fun () -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, Server, -1), - ok + Keys = mnesia:select(passwd, [{{'_', '$1', '_'}, + [{'==', {element, 1, '$1'}, User}, + {'==', {element, 2, '$1'}, Server}], + ['$1']}]), + lists:foreach(fun(Key) -> mnesia:delete({passwd, Key}) end, Keys), + mnesia:dirty_update_counter(reg_users_counter, Server, -1), + ok end, case mnesia:transaction(F) of {atomic, ok} -> @@ -206,45 +261,10 @@ remove_user(User, Server) -> need_transform(#reg_users_counter{}) -> false; -need_transform({passwd, {U, S}, Pass}) -> - case Pass of - _ when is_binary(Pass) -> - case store_type(S) of - scram -> - ?INFO_MSG("Passwords in Mnesia table 'passwd' " - "will be SCRAM'ed", []), - true; - plain -> - false - end; - {scram, _, _, _, _} -> - case store_type(S) of - scram -> - false; - plain -> - ?WARNING_MSG("Some passwords were stored in the database " - "as SCRAM, but 'auth_password_format' " - "is not configured as 'scram': some " - "authentication mechanisms such as DIGEST-MD5 " - "would *fail*", []), - false - end; - #scram{} -> - case store_type(S) of - scram -> - false; - plain -> - ?WARNING_MSG("Some passwords were stored in the database " - "as SCRAM, but 'auth_password_format' " - "is not configured as 'scram': some " - "authentication mechanisms such as DIGEST-MD5 " - "would *fail*", []), - false - end; - _ when is_list(U) orelse is_list(S) orelse is_list(Pass) -> - ?INFO_MSG("Mnesia table 'passwd' will be converted to binary", []), - true - end. +need_transform({passwd, {_U, _S, _T}, _Pass}) -> + false; +need_transform({passwd, {_U, _S}, _Pass}) -> + true. transform({passwd, {U, S}, Pass}) when is_list(U) orelse is_list(S) orelse is_list(Pass) -> @@ -263,24 +283,14 @@ transform({passwd, {U, S}, Pass}) transform(#passwd{us = NewUS, password = NewPass}); transform(#passwd{us = {U, S}, password = Password} = P) when is_binary(Password) -> - case store_type(S) of - scram -> - case jid:resourceprep(Password) of - error -> - ?ERROR_MSG("SASLprep failed for password of user ~ts@~ts", - [U, S]), - P; - _ -> - Scram = ejabberd_auth:password_to_scram(S, Password), - P#passwd{password = Scram} - end; - plain -> - P - end; -transform({passwd, _, {scram, _, _, _, _}} = P) -> - P; -transform(#passwd{password = #scram{}} = P) -> - P. + P#passwd{us = {U, S, plain}, password = Password}; +transform({passwd, {U, S}, {scram, SK, SEK, Salt, IC}}) -> + #passwd{us = {U, S, sha}, + password = #scram{storedkey = SK, serverkey = SEK, + salt = Salt, hash = sha, iterationcount = IC}}; +transform(#passwd{us = {U, S}, password = #scram{hash = Hash}} = P) -> + P#passwd{us = {U, S, Hash}}; +transform(Other) -> Other. import(LServer, [LUser, Password, _TimeStamp]) -> mnesia:dirty_write( diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index fefabdf76..b8ff98615 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -30,10 +30,10 @@ -behaviour(ejabberd_auth). --export([start/1, stop/1, set_password/3, try_register/3, +-export([start/1, stop/1, set_password_multiple/3, try_register_multiple/3, get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, plain_password_required/1, - export/1, which_users_exists/2]). + export/1, which_users_exists/2, drop_password_type/2, set_password_instance/3]). -export([sql_schemas/0]). -include_lib("xmpp/include/scram.hrl"). @@ -49,7 +49,34 @@ start(Host) -> ok. sql_schemas() -> - [#sql_schema{ + [ + #sql_schema{ + version = 2, + tables = + [#sql_table{ + name = <<"users">>, + columns = + [#sql_column{name = <<"username">>, type = text}, + #sql_column{name = <<"server_host">>, type = text}, + #sql_column{name = <<"type">>, type = smallint}, + #sql_column{name = <<"password">>, type = text}, + #sql_column{name = <<"serverkey">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"salt">>, type = {text, 128}, + default = true}, + #sql_column{name = <<"iterationcount">>, type = integer, + default = true}, + #sql_column{name = <<"created_at">>, type = timestamp, + default = true}], + indices = [#sql_index{ + columns = [<<"server_host">>, <<"username">>, <<"type">>], + unique = true}]}], + update = [ + {add_column, <<"users">>, <<"type">>}, + {update_primary_key,<<"users">>, + [<<"server_host">>, <<"username">>, <<"type">>]} + ]}, + #sql_schema{ version = 1, tables = [#sql_table{ @@ -78,42 +105,87 @@ plain_password_required(Server) -> store_type(Server) -> ejabberd_auth:password_format(Server). -set_password(User, Server, Password) -> +hash_to_num(plain) -> 1; +hash_to_num(sha) -> 2; +hash_to_num(sha256) -> 3; +hash_to_num(sha512) -> 4. + +num_to_hash(2) -> sha; +num_to_hash(3) -> sha256; +num_to_hash(4) -> sha512. + +set_password_instance(User, Server, #scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> + F = fun() -> + set_password_scram_t(User, Server, Hash, + SK, SEK, Salt, IC) + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, _} -> + ok; + {aborted, _} -> + {error, db_failure} + end; +set_password_instance(User, Server, Plain) -> + F = fun() -> + set_password_t(User, Server, Plain) + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, _} -> + ok; + {aborted, _} -> + {error, db_failure} + end. + +set_password_multiple(User, Server, Passwords) -> F = fun() -> - case Password of - #scram{hash = Hash, storedkey = SK, serverkey = SEK, - salt = Salt, iterationcount = IC} -> - SK2 = scram_hash_encode(Hash, SK), + ejabberd_sql:sql_query_t( + ?SQL("delete from users where username=%(User)s and %(Server)H")), + lists:foreach( + fun(#scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> set_password_scram_t( - User, Server, - SK2, SEK, Salt, IC); - _ -> - set_password_t(User, Server, Password) - end + User, Server, Hash, + SK, SEK, Salt, IC); + (Plain) -> + set_password_t(User, Server, Plain) + end, Passwords) end, case ejabberd_sql:sql_transaction(Server, F) of {atomic, _} -> - {cache, {ok, Password}}; + {cache, {ok, Passwords}}; {aborted, _} -> {nocache, {error, db_failure}} end. -try_register(User, Server, Password) -> - Res = - case Password of - #scram{hash = Hash, storedkey = SK, serverkey = SEK, - salt = Salt, iterationcount = IC} -> - SK2 = scram_hash_encode(Hash, SK), - add_user_scram( - Server, User, - SK2, SEK, Salt, IC); - _ -> - add_user(Server, User, Password) - end, - case Res of - {updated, 1} -> {cache, {ok, Password}}; - _ -> {nocache, {error, exists}} +try_register_multiple(User, Server, Passwords) -> + F = + fun() -> + case ejabberd_sql:sql_query_t( + ?SQL("select @(count(*))d from users where username=%(User)s and %(Server)H")) of + {selected, [{0}]} -> + lists:foreach( + fun(#scram{hash = Hash, storedkey = SK, serverkey = SEK, + salt = Salt, iterationcount = IC}) -> + set_password_scram_t( + User, Server, Hash, + SK, SEK, Salt, IC); + (Plain) -> + set_password_t(User, Server, Plain) + end, Passwords), + {cache, {ok, Passwords}}; + {selected, _} -> + {nocache, {error, exists}}; + _ -> + {nocache, {error, db_failure}} + end + end, + case ejabberd_sql:sql_transaction(Server, F) of + {atomic, Res} -> + Res; + {aborted, _} -> + {nocache, {error, db_failure}} end. get_users(Server, Opts) -> @@ -132,21 +204,41 @@ count_users(Server, Opts) -> get_password(User, Server) -> case get_password_scram(Server, User) of - {selected, [{Password, <<>>, <<>>, 0}]} -> - {cache, {ok, Password}}; - {selected, [{StoredKey, ServerKey, Salt, IterationCount}]} -> - {Hash, SK} = case StoredKey of - <<"sha256:", Rest/binary>> -> {sha256, Rest}; - <<"sha512:", Rest/binary>> -> {sha512, Rest}; - Other -> {sha, Other} - end, - {cache, {ok, #scram{storedkey = SK, - serverkey = ServerKey, - salt = Salt, - hash = Hash, - iterationcount = IterationCount}}}; {selected, []} -> {cache, error}; + {selected, Passwords} -> + Converted = lists:map( + fun({0, Password, <<>>, <<>>, 0}) -> + update_password_type(User, Server, 1), + Password; + ({_, Password, <<>>, <<>>, 0}) -> + Password; + ({0, StoredKey, ServerKey, Salt, IterationCount}) -> + {Hash, SK} = case StoredKey of + <<"sha256:", Rest/binary>> -> + update_password_type(User, Server, 3, Rest), + {sha256, Rest}; + <<"sha512:", Rest/binary>> -> + update_password_type(User, Server, 4, Rest), + {sha512, Rest}; + Other -> + update_password_type(User, Server, 2), + {sha, Other} + end, + #scram{storedkey = SK, + serverkey = ServerKey, + salt = Salt, + hash = Hash, + iterationcount = IterationCount}; + ({Type, StoredKey, ServerKey, Salt, IterationCount}) -> + Hash = num_to_hash(Type), + #scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt, + hash = Hash, + iterationcount = IterationCount} + end, Passwords), + {cache, {ok, Converted}}; _ -> {nocache, error} end. @@ -159,6 +251,13 @@ remove_user(User, Server) -> {error, db_failure} end. +drop_password_type(LServer, Hash) -> + Type = hash_to_num(Hash), + ejabberd_sql:sql_query( + LServer, + ?SQL("delete from users" + " where type=%(Type)d and %(LServer)H")). + scram_hash_encode(Hash, StoreKey) -> case Hash of sha -> StoreKey; @@ -166,12 +265,14 @@ scram_hash_encode(Hash, StoreKey) -> sha512 -> <<"sha512:", StoreKey/binary>> end. -set_password_scram_t(LUser, LServer, +set_password_scram_t(LUser, LServer, Hash, StoredKey, ServerKey, Salt, IterationCount) -> + Type = hash_to_num(Hash), ?SQL_UPSERT_T( "users", ["!username=%(LUser)s", "!server_host=%(LServer)s", + "!type=%(Type)d", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", @@ -182,40 +283,31 @@ set_password_t(LUser, LServer, Password) -> "users", ["!username=%(LUser)s", "!server_host=%(LServer)s", + "!type=1", "password=%(Password)s", "serverkey=''", "salt=''", "iterationcount=0"]). +update_password_type(LUser, LServer, Type, Password) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("update users set type=%(Type)d, password=%(Password)s" + " where username=%(LUser)s and type=0 and %(LServer)H")). + +update_password_type(LUser, LServer, Type) -> + ejabberd_sql:sql_query( + LServer, + ?SQL("update users set type=%(Type)d" + " where username=%(LUser)s and type=0 and %(LServer)H")). + get_password_scram(LServer, LUser) -> ejabberd_sql:sql_query( LServer, - ?SQL("select @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d" + ?SQL("select @(type)d, @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d" " from users" " where username=%(LUser)s and %(LServer)H")). -add_user_scram(LServer, LUser, - StoredKey, ServerKey, Salt, IterationCount) -> - ejabberd_sql:sql_query( - LServer, - ?SQL_INSERT( - "users", - ["username=%(LUser)s", - "server_host=%(LServer)s", - "password=%(StoredKey)s", - "serverkey=%(ServerKey)s", - "salt=%(Salt)s", - "iterationcount=%(IterationCount)d"])). - -add_user(LServer, LUser, Password) -> - ejabberd_sql:sql_query( - LServer, - ?SQL_INSERT( - "users", - ["username=%(LUser)s", - "server_host=%(LServer)s", - "password=%(Password)s"])). - del_user(LServer, LUser) -> ejabberd_sql:sql_query( LServer, @@ -224,7 +316,7 @@ del_user(LServer, LUser) -> list_users(LServer, []) -> ejabberd_sql:sql_query( LServer, - ?SQL("select @(username)s from users where %(LServer)H")); + ?SQL("select @(distinct username)s from users where %(LServer)H")); list_users(LServer, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> list_users(LServer, @@ -240,7 +332,7 @@ list_users(LServer, [{limit, Limit}, {offset, Offset}]) when is_integer(Limit) and is_integer(Offset) -> ejabberd_sql:sql_query( LServer, - ?SQL("select @(username)s from users " + ?SQL("select @(distinct username)s from users " "where %(LServer)H " "order by username " "limit %(Limit)d offset %(Offset)d")); @@ -252,7 +344,7 @@ list_users(LServer, SPrefix2 = <>, ejabberd_sql:sql_query( LServer, - ?SQL("select @(username)s from users " + ?SQL("select @(distinct username)s from users " "where username like %(SPrefix2)s %ESCAPE and %(LServer)H " "order by username " "limit %(Limit)d offset %(Offset)d")). @@ -269,11 +361,11 @@ users_number(LServer) -> " where oid = 'users'::regclass::oid")); _ -> ejabberd_sql:sql_query_t( - ?SQL("select @(count(*))d from users where %(LServer)H")) + ?SQL("select @(count(distinct username))d from users where %(LServer)H")) end; (_Type, _) -> ejabberd_sql:sql_query_t( - ?SQL("select @(count(*))d from users where %(LServer)H")) + ?SQL("select @(count(distinct username))d from users where %(LServer)H")) end). users_number(LServer, [{prefix, Prefix}]) @@ -282,7 +374,7 @@ users_number(LServer, [{prefix, Prefix}]) SPrefix2 = <>, ejabberd_sql:sql_query( LServer, - ?SQL("select @(count(*))d from users " + ?SQL("select @(count(distinct username))d from users " "where username like %(SPrefix2)s %ESCAPE and %(LServer)H")); users_number(LServer, []) -> users_number(LServer). @@ -290,7 +382,7 @@ users_number(LServer, []) -> which_users_exists(LServer, LUsers) when length(LUsers) =< 100 -> try ejabberd_sql:sql_query( LServer, - ?SQL("select @(username)s from users where username in %(LUsers)ls")) of + ?SQL("select @(distinct username)s from users where username in %(LUsers)ls")) of {selected, Matching} -> [U || {U} <- Matching]; {error, _} = E -> diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index f00755625..1852dde32 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -423,16 +423,27 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St Type = ejabberd_auth:store_type(LServer), Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer), - ScramHash = ejabberd_option:auth_scram_hash(LServer), - ShaAv = Type == plain orelse (Type == scram andalso ScramHash == sha), - Sha256Av = Type == plain orelse (Type == scram andalso ScramHash == sha256), - Sha512Av = Type == plain orelse (Type == scram andalso ScramHash == sha512), + {Digest, ShaAv, Sha256Av, Sha512Av} = + case ejabberd_option:auth_stored_password_types(LServer) of + [] -> + ScramHash = ejabberd_option:auth_scram_hash(LServer), + {Type == plain, + Type == plain orelse (Type == scram andalso ScramHash == sha), + Type == plain orelse (Type == scram andalso ScramHash == sha256), + Type == plain orelse (Type == scram andalso ScramHash == sha512)}; + Methods -> + HasPlain = lists:member(plain, Methods), + {HasPlain, + HasPlain orelse lists:member(scram_sha1, Methods), + HasPlain orelse lists:member(scram_sha256, Methods), + HasPlain orelse lists:member(scram_sha512, Methods)} + end, %% I re-created it from cyrsasl ets magic, but I think it's wrong %% TODO: need to check before 18.09 release lists:filter( fun(<<"ANONYMOUS">>) -> ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer); - (<<"DIGEST-MD5">>) -> Type == plain; + (<<"DIGEST-MD5">>) -> Digest; (<<"SCRAM-SHA-1">>) -> ShaAv; (<<"SCRAM-SHA-1-PLUS">>) -> ShaAv andalso Encrypted; (<<"SCRAM-SHA-256">>) -> Sha256Av; diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index cb3765b58..2a5c36963 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -19,6 +19,7 @@ -export([auth_opts/0, auth_opts/1]). -export([auth_password_format/0, auth_password_format/1]). -export([auth_scram_hash/0, auth_scram_hash/1]). +-export([auth_stored_password_types/0, auth_stored_password_types/1]). -export([auth_use_cache/0, auth_use_cache/1]). -export([c2s_cafile/0, c2s_cafile/1]). -export([c2s_ciphers/0, c2s_ciphers/1]). @@ -264,6 +265,13 @@ auth_scram_hash() -> auth_scram_hash(Host) -> ejabberd_config:get_option({auth_scram_hash, Host}). +-spec auth_stored_password_types() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_stored_password_types() -> + auth_stored_password_types(global). +-spec auth_stored_password_types(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_stored_password_types(Host) -> + ejabberd_config:get_option({auth_stored_password_types, Host}). + -spec auth_use_cache() -> boolean(). auth_use_cache() -> auth_use_cache(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 0e471059e..8dc88cd23 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -77,6 +77,8 @@ opt_type(auth_opts) -> {path_prefix, V} end, L) end; +opt_type(auth_stored_password_types) -> + econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); opt_type(auth_password_format) -> econf:enum([plain, scram]); opt_type(auth_scram_hash) -> @@ -546,6 +548,7 @@ options() -> {auth_opts, []}, {auth_password_format, plain}, {auth_scram_hash, sha}, + {auth_stored_password_types, []}, {auth_external_user_exists_check, true}, {auth_use_cache, fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 312e00f0e..0359b2dbf 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -399,6 +399,14 @@ doc() -> "You shouldn't change this if you already have passwords generated with " "a different algorithm - users that have such passwords will not be able " "to authenticate. The default value is 'sha'.")}}, + {auth_stored_password_types, + #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + desc => + ?T("List with password types that should be stored concurently for each user in database. " + "Each time user set it password, database will be updated to store passwords in format " + "compatible with each format listed here. This can be used to migrate user passwords " + "to more secure format. This options if set, will override values set in 'auth_scream_hash' " + "and 'auth_password_format'. By default this value is not set.")}}, {auth_external_user_exists_check, #{value => "true | false", note => "added in 23.10", diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index e0ffabc70..f08c28631 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -107,7 +107,7 @@ c2s_handle_sasl2_task_data({_, #{user := User, server := Server, #scram_upgrade_hash{data = SaltedPassword} -> StoredKey = scram:stored_key(Algo, scram:client_key(Algo, SaltedPassword)), ServerKey = scram:server_key(Algo, SaltedPassword), - ejabberd_auth:set_password(User, Server, + ejabberd_auth:set_password_instance(User, Server, #scram{hash = Algo, iterationcount = Iter, salt = Salt, serverkey = ServerKey, storedkey = StoredKey}), State2 = maps:remove(scram_upgrade, State), diff --git a/test/webadmin_tests.erl b/test/webadmin_tests.erl index 6a4ff7cac..fa12fd6f8 100644 --- a/test/webadmin_tests.erl +++ b/test/webadmin_tests.erl @@ -78,7 +78,7 @@ adduser(Config) -> "server/" ++ binary_to_list(Server) ++ "/users/", <<"register/user=", (mue(User))/binary, "®ister/password=", (mue(Password))/binary, "®ister=Register">>), - Password = ejabberd_auth:get_password(User, Server), + Password = ejabberd_auth:get_password_s(User, Server), ?match({_, _}, binary:match(Body, <<"User ", User/binary, "@", Server/binary, " successfully registered">>)). @@ -92,7 +92,7 @@ changepassword(Config) -> ++ "/user/" ++ binary_to_list(mue(User)) ++ "/", <<"change_password/newpass=", (mue(Password))/binary, "&change_password=Change+Password">>), - ?match(Password, ejabberd_auth:get_password(User, Server)), + ?match(Password, ejabberd_auth:get_password_s(User, Server)), ?match({_, _}, binary:match(Body, <<"
ok
">>)). removeuser(Config) -> From 968fbc94246e25e6c0e7e8121059557f5b183dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 26 Mar 2025 11:29:59 +0100 Subject: [PATCH 1051/1302] Fix compilation on Users = mnesia:dirty_select(passwd, [{#passwd{us = '$1', _ = '_'}, [{'==', {element, 2, '$1'}, Server}], ['$1']}]), - lists:uniq(lists:map(fun({U, S, _}) -> {U, S} end, Users)); + {_, Res} = lists:foldl( + fun({U, S, _}, {{U2, S2}, _} = Acc) when U == U2 andalso S == S2 -> + Acc; + ({U, S, _}, {_, Res}) -> + {{U, S}, [{U, S} | Res]} + end, {{none, none}, []}, Users), + Res; get_users(Server, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> get_users(Server, [{limit, End - Start + 1}, {offset, Start}]); From 6f9f4b36354c85664a5a137177e8d8a5879bf754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 26 Mar 2025 12:52:03 +0100 Subject: [PATCH 1052/1302] Fix issues with ldap authentication --- src/ejabberd_auth.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index aadbcf631..53b5e0f21 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -767,7 +767,8 @@ db_get_password(User, Server, Mod) -> case ets_cache:lookup(cache_tab(Mod), {User, Server}) of {ok, exists} -> error; not_found -> error; - Other -> Other + {ok, List} = V when is_list(List) -> V; + {ok, Single} -> {ok, [Single]}; end; false -> error; From 9c92fcc92d96142f984e671a16ce1b5265690c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 26 Mar 2025 13:03:41 +0100 Subject: [PATCH 1053/1302] Fix last commit --- src/ejabberd_auth.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 53b5e0f21..419aad54d 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -769,6 +769,7 @@ db_get_password(User, Server, Mod) -> not_found -> error; {ok, List} = V when is_list(List) -> V; {ok, Single} -> {ok, [Single]}; + Other -> Other end; false -> error; From 90a200be25d65ffdf2aef5b9c9273b4a8360d81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 27 Mar 2025 16:25:12 +0100 Subject: [PATCH 1054/1302] Tag deps --- mix.exs | 6 +++--- mix.lock | 14 +++++++------- rebar.config | 36 ++++++++++++++++++------------------ rebar.lock | 48 +++++++++++++++++++++++------------------------- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/mix.exs b/mix.exs index dc42f28d4..69f4a7455 100644 --- a/mix.exs +++ b/mix.exs @@ -130,8 +130,8 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "be24923968261c2661a9e116650dce5ac95c9d23", override: true}, - {:yconf, git: "https://github.com/processone/yconf", ref: "9682a6025ed543eedf34637e4cfcc66837074af6", override: true}] + {:xmpp, "~> 1.10.0"}, + {:yconf, ">= 1.0.18"}] ++ cond_deps() end @@ -161,7 +161,7 @@ defmodule Ejabberd.MixProject do {if_version_below(~c"22", true), {:lager, "~> 3.9.1"}}, {config(:lua), {:luerl, "~> 1.2.0"}}, {config(:mysql), {:p1_mysql, ">= 1.0.24"}}, - {config(:pgsql), {:p1_pgsql, ">= 1.1.26"}}, + {config(:pgsql), {:p1_pgsql, ">= 1.1.32"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}, {config(:stun), {:stun, "~> 1.0"}}], do: dep diff --git a/mix.lock b/mix.lock index e8ef48a9c..c0c7f254f 100644 --- a/mix.lock +++ b/mix.lock @@ -3,11 +3,11 @@ "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "eimp": {:hex, :eimp, "1.0.23", "aaf32efab061143403dadbaa63e699ef8e81702fbfa96fd436d5e9be4d6a8d3a", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "907c780023cb2893e4fc4bdbe6a4f02c355913862ac67f0ecc26605e816b628a"}, + "eimp": {:hex, :eimp, "1.0.24", "853db317ba394d479d2f1181e0b0135a3cd3c2c7eb48ff6cfb035a28dd354b14", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7d61432eb8a45659c0be475f44e75eeb651743aa64a1de8adf785cdad81961ad"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "esip": {:hex, :esip, "1.0.56", "63c0fdc667be751714e1e5c14621a9334f21b60ac1bb68be889454ca9ca021b7", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.15", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "9ef3660cef93b623f7368dcd5c79f4e704358631909e6dd464e335378815da1f"}, + "esip": {:hex, :esip, "1.0.57", "4b14e4832d08b9ffc10d855b5d10b3083232b1d53deb4c046679496ce85569c4", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.17", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "19c357e1817b1e04792ef359bf900400f3e6d0e5ade929fd72f88ea9b44af2ed"}, "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, @@ -25,15 +25,15 @@ "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "p1_acme": {:hex, :p1_acme, "1.0.25", "db91f0d6c193cd1d5c0b0fa3939a898dbf56a6075db4347cde26e802715de50c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a7b55b47495ddb4f98a15e65451ec3ad43f4637b955c74cd695d98e6a645d08c"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.25", "875d4cbdc7c9990270df3292cce2514e4c18a9fdfd19bef258cb4d0c45b4f243", [:rebar3], [], "hexpm", "e6187ffae95b726098e88f3ee6f2344ac259ce2c26e0ee403b05feef341ae434"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.31", "8339beac1f0f4a45f476ff5306be5135020f02979a61df0d8cf7b1c67e85e2fd", [:rebar3], [{:xmpp, "~> 1.9.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "b7fc45dfb2549187347871b7fd0573638ad5ea337f6263fba1b3840efab2ff49"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.32", "3f95d7e3413fc8f0be80abb4be1a0d7f67066a36905085cd5a423145598b0cb0", [:rebar3], [{:xmpp, "~> 1.10.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "268b01e8f4eb75c211a31495a25c2815c549aecce2f0df1a161c6e0a2cde061e"}, "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, - "stun": {:hex, :stun, "1.2.15", "eec510af6509201ff97f1f2c87b7977c833bf29c04e985383370ec21f04e4ccf", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "f6d8a541a29fd13f2ce658b676c0cc661262b96e045b52def1644b75ebc0edef"}, + "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "be24923968261c2661a9e116650dce5ac95c9d23", [ref: "be24923968261c2661a9e116650dce5ac95c9d23"]}, - "yconf": {:git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6", [ref: "9682a6025ed543eedf34637e4cfcc66837074af6"]}, + "xmpp": {:hex, :xmpp, "1.10.0", "68a6dff8db8987c4592b2d5dd71d3f947b4ebd15209c9acaca5909a642670630", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ceeae43b8fe97649d8f8546b3f7f2b38ecfc931c0cdd5c7445ffb3f80fcb7d85"}, + "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, } diff --git a/rebar.config b/rebar.config index f47955841..1abf4bab4 100644 --- a/rebar.config +++ b/rebar.config @@ -26,8 +26,8 @@ {if_version_below, "24", {base64url, "~> 1.0", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} }}, - {cache_tab, "~> 1.0.30", {git, "https://github.com/processone/cache_tab", {tag, "1.0.31"}}}, - {eimp, "~> 1.0.22", {git, "https://github.com/processone/eimp", {tag, "1.0.23"}}}, + {cache_tab, "~> 1.0.31", {git, "https://github.com/processone/cache_tab", {tag, "1.0.31"}}}, + {eimp, "~> 1.0.24", {git, "https://github.com/processone/eimp", {tag, "1.0.24"}}}, {if_var_true, pam, {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, @@ -41,12 +41,12 @@ {eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}} }}}, {if_var_true, sip, - {esip, "~> 1.0.52", {git, "https://github.com/processone/esip", {tag, "1.0.56"}}}}, + {esip, "~> 1.0.57", {git, "https://github.com/processone/esip", {tag, "1.0.57"}}}}, {if_var_true, zlib, - {ezlib, "~> 1.0.12", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.19", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.55"}}}, - {fast_yaml, "~> 1.0.36", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, + {ezlib, "~> 1.0.13", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, + {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, + {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", {tag, "1.1.55"}}}, + {fast_yaml, "~> 1.0.37", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}} @@ -63,22 +63,22 @@ {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}}, {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, - {mqtree, "~> 1.0.16", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.23", {git, "https://github.com/processone/p1_acme", {tag, "1.0.25"}}}, + {mqtree, "~> 1.0.17", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, + {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", {tag, "1.0.25"}}}, {if_var_true, mysql, - {p1_mysql, "~> 1.0.24", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.25"}}}}, + {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.26", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.31"}}}}, - {p1_utils, "~> 1.0.25", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, + {p1_pgsql, "~> 1.1.32", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.32"}}}}, + {p1_utils, "~> 1.0.27", {git, "https://github.com/processone/p1_utils", {tag, "1.0.27"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, - {sqlite3, "~> 1.1.14", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, - {stringprep, "~> 1.0.29", {git, "https://github.com/processone/stringprep", {tag, "1.0.30"}}}, + {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, + {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, - {stun, "~> 1.2.12", {git, "https://github.com/processone/stun", {tag, "1.2.15"}}}}, - {xmpp, "~> 1.9.2", {git, "https://github.com/processone/xmpp", "8929be60aa0c56653b13f5fcada1e460337a016b"}}, - {yconf, "~> 1.0.17", {git, "https://github.com/processone/yconf", "9682a6025ed543eedf34637e4cfcc66837074af6"}} + {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", {tag, "1.10.0"}}}, + {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. {gitonly_deps, [ejabberd_po]}. @@ -222,7 +222,7 @@ {dialyzer, [{get_warnings, false}, % Show warnings of dependencies {if_version_above, "25", {plt_extra_apps, - [asn1, odbc, public_key, stdlib, syntax_tools, + [asn1, public_key, stdlib, syntax_tools, idna, jose, cache_tab, eimp, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_oauth2, p1_utils, pkix, diff --git a/rebar.lock b/rebar.lock index 68e7d5ae7..2c212b330 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,10 +1,10 @@ {"1.2.0", [{<<"base64url">>,{pkg,<<"base64url">>,<<"1.0.1">>},1}, {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.31">>},0}, - {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.23">>},0}, + {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.24">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.56">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.57">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.55">>},0}, @@ -15,31 +15,25 @@ {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.25">>},0}, - {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.25">>},0}, + {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.31">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.32">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.15">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"8929be60aa0c56653b13f5fcada1e460337a016b"}}, - 0}, - {<<"yconf">>, - {git,"https://github.com/processone/yconf", - {ref,"9682a6025ed543eedf34637e4cfcc66837074af6"}}, - 0}]}. + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.10.0">>},0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, {<<"cache_tab">>, <<"E4097B50A6F373AB1E0A5F01BAB0BEF6626771A4CD6C93404ED6D54810E11FBC">>}, - {<<"eimp">>, <<"AAF32EFAB061143403DADBAA63E699EF8E81702FBFA96FD436D5E9BE4D6A8D3A">>}, + {<<"eimp">>, <<"853DB317BA394D479D2F1181E0B0135A3CD3C2C7EB48FF6CFB035A28DD354B14">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, - {<<"esip">>, <<"63C0FDC667BE751714E1E5C14621A9334F21B60AC1BB68BE889454CA9CA021B7">>}, + {<<"esip">>, <<"4B14E4832D08B9FFC10D855B5D10B3083232B1D53DEB4C046679496CE85569C4">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, {<<"fast_xml">>, <<"ACE020F2521F2A484AC8467D2822AF85534A346E2AAE03FFCBC34F29318BEFAF">>}, @@ -50,22 +44,24 @@ {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, {<<"p1_acme">>, <<"DB91F0D6C193CD1D5C0B0FA3939A898DBF56A6075DB4347CDE26E802715DE50C">>}, - {<<"p1_mysql">>, <<"875D4CBDC7C9990270DF3292CCE2514E4C18A9FDFD19BEF258CB4D0C45B4F243">>}, + {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"8339BEAC1F0F4A45F476FF5306BE5135020F02979A61DF0D8CF7B1C67E85E2FD">>}, + {<<"p1_pgsql">>, <<"3F95D7E3413FC8F0BE80ABB4BE1A0D7F67066A36905085CD5A423145598B0CB0">>}, {<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, - {<<"stun">>, <<"EEC510AF6509201FF97F1F2C87B7977C833BF29C04E985383370EC21F04E4CCF">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}]}, + {<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, + {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"xmpp">>, <<"68A6DFF8DB8987C4592B2D5DD71D3F947B4EBD15209C9ACACA5909A642670630">>}, + {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, - {<<"eimp">>, <<"907C780023CB2893E4FC4BDBE6A4F02C355913862AC67F0ECC26605E816B628A">>}, + {<<"eimp">>, <<"7D61432EB8A45659C0BE475F44E75EEB651743AA64A1DE8ADF785CDAD81961AD">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, - {<<"esip">>, <<"9EF3660CEF93B623F7368DCD5C79F4E704358631909E6DD464E335378815DA1F">>}, + {<<"esip">>, <<"19C357E1817B1E04792EF359BF900400F3E6D0E5ADE929FD72F88EA9B44AF2ED">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, {<<"fast_xml">>, <<"83F3E23A780ED5F567CDEC73953F06C95B838D709DBFA86B59A98A8D23C99F85">>}, @@ -76,13 +72,15 @@ {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, {<<"p1_acme">>, <<"A7B55B47495DDB4F98A15E65451EC3AD43F4637B955C74CD695D98E6A645D08C">>}, - {<<"p1_mysql">>, <<"E6187FFAE95B726098E88F3EE6F2344AC259CE2C26E0EE403B05FEEF341AE434">>}, + {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"B7FC45DFB2549187347871B7FD0573638AD5EA337F6263FBA1B3840EFAB2FF49">>}, + {<<"p1_pgsql">>, <<"268B01E8F4EB75C211A31495A25C2815C549AECCE2F0DF1A161C6E0A2CDE061E">>}, {<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, - {<<"stun">>, <<"F6D8A541A29FD13F2CE658B676C0CC661262B96E045B52DEF1644B75EBC0EDEF">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}]} + {<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, + {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"xmpp">>, <<"CEEAE43B8FE97649D8F8546B3F7F2B38ECFC931C0CDD5C7445FFB3F80FCB7D85">>}, + {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} ]. From 01a71dc1892c0f946f92ea2c8e9eb247f3f9436a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 27 Mar 2025 18:29:54 +0100 Subject: [PATCH 1055/1302] Restore odbc in dialyzer apps --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 1abf4bab4..0d09d7f22 100644 --- a/rebar.config +++ b/rebar.config @@ -222,7 +222,7 @@ {dialyzer, [{get_warnings, false}, % Show warnings of dependencies {if_version_above, "25", {plt_extra_apps, - [asn1, public_key, stdlib, syntax_tools, + [asn1, odbc, public_key, stdlib, syntax_tools, idna, jose, cache_tab, eimp, fast_tls, fast_xml, fast_yaml, mqtree, p1_acme, p1_oauth2, p1_utils, pkix, From a4fc448a52fa095f6aeebf68f3763e2c794bd4c0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 24 Mar 2025 17:37:52 +0100 Subject: [PATCH 1056/1302] Container: Bump versions to Erlang/OTP 27.3 and Elixir 1.18.3 --- .github/container/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 8aa356949..2a271215d 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,6 +1,6 @@ #' Define default build variables -ARG OTP_VSN='27.2' -ARG ELIXIR_VSN='1.18.1' +ARG OTP_VSN='27.3' +ARG ELIXIR_VSN='1.18.3' ARG UID='9000' ARG USER='ejabberd' ARG HOME="opt/$USER" From 78650f827e04d99ed1efb90e4e60555f52dfde46 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 16:58:44 +0100 Subject: [PATCH 1057/1302] Rephrase auth_stored_password_types documentation --- src/ejabberd_options_doc.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 0359b2dbf..7c35f96fa 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -401,12 +401,13 @@ doc() -> "to authenticate. The default value is 'sha'.")}}, {auth_stored_password_types, #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + note => "added in 25.03", desc => - ?T("List with password types that should be stored concurently for each user in database. " - "Each time user set it password, database will be updated to store passwords in format " - "compatible with each format listed here. This can be used to migrate user passwords " - "to more secure format. This options if set, will override values set in 'auth_scream_hash' " - "and 'auth_password_format'. By default this value is not set.")}}, + ?T("List of password types that should be stored simultaneously for each user in database. " + "When the user sets the account password, database will be updated to store the password in formats " + "compatible with each type listed here. This can be used to migrate user passwords " + "to a more secure format. If this option if set, it will override values set in _`auth_scram_hash`_ " + "and _`auth_password_format`_ options. The default value is `[]`.")}}, {auth_external_user_exists_check, #{value => "true | false", note => "added in 23.10", From 4fe5ee034ce8c6d8a7b970bb5eac2dc1e8ae3cdc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 09:33:06 +0100 Subject: [PATCH 1058/1302] Update version number in documentation to 25.03 --- CONTAINER.md | 2 +- ejabberd.doap | 2 +- src/ejabberd.erl | 2 +- src/ejabberd_admin.erl | 4 ++-- src/ejabberd_options_doc.erl | 2 ++ src/mod_adhoc_api.erl | 2 +- src/mod_configure.erl | 2 +- src/mod_matrix_gw.erl | 7 ++++--- src/mod_muc.erl | 2 +- src/mod_muc_admin.erl | 2 +- src/mod_muc_room.erl | 2 +- 11 files changed, 16 insertions(+), 13 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index ea29c2f4f..3afe913b3 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -910,7 +910,7 @@ Images Comparison Let's summarize the differences between both container images. Legend: - :sparkle: is the recommended alternative -- :orange_circle: added in the latest release (ejabberd 25.xx) +- :orange_circle: added in the latest release (ejabberd 25.03) - :high_brightness: added in the previous release (ejabberd 24.12) - :low_brightness: added in the pre-previous release (ejabberd 24.10) diff --git a/ejabberd.doap b/ejabberd.doap index c17c89ca7..00e171905 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -770,7 +770,7 @@ 0.4.0 24.02 complete - , 0.4.0 since 25.xx + , 0.4.0 since 25.03
diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 89cbd229c..5b6fea3aa 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -42,7 +42,7 @@ -protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 424, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). --protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.xx"}). +-protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.03"}). -protocol({xep, 485, '0.2.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 9f2cc84b8..4e7dfe739 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -600,7 +600,7 @@ get_commands_spec() -> #ejabberd_commands{name = mnesia_list_tables, tags = [mnesia], desc = "List of Mnesia tables", - note = "added in 25.xx", + note = "added in 25.03", module = ?MODULE, function = mnesia_list_tables, result = {tables, {list, {table, {tuple, [{name, atom}, {storage_type, binary}, @@ -618,7 +618,7 @@ get_commands_spec() -> #ejabberd_commands{name = mnesia_table_change_storage, tags = [mnesia], desc = "Change storage type of a Mnesia table", - note = "added in 25.xx", + note = "added in 25.03", longdesc = "Storage type can be: `ram_copies`, `disc_copies`, `disc_only_copies`, `remote_copy`.", module = ?MODULE, function = mnesia_table_change_storage, args = [{table, binary}, {storage_type, binary}], diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 7c35f96fa..36bebcd5c 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -545,6 +545,7 @@ doc() -> "and sophisticated setups. The default value is an empty list.")}}, {define_keyword, #{value => "{NAME: Value}", + note => "added in 25.03", desc => ?T("Allows to define configuration " "_`../configuration/file-format.md#macros-and-keywords|keywords`_. "), @@ -560,6 +561,7 @@ doc() -> "sql_username: \"prefix.@SQL_USERNAME@\""]}}, {define_macro, #{value => "{NAME: Value}", + note => "improved in 25.03", desc => ?T("Allows to define configuration " "_`../configuration/file-format.md#macros-and-keywords|macros`_. "), diff --git a/src/mod_adhoc_api.erl b/src/mod_adhoc_api.erl index 397f9be6c..d8a16b504 100644 --- a/src/mod_adhoc_api.erl +++ b/src/mod_adhoc_api.erl @@ -95,7 +95,7 @@ mod_doc() -> "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " "This module requires _`mod_adhoc`_ (to execute the commands), " "and recommends _`mod_disco`_ (to discover the commands)."), - note => "added in 25.xx", + note => "added in 25.03", opts => [{default_version, #{value => "integer() | string()", diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 8e4c88405..81e7d5e7b 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -1592,7 +1592,7 @@ mod_doc() -> opts => [{access, #{value => ?T("AccessName"), - note => "added in 25.xx", + note => "added in 25.03", desc => ?T("This option defines which access rule will be used to " "control who is allowed to access the features provided by this module. " diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 3088a40d4..3eeaa47dc 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -955,9 +955,10 @@ mod_options(Host) -> mod_doc() -> #{desc => - [?T("https://matrix.org/[Matrix] gateway. " - "Erlang/OTP 25 or higher is required to use this module.")], - note => "added in 24.02", + [?T("https://matrix.org/[Matrix] gateway. "), + ?T("Erlang/OTP 25 or higher is required to use this module."), + ?T("This module is available since ejabberd 24.02.")], + note => "improved in 25.03", example => ["listen:", " -", diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 42be73a93..ac87fec68 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1766,7 +1766,7 @@ mod_doc() -> "The default value is an empty string.")}}, {enable_hats, #{value => "true | false", - note => "improved in 25.xx", + note => "improved in 25.03", desc => ?T("Allow extended roles as defined in XEP-0317 Hats. " "Check the _`../../tutorials/muc-hats.md|MUC Hats`_ tutorial. " diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 59c5a0e93..1eadea684 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -196,7 +196,7 @@ get_commands_spec() -> "Each subscriber can have one or more nodes. " "In summary, `affiliations` is like `Type1=JID1;Type2=JID2` " "and `subscribers` is like `JID1=Nick1=Node1A=Node1B=Node1C;JID2=Nick2=Node2`.", - note = "modified in 25.xx", + note = "modified in 25.03", module = ?MODULE, function = create_room_with_opts, args_desc = ["Room name", "MUC service", "Server host", "List of options"], args_example = ["room1", "conference.example.com", "localhost", diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 81254ec91..64e7e0a2d 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 317, '0.2.0', '25.xx', "complete", ""}). +-protocol({xep, 317, '0.2.0', '21.12', "complete", "0.2.0 since 25.03"}). -protocol({xep, 410, '1.1.0', '18.12', "complete", ""}). -behaviour(p1_fsm). From 9c29457ee2db576f9ff484b547455eb5566bf85c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 17:19:16 +0100 Subject: [PATCH 1059/1302] mod_adhoc_api: Fix warning when running "make translations" --- src/mod_adhoc_api.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_adhoc_api.erl b/src/mod_adhoc_api.erl index d8a16b504..2e478333c 100644 --- a/src/mod_adhoc_api.erl +++ b/src/mod_adhoc_api.erl @@ -504,7 +504,7 @@ set_form_api_command(From, Host, CommandNameBin, XData, _Lang) -> var = <<"error">>}] end, FieldsResultWithHeads = - [#xdata_field{type = fixed, label = ?T("")}, + [#xdata_field{type = fixed, label = <<"">>}, #xdata_field{type = fixed, label = ?T("Result")} | FieldsResult2], From 113e5a322e2b59e6fe113485dfa38d74e444e216 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 17:45:50 +0100 Subject: [PATCH 1060/1302] New Tamil translation (thanks to TamilNeram) --- priv/msgs/ta.msg | 625 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 625 insertions(+) create mode 100644 priv/msgs/ta.msg diff --git a/priv/msgs/ta.msg b/priv/msgs/ta.msg new file mode 100644 index 000000000..fceafe5c0 --- /dev/null +++ b/priv/msgs/ta.msg @@ -0,0 +1,625 @@ +%% Generated automatically +%% DO NOT EDIT: run `make translations` instead +%% To improve translations please read: +%% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ + +{" (Add * to the end of field to match substring)"," (அடி மூலக்கூறுடன் பொருந்தக்கூடிய புலத்தின் முடிவில் * சேர்க்கவும்)"}. +{" has set the subject to: "," இதற்கு பொருள் அமைத்துள்ளது: "}. +{"# participants","# பங்கேற்பாளர்கள்"}. +{"A description of the node","முனையின் விளக்கம்"}. +{"A friendly name for the node","முனைக்கு ஒரு நட்பு பெயர்"}. +{"A password is required to enter this room","இந்த அறைக்குள் நுழைய கடவுச்சொல் தேவை"}. +{"A Web Page","ஒரு வலைப்பக்கம்"}. +{"Accept","ஏற்றுக்கொள்"}. +{"Access denied by service policy","பணி கொள்கையால் மறுக்கப்பட்டது"}. +{"Access model","அணுகல் மாதிரி"}. +{"Account doesn't exist","கணக்கு இல்லை"}. +{"Action on user","பயனரின் செயல்"}. +{"Add a hat to a user","ஒரு பயனருக்கு ஒரு தொப்பியைச் சேர்க்கவும்"}. +{"Add User","பயனரைச் சேர்க்கவும்"}. +{"Administration of ","நிர்வாகம் "}. +{"Administration","நிர்வாகம்"}. +{"Administrator privileges required","நிர்வாகி சலுகைகள் தேவை"}. +{"All activity","அனைத்து செயல்பாடுகளும்"}. +{"All Users","அனைத்து பயனர்களும்"}. +{"Allow subscription","சந்தாவை அனுமதிக்கவும்"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","இந்த பப்சப் முனைக்கு குழுசேர இந்த சாபர் ஐடியை அனுமதிக்கவா?"}. +{"Allow this person to register with the room?","இந்த நபரை அறையில் பதிவு செய்ய அனுமதிக்கவா?"}. +{"Allow users to change the subject","இந்த விசயத்தை மாற்ற பயனர்களை அனுமதிக்கவும்"}. +{"Allow users to query other users","பயனர்களை மற்ற பயனர்களை வினவ அனுமதிக்கவும்"}. +{"Allow users to send invites","அழைப்புகளை அனுப்ப பயனர்களை அனுமதிக்கவும்"}. +{"Allow users to send private messages","தனிப்பட்ட செய்திகளை அனுப்ப பயனர்களை அனுமதிக்கவும்"}. +{"Allow visitors to change nickname","பார்வையாளர்களை புனைப்பெயரை மாற்ற அனுமதிக்கவும்"}. +{"Allow visitors to send private messages to","தனிப்பட்ட செய்திகளை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"Allow visitors to send status text in presence updates","முன்னிலையில் புதுப்பிப்புகளில் நிலை உரையை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"Allow visitors to send voice requests","குரல் கோரிக்கைகளை அனுப்ப பார்வையாளர்களை அனுமதிக்கவும்"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","அறை உறுப்பினர்களை வரையறுக்கும் தொடர்புடைய எல்.டி.ஏ.பி குழு; இது ஒரு குழுவின் செயல்படுத்தல்-குறிப்பிட்ட அல்லது வரிசைப்படுத்தல்-குறிப்பிட்ட வரையறையின்படி எல்.டி.ஏ.பி புகழ்பெற்ற பெயராக இருக்க வேண்டும்."}. +{"Announcements","அறிவிப்புகள்"}. +{"Answer associated with a picture","ஒரு படத்துடன் தொடர்புடைய பதில்"}. +{"Answer associated with a video","வீடியோவுடன் தொடர்புடைய பதில்"}. +{"Answer associated with speech","பேச்சுடன் தொடர்புடைய பதில்"}. +{"Answer to a question","ஒரு கேள்விக்கு பதில்"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","குறிப்பிட்ட ரோச்டர் குழு (கள்) இல் உள்ள எவரும் உருப்படிகளை குழுசேரலாம் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone may associate leaf nodes with the collection","எவரும் இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Anyone may publish","எவரும் வெளியிடலாம்"}. +{"Anyone may subscribe and retrieve items","எவரும் பொருட்களை குழுசேர் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","அல்லது இரண்டின் இருப்பு சந்தா உள்ள எவரும் உருப்படிகளை குழுசேர் மற்றும் மீட்டெடுக்கலாம்"}. +{"Anyone with Voice","குரல் உள்ள எவரும்"}. +{"Anyone","யாரும்"}. +{"April","ப-சித்திரை"}. +{"Attribute 'channel' is required for this request","இந்த கோரிக்கைக்கு 'சேனல்' என்ற பண்புக்கூறு தேவை"}. +{"Attribute 'id' is mandatory for MIX messages","கலவை செய்திகளுக்கு 'ஐடி' கட்டாயமாகும்"}. +{"Attribute 'jid' is not allowed here","'சிட்' என்ற பண்புக்கூறு இங்கே அனுமதிக்கப்படவில்லை"}. +{"Attribute 'node' is not allowed here","'முனை' என்ற பண்புக்கூறு இங்கே அனுமதிக்கப்படவில்லை"}. +{"Attribute 'to' of stanza that triggered challenge","சவாலைத் தூண்டும் சரணத்தின் '' 'என்ற பண்புக்கூறு"}. +{"August","ஆ-ஆவணி"}. +{"Automatic node creation is not enabled","தானியங்கி முனை உருவாக்கம் இயக்கப்படவில்லை"}. +{"Backup Management","காப்பு மேலாண்மை"}. +{"Backup of ~p","~p இன் காப்புப்பிரதி"}. +{"Backup to File at ","தாக்கல் செய்ய காப்புப்பிரதி "}. +{"Backup","காப்புப்பிரதி"}. +{"Bad format","மோசமான வடிவம்"}. +{"Birthday","பிறந்த நாள்"}. +{"Both the username and the resource are required","பயனர்பெயர் மற்றும் சான்று இரண்டும் தேவை"}. +{"Bytestream already activated","பைட்டெச்ட்ரீம் ஏற்கனவே செயல்படுத்தப்பட்டது"}. +{"Cannot remove active list","செயலில் உள்ள பட்டியலை அகற்ற முடியாது"}. +{"Cannot remove default list","இயல்புநிலை பட்டியலை அகற்ற முடியாது"}. +{"CAPTCHA web page","கேப்ட்சா வலைப்பக்கம்"}. +{"Challenge ID","அறைகூவல் ஐடி"}. +{"Change Password","கடவுச்சொல்லை மாற்றவும்"}. +{"Change User Password","பயனர் கடவுச்சொல்லை மாற்றவும்"}. +{"Changing password is not allowed","கடவுச்சொல்லை மாற்ற அனுமதிக்கப்படவில்லை"}. +{"Changing role/affiliation is not allowed","பங்கு/இணைப்பை மாற்ற அனுமதிக்கப்படவில்லை"}. +{"Channel already exists","சேனல் ஏற்கனவே உள்ளது"}. +{"Channel does not exist","சேனல் இல்லை"}. +{"Channel JID","சேனல் சிட்"}. +{"Channels","சேனல்கள்"}. +{"Characters not allowed:","எழுத்துக்கள் அனுமதிக்கப்படவில்லை:"}. +{"Chatroom configuration modified","அரட்டை உள்ளமைவு மாற்றப்பட்டது"}. +{"Chatroom is created","அரட்டை அறை உருவாக்கப்பட்டது"}. +{"Chatroom is destroyed","அரட்டை அறை அழிக்கப்படுகிறது"}. +{"Chatroom is started","அரட்டை அறை தொடங்கப்பட்டது"}. +{"Chatroom is stopped","அரட்டை அறை நிறுத்தப்பட்டது"}. +{"Chatrooms","அரட்டை அறைகள்"}. +{"Choose a username and password to register with this server","இந்த சேவையகத்துடன் பதிவு செய்ய பயனர்பெயர் மற்றும் கடவுச்சொல்லைத் தேர்வுசெய்க"}. +{"Choose storage type of tables","அட்டவணைகளின் சேமிப்பக வகை என்பதைத் தேர்வுசெய்க"}. +{"Choose whether to approve this entity's subscription.","இந்த நிறுவனத்தின் சந்தாவை அங்கீகரிக்க வேண்டுமா என்பதைத் தேர்வுசெய்க."}. +{"City","நகரம்"}. +{"Client acknowledged more stanzas than sent by server","சேவையகத்தால் அனுப்பப்பட்டதை விட கிளையன்ட் அதிக சரணத்தை ஒப்புக் கொண்டார்"}. +{"Commands","கட்டளைகள்"}. +{"Conference room does not exist","மாநாட்டு அறை இல்லை"}. +{"Configuration of room ~s","அறையின் உள்ளமைவு ~s"}. +{"Configuration","உள்ளமைவு"}. +{"Contact Addresses (normally, room owner or owners)","தொடர்பு முகவரிகள் (பொதுவாக, அறை உரிமையாளர் அல்லது உரிமையாளர்கள்)"}. +{"Country","நாடு"}. +{"Current Discussion Topic","தற்போதைய கலந்துரையாடல் தலைப்பு"}. +{"Database failure","தரவுத்தள தோல்வி"}. +{"Database Tables Configuration at ","தரவுத்தள அட்டவணைகள் உள்ளமைவு "}. +{"Database","தரவுத்தளம்"}. +{"December","கா-மார்கழி"}. +{"Default users as participants","பங்கேற்பாளர்களாக இயல்புநிலை பயனர்கள்"}. +{"Delete message of the day on all hosts","அனைத்து புரவலர்களிலும் அன்றைய செய்தியை நீக்கு"}. +{"Delete message of the day","அன்றைய செய்தியை நீக்கு"}. +{"Delete User","பயனரை நீக்கு"}. +{"Deliver event notifications","நிகழ்வு அறிவிப்புகளை வழங்கவும்"}. +{"Deliver payloads with event notifications","நிகழ்வு அறிவிப்புகளுடன் பேலோடுகளை வழங்கவும்"}. +{"Disc only copy","வட்டு மட்டுமே நகலெடுக்கவும்"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","உங்கள் கடவுச்சொல்லை யாரிடமும் சொல்லாதீர்கள், எக்ச்எம்பி.பி சேவையகத்தின் நிர்வாகிகள் கூட இல்லை."}. +{"Dump Backup to Text File at ","உரை கோப்பில் காப்புப்பிரதியை டம்ப் செய்யுங்கள் "}. +{"Dump to Text File","உரை கோப்பில் டம்ப் செய்யுங்கள்"}. +{"Duplicated groups are not allowed by RFC6121","நகல் குழுக்கள் RFC6121 ஆல் அனுமதிக்கப்படவில்லை"}. +{"Dynamically specify a replyto of the item publisher","உருப்படி வெளியீட்டாளரின் பதிலை மாறும் வகையில் குறிப்பிடவும்"}. +{"Edit Properties","பண்புகளைத் திருத்து"}. +{"Either approve or decline the voice request.","குரல் கோரிக்கையை அங்கீகரிக்கவும் அல்லது நிராகரிக்கவும்."}. +{"ejabberd HTTP Upload service","EJABBERD HTTP பதிவேற்ற பணி"}. +{"ejabberd MUC module","EJABBERD MUC தொகுதி"}. +{"ejabberd Multicast service","எசாபர்ட் மல்டிகாச்ட் பணி"}. +{"ejabberd Publish-Subscribe module","EJABBERD வெளியீட்டு-சந்தா தொகுதி"}. +{"ejabberd SOCKS5 Bytestreams module","EJABBERD SOCKS5 BYTESTREAMS தொகுதி"}. +{"ejabberd vCard module","EJABBERD VCARD தொகுதி"}. +{"ejabberd Web Admin","எசாபர்ட் வலை நிர்வாகி"}. +{"ejabberd","எசாபர்ட்"}. +{"Email Address","மின்னஞ்சல் முகவரி"}. +{"Email","மின்னஞ்சல்"}. +{"Enable hats","தொப்பிகளை இயக்கவும்"}. +{"Enable logging","பதிவை இயக்கவும்"}. +{"Enable message archiving","செய்தி காப்பகத்தை இயக்கவும்"}. +{"Enabling push without 'node' attribute is not supported","'முனை' பண்புக்கூறு இல்லாமல் உந்துதலை இயக்குவது ஆதரிக்கப்படவில்லை"}. +{"End User Session","இறுதி பயனர் அமர்வு"}. +{"Enter nickname you want to register","நீங்கள் பதிவு செய்ய விரும்பும் புனைப்பெயரை உள்ளிடவும்"}. +{"Enter path to backup file","காப்புப்பிரதி கோப்பிற்கு பாதையை உள்ளிடவும்"}. +{"Enter path to jabberd14 spool dir","Jabberd14 Spool அடைவு க்கு பாதையை உள்ளிடவும்"}. +{"Enter path to jabberd14 spool file","சாபர்ட் 14 ச்பூல் கோப்புக்கு பாதையை உள்ளிடவும்"}. +{"Enter path to text file","உரை கோப்புக்கு பாதையை உள்ளிடவும்"}. +{"Enter the text you see","நீங்கள் பார்க்கும் உரையை உள்ளிடவும்"}. +{"Erlang XMPP Server","எர்லாங் எக்ச்எம்பிபி சேவையகம்"}. +{"Exclude Jabber IDs from CAPTCHA challenge","கேப்ட்சா சேலஞ்சில் இருந்து சாபர் ஐடிகளை விலக்குங்கள்"}. +{"Export all tables as SQL queries to a file:","அனைத்து அட்டவணைகளையும் கவிமொ வினவல்களாக ஒரு கோப்பில் ஏற்றுமதி செய்யுங்கள்:"}. +{"Export data of all users in the server to PIEFXIS files (XEP-0227):","சேவையகத்தில் உள்ள அனைத்து பயனர்களின் தரவை Piefxis கோப்புகளுக்கு (XEP-0227) ஏற்றுமதி செய்யுங்கள்:"}. +{"Export data of users in a host to PIEFXIS files (XEP-0227):","ஓச்டில் பயனர்களின் தரவை Piefxis கோப்புகளுக்கு ஏற்றுமதி செய்யுங்கள் (XEP-0227):"}. +{"External component failure","வெளிப்புற கூறு தோல்வி"}. +{"External component timeout","வெளிப்புற கூறு நேரம் முடிந்தது"}. +{"Failed to activate bytestream","பைட்டெச்ட்ரீமை செயல்படுத்தத் தவறிவிட்டது"}. +{"Failed to extract JID from your voice request approval","உங்கள் குரல் கோரிக்கை ஒப்புதலிலிருந்து சிட் பிரித்தெடுப்பதில் தோல்வி"}. +{"Failed to map delegated namespace to external component","வெளிப்புற கூறுகளுக்கு பிரதிநிதித்துவ பெயர்வெளியை வரைபடமாக்குவதில் தோல்வி"}. +{"Failed to parse HTTP response","HTTP பதிலை அலசத் தவறிவிட்டது"}. +{"Failed to process option '~s'","விருப்பத்தை '~s' செயலாக்குவதில் தோல்வி"}. +{"Family Name","குடும்ப பெயர்"}. +{"FAQ Entry","கேள்விகள் நுழைவு"}. +{"February","தை-மாசி"}. +{"File larger than ~w bytes","~w பைட்டுகளை விட பெரியது"}. +{"Fill in the form to search for any matching XMPP User","பொருந்தக்கூடிய எக்ச்எம்பிபி பயனரைத் தேட படிவத்தை நிரப்பவும்"}. +{"Friday","வெள்ளிக்கிழமை"}. +{"From ~ts","~ts இலிருந்து"}. +{"Full List of Room Admins","அறை நிர்வாகிகளின் முழு பட்டியல்"}. +{"Full List of Room Owners","அறை உரிமையாளர்களின் முழு பட்டியல்"}. +{"Full Name","முழு பெயர்"}. +{"Get List of Online Users","நிகழ்நிலை பயனர்களின் பட்டியலைப் பெறுங்கள்"}. +{"Get List of Registered Users","பதிவுசெய்யப்பட்ட பயனர்களின் பட்டியலைப் பெறுங்கள்"}. +{"Get Number of Online Users","நிகழ்நிலை பயனர்களின் எண்ணிக்கையைப் பெறுங்கள்"}. +{"Get Number of Registered Users","பதிவுசெய்யப்பட்ட பயனர்களின் எண்ணிக்கையைப் பெறுங்கள்"}. +{"Get Pending","நிலுவையில் செல்லுங்கள்"}. +{"Get User Last Login Time","பயனர் கடைசி உள்நுழைவு நேரத்தைப் பெறுங்கள்"}. +{"Get User Statistics","பயனர் புள்ளிவிவரங்களைப் பெறுங்கள்"}. +{"Given Name","கொடுக்கப்பட்ட பெயர்"}. +{"Grant voice to this person?","இந்த நபருக்கு குரல் கொடுக்கவா?"}. +{"has been banned","தடைசெய்யப்பட்டுள்ளது"}. +{"has been kicked because of a system shutdown","கணினி பணிநிறுத்தம் காரணமாக உதைக்கப்பட்டுள்ளது"}. +{"has been kicked because of an affiliation change","இணைப்பு மாற்றம் காரணமாக உதைக்கப்பட்டுள்ளது"}. +{"has been kicked because the room has been changed to members-only","அறை உறுப்பினர்களுக்கு மட்டுமே மாற்றப்பட்டதால் உதைக்கப்பட்டுள்ளது"}. +{"has been kicked","உதைக்கப்பட்டுள்ளது"}. +{"Hash of the vCard-temp avatar of this room","இந்த அறையின் vcard-temp அவதாரத்தின் ஆச்"}. +{"Hat title","தொப்பி தலைப்பு"}. +{"Hat URI","தொப்பி யூரி"}. +{"Hats limit exceeded","தொப்பிகள் வரம்பு மீறியது"}. +{"Host unknown","புரவலன் தெரியவில்லை"}. +{"HTTP File Upload","HTTP கோப்பு பதிவேற்றம்"}. +{"Idle connection","செயலற்ற இணைப்பு"}. +{"If you don't see the CAPTCHA image here, visit the web page.","நீங்கள் இங்கே கேப்ட்சா படத்தைக் காணவில்லை என்றால், வலைப்பக்கத்தைப் பார்வையிடவும்."}. +{"Import Directory","இறக்குமதி அடைவு"}. +{"Import File","கோப்பு இறக்குமதி"}. +{"Import user data from jabberd14 spool file:","சாபர்ட் 14 ச்பூல் கோப்பிலிருந்து பயனர் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import User from File at ","கோப்பிலிருந்து பயனரை இறக்குமதி செய்யுங்கள் "}. +{"Import users data from a PIEFXIS file (XEP-0227):","PIEFXIS கோப்பிலிருந்து (XEP-0227) பயனர்களின் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import users data from jabberd14 spool directory:","சாபர்ட் 14 ச்பூல் கோப்பகத்திலிருந்து பயனர்களின் தரவை இறக்குமதி செய்யுங்கள்:"}. +{"Import Users from Dir at ","அடைவு இலிருந்து பயனர்களை இறக்குமதி செய்யுங்கள் "}. +{"Import Users From jabberd14 Spool Files","சாபர்ட் 14 ச்பூல் கோப்புகளிலிருந்து பயனர்களை இறக்குமதி செய்யுங்கள்"}. +{"Improper domain part of 'from' attribute","'ஃப்ரம்' பண்புக்கூறின் முறையற்ற டொமைன் பகுதி"}. +{"Improper message type","முறையற்ற செய்தி வகை"}. +{"Incorrect CAPTCHA submit","தவறான கேப்ட்சா சமர்ப்பிக்கவும்"}. +{"Incorrect data form","தவறான தரவு வடிவம்"}. +{"Incorrect password","தவறான கடவுச்சொல்"}. +{"Incorrect value of 'action' attribute","'செயல்' பண்புக்கூறின் தவறான மதிப்பு"}. +{"Incorrect value of 'action' in data form","தரவு வடிவத்தில் 'செயல்' இன் தவறான மதிப்பு"}. +{"Incorrect value of 'path' in data form","தரவு வடிவத்தில் 'பாதை' இன் தவறான மதிப்பு"}. +{"Installed Modules:","நிறுவப்பட்ட தொகுதிகள்:"}. +{"Install","நிறுவவும்"}. +{"Insufficient privilege","போதிய சலுகை"}. +{"Internal server error","உள் சேவையக பிழை"}. +{"Invalid 'from' attribute in forwarded message","அனுப்பப்பட்ட செய்தியில் 'இலிருந்து' பண்புக்கூறு"}. +{"Invalid node name","தவறான முனை பெயர்"}. +{"Invalid 'previd' value","தவறான 'முந்தைய' மதிப்பு"}. +{"Invitations are not allowed in this conference","இந்த மாநாட்டில் அழைப்புகள் அனுமதிக்கப்படவில்லை"}. +{"IP addresses","ஐபி முகவரிகள்"}. +{"is now known as","இப்போது அழைக்கப்படுகிறது"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","பிழை செய்திகளை அறைக்கு அனுப்ப அனுமதிக்கப்படவில்லை. பங்கேற்பாளர் (~s) ஒரு பிழை செய்தியை (~s) அனுப்பி அறையிலிருந்து உதைத்துள்ளார்"}. +{"It is not allowed to send private messages of type \"groupchat\"","\"குரூப்சாட்\" என்ற வகை தனிப்பட்ட செய்திகளை அனுப்ப அனுமதிக்கப்படவில்லை"}. +{"It is not allowed to send private messages to the conference","தனிப்பட்ட செய்திகளை மாநாட்டிற்கு அனுப்ப அனுமதிக்கப்படவில்லை"}. +{"Jabber ID","சாபர் ஐடி"}. +{"January","மா-தை"}. +{"JID normalization denied by service policy","பணி கொள்கையால் மறுக்கப்பட்ட சிட் இயல்பாக்கம்"}. +{"JID normalization failed","சிட் இயல்பாக்கம் தோல்வியடைந்தது"}. +{"Joined MIX channels of ~ts","~ts இன் கலவை சேனல்களில் சேர்ந்தது"}. +{"Joined MIX channels:","இணைந்த கலவை சேனல்கள்:"}. +{"joins the room","அறையில் இணைகிறது"}. +{"July","ஆ-ஆடி"}. +{"June","வை-ஆனி"}. +{"Just created","இப்போது உருவாக்கப்பட்டது"}. +{"Last Activity","கடைசி செயல்பாடு"}. +{"Last login","கடைசி உள்நுழைவு"}. +{"Last message","கடைசி செய்தி"}. +{"Last month","கடந்த மாதம்"}. +{"Last year","கடந்த ஆண்டு"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","உரையின் SHA-256 ஆசின் குறைந்த குறிப்பிடத்தக்க பிட்கள் எக்சாடெசிமல் சிட்டை சமமாக இருக்க வேண்டும்"}. +{"leaves the room","அறையை விட்டு வெளியேறுகிறது"}. +{"List of users with hats","தொப்பிகளைக் கொண்ட பயனர்களின் பட்டியல்"}. +{"List users with hats","தொப்பிகளுடன் பயனர்களை பட்டியலிடுங்கள்"}. +{"Logged Out","வெளியேறியது"}. +{"Logging","பதிவு"}. +{"Make participants list public","பங்கேற்பாளர்கள் பட்டியலிடுங்கள்"}. +{"Make room CAPTCHA protected","அறை கேப்ட்சா பாதுகாக்கவும்"}. +{"Make room members-only","அறை உறுப்பினர்களை மட்டும் செய்யுங்கள்"}. +{"Make room moderated","அறை மிதமானதாக்குங்கள்"}. +{"Make room password protected","அறை கடவுச்சொல்லைப் பாதுகாக்கவும்"}. +{"Make room persistent","அறையை தொடர்ந்து செய்யுங்கள்"}. +{"Make room public searchable","அறையை பொதுவில் தேடலாம்"}. +{"Malformed username","தவறான பயனர்பெயர்"}. +{"MAM preference modification denied by service policy","பணி கொள்கையால் மறுக்கப்பட்ட மாம் விருப்பத்தேர்வு மாற்றம்"}. +{"March","மா-பங்குனி"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","அதிகபட்சம் # தொடர்ச்சியாக இருக்க வேண்டும், அல்லது அதிகபட்சமாக விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பும் இல்லாமல் `அதிகபட்சம்"}. +{"Max payload size in bytes","பைட்டுகளில் அதிகபட்ச பேலோட் அளவு"}. +{"Maximum file size","அதிகபட்ச கோப்பு அளவு"}. +{"Maximum Number of History Messages Returned by Room","அறையால் திரும்பிய அதிகபட்ச வரலாற்று செய்திகளின் எண்ணிக்கை"}. +{"Maximum number of items to persist","தொடர்ந்து அதிகபட்ச உருப்படிகளின் எண்ணிக்கை"}. +{"Maximum Number of Occupants","அதிகபட்ச குடியிருப்பாளர்களின் எண்ணிக்கை"}. +{"May","சி-வைகாசி"}. +{"Membership is required to enter this room","இந்த அறைக்குள் நுழைய உறுப்பினர் தேவை"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","உங்கள் கடவுச்சொல்லை மனப்பாடம் செய்யுங்கள், அல்லது பாதுகாப்பான இடத்தில் வைக்கப்பட்டுள்ள ஒரு காகிதத்தில் எழுதுங்கள். உங்கள் கடவுச்சொல்லை மறந்துவிட்டால் அதை மீட்டெடுக்க எக்ச்எம்பிபியில் தானியங்கு வழி இல்லை."}. +{"Mere Availability in XMPP (No Show Value)","எக்ச்எம்பிபியில் வெறும் கிடைக்கும் (காட்சி மதிப்பு இல்லை)"}. +{"Message body","செய்தி உடல்"}. +{"Message not found in forwarded payload","அனுப்பப்பட்ட பேலோடில் செய்தி காணப்படவில்லை"}. +{"Messages from strangers are rejected","அந்நியர்களிடமிருந்து செய்திகள் நிராகரிக்கப்படுகின்றன"}. +{"Messages of type headline","வகை தலைப்பின் செய்திகள்"}. +{"Messages of type normal","வகை இயல்பான செய்திகள்"}. +{"Middle Name","நடுத்தர பெயர்"}. +{"Minimum interval between voice requests (in seconds)","குரல் கோரிக்கைகளுக்கு இடையில் குறைந்தபட்ச இடைவெளி (நொடிகளில்)"}. +{"Moderator privileges required","மதிப்பீட்டாளர் சலுகைகள் தேவை"}. +{"Moderators Only","மதிப்பீட்டாளர்கள் மட்டுமே"}. +{"Moderator","மதிப்பீட்டாளர்"}. +{"Module failed to handle the query","தொகுதி வினவலைக் கையாளத் தவறிவிட்டது"}. +{"Monday","திங்கள்"}. +{"Multicast","மல்டிகாச்ட்"}. +{"Multiple elements are not allowed by RFC6121","பல <உருப்படி/> கூறுகள் RFC6121 ஆல் அனுமதிக்கப்படாது"}. +{"Multi-User Chat","பல பயனர் அரட்டை"}. +{"Name","பெயர்"}. +{"Natural Language for Room Discussions","அறை விவாதங்களுக்கு இயற்கை மொழி"}. +{"Natural-Language Room Name","இயற்கை மொழி அறை பெயர்"}. +{"Neither 'jid' nor 'nick' attribute found","'சிட்' அல்லது 'நிக்' பண்புக்கூறு இல்லை"}. +{"Neither 'role' nor 'affiliation' attribute found","'பங்கு' அல்லது 'இணைப்பு' பண்புக்கூறு இல்லை"}. +{"Never","ஒருபோதும்"}. +{"New Password:","புதிய கடவுச்சொல்:"}. +{"Nickname can't be empty","புனைப்பெயர் காலியாக இருக்க முடியாது"}. +{"Nickname Registration at ","இல் புனைப்பெயர் பதிவு "}. +{"Nickname ~s does not exist in the room","அறையில் ~s புனைப்பெயர் இல்லை"}. +{"Nickname","புனைப்பெயர்"}. +{"No address elements found","முகவரி கூறுகள் எதுவும் கிடைக்கவில்லை"}. +{"No addresses element found","முகவரிகள் உறுப்பு இல்லை"}. +{"No 'affiliation' attribute found","'இணைப்பு' பண்புக்கூறு இல்லை"}. +{"No available resource found","கிடைக்கக்கூடிய வளங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No body provided for announce message","அறிவிப்பு செய்திக்கு எந்த உடலும் வழங்கப்படவில்லை"}. +{"No child elements found","குழந்தை கூறுகள் எதுவும் கிடைக்கவில்லை"}. +{"No data form found","தரவு படிவம் எதுவும் கிடைக்கவில்லை"}. +{"No Data","தரவு இல்லை"}. +{"No features available","நற்பொருத்தங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No element found","இல்லை <முன்னோக்கி/> உறுப்பு காணப்பட்டது"}. +{"No hook has processed this command","இந்த கட்டளையை எந்த ஊக்கும் செயலாக்கவில்லை"}. +{"No info about last activity found","கடைசி செயல்பாட்டைப் பற்றிய எந்த தகவலும் கிடைக்கவில்லை"}. +{"No 'item' element found","'உருப்படி' உறுப்பு இல்லை"}. +{"No items found in this query","இந்த வினவலில் எந்த உருப்படிகளும் காணப்படவில்லை"}. +{"No limit","வரம்பு இல்லை"}. +{"No module is handling this query","இந்த வினவலை எந்த தொகுதியும் கையாளவில்லை"}. +{"No node specified","எந்த முனையும் குறிப்பிடப்படவில்லை"}. +{"No 'password' found in data form","தரவு வடிவத்தில் 'கடவுச்சொல்' காணப்படவில்லை"}. +{"No 'password' found in this query","இந்த வினவலில் 'கடவுச்சொல்' இல்லை"}. +{"No 'path' found in data form","தரவு வடிவத்தில் 'பாதை' இல்லை"}. +{"No pending subscriptions found","நிலுவையில் உள்ள சந்தாக்கள் எதுவும் கிடைக்கவில்லை"}. +{"No privacy list with this name found","இந்த பெயருடன் தனியுரிமை பட்டியல் எதுவும் இல்லை"}. +{"No private data found in this query","இந்த வினவலில் தனிப்பட்ட தரவு எதுவும் காணப்படவில்லை"}. +{"No running node found","இயங்கும் முனை எதுவும் கிடைக்கவில்லை"}. +{"No services available","சேவைகள் எதுவும் கிடைக்கவில்லை"}. +{"No statistics found for this item","இந்த உருப்படிக்கு புள்ளிவிவரங்கள் எதுவும் கிடைக்கவில்லை"}. +{"No 'to' attribute found in the invitation","அழைப்பில் காணப்படும் 'to' பண்புக்கூறு"}. +{"Nobody","யாரும்"}. +{"Node already exists","முனை ஏற்கனவே உள்ளது"}. +{"Node ID","முனை ஐடி"}. +{"Node index not found","முனை குறியீடு கிடைக்கவில்லை"}. +{"Node not found","முனை கிடைக்கவில்லை"}. +{"Node ~p","முனை ~p"}. +{"Nodeprep has failed","நோடெப்ரெப் தோல்வியுற்றது"}. +{"Nodes","முனைகள்"}. +{"Node","கணு"}. +{"None","எதுவுமில்லை"}. +{"Not allowed","அனுமதிக்கப்படவில்லை"}. +{"Not Found","கண்டுபிடிக்கப்படவில்லை"}. +{"Not subscribed","குழுசேரவில்லை"}. +{"Notify subscribers when items are removed from the node","முனையிலிருந்து உருப்படிகள் அகற்றப்படும்போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"Notify subscribers when the node configuration changes","முனை உள்ளமைவு மாறும்போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"Notify subscribers when the node is deleted","முனை நீக்கப்படும் போது சந்தாதாரர்களுக்கு அறிவிக்கவும்"}. +{"November","ஐ-கார்த்திகை"}. +{"Number of answers required","தேவையான பதில்களின் எண்ணிக்கை"}. +{"Number of occupants","குடியிருப்பாளர்களின் எண்ணிக்கை"}. +{"Number of Offline Messages","இணைப்பில்லாத செய்திகளின் எண்ணிக்கை"}. +{"Number of online users","நிகழ்நிலை பயனர்களின் எண்ணிக்கை"}. +{"Number of registered users","பதிவுசெய்யப்பட்ட பயனர்களின் எண்ணிக்கை"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","உருப்படிகளை தானாக தூய்மைப்படுத்துவதற்கான விநாடிகளின் எண்ணிக்கை, அல்லது அதிகபட்சம் விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பையும் `அதிகபட்சம்`"}. +{"Occupants are allowed to invite others","குடியிருப்பாளர்கள் மற்றவர்களை அழைக்க அனுமதிக்கப்படுகிறார்கள்"}. +{"Occupants are allowed to query others","குடியிருப்பாளர்கள் மற்றவர்களை வினவ அனுமதிக்கப்படுகிறார்கள்"}. +{"Occupants May Change the Subject","குடியிருப்பாளர்கள் இந்த விசயத்தை மாற்றலாம்"}. +{"October","பு-ஐப்பசி"}. +{"OK","சரி"}. +{"Old Password:","பழைய கடவுச்சொல்:"}. +{"Online Users","நிகழ்நிலை பயனர்கள்"}. +{"Online","ஆன்லைனில்"}. +{"Only collection node owners may associate leaf nodes with the collection","சேகரிப்பு முனை உரிமையாளர்கள் மட்டுமே இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Only deliver notifications to available users","கிடைக்கக்கூடிய பயனர்களுக்கு மட்டுமே அறிவிப்புகளை வழங்கவும்"}. +{"Only or tags are allowed"," அல்லது <முடக்கு/> குறிச்சொற்கள் மட்டுமே அனுமதிக்கப்படுகின்றன"}. +{"Only element is allowed in this query","இந்த வினவலில் <பட்டியல்/> உறுப்பு மட்டுமே அனுமதிக்கப்படுகிறது"}. +{"Only members may query archives of this room","உறுப்பினர்கள் மட்டுமே இந்த அறையின் காப்பகங்களை வினவலாம்"}. +{"Only moderators and participants are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மற்றும் பங்கேற்பாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators are allowed to retract messages","மதிப்பீட்டாளர்கள் மட்டுமே செய்திகளைத் திரும்பப் பெற அனுமதிக்கப்படுகிறார்கள்"}. +{"Only moderators can approve voice requests","மதிப்பீட்டாளர்கள் மட்டுமே குரல் கோரிக்கைகளை அங்கீகரிக்க முடியும்"}. +{"Only occupants are allowed to send messages to the conference","குடியிருப்பாளர்கள் மட்டுமே மாநாட்டிற்கு செய்திகளை அனுப்ப அனுமதிக்கப்படுகிறார்கள்"}. +{"Only occupants are allowed to send queries to the conference","மாநாட்டிற்கு வினவல்களை அனுப்ப குடியிருப்பாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only publishers may publish","வெளியீட்டாளர்கள் மட்டுமே வெளியிடலாம்"}. +{"Only service administrators are allowed to send service messages","பணி செய்திகளை அனுப்ப பணி நிர்வாகிகள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. +{"Only those on a whitelist may associate leaf nodes with the collection","அனுமதிப்பட்டியலில் இருப்பவர்கள் மட்டுமே இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Only those on a whitelist may subscribe and retrieve items","அனுமதிப்பட்டியலில் இருப்பவர்கள் மட்டுமே உருப்படிகளை குழுசேரலாம் மற்றும் மீட்டெடுக்கலாம்"}. +{"Organization Name","அமைப்பு பெயர்"}. +{"Organization Unit","அமைப்பு பிரிவு"}. +{"Other Modules Available:","பிற தொகுதிகள் கிடைக்கின்றன:"}. +{"Outgoing s2s Connections","வெளிச்செல்லும் எச் 2 எச் இணைப்புகள்"}. +{"Owner privileges required","உரிமையாளர் சலுகைகள் தேவை"}. +{"Packet relay is denied by service policy","பணி கொள்கையால் பாக்கெட் ரிலே மறுக்கப்படுகிறது"}. +{"Participant ID","பங்கேற்பாளர் ஐடி"}. +{"Participant","பங்கேற்பாளர்"}. +{"Password Verification","கடவுச்சொல் சரிபார்ப்பு"}. +{"Password Verification:","கடவுச்சொல் சரிபார்ப்பு:"}. +{"Password","கடவுச்சொல்"}. +{"Password:","கடவுச்சொல்:"}. +{"Path to Dir","அடைவு க்கு பாதை"}. +{"Path to File","தாக்கல் செய்வதற்கான பாதை"}. +{"Payload semantic type information","பேலோட் சொற்பொருள் வகை செய்தி"}. +{"Period: ","காலம்: "}. +{"Persist items to storage","சேமிப்பகத்திற்கு உருப்படிகளைத் தொடருங்கள்"}. +{"Persistent","விடாமுயற்சி"}. +{"Ping query is incorrect","பிங் வினவல் தவறானது"}. +{"Ping","பிங்"}. +{"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","இந்த விருப்பங்கள் பில்டின் மென்சியா தரவுத்தளத்தை மட்டுமே காப்புப் பிரதி எடுக்கும் என்பதை நினைவில் கொள்க. நீங்கள் ODBC தொகுதியைப் பயன்படுத்துகிறீர்கள் என்றால், உங்கள் கவிமொ தரவுத்தளத்தையும் தனித்தனியாக காப்புப் பிரதி எடுக்க வேண்டும்."}. +{"Please, wait for a while before sending new voice request","தயவுசெய்து, புதிய குரல் கோரிக்கையை அனுப்புவதற்கு முன் சிறிது நேரம் காத்திருங்கள்"}. +{"Pong","பாங்"}. +{"Possessing 'ask' attribute is not allowed by RFC6121","'கேளுங்கள்' பண்புக்கூறு வைத்திருப்பது RFC6121 ஆல் அனுமதிக்கப்படவில்லை"}. +{"Present real Jabber IDs to","உண்மையான சாபர் ஐடிகளை வழங்கவும்"}. +{"Previous session not found","முந்தைய அமர்வு காணப்படவில்லை"}. +{"Previous session PID has been killed","முந்தைய அமர்வு பிஐடி கொல்லப்பட்டுள்ளது"}. +{"Previous session PID has exited","முந்தைய அமர்வு பிஐடி வெளியேறியது"}. +{"Previous session PID is dead","முந்தைய அமர்வு பிஐடி இறந்துவிட்டது"}. +{"Previous session timed out","முந்தைய அமர்வு நேரம் முடிந்தது"}. +{"private, ","தனிப்பட்ட, "}. +{"Public","பொது"}. +{"Publish model","மாதிரி வெளியிடு"}. +{"Publish-Subscribe","வெளியீட்டு-சந்தா"}. +{"PubSub subscriber request","பப்சப் சந்தாதாரர் கோரிக்கை"}. +{"Purge all items when the relevant publisher goes offline","தொடர்புடைய வெளியீட்டாளர் ஆஃப்லைனில் செல்லும்போது எல்லா பொருட்களையும் தூய்மைப்படுத்துங்கள்"}. +{"Push record not found","புச் பதிவு கிடைக்கவில்லை"}. +{"Queries to the conference members are not allowed in this room","இந்த அறையில் மாநாட்டு உறுப்பினர்களுக்கு வினவல்கள் அனுமதிக்கப்படவில்லை"}. +{"Query to another users is forbidden","மற்றொரு பயனர்களுக்கு வினவல் தடைசெய்யப்பட்டுள்ளது"}. +{"RAM and disc copy","ராம் மற்றும் வட்டு நகல்"}. +{"RAM copy","ரேம் நகல்"}. +{"Really delete message of the day?","அன்றைய செய்தியை உண்மையில் நீக்கவா?"}. +{"Receive notification from all descendent nodes","அனைத்து வழித்தோன்றல்களிலிருந்தும் அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification from direct child nodes only","நேரடி குழந்தை முனைகளிலிருந்து மட்டுமே அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification of new items only","புதிய பொருட்களின் அறிவிப்பைப் பெறுங்கள்"}. +{"Receive notification of new nodes only","புதிய முனைகளின் அறிவிப்பைப் பெறுங்கள்"}. +{"Recipient is not in the conference room","பெறுநர் மாநாட்டு அறையில் இல்லை"}. +{"Register an XMPP account","எக்ச்எம்பி.பி கணக்கை பதிவு செய்யுங்கள்"}. +{"Register","பதிவு செய்யுங்கள்"}. +{"Remote copy","தொலை நகல்"}. +{"Remove a hat from a user","ஒரு பயனரிடமிருந்து ஒரு தொப்பியை அகற்றவும்"}. +{"Remove User","பயனரை அகற்று"}. +{"Replaced by new connection","புதிய இணைப்பு மூலம் மாற்றப்பட்டது"}. +{"Request has timed out","கோரிக்கை நேரம் முடிந்துவிட்டது"}. +{"Request is ignored","கோரிக்கை புறக்கணிக்கப்படுகிறது"}. +{"Requested role","கோரப்பட்ட பங்கு"}. +{"Resources","வளங்கள்"}. +{"Restart Service","சேவையை மறுதொடக்கம் செய்யுங்கள்"}. +{"Restore Backup from File at ","கோப்பிலிருந்து காப்புப்பிரதியை மீட்டெடுக்கவும் "}. +{"Restore binary backup after next ejabberd restart (requires less memory):","அடுத்த EJABBERD மறுதொடக்கத்திற்குப் பிறகு பைனரி காப்புப்பிரதியை மீட்டமைக்கவும் (குறைவான நினைவகம் தேவை):"}. +{"Restore binary backup immediately:","பைனரி காப்புப்பிரதியை உடனடியாக மீட்டமைக்கவும்:"}. +{"Restore plain text backup immediately:","எளிய உரை காப்புப்பிரதியை உடனடியாக மீட்டெடுக்கவும்:"}. +{"Restore","மீட்டமை"}. +{"Roles and Affiliations that May Retrieve Member List","உறுப்பினர் பட்டியலை மீட்டெடுக்கக்கூடிய பாத்திரங்கள் மற்றும் இணைப்புகள்"}. +{"Roles for which Presence is Broadcasted","இருப்பு ஒளிபரப்பப்படும் பாத்திரங்கள்"}. +{"Roles that May Send Private Messages","தனிப்பட்ட செய்திகளை அனுப்பக்கூடிய பாத்திரங்கள்"}. +{"Room Configuration","அறை உள்ளமைவு"}. +{"Room creation is denied by service policy","பணி கொள்கையால் அறை உருவாக்கம் மறுக்கப்படுகிறது"}. +{"Room description","அறை விளக்கம்"}. +{"Room Occupants","அறை குடியிருப்பாளர்கள்"}. +{"Room terminates","அறை முடிவடைகிறது"}. +{"Room title","அறை தலைப்பு"}. +{"Roster groups allowed to subscribe","பட்டியல் குழுக்கள் குழுசேர அனுமதிக்கப்பட்டன"}. +{"Roster size","பட்டியல் அளவு"}. +{"Running Nodes","இயங்கும் முனைகள்"}. +{"~s invites you to the room ~s","~s எச் உங்களை அறைக்கு அழைக்கிறது ~s கள்"}. +{"Saturday","காரிக்கிழமை"}. +{"Search from the date","தேதியிலிருந்து தேடுங்கள்"}. +{"Search Results for ","தேடல் முடிவுகள் "}. +{"Search the text","உரையைத் தேடுங்கள்"}. +{"Search until the date","தேதி வரை தேடுங்கள்"}. +{"Search users in ","பயனர்களைத் தேடுங்கள் "}. +{"Send announcement to all online users on all hosts","அனைத்து ஓச்ட்களிலும் அனைத்து நிகழ்நிலை பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all online users","அனைத்து நிகழ்நிலை பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all users on all hosts","அனைத்து ஓச்ட்களிலும் உள்ள அனைத்து பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"Send announcement to all users","அனைத்து பயனர்களுக்கும் அறிவிப்பை அனுப்பவும்"}. +{"September","ஆ-புரட்டாசி"}. +{"Server:","சேவையகம்:"}. +{"Service list retrieval timed out","பணி பட்டியல் மீட்டெடுப்பு நேரம் முடிந்தது"}. +{"Session state copying timed out","அமர்வு நிலை நகலெடுக்கும் நேரம் முடிந்தது"}. +{"Set message of the day and send to online users","அன்றைய செய்தியை அமைத்து நிகழ்நிலை பயனர்களுக்கு அனுப்பவும்"}. +{"Set message of the day on all hosts and send to online users","அனைத்து ஓச்ட்களிலும் அன்றைய செய்தியை அமைத்து நிகழ்நிலை பயனர்களுக்கு அனுப்பவும்"}. +{"Shared Roster Groups","பகிரப்பட்ட பட்டியல் குழுக்கள்"}. +{"Show Integral Table","ஒருங்கிணைந்த அட்டவணையைக் காட்டு"}. +{"Show Occupants Join/Leave","காட்டு குடியிருப்பாளர்கள் சேர/விடுகிறார்கள்"}. +{"Show Ordinary Table","சாதாரண அட்டவணையைக் காட்டு"}. +{"Shut Down Service","சேவையை மூடு"}. +{"SOCKS5 Bytestreams","SOCKS5 BYTESTREAMS"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","சில எக்ச்எம்பிபி வாடிக்கையாளர்கள் உங்கள் கடவுச்சொல்லை கணினியில் சேமிக்க முடியும், ஆனால் பாதுகாப்பு காரணங்களுக்காக இதை உங்கள் தனிப்பட்ட கணினியில் மட்டுமே செய்ய வேண்டும்."}. +{"Sources Specs:","ஆதார விவரக்குறிப்புகள்:"}. +{"Specify the access model","அணுகல் மாதிரியைக் குறிப்பிடவும்"}. +{"Specify the event message type","நிகழ்வு செய்தி வகையைக் குறிப்பிடவும்"}. +{"Specify the publisher model","வெளியீட்டாளர் மாதிரியைக் குறிப்பிடவும்"}. +{"Stanza id is not valid","முடிப்பு ஐடி செல்லுபடியாகாது"}. +{"Stanza ID","முடிப்பு"}. +{"Statically specify a replyto of the node owner(s)","முனை உரிமையாளரின் (கள்) ஒரு பதிலை நிலையான முறையில் குறிப்பிடவும்"}. +{"Stopped Nodes","நிறுத்தப்பட்ட முனைகள்"}. +{"Store binary backup:","பைனரி காப்புப்பிரதியை சேமிக்கவும்:"}. +{"Store plain text backup:","எளிய உரை காப்புப்பிரதியை சேமிக்கவும்:"}. +{"Stream management is already enabled","ச்ட்ரீம் மேலாண்மை ஏற்கனவே இயக்கப்பட்டது"}. +{"Stream management is not enabled","ச்ட்ரீம் மேலாண்மை இயக்கப்படவில்லை"}. +{"Subject","பொருள்"}. +{"Submitted","சமர்ப்பிக்கப்பட்டது"}. +{"Subscriber Address","சந்தாதாரர் முகவரி"}. +{"Subscribers may publish","சந்தாதாரர்கள் வெளியிடலாம்"}. +{"Subscription requests must be approved and only subscribers may retrieve items","சந்தா கோரிக்கைகள் அங்கீகரிக்கப்பட வேண்டும் மற்றும் சந்தாதாரர்கள் மட்டுமே உருப்படிகளை மீட்டெடுக்க முடியும்"}. +{"Subscriptions are not allowed","சந்தாக்கள் அனுமதிக்கப்படவில்லை"}. +{"Sunday","ஞாயிற்றுக்கிழமை"}. +{"Text associated with a picture","ஒரு படத்துடன் தொடர்புடைய உரை"}. +{"Text associated with a sound","ஒலி ஒரு ஒலியுடன் தொடர்புடையது"}. +{"Text associated with a video","வீடியோவுடன் தொடர்புடைய உரை"}. +{"Text associated with speech","பேச்சுடன் தொடர்புடைய உரை"}. +{"That nickname is already in use by another occupant","அந்த புனைப்பெயர் ஏற்கனவே மற்றொரு குடியிருப்பாளரால் பயன்பாட்டில் உள்ளது"}. +{"That nickname is registered by another person","அந்த புனைப்பெயர் மற்றொரு நபரால் பதிவு செய்யப்பட்டுள்ளது"}. +{"The account already exists","கணக்கு ஏற்கனவே உள்ளது"}. +{"The account was not unregistered","கணக்கு பதிவு செய்யப்படவில்லை"}. +{"The body text of the last received message","கடைசியாக பெறப்பட்ட செய்தியின் உடல் உரை"}. +{"The CAPTCHA is valid.","கேப்ட்சா செல்லுபடியாகும்."}. +{"The CAPTCHA verification has failed","கேப்ட்சா சரிபார்ப்பு தோல்வியுற்றது"}. +{"The captcha you entered is wrong","நீங்கள் உள்ளிட்ட கேப்ட்சா தவறு"}. +{"The child nodes (leaf or collection) associated with a collection","சேகரிப்புடன் தொடர்புடைய குழந்தை முனைகள் (இலை அல்லது சேகரிப்பு)"}. +{"The collections with which a node is affiliated","ஒரு முனை இணைக்கப்பட்ட தொகுப்புகள்"}. +{"The DateTime at which a leased subscription will end or has ended","குத்தகைக்கு விடப்பட்ட சந்தா முடிவடையும் அல்லது முடிவடைந்த தேதிநேரம்"}. +{"The datetime when the node was created","முனை உருவாக்கப்பட்ட தேதிநேரம்"}. +{"The default language of the node","முனையின் இயல்புநிலை மொழி"}. +{"The feature requested is not supported by the conference","கோரப்பட்ட நற்பொருத்தம் மாநாட்டால் ஆதரிக்கப்படவில்லை"}. +{"The JID of the node creator","முனை படைப்பாளரின் சிட்"}. +{"The JIDs of those to contact with questions","கேள்விகளுடன் தொடர்பு கொள்ளுபவர்களின் குழந்தைகள்"}. +{"The JIDs of those with an affiliation of owner","உரிமையாளரின் இணைப்பு உள்ளவர்களின் குழந்தைகள்"}. +{"The JIDs of those with an affiliation of publisher","வெளியீட்டாளரின் இணைப்பு உள்ளவர்களின் குழந்தைகள்"}. +{"The list of all online users","அனைத்து நிகழ்நிலை பயனர்களின் பட்டியல்"}. +{"The list of all users","அனைத்து பயனர்களின் பட்டியல்"}. +{"The list of JIDs that may associate leaf nodes with a collection","இலை முனைகளை ஒரு தொகுப்போடு தொடர்புபடுத்தக்கூடிய JIDS இன் பட்டியல்"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","ஒரு சேகரிப்புடன் தொடர்புடைய அதிகபட்ச குழந்தை முனைகளின் எண்ணிக்கை, அல்லது அதிகபட்சமாக விதிக்கப்பட்ட சேவையகத்தைத் தவிர வேறு எந்த குறிப்பிட்ட வரம்பும் இல்லாமல் `அதிகபட்சம் '"}. +{"The minimum number of milliseconds between sending any two notification digests","இரண்டு அறிவிப்பு செரிமானங்களையும் அனுப்புவதற்கு இடையில் குறைந்தபட்ச மில்லி விநாடிகளின் எண்ணிக்கை"}. +{"The name of the node","முனையின் பெயர்"}. +{"The node is a collection node","முனை ஒரு சேகரிப்பு முனை"}. +{"The node is a leaf node (default)","முனை ஒரு இலை முனை (இயல்புநிலை)"}. +{"The NodeID of the relevant node","தொடர்புடைய முனையின் நோடிட்"}. +{"The number of pending incoming presence subscription requests","நிலுவையில் உள்ள உள்வரும் இருப்பு சந்தா கோரிக்கைகளின் எண்ணிக்கை"}. +{"The number of subscribers to the node","முனைக்கு சந்தாதாரர்களின் எண்ணிக்கை"}. +{"The number of unread or undelivered messages","படிக்காத அல்லது வழங்கப்படாத செய்திகளின் எண்ணிக்கை"}. +{"The password contains unacceptable characters","கடவுச்சொல்லில் ஏற்றுக்கொள்ள முடியாத எழுத்துக்கள் உள்ளன"}. +{"The password is too weak","கடவுச்சொல் மிகவும் பலவீனமாக உள்ளது"}. +{"the password is","கடவுச்சொல்"}. +{"The password of your XMPP account was successfully changed.","உங்கள் எக்ச்எம்பிபி கணக்கின் கடவுச்சொல் வெற்றிகரமாக மாற்றப்பட்டது."}. +{"The password was not changed","கடவுச்சொல் மாற்றப்படவில்லை"}. +{"The passwords are different","கடவுச்சொற்கள் வேறுபட்டவை"}. +{"The presence states for which an entity wants to receive notifications","ஒரு நிறுவனம் அறிவிப்புகளைப் பெற விரும்பும் இருப்பு நிலைகள்"}. +{"The query is only allowed from local users","வினவல் உள்ளக பயனர்களிடமிருந்து மட்டுமே அனுமதிக்கப்படுகிறது"}. +{"The query must not contain elements","வினவலில் <பொருள்/> கூறுகள் இருக்கக்கூடாது"}. +{"The room subject can be modified by participants","அறை பொருள் பங்கேற்பாளர்களால் மாற்றப்படலாம்"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","முனையில் உள்ள தரவின் சொற்பொருள் வகை செய்தி, பொதுவாக பேலோடின் பெயர்வெளியால் குறிப்பிடப்படுகிறது (ஏதேனும் இருந்தால்)"}. +{"The sender of the last received message","கடைசியாக பெறப்பட்ட செய்தியை அனுப்பு"}. +{"The stanza MUST contain only one element, one element, or one element","ச்டான்சாவில் ஒரே <செயலில்/> உறுப்பு, ஒரு <இயல்புநிலை/> உறுப்பு அல்லது ஒரு <பட்டியல்/> உறுப்பு மட்டுமே இருக்க வேண்டும்"}. +{"The subscription identifier associated with the subscription request","சந்தா கோரிக்கையுடன் தொடர்புடைய சந்தா அடையாளங்காட்டி"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","பொருத்தமான செய்தி உடல் உறுப்பை உருவாக்குவதற்காக பேலோடுகளுக்கு பயன்படுத்தக்கூடிய எக்ச்எச்எல் மாற்றத்தின் முகவரி."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","செல்லுபடியாகும் தரவு படிவங்களை உருவாக்குவதற்காக பேலோட் வடிவத்தில் பயன்படுத்தக்கூடிய ஒரு எக்ச்எச்எல் உருமாற்றத்தின் முகவரி, கிளையன்ட் பொதுவான தரவு படிவங்கள் வழங்குதல் எஞ்சின் பயன்படுத்தி காண்பிக்க முடியும்"}. +{"There was an error changing the password: ","கடவுச்சொல்லை மாற்றுவதில் பிழை ஏற்பட்டது: "}. +{"There was an error creating the account: ","கணக்கை உருவாக்கும் பிழை இருந்தது: "}. +{"There was an error deleting the account: ","கணக்கை நீக்குவதில் பிழை ஏற்பட்டது: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","இது வழக்கு உணர்வற்றது: மாக்பெத் மற்றும் மாக்பெத் தான்."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","இந்த XMPP சேவையகத்தில் ஒரு XMPP கணக்கை பதிவு செய்ய இந்த பக்கம் அனுமதிக்கிறது. உங்கள் சிட் (சாபர் ஐடி) படிவமாக இருக்கும்: பயனர்பெயர்@சேவையகம். புலங்களை சரியாக நிரப்ப வழிமுறைகளை கவனமாகப் படியுங்கள்."}. +{"This page allows to unregister an XMPP account in this XMPP server.","இந்த எக்ச்எம்பி.பி சேவையகத்தில் எக்ச்எம்பி.பி கணக்கை பதிவு செய்ய இந்த பக்கம் அனுமதிக்கிறது."}. +{"This room is not anonymous","இந்த அறை அநாமதேயமானது அல்ல"}. +{"This service can not process the address: ~s","இந்த பணி முகவரியை செயலாக்க முடியாது: ~s"}. +{"Thursday","வியாழக்கிழமை"}. +{"Time delay","நேர நேரந்தவறுகை"}. +{"Timed out waiting for stream resumption","ச்ட்ரீம் மறுதொடக்கத்திற்காக காத்திருக்கும் நேரம்"}. +{"To register, visit ~s","பதிவு செய்ய, ~s பார்வையிடவும்"}. +{"To ~ts","~ts K"}. +{"Token TTL","கிள்ளாக்கு டி.டி.எல்"}. +{"Too many active bytestreams","பல செயலில் உள்ள பைட்டிரீம்கள்"}. +{"Too many CAPTCHA requests","பல கேப்ட்சா கோரிக்கைகள்"}. +{"Too many child elements","பல குழந்தை கூறுகள்"}. +{"Too many elements","பல <உருப்படி/> கூறுகள்"}. +{"Too many elements","பல <பட்டியல்/> கூறுகள்"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","இந்த ஐபி முகவரியிலிருந்து (~p) பல (~s) தோல்வியுற்ற அங்கீகாரங்கள் ~s. முகவரி UTC இல் தடைசெய்யப்படும்"}. +{"Too many receiver fields were specified","அதிகமான ரிசீவர் புலங்கள் குறிப்பிடப்பட்டன"}. +{"Too many unacked stanzas","பல அறியப்படாத சரணங்கள்"}. +{"Too many users in this conference","இந்த மாநாட்டில் அதிகமான பயனர்கள்"}. +{"Traffic rate limit is exceeded","போக்குவரத்து வீத வரம்பு மீறப்பட்டது"}. +{"~ts's MAM Archive","~ts இன் மாம் காப்பகம்"}. +{"~ts's Offline Messages Queue","~ts இன் இணைப்பில்லாத செய்திகள் வரிசை"}. +{"Tuesday","செவ்வாய்க்கிழமை"}. +{"Unable to generate a CAPTCHA","கேப்ட்சாவை உருவாக்க முடியவில்லை"}. +{"Unable to register route on existing local domain","தற்போதுள்ள உள்ளக களத்தில் வழியை பதிவு செய்ய முடியவில்லை"}. +{"Unauthorized","அங்கீகரிக்கப்படாதது"}. +{"Unexpected action","எதிர்பாராத நடவடிக்கை"}. +{"Unexpected error condition: ~p","எதிர்பாராத பிழை நிலை: ~p"}. +{"Uninstall","நிறுவல் நீக்க"}. +{"Unregister an XMPP account","ஒரு எக்ச்எம்பிபி கணக்கை பதிவு செய்யவும்"}. +{"Unregister","பதிவு செய்யப்படாதது"}. +{"Unsupported element","ஆதரிக்கப்படாத <குறியீட்டு/> உறுப்பு"}. +{"Unsupported version","ஆதரிக்கப்படாத பதிப்பு"}. +{"Update message of the day (don't send)","அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. +{"Update message of the day on all hosts (don't send)","எல்லா ஓச்ட்களிலும் அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. +{"Update specs to get modules source, then install desired ones.","தொகுதிகள் மூலத்தைப் பெற விவரக்குறிப்புகளைப் புதுப்பிக்கவும், பின்னர் விரும்பியவற்றை நிறுவவும்."}. +{"Update Specs","விவரக்குறிப்புகளைப் புதுப்பிக்கவும்"}. +{"Updating the vCard is not supported by the vCard storage backend","VCARD ஐப் புதுப்பிப்பது VCARD சேமிப்பக பின்தளத்தில் ஆதரிக்கப்படவில்லை"}. +{"Upgrade","மேம்படுத்தல்"}. +{"URL for Archived Discussion Logs","காப்பகப்படுத்தப்பட்ட கலந்துரையாடல் பதிவுகளுக்கான முகவரி"}. +{"User already exists","பயனர் ஏற்கனவே உள்ளது"}. +{"User (jid)","பயனர் (சிட்)"}. +{"User JID","பயனர் சிட்"}. +{"User Management","பயனர் மேலாண்மை"}. +{"User not allowed to perform an IQ set on another user's vCard.","மற்றொரு பயனரின் VCARD இல் IQ தொகுப்பை செய்ய பயனர் அனுமதிக்கப்படவில்லை."}. +{"User removed","பயனர் அகற்றப்பட்டார்"}. +{"User session not found","பயனர் அமர்வு காணப்படவில்லை"}. +{"User session terminated","பயனர் அமர்வு நிறுத்தப்பட்டது"}. +{"User ~ts","பயனர் ~ts"}. +{"Username:","பயனர்பெயர்:"}. +{"Users are not allowed to register accounts so quickly","பயனர்கள் கணக்குகளை விரைவாக பதிவு செய்ய அனுமதிக்கப்படுவதில்லை"}. +{"Users Last Activity","பயனர்கள் கடைசி செயல்பாடு"}. +{"Users","பயனர்கள்"}. +{"User","பயனர்"}. +{"Value 'get' of 'type' attribute is not allowed","'வகை' பண்புக்கூறின் மதிப்பு 'பெறுதல்' அனுமதிக்கப்படவில்லை"}. +{"Value of '~s' should be boolean","'~s' மதிப்பு பூலியனாக இருக்க வேண்டும்"}. +{"Value of '~s' should be datetime string","'~s' இன் மதிப்பு தேதிநேர சரமாக இருக்க வேண்டும்"}. +{"Value of '~s' should be integer","'~s' இன் மதிப்பு முழு எண்ணாக இருக்க வேண்டும்"}. +{"Value 'set' of 'type' attribute is not allowed","'வகை' பண்புக்கூறின் 'தொகுப்பு' மதிப்பு அனுமதிக்கப்படவில்லை"}. +{"vCard User Search","VCARD பயனர் தேடல்"}. +{"View joined MIX channels","கலப்பு சேனல்களில் சேர்ந்தார்"}. +{"Virtual Hosts","மெய்நிகர் ஓச்ட்கள்"}. +{"Visitors are not allowed to change their nicknames in this room","இந்த அறையில் பார்வையாளர்கள் தங்கள் புனைப்பெயர்களை மாற்ற அனுமதிக்கப்படுவதில்லை"}. +{"Visitors are not allowed to send messages to all occupants","பார்வையாளர்கள் அனைத்து குடியிருப்பாளர்களுக்கும் செய்திகளை அனுப்ப அனுமதிக்கப்படுவதில்லை"}. +{"Visitor","பார்வையாளர்"}. +{"Voice requests are disabled in this conference","இந்த மாநாட்டில் குரல் கோரிக்கைகள் முடக்கப்பட்டுள்ளன"}. +{"Voice request","குரல் கோரிக்கை"}. +{"Wednesday","புதன்கிழமை"}. +{"When a new subscription is processed and whenever a subscriber comes online","புதிய சந்தா செயலாக்கப்படும் போது, சந்தாதாரர் ஆன்லைனில் வரும்போதெல்லாம்"}. +{"When a new subscription is processed","புதிய சந்தா செயலாக்கப்படும் போது"}. +{"When to send the last published item","கடைசியாக வெளியிடப்பட்ட உருப்படியை எப்போது அனுப்ப வேண்டும்"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","ஒரு நிறுவனம் பேலோட் வடிவமைப்பிற்கு கூடுதலாக ஒரு எக்ச்எம்பிபி செய்தி உடலைப் பெற விரும்புகிறதா என்பதை"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","ஒரு நிறுவனம் அறிவிப்புகளின் செரிமானங்களை (திரட்டல்கள்) பெற விரும்புகிறதா அல்லது அனைத்து அறிவிப்புகளையும் தனித்தனியாகப் பெற விரும்புகிறதா"}. +{"Whether an entity wants to receive or disable notifications","ஒரு நிறுவனம் அறிவிப்புகளைப் பெற விரும்புகிறதா அல்லது முடக்க விரும்புகிறதா"}. +{"Whether owners or publisher should receive replies to items","உரிமையாளர்கள் அல்லது வெளியீட்டாளர் உருப்படிகளுக்கான பதில்களைப் பெற வேண்டுமா"}. +{"Whether the node is a leaf (default) or a collection","முனை ஒரு இலை (இயல்புநிலை) அல்லது தொகுப்பாக இருந்தாலும் சரி"}. +{"Whether to allow subscriptions","சந்தாக்களை அனுமதிக்க வேண்டுமா"}. +{"Whether to make all subscriptions temporary, based on subscriber presence","அனைத்து சந்தாக்களையும் தற்காலிகமாக மாற்றலாமா, சந்தாதாரர் இருப்பின் அடிப்படையில்"}. +{"Whether to notify owners about new subscribers and unsubscribes","புதிய சந்தாதாரர்கள் மற்றும் குழுவிலகங்களைப் பற்றி உரிமையாளர்களுக்கு அறிவிக்க வேண்டுமா"}. +{"Who can send private messages","தனிப்பட்ட செய்திகளை யார் அனுப்ப முடியும்"}. +{"Who may associate leaf nodes with a collection","இலை முனைகளை யார் சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. +{"Wrong parameters in the web formulary","வலை சூத்திரத்தில் தவறான அளவுருக்கள்"}. +{"Wrong xmlns","தவறான xmlns"}. +{"XMPP Account Registration","எக்ச்எம்பிபி கணக்கு பதிவு"}. +{"XMPP Domains","எக்ச்எம்பிபி களங்கள்"}. +{"XMPP Show Value of Away","எக்ச்எம்பிபி சோ மதிப்பைக் காட்டுகிறது"}. +{"XMPP Show Value of Chat","எக்ச்எம்பிபி அரட்டையின் மதிப்பைக் காட்டுகிறது"}. +{"XMPP Show Value of DND (Do Not Disturb)","எக்ச்எம்பிபி டி.என்.டி யின் மதிப்பைக் காட்டுகிறது (தொந்தரவு செய்யாதீர்கள்)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP XA இன் மதிப்பைக் காட்டுகிறது (நீட்டிக்கப்பட்டது)"}. +{"XMPP URI of Associated Publish-Subscribe Node","அசோசியேட்டட் பப்ளிச்-சப்ச்கிரிப்ட் முனையின் எக்ச்எம்பிபி யுஆர்ஐ"}. +{"You are being removed from the room because of a system shutdown","கணினி பணிநிறுத்தம் காரணமாக நீங்கள் அறையிலிருந்து அகற்றப்படுகிறீர்கள்"}. +{"You are not allowed to send private messages","தனிப்பட்ட செய்திகளை அனுப்ப உங்களுக்கு இசைவு இல்லை"}. +{"You are not joined to the channel","நீங்கள் சேனலுடன் சேரவில்லை"}. +{"You can later change your password using an XMPP client.","எக்ச்எம்பிபி கிளையண்டைப் பயன்படுத்தி உங்கள் கடவுச்சொல்லை பின்னர் மாற்றலாம்."}. +{"You have been banned from this room","இந்த அறையிலிருந்து நீங்கள் தடை செய்யப்பட்டுள்ளீர்கள்"}. +{"You have joined too many conferences","நீங்கள் பல மாநாடுகளில் சேர்ந்துள்ளீர்கள்"}. +{"You must fill in field \"Nickname\" in the form","நீங்கள் வடிவத்தில் புலம் \"புனைப்பெயரை\" நிரப்ப வேண்டும்"}. +{"You need a client that supports x:data and CAPTCHA to register","எக்ச்: தரவு மற்றும் கேப்ட்சா பதிவு செய்ய உங்களுக்கு ஒரு வாங்கி தேவை"}. +{"You need a client that supports x:data to register the nickname","புனைப்பெயரை பதிவு செய்ய எக்ச்: தரவை ஆதரிக்கும் வாங்கி உங்களுக்குத் தேவை"}. +{"You need an x:data capable client to search","தேட உங்களுக்கு ஒரு ஃச் தேவை: தேட தரவு திறன் கொண்ட வாங்கி"}. +{"Your active privacy list has denied the routing of this stanza.","உங்கள் செயலில் உள்ள தனியுரிமை பட்டியல் இந்த சரணத்தை வழிநடத்துவதை மறுத்துள்ளது."}. +{"Your contact offline message queue is full. The message has been discarded.","உங்கள் தொடர்பு இணைப்பில்லாத செய்தி வரிசை நிரம்பியுள்ளது. செய்தி நிராகரிக்கப்பட்டுள்ளது."}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","உங்கள் சந்தா கோரிக்கை மற்றும்/அல்லது செய்திகளுக்கான செய்திகள் தடுக்கப்பட்டுள்ளன ~s. உங்கள் சந்தா கோரிக்கையைத் தடுக்க, ~s கள் பார்வையிடவும்"}. +{"Your XMPP account was successfully registered.","உங்கள் எக்ச்எம்பிபி கணக்கு வெற்றிகரமாக பதிவு செய்யப்பட்டது."}. +{"Your XMPP account was successfully unregistered.","உங்கள் எக்ச்எம்பிபி கணக்கு வெற்றிகரமாக பதிவு செய்யப்படவில்லை."}. +{"You're not allowed to create nodes","முனைகளை உருவாக்க உங்களுக்கு இசைவு இல்லை"}. From 4967acaec939a2eb121811c5d5377e2a5303be1a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 17:47:29 +0100 Subject: [PATCH 1061/1302] =?UTF-8?q?Update=20Ukrainian=20translation=20(t?= =?UTF-8?q?hanks=20to=20=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93?= =?UTF-8?q?=D0=BE=D1=80=D0=BF=D0=B8=D0=BD=D1=96=D1=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/uk.msg | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 5992bfdf1..48f98b60d 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -108,6 +108,7 @@ {"Dump Backup to Text File at ","Копіювання в текстовий файл на "}. {"Dump to Text File","Копіювання в текстовий файл"}. {"Duplicated groups are not allowed by RFC6121","RFC6121 забороняє дублювати групи"}. +{"Dynamically specify a replyto of the item publisher","Динамічно вказуйте відповідь видавця елемента"}. {"Edit Properties","Змінити параметри"}. {"Either approve or decline the voice request.","Підтвердіть або відхиліть голосовий запит."}. {"ejabberd HTTP Upload service","Служба відвантаження по HTTP для ejabberd"}. @@ -123,6 +124,7 @@ {"Enable hats","Увімкнути капелюхи"}. {"Enable logging","Увімкнути журнал роботи"}. {"Enable message archiving","Ввімкнути архівацію повідомлень"}. +{"Enabling push without 'node' attribute is not supported","Увімкнення push без атрибута node не підтримується"}. {"End User Session","Закінчити Сеанс Користувача"}. {"Enter nickname you want to register","Введіть псевдонім, який ви хочете зареєструвати"}. {"Enter path to backup file","Введіть шлях до резервного файла"}. @@ -166,6 +168,7 @@ {"has been kicked because of an affiliation change","вигнано з кімнати внаслідок зміни рангу"}. {"has been kicked because the room has been changed to members-only","вигнано з кімнати тому, що вона стала тільки для учасників"}. {"has been kicked","вигнали з кімнати"}. +{"Hash of the vCard-temp avatar of this room","Хеш тимчасового аватара vCard цієї кімнати"}. {"Hat title","Назва кімнати"}. {"Hat URI","Назва URI"}. {"Hats limit exceeded","Перевищено швидкість передачі інформації"}. @@ -206,6 +209,8 @@ {"January","січня"}. {"JID normalization denied by service policy","Створювати конференцію заборонено політикою служби"}. {"JID normalization failed","Помилка нормалізації JID"}. +{"Joined MIX channels of ~ts","Приєднався до каналів MIX ~ts"}. +{"Joined MIX channels:","Приєднався до каналів MIX:"}. {"joins the room","увійшов(ла) в кімнату"}. {"July","липня"}. {"June","червня"}. @@ -219,6 +224,7 @@ {"leaves the room","вийшов(ла) з кімнати"}. {"List of users with hats","Список користувачів із капелюхами"}. {"List users with hats","Список користувачів із капелюхами"}. +{"Logged Out","Вийшов із системи"}. {"Logging","Журналювання"}. {"Make participants list public","Зробити список учасників видимим всім"}. {"Make room CAPTCHA protected","Зробити кімнату захищеною капчею"}. @@ -382,12 +388,19 @@ {"RAM copy","Копія оперативної пам'яті"}. {"Really delete message of the day?","Дійсно видалити повідомлення дня?"}. {"Receive notification from all descendent nodes","Отримувати сповіщення від усіх підпорядкованих вузлів"}. +{"Receive notification from direct child nodes only","Отримувати сповіщення лише від прямих дочірніх вузлів"}. +{"Receive notification of new items only","Отримувати сповіщення лише про нові товари"}. +{"Receive notification of new nodes only","Отримувати сповіщення лише про нові вузли"}. {"Recipient is not in the conference room","Адресата немає в конференції"}. {"Register an XMPP account","Зареєструвати XMPP-запис"}. {"Register","Реєстрація"}. {"Remote copy","не зберігаеться локально"}. +{"Remove a hat from a user","Зняти шапку з користувача"}. {"Remove User","Видалити користувача"}. {"Replaced by new connection","Замінено новим з'єднанням"}. +{"Request has timed out","Час очікування запиту минув"}. +{"Request is ignored","Запит ігнорується"}. +{"Requested role","Запитана роль"}. {"Resources","Ресурси"}. {"Restart Service","Перезапустити Сервіс"}. {"Restore Backup from File at ","Відновлення з резервної копії на "}. @@ -395,6 +408,7 @@ {"Restore binary backup immediately:","Відновити з бінарної резервної копії негайно:"}. {"Restore plain text backup immediately:","Відновити з текстової резервної копії негайно:"}. {"Restore","Відновлення з резервної копії"}. +{"Roles and Affiliations that May Retrieve Member List","Ролі та зв’язки, які можуть отримати список учасників"}. {"Roles for which Presence is Broadcasted","Ролі для яких поширюється наявність"}. {"Roles that May Send Private Messages","Ролі, що можуть надсилати приватні повідомлення"}. {"Room Configuration","Конфігурація кімнати"}. @@ -408,7 +422,10 @@ {"Running Nodes","Працюючі вузли"}. {"~s invites you to the room ~s","~s запрошує вас до кімнати ~s"}. {"Saturday","Субота"}. +{"Search from the date","Пошук від дати"}. {"Search Results for ","Результати пошуку в "}. +{"Search the text","Знайдіть текст"}. +{"Search until the date","Пошук до дати"}. {"Search users in ","Пошук користувачів в "}. {"Send announcement to all online users on all hosts","Надіслати сповіщення всім підключеним користувачам на всіх віртуальних серверах"}. {"Send announcement to all online users","Надіслати сповіщення всім підключеним користувачам"}. @@ -416,42 +433,94 @@ {"Send announcement to all users","Надіслати сповіщення всім користувачам"}. {"September","вересня"}. {"Server:","Сервер:"}. +{"Service list retrieval timed out","Час очікування отримання списку послуг минув"}. +{"Session state copying timed out","Час очікування копіювання стану сеансу минув"}. {"Set message of the day and send to online users","Встановити повідомлення дня та надіслати його підключеним користувачам"}. {"Set message of the day on all hosts and send to online users","Встановити повідомлення дня на всіх хостах та надійслати його підключеним користувачам"}. {"Shared Roster Groups","Спільні групи контактів"}. {"Show Integral Table","Показати інтегральну таблицю"}. +{"Show Occupants Join/Leave","Показати мешканцям приєднатися/вийти"}. {"Show Ordinary Table","Показати звичайну таблицю"}. {"Shut Down Service","Вимкнути Сервіс"}. +{"SOCKS5 Bytestreams","Потоки байтів SOCKS5"}. +{"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Деякі клієнти XMPP можуть зберігати ваш пароль на комп’ютері, але ви повинні робити це лише на своєму персональному комп’ютері з міркувань безпеки."}. +{"Sources Specs:","Специфікації джерел:"}. {"Specify the access model","Визначити модель доступу"}. {"Specify the event message type","Вкажіть тип повідомлень зі сповіщеннями про події"}. {"Specify the publisher model","Умови публікації"}. +{"Stanza id is not valid","Ідентифікатор строфи недійсний"}. +{"Stanza ID","ID кімнати"}. +{"Statically specify a replyto of the node owner(s)","Статично вкажіть відповідь власника(ів) вузла"}. {"Stopped Nodes","Зупинені вузли"}. {"Store binary backup:","Зберегти бінарну резервну копію:"}. {"Store plain text backup:","Зберегти текстову резервну копію:"}. +{"Stream management is already enabled","Керування потоком уже ввімкнено"}. +{"Stream management is not enabled","Керування потоком не ввімкнено"}. {"Subject","Тема"}. {"Submitted","Відправлено"}. {"Subscriber Address","Адреса абонента"}. +{"Subscribers may publish","Підписники можуть публікувати"}. +{"Subscription requests must be approved and only subscribers may retrieve items","Запити на підписку мають бути схвалені, і лише передплатники можуть отримувати елементи"}. +{"Subscriptions are not allowed","Підписка заборонена"}. {"Sunday","Неділя"}. +{"Text associated with a picture","Текст, пов'язаний із зображенням"}. +{"Text associated with a sound","Текст, пов'язаний зі звуком"}. +{"Text associated with a video","Текст, пов’язаний із відео"}. +{"Text associated with speech","Текст, пов'язаний з мовленням"}. {"That nickname is already in use by another occupant","Псевдонім зайнято кимось з присутніх"}. {"That nickname is registered by another person","Псевдонім зареєстровано кимось іншим"}. +{"The account already exists","Обліковий запис уже існує"}. {"The account was not unregistered","Обліковий запис не було видалено"}. +{"The body text of the last received message","Основний текст останнього отриманого повідомлення"}. {"The CAPTCHA is valid.","Перевірку CAPTCHA успішно завершено."}. {"The CAPTCHA verification has failed","Перевірку капчею не пройдено"}. +{"The captcha you entered is wrong","Captcha, яку ви ввели, неправильна"}. +{"The child nodes (leaf or collection) associated with a collection","Дочірні вузли (лист або колекція), пов’язані з колекцією"}. {"The collections with which a node is affiliated","Колекція, до якої входить вузол"}. +{"The DateTime at which a leased subscription will end or has ended","DateTime, коли орендована підписка закінчиться або закінчилася"}. +{"The datetime when the node was created","Дата і час створення вузла"}. +{"The default language of the node","Мова вузла за замовчуванням"}. +{"The feature requested is not supported by the conference","Запитана функція не підтримується конференцією"}. +{"The JID of the node creator","JID творця вузла"}. +{"The JIDs of those to contact with questions","JID тих, до кого можна звернутися із запитаннями"}. +{"The JIDs of those with an affiliation of owner","JID тих, хто є афілійованим власником"}. +{"The JIDs of those with an affiliation of publisher","JID тих, хто пов’язаний із видавцем"}. +{"The list of all online users","Список усіх онлайн-користувачів"}. +{"The list of all users","Список усіх користувачів"}. +{"The list of JIDs that may associate leaf nodes with a collection","Список JID, які можуть пов’язувати листові вузли з колекцією"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Максимальна кількість дочірніх вузлів, які можна пов’язати з колекцією, або `max` для відсутності конкретного обмеження, окрім максимального накладеного сервером"}. +{"The minimum number of milliseconds between sending any two notification digests","Мінімальна кількість мілісекунд між надсиланням будь-яких двох дайджестів сповіщень"}. +{"The name of the node","Ім'я вузла"}. +{"The node is a collection node","Вузол є вузлом колекції"}. +{"The node is a leaf node (default)","Вузол є листовим вузлом (за замовчуванням)"}. +{"The NodeID of the relevant node","NodeID відповідного вузла"}. +{"The number of pending incoming presence subscription requests","Кількість вхідних запитів на підписку про присутність, що очікують на розгляд"}. +{"The number of subscribers to the node","Кількість передплатників вузла"}. +{"The number of unread or undelivered messages","Кількість непрочитаних або недоставлених повідомлень"}. +{"The password contains unacceptable characters","Пароль містить неприйнятні символи"}. {"The password is too weak","Пароль надто простий"}. {"the password is","паролем є"}. +{"The password of your XMPP account was successfully changed.","Пароль вашого облікового запису XMPP успішно змінено."}. +{"The password was not changed","Пароль не змінено"}. +{"The passwords are different","Паролі різні"}. {"The presence states for which an entity wants to receive notifications","Стан присутності, для якого сутність хоче отримувати сповіщення"}. {"The query is only allowed from local users","Запит дозволено лише від локальних користувачів"}. {"The query must not contain elements","Запит не повинен містити елементів "}. {"The room subject can be modified by participants","Тема кімнати може бути змінена учасниками"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Інформація про семантичний тип даних у вузлі, зазвичай визначена простором імен корисного навантаження (якщо є)"}. {"The sender of the last received message","Відправник останнього отриманого повідомлення"}. {"The stanza MUST contain only one element, one element, or one element","Строфа ПОВИННА містити лише один елемент , один елемент або один елемент "}. {"The subscription identifier associated with the subscription request","Ідентифікатор підписки, пов’язаний із запитом на підписку"}. +{"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","URL-адреса перетворення XSL, яке можна застосувати до корисних даних, щоб створити відповідний елемент тіла повідомлення."}. +{"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","URL-адреса трансформації XSL, яку можна застосувати до формату корисного навантаження, щоб створити дійсний результат форм даних, який клієнт міг би відобразити за допомогою загального механізму візуалізації форм даних"}. {"There was an error changing the password: ","Помилка при зміні пароля: "}. {"There was an error creating the account: ","Помилка при створенні облікового запису: "}. {"There was an error deleting the account: ","Помилка при видаленні акаунту: "}. +{"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","Тут не враховується регістр: Макбет – це те саме, що Макбет і Макбет."}. +{"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","Ця сторінка дозволяє зареєструвати обліковий запис XMPP на цьому сервері XMPP. Ваш JID (Jabber ID) матиме такий вигляд: ім’я користувача@сервер. Уважно прочитайте інструкції, щоб правильно заповнити поля."}. {"This page allows to unregister an XMPP account in this XMPP server.","Ця сторінка дозволяє видалити свій обліковий запис з XMPP-сервера."}. {"This room is not anonymous","Ця кімната не анонімна"}. +{"This service can not process the address: ~s","Ця служба не може обробити адресу: ~s"}. {"Thursday","Четвер"}. {"Time delay","Час затримки"}. {"Timed out waiting for stream resumption","Час очікування на відновлення потоку закінчився"}. @@ -468,6 +537,7 @@ {"Too many unacked stanzas","Занадто багато пакетів без відповідей"}. {"Too many users in this conference","Надто багато користувачів у цій конференції"}. {"Traffic rate limit is exceeded","Швидкість передачі інформації було перевищено"}. +{"~ts's MAM Archive","Архів МАМ ~ts"}. {"~ts's Offline Messages Queue","Черга автономних повідомлень ~ts"}. {"Tuesday","Вівторок"}. {"Unable to generate a CAPTCHA","Нема можливості згенерувати капчу"}. @@ -475,17 +545,23 @@ {"Unauthorized","Не авторизовано"}. {"Unexpected action","Несподівана дія"}. {"Unexpected error condition: ~p","Умова несподіваної помилки: ~p"}. +{"Uninstall","Видалити"}. {"Unregister an XMPP account","Видалити обліковий запис XMPP"}. {"Unregister","Видалити"}. {"Unsupported element","Непідтримуваний елемент "}. {"Unsupported version","Непідтримувана версія"}. {"Update message of the day (don't send)","Оновити повідомлення дня (не надсилати)"}. {"Update message of the day on all hosts (don't send)","Оновити повідомлення дня на всіх хостах (не надсилати)"}. +{"Update specs to get modules source, then install desired ones.","Оновіть специфікації, щоб отримати джерело модулів, а потім встановіть потрібні."}. +{"Update Specs","Оновити характеристики"}. +{"Updating the vCard is not supported by the vCard storage backend","Оновлення vCard не підтримується системою зберігання vCard"}. +{"Upgrade","Оновлення"}. {"URL for Archived Discussion Logs","URL-адреса для журналів архівних обговорень"}. {"User already exists","Користувач уже існує"}. {"User JID","JID Користувача"}. {"User (jid)","Користувач (jid)"}. {"User Management","Управління Користувачами"}. +{"User not allowed to perform an IQ set on another user's vCard.","Користувачеві заборонено виконувати тест IQ на vCard іншого користувача."}. {"User removed","Користувача видалено"}. {"User session not found","Сеанс користувача не знайдено"}. {"User session terminated","Сеанс користувача припинено"}. @@ -495,10 +571,13 @@ {"Users Last Activity","Статистика останнього підключення користувачів"}. {"Users","Користувачі"}. {"User","Користувач"}. +{"Value 'get' of 'type' attribute is not allowed","Значення 'get' атрибута 'type' не дозволене"}. {"Value of '~s' should be boolean","Значення \"~s\" має бути логічним"}. {"Value of '~s' should be datetime string","Значення \"~s\" має бути рядком дати і часу"}. {"Value of '~s' should be integer","Значення \"~s\" має бути цілим числом"}. +{"Value 'set' of 'type' attribute is not allowed","Значення 'set' атрибута 'type' не допускається"}. {"vCard User Search","Пошук користувачів по vCard"}. +{"View joined MIX channels","Перегляд приєднаних каналів MIX"}. {"Virtual Hosts","віртуальні хости"}. {"Visitors are not allowed to change their nicknames in this room","Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті"}. {"Visitors are not allowed to send messages to all occupants","Відвідувачам не дозволяється надсилати повідомлення всім присутнім"}. @@ -506,20 +585,30 @@ {"Voice requests are disabled in this conference","Голосові запити відключені в цій конференції"}. {"Voice request","Голосовий запит"}. {"Wednesday","Середа"}. +{"When a new subscription is processed and whenever a subscriber comes online","Коли обробляється нова підписка та щоразу, коли абонент виходить в Інтернет"}. {"When a new subscription is processed","Під час обробки нової підписки"}. {"When to send the last published item","Коли надсилати останній опублікований елемент"}. +{"Whether an entity wants to receive an XMPP message body in addition to the payload format","Чи бажає суб’єкт отримувати тіло повідомлення XMPP на додаток до формату корисного навантаження"}. +{"Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually","Чи бажає організація отримувати дайджести (агрегації) сповіщень чи всі сповіщення окремо"}. +{"Whether an entity wants to receive or disable notifications","Чи бажає суб’єкт отримувати або вимикати сповіщення"}. {"Whether owners or publisher should receive replies to items","Чи повинні власники або видавець отримувати відповіді на елементи"}. {"Whether the node is a leaf (default) or a collection","Чи є вузол листом (типово) чи колекцією"}. {"Whether to allow subscriptions","Дозволяти підписку"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Чи робити всі підписки тимчасовими, залежно від присутності читача"}. {"Whether to notify owners about new subscribers and unsubscribes","Чи повідомляти власників про нових читачів та їх втрату"}. +{"Who can send private messages","Хто може надсилати приватні повідомлення"}. {"Who may associate leaf nodes with a collection","Хто може пов’язувати листові вузли з колекцією"}. {"Wrong parameters in the web formulary","Неправильні параметри у веб-формі"}. {"Wrong xmlns","Неправильний xmlns"}. {"XMPP Account Registration","Реєстрація облікового запису XMPP"}. {"XMPP Domains","Домени XMPP"}. +{"XMPP Show Value of Away","XMPP Показати значення Away"}. +{"XMPP Show Value of Chat","XMPP Показати значення чату"}. +{"XMPP Show Value of DND (Do Not Disturb)","XMPP Показати значення DND (Не турбувати)"}. +{"XMPP Show Value of XA (Extended Away)","XMPP Показати значення XA (розширено)"}. {"XMPP URI of Associated Publish-Subscribe Node","XMPP URI-адреса асоційованого вузла публікацій-підписок"}. {"You are being removed from the room because of a system shutdown","Ви будете видалені з кімнати через завершення роботи системи"}. +{"You are not allowed to send private messages","Ви не можете надсилати приватні повідомлення"}. {"You are not joined to the channel","Ви не приєднані до каналу"}. {"You can later change your password using an XMPP client.","Пізніше ви можете змінити пароль за допомогою XMPP-клієнта."}. {"You have been banned from this room","Вам заборонено входити в цю конференцію"}. From 322e642f194669a5c2d0e0f11d966e7e004a7853 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 17:48:27 +0100 Subject: [PATCH 1062/1302] Update Chinese (Simplified) translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 301 +++++++++++++++++++++++------------------------ 1 file changed, 150 insertions(+), 151 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index c1bfd98b2..ab7053b82 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -8,7 +8,7 @@ {"# participants","# 参与者"}. {"A description of the node","节点的描述"}. {"A friendly name for the node","节点的易记名称"}. -{"A password is required to enter this room","进入此群聊需要密码"}. +{"A password is required to enter this room","需要密码才能进入此房间"}. {"A Web Page","网页"}. {"Accept","接受"}. {"Access denied by service policy","服务策略拒绝访问"}. @@ -23,26 +23,26 @@ {"All activity","所有活动"}. {"All Users","所有用户"}. {"Allow subscription","允许订阅"}. -{"Allow this Jabber ID to subscribe to this pubsub node?","允许此 Jabber ID 订阅此 pubsub 节点?"}. -{"Allow this person to register with the room?","允许此用户注册此群聊?"}. +{"Allow this Jabber ID to subscribe to this pubsub node?","是否允许此 Jabber ID 订阅此 pubsub 节点?"}. +{"Allow this person to register with the room?","是否允许此用户在房间注册?"}. {"Allow users to change the subject","允许用户更改话题"}. {"Allow users to query other users","允许用户查询其他用户"}. {"Allow users to send invites","允许用户发送邀请"}. {"Allow users to send private messages","允许用户发送私信"}. -{"Allow visitors to change nickname","允许访客更改昵称"}. -{"Allow visitors to send private messages to","允许访客发送私信至"}. -{"Allow visitors to send status text in presence updates","允许访客在在线状态更新中发送状态文本"}. -{"Allow visitors to send voice requests","允许访客发送发言请求"}. -{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义群聊成员资格相关联的 LDAP 组;根据组的特定实施或特定部署的定义,使用 LDAP 专有名称。"}. +{"Allow visitors to change nickname","允许参观者更改昵称"}. +{"Allow visitors to send private messages to","允许参观者发送私信至"}. +{"Allow visitors to send status text in presence updates","允许参观者在在线状态更新中发送状态文本"}. +{"Allow visitors to send voice requests","允许参观者发送发言权请求"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","与定义房间成员资格相关联的 LDAP 组;根据组的特定实施或特定部署的定义,使用 LDAP 专有名称。"}. {"Announcements","公告"}. {"Answer associated with a picture","与图片相关的答案"}. {"Answer associated with a video","与视频相关的答案"}. {"Answer associated with speech","与讲话相关的答案"}. {"Answer to a question","问题的答案"}. -{"Anyone in the specified roster group(s) may subscribe and retrieve items","指定花名册组中的任何人都可以订阅和检索项目"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","指定名册组中的任何人都可以订阅和检索项目"}. {"Anyone may associate leaf nodes with the collection","任何人都可以将叶节点与集合关联"}. {"Anyone may publish","任何人都可以发布"}. -{"Anyone may subscribe and retrieve items","任何人都可以订阅和检索内容项"}. +{"Anyone may subscribe and retrieve items","任何人都可以订阅和检索项目"}. {"Anyone with a presence subscription of both or from may subscribe and retrieve items","任何拥有 both 或 from 的在线状态订阅的用户都可以订阅和检索项目"}. {"Anyone with Voice","任何有发言权的人"}. {"Anyone","任何人"}. @@ -75,22 +75,22 @@ {"Channel JID","频道 JID"}. {"Channels","频道"}. {"Characters not allowed:","不允许字符:"}. -{"Chatroom configuration modified","群聊配置已修改"}. -{"Chatroom is created","群聊已创建"}. -{"Chatroom is destroyed","群聊已解散"}. -{"Chatroom is started","群聊已开始"}. -{"Chatroom is stopped","群聊已停止"}. -{"Chatrooms","群聊"}. +{"Chatroom configuration modified","聊天室配置已修改"}. +{"Chatroom is created","已创建聊天室"}. +{"Chatroom is destroyed","已解散聊天室"}. +{"Chatroom is started","已启动聊天室"}. +{"Chatroom is stopped","已停止聊天室"}. +{"Chatrooms","聊天室"}. {"Choose a username and password to register with this server","请选择要在此服务器中注册的用户名和密码"}. {"Choose storage type of tables","选择表的存储类型"}. {"Choose whether to approve this entity's subscription.","选择是否批准此实体的订阅。"}. {"City","城市"}. {"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. {"Commands","命令"}. -{"Conference room does not exist","群聊不存在"}. -{"Configuration of room ~s","群聊 ~s 的配置"}. +{"Conference room does not exist","会议室不存在"}. +{"Configuration of room ~s","房间 ~s 的配置"}. {"Configuration","配置"}. -{"Contact Addresses (normally, room owner or owners)","联系人地址(通常为群聊所有者)"}. +{"Contact Addresses (normally, room owner or owners)","联系地址(通常为房间所有者)"}. {"Country","国家/地区"}. {"Current Discussion Topic","当前讨论话题"}. {"Database failure","数据库失败"}. @@ -104,16 +104,16 @@ {"Deliver event notifications","传递事件通知"}. {"Deliver payloads with event notifications","用事件通知传递有效负载"}. {"Disc only copy","仅磁盘副本"}. -{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人,甚至是 XMPP 服务的管理员。"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将您的密码告诉任何人,甚至是 XMPP 服务器的管理员。"}. {"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. {"Duplicated groups are not allowed by RFC6121","按照 RFC6121 的规则,不允许重复的组"}. {"Dynamically specify a replyto of the item publisher","动态指定项目发布者的 replyto"}. {"Edit Properties","编辑属性"}. -{"Either approve or decline the voice request.","批准或拒绝发言请求。"}. +{"Either approve or decline the voice request.","批准或拒绝发言权请求。"}. {"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. -{"ejabberd Multicast service","ejabberd 多重映射服务"}. +{"ejabberd Multicast service","ejabberd 多播服务"}. {"ejabberd Publish-Subscribe module","ejabberd 发布–订阅模块"}. {"ejabberd SOCKS5 Bytestreams module","ejabberd SOCKS5 字节流模块"}. {"ejabberd vCard module","ejabberd vCard 模块"}. @@ -123,7 +123,7 @@ {"Email","电子邮件"}. {"Enable hats","启用头衔"}. {"Enable logging","启用日志记录"}. -{"Enable message archiving","启用消息存档"}. +{"Enable message archiving","启用消息归档"}. {"Enabling push without 'node' attribute is not supported","不支持没有“node”属性就启用推送"}. {"End User Session","结束用户会话"}. {"Enter nickname you want to register","请输入要注册的昵称"}. @@ -133,26 +133,26 @@ {"Enter path to text file","请输入文本文件的路径"}. {"Enter the text you see","请输入您看到的文本"}. {"Erlang XMPP Server","Erlang XMPP 服务器"}. -{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除 Jabber ID"}. +{"Exclude Jabber IDs from CAPTCHA challenge","从验证码挑战中排除的 Jabber ID"}. {"Export all tables as SQL queries to a file:","将所有表以 SQL 查询导出到文件:"}. {"Export data of all users in the server to PIEFXIS files (XEP-0227):","将服务器中所有用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. {"Export data of users in a host to PIEFXIS files (XEP-0227):","将主机中用户的数据导出到 PIEFXIS 文件(XEP-0227):"}. {"External component failure","外部组件故障"}. {"External component timeout","外部组件超时"}. -{"Failed to activate bytestream","激活字节流失败"}. -{"Failed to extract JID from your voice request approval","无法从发言请求批准中提取 JID"}. -{"Failed to map delegated namespace to external component","未能将委托命名空间映射到外部组件"}. -{"Failed to parse HTTP response","HTTP 响应解析失败"}. +{"Failed to activate bytestream","无法激活字节流"}. +{"Failed to extract JID from your voice request approval","无法从您的发言权请求批准中提取 JID"}. +{"Failed to map delegated namespace to external component","无法将委托命名空间映射到外部组件"}. +{"Failed to parse HTTP response","无法解析 HTTP 响应"}. {"Failed to process option '~s'","无法处理选项“~s”"}. {"Family Name","姓氏"}. -{"FAQ Entry","常见问题解答"}. +{"FAQ Entry","常见问题条目"}. {"February","二月"}. {"File larger than ~w bytes","文件大于 ~w 字节"}. {"Fill in the form to search for any matching XMPP User","填写表单以搜索任何匹配的 XMPP 用户"}. {"Friday","周五"}. {"From ~ts","来自 ~ts"}. -{"Full List of Room Admins","群聊管理员完整列表"}. -{"Full List of Room Owners","群聊所有者完整列表"}. +{"Full List of Room Admins","房间管理员的完整列表"}. +{"Full List of Room Owners","房间所有者的完整列表"}. {"Full Name","全名"}. {"Get List of Online Users","获取在线用户列表"}. {"Get List of Registered Users","获取注册用户列表"}. @@ -161,14 +161,14 @@ {"Get Pending","获取待处理"}. {"Get User Last Login Time","获取用户上次登录时间"}. {"Get User Statistics","获取用户统计数据"}. -{"Given Name","中间名"}. -{"Grant voice to this person?","授予此用户发言权?"}. +{"Given Name","名字"}. +{"Grant voice to this person?","是否授予此用户发言权?"}. {"has been banned","已被封禁"}. {"has been kicked because of a system shutdown","因系统关闭而被踢出"}. {"has been kicked because of an affiliation change","由于从属关系的更改而被踢出"}. -{"has been kicked because the room has been changed to members-only","被踢出,因为群聊已更改为仅成员进入"}. +{"has been kicked because the room has been changed to members-only","被踢出,因为房间已更改为仅成员"}. {"has been kicked","已被踢出"}. -{"Hash of the vCard-temp avatar of this room","此群聊 vCard-temp 头像的散列值"}. +{"Hash of the vCard-temp avatar of this room","此房间的 vCard-temp 头像的散列"}. {"Hat title","头衔标题"}. {"Hat URI","头衔 URI"}. {"Hats limit exceeded","已超过头衔限制"}. @@ -197,135 +197,134 @@ {"Insufficient privilege","权限不足"}. {"Internal server error","内部服务器错误"}. {"Invalid 'from' attribute in forwarded message","转发消息中的“from”属性无效"}. -{"Invalid node name","无效的节点名称"}. +{"Invalid node name","节点名称无效"}. {"Invalid 'previd' value","“previd”值无效"}. -{"Invitations are not allowed in this conference","此群聊不允许邀请"}. +{"Invitations are not allowed in this conference","此会议不允许邀请"}. {"IP addresses","IP 地址"}. {"is now known as","现在昵称为"}. -{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此群聊发送错误消息。参与者(~s)发送了错误消息(~s),被踢出了群聊"}. +{"It is not allowed to send error messages to the room. The participant (~s) has sent an error message (~s) and got kicked from the room","不允许向此房间发送错误消息。参与者(~s)发送了错误消息(~s),被踢出了房间"}. {"It is not allowed to send private messages of type \"groupchat\"","不允许发送“groupchat”类型的私信"}. -{"It is not allowed to send private messages to the conference","不允许向群聊发送私信"}. +{"It is not allowed to send private messages to the conference","不允许向会议发送私信"}. {"Jabber ID","Jabber ID"}. {"January","一月"}. {"JID normalization denied by service policy","服务策略拒绝 JID 规范化"}. {"JID normalization failed","JID 规范化失败"}. {"Joined MIX channels of ~ts","加入了 ~ts 的 MIX 频道"}. {"Joined MIX channels:","加入了 MIX 频道:"}. -{"joins the room","加入群聊"}. +{"joins the room","加入房间"}. {"July","七月"}. {"June","六月"}. {"Just created","刚刚创建"}. {"Last Activity","上次活动"}. {"Last login","上次登录"}. -{"Last message","最近消息"}. +{"Last message","最后一条消息"}. {"Last month","上个月"}. {"Last year","去年"}. -{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的 SHA-256 哈希的最低有效位应等于十六进制标签"}. -{"leaves the room","离开群聊"}. -{"List of users with hats","有头衔用户的列表"}. -{"List users with hats","有头衔用户列表"}. +{"Least significant bits of SHA-256 hash of text should equal hexadecimal label","文本的 SHA-256 散列的最低有效位应等于十六进制标签"}. +{"leaves the room","离开房间"}. +{"List of users with hats","有头衔的用户列表"}. +{"List users with hats","列出有头衔的用户"}. {"Logged Out","已登出"}. {"Logging","日志记录"}. {"Make participants list public","公开参与者列表"}. -{"Make room CAPTCHA protected","开启群聊验证码保护"}. -{"Make room members-only","仅成员进入的群聊"}. -{"Make room moderated","开启群聊发言审核"}. -{"Make room password protected","开启群聊密码保护"}. -{"Make room persistent","持续存在的群聊"}. -{"Make room public searchable","可公开搜索的群聊"}. +{"Make room CAPTCHA protected","开启房间验证码保护"}. +{"Make room members-only","将房间设为仅成员"}. +{"Make room moderated","开启房间发言审核"}. +{"Make room password protected","开启房间密码保护"}. +{"Make room persistent","将房间设为持久"}. +{"Make room public searchable","将房间设为公开可搜索"}. {"Malformed username","用户名格式不正确"}. {"MAM preference modification denied by service policy","服务策略拒绝修改 MAM 首选项"}. {"March","三月"}. -{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要持久化的最大项目数 #,或“max”表示除服务器施加的最大值之外没有特定限制"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","要保留的最大项目数 #,或 `max` 表示除服务器强制规定的最大值外无其他特定限制"}. {"Max payload size in bytes","最大有效负载大小(字节)"}. {"Maximum file size","最大文件大小"}. -{"Maximum Number of History Messages Returned by Room","群聊返回的聊天记录消息的最大值"}. -{"Maximum number of items to persist","要持久化的最大项目数"}. -{"Maximum Number of Occupants","最大参与者人数"}. +{"Maximum Number of History Messages Returned by Room","房间返回的最大历史消息数"}. +{"Maximum number of items to persist","要保留的最大项目数"}. +{"Maximum Number of Occupants","最大使用者数"}. {"May","五月"}. -{"Membership is required to enter this room","进入此群聊需要成员资格"}. -{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住密码,或将密码写在纸上,放在安全的地方。在 XMPP 中,如果您忘记密码,没有自动恢复密码的方法。"}. +{"Membership is required to enter this room","进入此房间需要成员资格"}. +{"Memorize your password, or write it in a paper placed in a safe place. In XMPP there isn't an automated way to recover your password if you forget it.","请记住您的密码,或写在放在安全地方的纸上。在 XMPP 中,如果您忘记密码,没有自动恢复密码的方法。"}. {"Mere Availability in XMPP (No Show Value)","XMPP 中的可用性(无显示值)"}. {"Message body","消息正文"}. -{"Message not found in forwarded payload","在转发的有效负载中找不到消息"}. +{"Message not found in forwarded payload","在转发的有效负载中未找到消息"}. {"Messages from strangers are rejected","拒绝来自陌生人的消息"}. {"Messages of type headline","标题类型的消息"}. {"Messages of type normal","普通类型的消息"}. {"Middle Name","中间名"}. -{"Minimum interval between voice requests (in seconds)","发言请求之间的最短间隔时间(秒)"}. +{"Minimum interval between voice requests (in seconds)","发言权请求的最短间隔时间(秒)"}. {"Moderator privileges required","需要主持人权限"}. {"Moderators Only","仅主持人"}. {"Moderator","主持人"}. {"Module failed to handle the query","模块无法处理查询"}. {"Monday","周一"}. -{"Multicast","多重映射"}. +{"Multicast","多播"}. {"Multiple elements are not allowed by RFC6121","按照 RFC6121,多个 元素是不允许的"}. {"Multi-User Chat","多用户聊天"}. {"Name","名称"}. -{"Natural Language for Room Discussions","群聊讨论的自然语言"}. -{"Natural-Language Room Name","自然语言群聊名称"}. +{"Natural Language for Room Discussions","房间讨论的自然语言"}. +{"Natural-Language Room Name","自然语言房间名称"}. {"Neither 'jid' nor 'nick' attribute found","未找到“jid”和“nick”属性"}. {"Neither 'role' nor 'affiliation' attribute found","未找到“role”或“affiliation”属性"}. {"Never","从不"}. {"New Password:","新密码:"}. {"Nickname can't be empty","昵称不能为空"}. {"Nickname Registration at ","昵称注册于 "}. -{"Nickname ~s does not exist in the room","昵称 ~s 不在此群聊"}. +{"Nickname ~s does not exist in the room","昵称 ~s 在房间中不存在"}. {"Nickname","昵称"}. -{"No address elements found","找不到地址元素"}. -{"No addresses element found","找不到地址元素"}. +{"No address elements found","未找到地址元素"}. +{"No addresses element found","未找到地址元素"}. {"No 'affiliation' attribute found","未找到“affiliation”属性"}. -{"No available resource found","找不到可用资源"}. -{"No body provided for announce message","没有为公告消息提供正文"}. -{"No child elements found","没有找到子元素"}. -{"No data form found","没有找到数据表单"}. -{"No Data","没有数据"}. -{"No features available","没有可用功能"}. +{"No available resource found","未找到可用资源"}. +{"No body provided for announce message","未提供公告消息正文"}. +{"No child elements found","未找到子元素"}. +{"No data form found","未找到数据表单"}. +{"No Data","无数据"}. +{"No features available","无可用功能"}. {"No element found","未找到 元素"}. {"No hook has processed this command","没有钩子处理此命令"}. {"No info about last activity found","未找到有关上次活动的信息"}. {"No 'item' element found","未找到“item”元素"}. -{"No items found in this query","在此查询中找不到任何项目"}. -{"No limit","没有限制"}. +{"No items found in this query","在此查询中未找到任何项目"}. +{"No limit","无限制"}. {"No module is handling this query","没有模块正在处理此查询"}. {"No node specified","未指定节点"}. -{"No 'password' found in data form","在数据表单中找不到“password”"}. -{"No 'password' found in this query","在此查询中找不到“password”"}. -{"No 'path' found in data form","在数据表单中找不到“path”"}. +{"No 'password' found in data form","在数据表单中未找到“password”"}. +{"No 'password' found in this query","在此查询中未找到“password”"}. +{"No 'path' found in data form","在数据表单中未找到“path”"}. {"No pending subscriptions found","未找到待处理的订阅"}. {"No privacy list with this name found","未找到具有此名称的隐私列表"}. -{"No private data found in this query","在此查询中找不到专用数据"}. -{"No element found","未找到 元素"}. -{"No running node found","找不到正在运行的节点"}. +{"No private data found in this query","在此查询中未找到专用数据"}. +{"No running node found","未找到正在运行的节点"}. {"No services available","无可用服务"}. {"No statistics found for this item","未找到此项目的统计数据"}. {"No 'to' attribute found in the invitation","邀请中未找到“to”属性"}. {"Nobody","没有人"}. {"Node already exists","节点已存在"}. {"Node ID","节点 ID"}. -{"Node index not found","没有找到节点索引"}. -{"Node not found","没有找到节点"}. +{"Node index not found","未找到节点索引"}. +{"Node not found","未找到节点"}. {"Node ~p","节点 ~p"}. -{"Nodeprep has failed","Nodeprep 已失效"}. +{"Nodeprep has failed","Nodeprep 失败了"}. {"Nodes","节点"}. {"Node","节点"}. {"None","无"}. {"Not allowed","不允许"}. -{"Not Found","没有找到"}. +{"Not Found","未找到"}. {"Not subscribed","未订阅"}. {"Notify subscribers when items are removed from the node","从节点中移除项目时通知订阅者"}. {"Notify subscribers when the node configuration changes","节点配置更改时通知订阅者"}. {"Notify subscribers when the node is deleted","删除节点时通知订阅者"}. {"November","十一月"}. -{"Number of answers required","所需答案数量"}. -{"Number of occupants","参与者人数"}. -{"Number of Offline Messages","离线消息数量"}. +{"Number of answers required","所需答案数"}. +{"Number of occupants","使用者数"}. +{"Number of Offline Messages","离线消息数"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. -{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","等待多少秒后自动清除项目,“max”表示除服务器施加的最大值外没有特定限制"}. -{"Occupants are allowed to invite others","允许参与者邀请别人"}. -{"Occupants are allowed to query others","允许参与者查询别人"}. -{"Occupants May Change the Subject","参与者可以更改话题"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","自动清除项目前的秒数,`max` 表示除服务器强制规定的最大值外无其他特定限制"}. +{"Occupants are allowed to invite others","允许使用者邀请他人"}. +{"Occupants are allowed to query others","允许使用者查询他人"}. +{"Occupants May Change the Subject","使用者可以更改话题"}. {"October","十月"}. {"OK","确定"}. {"Old Password:","旧密码:"}. @@ -335,13 +334,13 @@ {"Only deliver notifications to available users","仅向在线用户发送通知"}. {"Only or tags are allowed","仅允许 标签"}. {"Only element is allowed in this query","此查询中只允许 元素"}. -{"Only members may query archives of this room","只有成员才能查询此群聊的存档"}. -{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者在此群聊更改话题"}. -{"Only moderators are allowed to change the subject in this room","只允许主持人在此群聊更改话题"}. +{"Only members may query archives of this room","只有成员才能查询此房间的归档"}. +{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者更改此房间的话题"}. +{"Only moderators are allowed to change the subject in this room","只允许主持人更改此房间的话题"}. {"Only moderators are allowed to retract messages","只允许主持人撤回消息"}. -{"Only moderators can approve voice requests","只有主持人才能批准发言请求"}. -{"Only occupants are allowed to send messages to the conference","只允许参与者向群聊发送消息"}. -{"Only occupants are allowed to send queries to the conference","只允许参与者向群聊发送查询"}. +{"Only moderators can approve voice requests","只有主持人可以批准发言权请求"}. +{"Only occupants are allowed to send messages to the conference","只允许使用者向会议发送消息"}. +{"Only occupants are allowed to send queries to the conference","只允许使用者向会议发送查询"}. {"Only publishers may publish","只有发布者才能发布"}. {"Only service administrators are allowed to send service messages","只允许服务管理员发送服务消息"}. {"Only those on a whitelist may associate leaf nodes with the collection","只有白名单上的那些可以将叶节点与集合关联"}. @@ -361,17 +360,17 @@ {"Path to Dir","目录路径"}. {"Path to File","文件路径"}. {"Payload semantic type information","有效负载语义类型信息"}. -{"Period: ","持续时间: "}. -{"Persist items to storage","将项目持久化到存储"}. +{"Period: ","时段: "}. +{"Persist items to storage","将项目保留到存储"}. {"Persistent","持久"}. {"Ping query is incorrect","Ping 查询不正确"}. {"Ping","Ping"}. {"Please note that these options will only backup the builtin Mnesia database. If you are using the ODBC module, you also need to backup your SQL database separately.","注意:这些选项只会备份内置的 Mnesia 数据库。如果使用 ODBC 模块,还需要单独备份 SQL 数据库。"}. -{"Please, wait for a while before sending new voice request","请稍候,然后再发送新的发言请求"}. +{"Please, wait for a while before sending new voice request","请稍候再发送新的发言权请求"}. {"Pong","Pong"}. {"Possessing 'ask' attribute is not allowed by RFC6121","按照 RFC6121, 不允许有“ask”属性"}. -{"Present real Jabber IDs to","将用户真实 JID 显示给"}. -{"Previous session not found","上一个会话未找到"}. +{"Present real Jabber IDs to","将真实 Jabber ID 显示给"}. +{"Previous session not found","未找到上一个会话"}. {"Previous session PID has been killed","上一个会话 PID 已终止"}. {"Previous session PID has exited","上一个会话 PID 已退出"}. {"Previous session PID is dead","上一个会话 PID 已失效"}. @@ -382,8 +381,8 @@ {"Publish-Subscribe","发布–订阅"}. {"PubSub subscriber request","PubSub 订阅者请求"}. {"Purge all items when the relevant publisher goes offline","相关发布者离线后清除所有项目"}. -{"Push record not found","推送记录未找到"}. -{"Queries to the conference members are not allowed in this room","不允许在此群聊中查询群聊成员"}. +{"Push record not found","未找到推送记录"}. +{"Queries to the conference members are not allowed in this room","此房间不允许向会议成员查询"}. {"Query to another users is forbidden","禁止查询其他用户"}. {"RAM and disc copy","RAM 和磁盘副本"}. {"RAM copy","RAM 副本"}. @@ -392,7 +391,7 @@ {"Receive notification from direct child nodes only","仅接收直接子节点的通知"}. {"Receive notification of new items only","仅接收新项目的通知"}. {"Receive notification of new nodes only","仅接收新节点的通知"}. -{"Recipient is not in the conference room","接收者不在群聊"}. +{"Recipient is not in the conference room","接收者不在会议室"}. {"Register an XMPP account","注册 XMPP 账号"}. {"Register","注册"}. {"Remote copy","远程副本"}. @@ -407,21 +406,21 @@ {"Restore Backup from File at ","从以下位置的文件恢复备份 "}. {"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(所需内存较少):"}. {"Restore binary backup immediately:","立即恢复二进制备份:"}. -{"Restore plain text backup immediately:","立即恢复明文备份:"}. +{"Restore plain text backup immediately:","立即恢复纯文本备份:"}. {"Restore","恢复"}. {"Roles and Affiliations that May Retrieve Member List","可以检索成员列表的角色和从属关系"}. {"Roles for which Presence is Broadcasted","广播在线状态的角色"}. {"Roles that May Send Private Messages","可以发送私信的角色"}. -{"Room Configuration","群聊配置"}. -{"Room creation is denied by service policy","服务策略拒绝群聊创建"}. -{"Room description","群聊描述"}. -{"Room Occupants","群聊参与者"}. -{"Room terminates","群聊终止"}. -{"Room title","群聊标题"}. -{"Roster groups allowed to subscribe","允许订阅的花名册组"}. -{"Roster size","花名册大小"}. +{"Room Configuration","房间配置"}. +{"Room creation is denied by service policy","服务策略拒绝创建房间"}. +{"Room description","房间描述"}. +{"Room Occupants","房间使用者"}. +{"Room terminates","房间终止"}. +{"Room title","房间标题"}. +{"Roster groups allowed to subscribe","允许订阅的名册组"}. +{"Roster size","名册大小"}. {"Running Nodes","正在运行的节点"}. -{"~s invites you to the room ~s","~s 邀请您加入群聊 ~s"}. +{"~s invites you to the room ~s","~s 邀请您加入房间 ~s"}. {"Saturday","周六"}. {"Search from the date","从日期搜索"}. {"Search Results for ","搜索结果 "}. @@ -438,9 +437,9 @@ {"Session state copying timed out","会话状态复制超时"}. {"Set message of the day and send to online users","设置每日消息并发送给在线用户"}. {"Set message of the day on all hosts and send to online users","在所有主机上设置每日消息并发送给在线用户"}. -{"Shared Roster Groups","共享的花名册组"}. +{"Shared Roster Groups","共享名册组"}. {"Show Integral Table","显示完整表"}. -{"Show Occupants Join/Leave","显示参与者加入/离开"}. +{"Show Occupants Join/Leave","显示使用者加入/离开"}. {"Show Ordinary Table","显示普通表"}. {"Shut Down Service","关闭服务"}. {"SOCKS5 Bytestreams","SOCKS5 字节流"}. @@ -454,9 +453,9 @@ {"Statically specify a replyto of the node owner(s)","静态指定节点所有者的 replyto"}. {"Stopped Nodes","已停止的节点"}. {"Store binary backup:","存储二进制备份:"}. -{"Store plain text backup:","存储明文备份:"}. -{"Stream management is already enabled","流管理已启用"}. -{"Stream management is not enabled","流管理未启用"}. +{"Store plain text backup:","存储纯文本备份:"}. +{"Stream management is already enabled","已启用流管理"}. +{"Stream management is not enabled","未启用流管理"}. {"Subject","话题"}. {"Submitted","已提交"}. {"Subscriber Address","订阅者地址"}. @@ -468,35 +467,35 @@ {"Text associated with a sound","与声音相关的文字"}. {"Text associated with a video","与视频相关的文字"}. {"Text associated with speech","与语音相关的文字"}. -{"That nickname is already in use by another occupant","该昵称已被另一参与者使用了"}. +{"That nickname is already in use by another occupant","该昵称已被其他使用者使用"}. {"That nickname is registered by another person","该昵称已被另一用户注册了"}. {"The account already exists","此账号已存在"}. {"The account was not unregistered","此账号未注销"}. {"The body text of the last received message","最后收到的消息的正文"}. {"The CAPTCHA is valid.","验证码有效。"}. -{"The CAPTCHA verification has failed","验证码检查失败"}. +{"The CAPTCHA verification has failed","验证码验证失败"}. {"The captcha you entered is wrong","您输入的验证码错误"}. {"The child nodes (leaf or collection) associated with a collection","与集合关联的子节点(叶或集合)"}. {"The collections with which a node is affiliated","节点所属的集合"}. {"The DateTime at which a leased subscription will end or has ended","租赁订阅将结束或已结束的日期时间"}. {"The datetime when the node was created","创建节点的日期时间"}. {"The default language of the node","节点的默认语言"}. -{"The feature requested is not supported by the conference","群聊不支持请求的功能"}. +{"The feature requested is not supported by the conference","会议不支持所请求的功能"}. {"The JID of the node creator","节点创建者的 JID"}. -{"The JIDs of those to contact with questions","有问题要联系的人的 JID"}. -{"The JIDs of those with an affiliation of owner","与所有者的从属关系有关的用户 JID"}. -{"The JIDs of those with an affiliation of publisher","与发布者的从属关系有关的用户 JID"}. +{"The JIDs of those to contact with questions","有疑问时需联系的人员的 JID"}. +{"The JIDs of those with an affiliation of owner","有所有者从属关系的人员的 JID"}. +{"The JIDs of those with an affiliation of publisher","有发布者从属关系的人员的 JID"}. {"The list of all online users","所有在线用户的列表"}. {"The list of all users","所有用户的列表"}. {"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的 JID 列表"}. -{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合关联的子节点的最大数量,或“max”表示除服务器施加的最大值外没有特定限制"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","可以与集合关联的子节点的最大数量,或 `max` 表示除服务器强制规定的最大值外无其他特定限制"}. {"The minimum number of milliseconds between sending any two notification digests","发送任意两个通知摘要之间的最小毫秒数"}. {"The name of the node","节点的名称"}. {"The node is a collection node","节点是集合节点"}. {"The node is a leaf node (default)","节点是叶节点(默认)"}. {"The NodeID of the relevant node","相关节点的 NodeID"}. {"The number of pending incoming presence subscription requests","待处理的传入在线状态订阅请求数"}. -{"The number of subscribers to the node","节点的订阅者数量"}. +{"The number of subscribers to the node","节点的订阅者数"}. {"The number of unread or undelivered messages","未读或未传递的消息数"}. {"The password contains unacceptable characters","密码包含不可接受的字符"}. {"The password is too weak","密码太弱"}. @@ -505,9 +504,9 @@ {"The password was not changed","密码未更改"}. {"The passwords are different","密码不同"}. {"The presence states for which an entity wants to receive notifications","实体要接收通知的在线状态"}. -{"The query is only allowed from local users","仅允许本地用户查询"}. +{"The query is only allowed from local users","仅允许来自本地用户的查询"}. {"The query must not contain elements","查询不能包含 元素"}. -{"The room subject can be modified by participants","群聊话题可由参与者修改"}. +{"The room subject can be modified by participants","参与者可以修改房间话题"}. {"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. {"The sender of the last received message","最后收到的消息的发送者"}. {"The stanza MUST contain only one element, one element, or one element","节必须仅包含一个 元素、一个 元素或一个 元素"}. @@ -520,7 +519,7 @@ {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写:MacBeth 和 Macbeth 都是 macbeth。"}. {"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器中注册 XMPP 账号,您的 JID(Jabber ID)的格式为:用户名@服务器。请仔细阅读说明以正确填写字段。"}. {"This page allows to unregister an XMPP account in this XMPP server.","本页面允许在此 XMPP 服务器中注销 XMPP 账号。"}. -{"This room is not anonymous","此群聊是非匿名的"}. +{"This room is not anonymous","此房间是非匿名的"}. {"This service can not process the address: ~s","此服务无法处理地址:~s"}. {"Thursday","周四"}. {"Time delay","时间延迟"}. @@ -530,21 +529,21 @@ {"Token TTL","令牌 TTL"}. {"Too many active bytestreams","活动字节流太多"}. {"Too many CAPTCHA requests","验证码请求太多"}. -{"Too many child elements","太多子元素"}. -{"Too many elements","太多 元素"}. -{"Too many elements","太多 元素"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","有太多 (~p) 失败的身份验证来自此 IP 地址 (~s),将在 UTC 时间 ~s 解除对该地址的屏蔽"}. +{"Too many child elements","子元素太多"}. +{"Too many elements"," 元素太多"}. +{"Too many elements"," 元素太多"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","有太多(~p)失败的身份验证来自此 IP 地址(~s),将在 UTC 时间 ~s 取消对该地址的屏蔽"}. {"Too many receiver fields were specified","指定的接收者字段太多"}. -{"Too many unacked stanzas","太多未确认的节"}. -{"Too many users in this conference","此群聊中的用户太多"}. +{"Too many unacked stanzas","未确认的节太多"}. +{"Too many users in this conference","此会议中的用户太多"}. {"Traffic rate limit is exceeded","超过流量速率限制"}. -{"~ts's MAM Archive","~ts 的 MAM 存档"}. +{"~ts's MAM Archive","~ts 的 MAM 归档"}. {"~ts's Offline Messages Queue","~ts 的离线消息队列"}. {"Tuesday","周二"}. {"Unable to generate a CAPTCHA","无法生成验证码"}. {"Unable to register route on existing local domain","无法在现有本地域上注册路由"}. {"Unauthorized","未经授权"}. -{"Unexpected action","意外的操作"}. +{"Unexpected action","意外操作"}. {"Unexpected error condition: ~p","意外错误条件:~p"}. {"Uninstall","卸载"}. {"Unregister an XMPP account","注销 XMPP 账号"}. @@ -557,14 +556,14 @@ {"Update Specs","更新规格"}. {"Updating the vCard is not supported by the vCard storage backend","vCard 存储后端不支持更新 vCard"}. {"Upgrade","升级"}. -{"URL for Archived Discussion Logs","存档讨论日志的 URL"}. +{"URL for Archived Discussion Logs","已归档的讨论日志 URL"}. {"User already exists","用户已存在"}. -{"User (jid)","用户 (jid)"}. {"User JID","用户 JID"}. +{"User (jid)","用户(JID)"}. {"User Management","用户管理"}. {"User not allowed to perform an IQ set on another user's vCard.","不允许用户在其他用户的 vCard 上执行 IQ 设置。"}. {"User removed","用户已移除"}. -{"User session not found","用户会话未找到"}. +{"User session not found","未找到用户会话"}. {"User session terminated","用户会话已终止"}. {"User ~ts","用户 ~ts"}. {"Username:","用户名:"}. @@ -580,11 +579,11 @@ {"vCard User Search","vCard 用户搜索"}. {"View joined MIX channels","查看已加入的 MIX 频道"}. {"Virtual Hosts","虚拟主机"}. -{"Visitors are not allowed to change their nicknames in this room","不允许访客在此群聊中更改其昵称"}. -{"Visitors are not allowed to send messages to all occupants","不允许访客向所有参与者发送消息"}. -{"Visitor","访客"}. -{"Voice requests are disabled in this conference","此群聊中禁用发言请求"}. -{"Voice request","发言请求"}. +{"Visitors are not allowed to change their nicknames in this room","不允许参观者在此房间中更改其昵称"}. +{"Visitors are not allowed to send messages to all occupants","不允许参观者向所有使用者发送消息"}. +{"Visitor","参观者"}. +{"Voice requests are disabled in this conference","此会议中禁用了发言权请求"}. +{"Voice request","发言权请求"}. {"Wednesday","周三"}. {"When a new subscription is processed and whenever a subscriber comes online","处理新订阅时和订阅者上线时"}. {"When a new subscription is processed","处理新订阅时"}. @@ -599,7 +598,7 @@ {"Whether to notify owners about new subscribers and unsubscribes","是否通知所有者新的订阅者和退订者"}. {"Who can send private messages","谁可以发送私信"}. {"Who may associate leaf nodes with a collection","谁可以将叶节点与集合关联"}. -{"Wrong parameters in the web formulary","web 公式中的参数错误"}. +{"Wrong parameters in the web formulary","Web 表单集中的参数错误"}. {"Wrong xmlns","错误的 xmlns"}. {"XMPP Account Registration","XMPP 账号注册"}. {"XMPP Domains","XMPP 域"}. @@ -608,19 +607,19 @@ {"XMPP Show Value of DND (Do Not Disturb)","XMPP 的 DND(请勿打扰)显示值"}. {"XMPP Show Value of XA (Extended Away)","XMPP 的 XA(延长离开)显示值"}. {"XMPP URI of Associated Publish-Subscribe Node","关联发布–订阅节点的 XMPP URI"}. -{"You are being removed from the room because of a system shutdown","由于系统关闭,您将会从群聊中移除"}. +{"You are being removed from the room because of a system shutdown","由于系统关闭,您将被移出房间"}. {"You are not allowed to send private messages","不允许您发送私信"}. {"You are not joined to the channel","您未加入频道"}. {"You can later change your password using an XMPP client.","您之后可以使用 XMPP 客户端更改密码。"}. -{"You have been banned from this room","禁止您进入此群聊"}. -{"You have joined too many conferences","您加入了太多群聊"}. +{"You have been banned from this room","禁止您进入此房间"}. +{"You have joined too many conferences","您加入了太多会议"}. {"You must fill in field \"Nickname\" in the form","您必须在表单中填写“昵称”字段"}. {"You need a client that supports x:data and CAPTCHA to register","您需要支持 x:data 和验证码的客户端来注册"}. {"You need a client that supports x:data to register the nickname","您需要支持 x:data 的客户端来注册昵称"}. {"You need an x:data capable client to search","您需要支持 x:data 的客户端来搜索"}. {"Your active privacy list has denied the routing of this stanza.","您的活动隐私列表已拒绝路由此节。"}. {"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满。消息已被丢弃。"}. -{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您对 ~s 的订阅请求和/或消息已被屏蔽。若要解除屏蔽您的订阅请求,请访问 ~s"}. +{"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发给 ~s 的订阅请求和/或消息已被屏蔽。若要取消屏蔽您的订阅请求,请访问 ~s"}. {"Your XMPP account was successfully registered.","您的 XMPP 账号注册成功。"}. {"Your XMPP account was successfully unregistered.","您的 XMPP 账号注销成功。"}. {"You're not allowed to create nodes","不允许您创建节点"}. From d831fd4789d4fda3284a91fa871478d34002c413 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 17:49:08 +0100 Subject: [PATCH 1063/1302] Result of runing "make translations" --- priv/msgs/bg.msg | 1 - priv/msgs/ca.msg | 5 ++--- priv/msgs/de.msg | 1 - priv/msgs/es.msg | 1 - priv/msgs/it.msg | 1 - priv/msgs/pt-br.msg | 1 - priv/msgs/sq.msg | 1 - 7 files changed, 2 insertions(+), 9 deletions(-) diff --git a/priv/msgs/bg.msg b/priv/msgs/bg.msg index 128de8673..bf0b8eca0 100644 --- a/priv/msgs/bg.msg +++ b/priv/msgs/bg.msg @@ -295,7 +295,6 @@ {"No pending subscriptions found","Не са намерени чакащи абонаменти"}. {"No privacy list with this name found","Не е намерен списък за поверителност с това име"}. {"No private data found in this query","Няма открити лични данни в тази заявка"}. -{"No element found","Елементът не е намерен"}. {"No running node found","Не е намерен работещ нод"}. {"No services available","Няма налични услуги"}. {"No statistics found for this item","Не е налична статистика за този елемент"}. diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 10da46007..2982b1211 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -22,7 +22,7 @@ {"Administrator privileges required","Es necessita tenir privilegis d'administrador"}. {"All activity","Tota l'activitat"}. {"All Users","Tots els usuaris"}. -{"Allow subscription","Permetre subscripcions"}. +{"Allow subscription","Permetre subscripció"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Permetre que aquesta Jabber ID es puga subscriure a aquest node pubsub?"}. {"Allow this person to register with the room?","Permetre a esta persona registrar-se a la sala?"}. {"Allow users to change the subject","Permetre que els usuaris canviïn el tema"}. @@ -295,7 +295,6 @@ {"No pending subscriptions found","No s'han trobat subscripcions pendents"}. {"No privacy list with this name found","No s'ha trobat cap llista de privacitat amb aquest nom"}. {"No private data found in this query","No s'ha trobat dades privades en esta petició"}. -{"No element found","No s'ha trobat cap element "}. {"No running node found","No s'ha trobat node en marxa"}. {"No services available","No n'hi ha serveis disponibles"}. {"No statistics found for this item","No n'hi ha estadístiques disponibles per a aquest element"}. @@ -594,7 +593,7 @@ {"Whether an entity wants to receive or disable notifications","Si una entitat vol rebre notificacions o no"}. {"Whether owners or publisher should receive replies to items","Si el propietaris o publicadors deurien de rebre respostes als elements"}. {"Whether the node is a leaf (default) or a collection","Si el node es fulla (per defecte) o es una col·lecció"}. -{"Whether to allow subscriptions","Permetre subscripcions"}. +{"Whether to allow subscriptions","Si s'hauria de permetre subscripcions"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Si fer totes les subscripcions temporals, basat en la presencia del subscriptor"}. {"Whether to notify owners about new subscribers and unsubscribes","Si notificar als propietaris sobre noves subscripcions i desubscripcions"}. {"Who can send private messages","Qui pot enviar missatges privats"}. diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index 4e4bc0b3a..8c97e427d 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -294,7 +294,6 @@ {"No pending subscriptions found","Keine ausstehenden Abonnements gefunden"}. {"No privacy list with this name found","Keine Privacy-Liste mit diesem Namen gefunden"}. {"No private data found in this query","Keine privaten Daten in dieser Anfrage gefunden"}. -{"No element found","Kein -Element gefunden"}. {"No running node found","Kein laufender Knoten gefunden"}. {"No services available","Keine Dienste verfügbar"}. {"No statistics found for this item","Keine Statistiken für dieses Item gefunden"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index 607b95997..a81091566 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -295,7 +295,6 @@ {"No pending subscriptions found","No se han encontrado suscripciones pendientes"}. {"No privacy list with this name found","No se ha encontrado una lista de privacidad con este nombre"}. {"No private data found in this query","No se ha encontrado ningún elemento de dato privado en esta petición"}. -{"No element found","No se encontró ningún elemento "}. {"No running node found","No se ha encontrado ningún nodo activo"}. {"No services available","No hay servicios disponibles"}. {"No statistics found for this item","No se han encontrado estadísticas para este elemento"}. diff --git a/priv/msgs/it.msg b/priv/msgs/it.msg index f7b5c5451..926d16687 100644 --- a/priv/msgs/it.msg +++ b/priv/msgs/it.msg @@ -295,7 +295,6 @@ {"No pending subscriptions found","Nessuna sottoscrizione in attesa trovata"}. {"No privacy list with this name found","Nessun elenco di privacy con questo nome trovato"}. {"No private data found in this query","Non sono stati trovati dati privati in questa query"}. -{"No element found","Nessun elemento trovato"}. {"No running node found","Nessun nodo in esecuzione trovato"}. {"No services available","Nessun servizio disponibile"}. {"No statistics found for this item","Nessuna statistica trovata per questa voce"}. diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 882c03ec2..244138db5 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -295,7 +295,6 @@ {"No pending subscriptions found","Não foram encontradas subscrições"}. {"No privacy list with this name found","Nenhuma lista de privacidade encontrada com este nome"}. {"No private data found in this query","Nenhum dado privado encontrado nesta consulta"}. -{"No element found","Nenhum elemento foi encontrado"}. {"No running node found","Nenhum nó em execução foi encontrado"}. {"No services available","Não há serviços disponíveis"}. {"No statistics found for this item","Não foram encontradas estatísticas para este item"}. diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index 4469cea98..2cc43274d 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -186,7 +186,6 @@ {"No node specified","S’u përcaktua nyjë"}. {"No pending subscriptions found","S’u gjetën pajtime pezull"}. {"No privacy list with this name found","S’u gjet listë privatësie me atë emër"}. -{"No element found","S’u gjetën elementë "}. {"No running node found","S’u gjet nyjë në funksionim"}. {"No services available","S’ka shërbime të gatshme"}. {"No statistics found for this item","S’u gjetën statistika për këtë objekt"}. From 91fb02d62e42c59bfebde8e3c288cf704404f5de Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 18:08:57 +0100 Subject: [PATCH 1064/1302] Result of running "make doap" --- ejabberd.doap | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index 00e171905..b1df5efc7 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -134,6 +134,15 @@ mod_muc + + + + 1.2 + 0.5.0 + complete + mod_pubsub + + @@ -476,6 +485,15 @@ mod_s2s_dialback + + + + 1.1.1 + 2.0.0 + complete + mod_pubsub + + @@ -512,6 +530,15 @@ mod_pubsub + + + + 1.2 + 0.5.0 + complete + mod_muc + + @@ -524,7 +551,7 @@ - 0.13.2 + 1.0.1 13.06 complete mod_carboncopy @@ -551,10 +578,10 @@ - 0.1 + 0.2.0 21.12 complete - mod_muc_room, conversejs/prosody compatible + mod_muc_room, 0.2.0 since 25.03 From f58a0cdbfdbbf74fdb2f4f7e7a7baa22997e9a5d Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 20:16:02 +0100 Subject: [PATCH 1065/1302] Add some apps to rebar2 OTP releases --- rel/reltool.config.script | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rel/reltool.config.script b/rel/reltool.config.script index 0a87ce735..4f142efe2 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -38,11 +38,12 @@ Vars = case file:consult(filename:join([TopDir, "vars.config"])) of RequiredOTPApps = [sasl, crypto, public_key, ssl, mnesia, inets, compiler, asn1, + observer, tools, syntax_tools, os_mon, xmerl], ConfiguredOTPApps = lists:flatmap( fun({tools, true}) -> - [tools, runtime_tools]; + [runtime_tools]; ({odbc, true}) -> [odbc]; (_) -> From 9087867631126abe8cca2809b6f1bd1b837836f2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 22:46:31 +0100 Subject: [PATCH 1066/1302] Container: Document the used Erlang and Elixir versions --- CONTAINER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTAINER.md b/CONTAINER.md index 3afe913b3..8badba852 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -920,7 +920,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.2-alpine
Elixir 1.18.1 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included | From 2ed7ce49a22e6f5041b7f2498820793a25c91bca Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 27 Mar 2025 23:11:29 +0100 Subject: [PATCH 1067/1302] Update documentation about XEP-0424 support and move to proper file (#3340) --- ejabberd.doap | 6 +++--- src/ejabberd.erl | 1 - src/mod_mam.erl | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index b1df5efc7..1fc7df52e 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -758,10 +758,10 @@ - 0.4.0 + 0.4.2 24.02 - complete - + partial + mod_mam, Tombstones not implemented diff --git a/src/ejabberd.erl b/src/ejabberd.erl index 5b6fea3aa..b49d11f7f 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -40,7 +40,6 @@ -protocol({xep, 368, '1.1.0', '17.09', "complete", ""}). -protocol({xep, 386, '0.3.0', '24.02', "complete", ""}). -protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). --protocol({xep, 424, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.03"}). -protocol({xep, 485, '0.2.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). diff --git a/src/mod_mam.erl b/src/mod_mam.erl index a7b47fe91..8df6900ee 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -28,6 +28,7 @@ -protocol({xep, 313, '0.6.1', '15.06', "complete", ""}). -protocol({xep, 334, '0.2', '16.01', "complete", ""}). -protocol({xep, 359, '0.5.0', '15.09', "complete", ""}). +-protocol({xep, 424, '0.4.2', '24.02', "partial", "Tombstones not implemented"}). -protocol({xep, 425, '0.3.0', '24.06', "complete", ""}). -protocol({xep, 441, '0.2.0', '15.06', "complete", ""}). From 05f8992e3ea73f5a24dd0ccbb37b474364385792 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 28 Mar 2025 10:25:59 +0100 Subject: [PATCH 1068/1302] Update man page to 25.03 --- man/ejabberd.yml.5 | 403 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 345 insertions(+), 58 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 493aaee19..90cffcc9a 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 12/17/2024 +.\" Date: 03/28/2025 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "12/17/2024" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "03/28/2025" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,14 +82,14 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/24\&.12/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.03/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 24\&.12\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 25\&.03\&. The options that changed in this version are marked with 🟤\&. .PP -\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLRules|ACLName}}\fR +\fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR .RS 4 This option defines \fIbasic\&.md#access\-rules|Access Rules\fR\&. Each access rule is assigned a name that can be referenced from other parts of the configuration file (mostly from @@ -305,7 +305,7 @@ acme: \fBallow_contrib_modules\fR: \fItrue | false\fR .RS 4 Whether to allow installation of third\-party modules or not\&. See -\fI\&.\&./\&.\&./developer/extending\-ejabberd/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR documentation section\&. The default value is \fItrue\fR\&. .RE @@ -423,7 +423,7 @@ methods to use\&. If several methods are defined, authentication is considered s This is used by the contributed module \fIejabberd_auth_http\fR that can be installed from the -ejabberd\-contrib +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR Git repository\&. Please refer to that module\(cqs README file for details\&. .RE .PP @@ -469,6 +469,17 @@ format\&. You shouldn\(cqt change this if you already have passwords generated w \fIsha\fR\&. .RE .PP +\fBauth_stored_password_types 🟤\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.03\&. List of password types that should be stored simultaneously for each user in database\&. When the user sets the account password, database will be updated to store the password in formats compatible with each type listed here\&. This can be used to migrate user passwords to a more secure format\&. If this option if set, it will override values set in +\fIauth_scram_hash\fR +and +\fIauth_password_format\fR +options\&. The default value is +[]\&. +.RE +.PP \fBauth_use_cache\fR: \fItrue | false\fR .RS 4 Same as @@ -587,11 +598,11 @@ A maximum number of items (not memory!) in cache\&. The rule of thumb, for all t \fINote\fR about this option: improved in 23\&.01\&. Full path to a script that generates \fIbasic\&.md#captcha|CAPTCHA\fR -images\&. +images\&. The keyword \fI@VERSION@\fR is replaced with ejabberd version number in \fIXX\&.YY\fR -format\&. +format\&. The keyword \fI@SEMVER@\fR is replaced with ejabberd version number in semver format when compiled with Elixir\(cqs mix, or XX\&.YY format otherwise\&. Alternatively, it can be the name of a module that implements ejabberd CAPTCHA support\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. .sp @@ -696,14 +707,38 @@ Default volatile (in\-memory) storage for ejabberd\&. Modules and other componen \fImnesia\fR\&. .RE .PP -\fBdefine_macro\fR: \fI{MacroName: MacroValue}\fR +\fBdefine_keyword 🟤\fR: \fI{NAME: Value}\fR .RS 4 -Defines a -\fI\&.\&./configuration/file\-format\&.md#macros\-in\-configuration\-file|macro\fR\&. The value can be any valid arbitrary YAML value\&. For convenience, it\(cqs recommended to define a -\fIMacroName\fR -in capital letters\&. Duplicated macros are not allowed\&. Macros are processed after additional configuration files have been included, so it is possible to use macros that are defined in configuration files included before the usage\&. It is possible to use a -\fIMacroValue\fR -in the definition of another macro\&. +\fINote\fR +about this option: added in 25\&.03\&. Allows to define configuration +\fI\&.\&./configuration/file\-format\&.md#macros\-and\-keywords|keywords\fR\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +define_keyword: + SQL_USERNAME: "eja\&.global" + +host_config: + localhost: + define_keyword: + SQL_USERNAME: "eja\&.localhost" + +sql_username: "prefix\&.@SQL_USERNAME@" +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBdefine_macro 🟤\fR: \fI{NAME: Value}\fR +.RS 4 +\fINote\fR +about this option: improved in 25\&.03\&. Allows to define configuration +\fI\&.\&./configuration/file\-format\&.md#macros\-and\-keywords|macros\fR\&. .sp \fBExample\fR: .sp @@ -911,9 +946,7 @@ host_config: .RS 4 List of one or more \fI\&.\&./configuration/basic\&.md#host\-names|host names\fR -(or domains) that -\fIejabberd\fR -will serve\&. This is a +(or domains) that ejabberd will serve\&. This is a \fBmandatory\fR option\&. .RE @@ -946,7 +979,7 @@ Disallows the usage of those options in the included file .RS 4 \fINote\fR about this option: added in 23\&.10\&. Modules to install from -\fI\&.\&./\&.\&./developer/extending\-ejabberd/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR +\fI\&.\&./\&.\&./admin/guide/modules\&.md#ejabberd\-contrib|ejabberd\-contrib\fR at start time\&. The default value is an empty list of modules: \fI[]\fR\&. .RE @@ -1008,10 +1041,23 @@ Whether to dereference aliases or not\&. The default value is \fBldap_dn_filter\fR: \fI{Filter: FilterAttrs}\fR .RS 4 This filter is applied on the results returned by the main filter\&. The filter performs an additional LDAP lookup to make the complete result\&. This is useful when you are unable to define all filter rules in -\fIldap_filter\fR\&. You can define "%u", "%d", "%s" and "%D" pattern variables in -\fIFilter\fR: "%u" is replaced by a user\(cqs part of the JID, "%d" is replaced by the corresponding domain (virtual host), all "%s" variables are consecutively replaced by values from the attributes in +\fIldap_filter\fR\&. You can define +\fI"%u"\fR, +\fI"%d"\fR, +\fI"%s"\fR +and +\fI"%D"\fR +pattern variables in +\fIFilter: "%u"\fR +is replaced by a user\(cqs part of the JID, +\fI"%d"\fR +is replaced by the corresponding domain (virtual host), all +\fI"%s"\fR +variables are consecutively replaced by values from the attributes in \fIFilterAttrs\fR -and "%D" is replaced by Distinguished Name from the result set\&. There is no default value, which means the result is not filtered\&. WARNING: Since this filter makes additional LDAP lookups, use it only as the last resort: try to define all filter rules in +and +\fI"%D"\fR +is replaced by Distinguished Name from the result set\&. There is no default value, which means the result is not filtered\&. WARNING: Since this filter makes additional LDAP lookups, use it only as the last resort: try to define all filter rules in \fIldap_filter\fR option if possible\&. .sp @@ -1038,7 +1084,10 @@ Whether to encrypt LDAP connection using TLS or not\&. The default value is \fBldap_filter\fR: \fIFilter\fR .RS 4 An LDAP filter as defined in -RFC4515\&. There is no default value\&. Example: "(&(objectClass=shadowAccount)(memberOf=XMPP Users))"\&. NOTE: don\(cqt forget to close brackets and don\(cqt use superfluous whitespaces\&. Also you must not use "uid" attribute in the filter because this attribute will be appended to the filter automatically\&. +RFC4515\&. There is no default value\&. Example: +\fI"(&(objectClass=shadowAccount)(memberOf=XMPP Users))"\fR\&. NOTE: don\(cqt forget to close brackets and don\(cqt use superfluous whitespaces\&. Also you must not use +\fI"uid"\fR +attribute in the filter because this attribute will be appended to the filter automatically\&. .RE .PP \fBldap_password\fR: \fIPassword\fR @@ -1101,11 +1150,15 @@ LDAP attributes which hold a list of attributes to use as alternatives for getti \fIAttr\fR is an LDAP attribute which holds the user\(cqs part of the JID and \fIAttrFormat\fR -must contain one and only one pattern variable "%u" which will be replaced by the user\(cqs part of the JID\&. For example, "%u@example\&.org"\&. If the value is in the form of +must contain one and only one pattern variable +\fI"%u"\fR +which will be replaced by the user\(cqs part of the JID\&. For example, +\fI"%\fR\fIu@example\fR\fI\&.org"\fR\&. If the value is in the form of \fI[Attr]\fR then \fIAttrFormat\fR -is assumed to be "%u"\&. +is assumed to be +\fI"%u"\fR\&. .RE .PP \fBlisten\fR: \fI[Options, \&.\&.\&.]\fR @@ -1193,7 +1246,7 @@ This option can be used to tune tick time parameter of .RS 4 Whether to use the \fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/24\&.12/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/25\&.03/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1434,7 +1487,7 @@ or if the latter is not set\&. .RE .PP -\fBredis_server 🟤\fR: \fIHost | IP Address | Unix Socket Path\fR +\fBredis_server\fR: \fIHost | IP Address | Unix Socket Path\fR .RS 4 \fINote\fR about this option: improved in 24\&.12\&. A hostname, IP address or unix domain socket file of the @@ -1675,7 +1728,7 @@ shaper: .\} .RE .PP -\fBshaper_rules\fR: \fI{ShaperRuleName: {Number|ShaperName: ACLRule|ACLName}}\fR +\fBshaper_rules\fR: \fI{ShaperRuleName: {Number|ShaperName: ACLName|ACLDefinition}}\fR .RS 4 This option defines \fI\&.\&./configuration/basic\&.md#shaper\-rules|shaper rules\fR @@ -1990,7 +2043,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 24\&.12\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 25\&.03\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -2009,6 +2062,63 @@ Provide the Commands item in the Service Discovery\&. Default value: \fIfalse\fR\&. .RE .RE +.SS "mod_adhoc_api 🟤" +.sp +\fINote\fR about this option: added in 25\&.03\&. +.sp +Execute API Commands in a XMPP client using XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBdefault_version\fR: \fIinteger() | string()\fR +.RS 4 +What API version to use\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting +\fI"24\&.06"\fR +in this option implies +\fI2\fR\&. The default value is the latest version\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: jan@localhost + +api_permissions: + "adhoc commands": + from: mod_adhoc_api + who: admin + what: + \- "[tag:roster]" + \- "[tag:session]" + \- stats + \- status + +modules: + mod_adhoc_api: + default_version: 2 +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_admin_extra" .sp This module provides additional administrative commands\&. @@ -2097,13 +2207,27 @@ This module can be used to update existing SQL database from the default to the The module has no options\&. .SS "mod_announce" .sp -This module enables configured users to broadcast announcements and to set the message of the day (MOTD)\&. Configured users can perform these actions with an XMPP client either using Ad\-hoc Commands or sending messages to specific JIDs\&. +This module enables configured users to broadcast announcements and to set the message of the day (MOTD)\&. Configured users can perform these actions with an XMPP client either using Ad\-Hoc Commands or sending messages to specific JIDs\&. +.if n \{\ .sp -Note that this module can be resource intensive on large deployments as it may broadcast a lot of messages\&. This module should be disabled for instances of ejabberd with hundreds of thousands users\&. +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br .sp -The Ad\-hoc Commands are listed in the Server Discovery\&. For this feature to work, \fImod_adhoc\fR must be enabled\&. +This module can be resource intensive on large deployments as it may broadcast a lot of messages\&. This module should be disabled for instances of ejabberd with hundreds of thousands users\&. +.sp .5v +.RE .sp -The specific JIDs where messages can be sent are listed below\&. The first JID in each entry will apply only to the specified virtual host example\&.org, while the JID between brackets will apply to all virtual hosts in ejabberd: +To send announcements using XEP\-0050: Ad\-Hoc Commands, this module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +To send announcements by sending messages to specific JIDs, these are the destination JIDs: .sp .RS 4 .ie n \{\ @@ -2113,7 +2237,7 @@ The specific JIDs where messages can be sent are listed below\&. The first JID i .sp -1 .IP \(bu 2.3 .\} -example\&.org/announce/all (example\&.org/announce/all\-hosts/all):: The message is sent to all registered users\&. If the user is online and connected to several resources, only the resource with the highest priority will receive the message\&. If the registered user is not connected, the message will be stored offline in assumption that offline storage (see +\fIexample\&.org/announce/all\fR: Send the message to all registered users in that vhost\&. If the user is online and connected to several resources, only the resource with the highest priority will receive the message\&. If the registered user is not connected, the message is stored offline in assumption that offline storage (see \fImod_offline\fR) is enabled\&. .RE .sp @@ -2125,7 +2249,7 @@ example\&.org/announce/all (example\&.org/announce/all\-hosts/all):: The message .sp -1 .IP \(bu 2.3 .\} -example\&.org/announce/online (example\&.org/announce/all\-hosts/online):: The message is sent to all connected users\&. If the user is online and connected to several resources, all resources will receive the message\&. +\fIexample\&.org/announce/online\fR: Send the message to all connected users\&. If the user is online and connected to several resources, all resources will receive the message\&. .RE .sp .RS 4 @@ -2136,7 +2260,8 @@ example\&.org/announce/online (example\&.org/announce/all\-hosts/online):: The m .sp -1 .IP \(bu 2.3 .\} -example\&.org/announce/motd (example\&.org/announce/all\-hosts/motd):: The message is set as the message of the day (MOTD) and is sent to users when they login\&. In addition the message is sent to all connected users (similar to announce/online)\&. +\fIexample\&.org/announce/motd\fR: Set the message of the day (MOTD) that is sent to users when they login\&. Also sends the message to all connected users (similar to +\fIannounce/online\fR)\&. .RE .sp .RS 4 @@ -2147,7 +2272,7 @@ example\&.org/announce/motd (example\&.org/announce/all\-hosts/motd):: The messa .sp -1 .IP \(bu 2.3 .\} -example\&.org/announce/motd/update (example\&.org/announce/all\-hosts/motd/update):: The message is set as message of the day (MOTD) and is sent to users when they login\&. The message is not sent to any currently connected user\&. +\fIexample\&.org/announce/motd/update\fR: Set the message of the day (MOTD) that is sent to users when they login\&. This does not send the message to any currently connected user\&. .RE .sp .RS 4 @@ -2158,7 +2283,64 @@ example\&.org/announce/motd/update (example\&.org/announce/all\-hosts/motd/updat .sp -1 .IP \(bu 2.3 .\} -example\&.org/announce/motd/delete (example\&.org/announce/all\-hosts/motd/delete):: Any message sent to this JID removes the existing message of the day (MOTD)\&. +\fIexample\&.org/announce/motd/delete\fR: Remove the existing message of the day (MOTD) by sending a message to this JID\&. +.RE +.sp +There are similar destination JIDs to apply to all virtual hosts in ejabberd: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/all\fR: send to all registered accounts +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/online\fR: send to online sessions +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd\fR: set MOTD and send to online +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd/update\fR: update MOTD +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIexample\&.org/announce/all\-hosts/motd/delete\fR: delete MOTD .RE .sp .it 1 an-trap @@ -2211,11 +2393,11 @@ Same as top\-level option, but applied to this module only\&. .RE .RE -.SS "mod_auth_fast 🟤" +.SS "mod_auth_fast" .sp \fINote\fR about this option: added in 24\&.12\&. .sp -The module adds support for XEP\-0480: Fast Authentication Streamlining Tokens that allows users to authenticate using self managed tokens\&. +The module adds support for XEP\-0484: Fast Authentication Streamlining Tokens that allows users to authenticate using self\-managed tokens\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -2225,7 +2407,7 @@ The module adds support for XEP\-0480: Fast Authentication Streamlining Tokens t \fBAvailable options:\fR .RS 4 .PP -\fBdb_type\fR: \fImnesia | sql\fR +\fBdb_type\fR: \fImnesia\fR .RS 4 Same as top\-level \fIdefault_db\fR @@ -2234,7 +2416,7 @@ option, but applied to this module only\&. .PP \fBtoken_lifetime\fR: \fItimeout()\fR .RS 4 -Time that tokens will be keept, measured from it\(cqs creation time\&. Default value set to 30 days +Time that tokens will be kept, measured from it\(cqs creation time\&. Default value set to 30 days .RE .PP \fBtoken_refresh_age\fR: \fItimeout()\fR @@ -2257,7 +2439,7 @@ This time determines age of token, that qualifies for automatic refresh\&. Defau .nf modules: mod_auth_fast: - token_timeout: 14days + token_lifetime: 14days .fi .if n \{\ .RE @@ -2578,9 +2760,90 @@ While a client is inactive, queue presence stanzas that indicate (un)availabilit .RE .SS "mod_configure" .sp -The module provides server configuration functionality via XEP\-0050: Ad\-Hoc Commands\&. Implements many commands as defined in XEP\-0133: Service Administration\&. This module requires \fImod_adhoc\fR to be loaded\&. +The module provides server configuration functionalities using XEP\-0030: Service Discovery and XEP\-0050: Ad\-Hoc Commands: .sp -The module has no options\&. +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +List and discover outgoing s2s, online client sessions and all registered accounts +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Most of the ad\-hoc commands defined in +XEP\-0133: Service Administration +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Additional custom ad\-hoc commands specific to ejabberd +.RE +.sp +This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +.sp +Please notice that all the ad\-hoc commands implemented by this module have an equivalent API Command that you can execute using \fImod_adhoc_api\fR or any other API frontend\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess 🟤\fR: \fIAccessName\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.03\&. This option defines which access rule will be used to control who is allowed to access the features provided by this module\&. The default value is +\fIconfigure\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +acl: + admin: + user: sun@localhost + +access_rules: + configure: + allow: admin + +modules: + mod_configure: + access: configure +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_conversejs" .sp \fINote\fR about this option: added in 21\&.12 and improved in 22\&.05\&. @@ -3060,7 +3323,8 @@ To run a command, send a POST request to the corresponding URL: \fIhttp://localh .PP \fBdefault_version\fR: \fIinteger() | string()\fR .RS 4 -What API version to use when none is specified in the URL path\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting +\fINote\fR +about this option: added in 24\&.12\&. What API version to use when none is specified in the URL path\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting \fI"24\&.06"\fR in this option implies \fI2\fR\&. The default value is the latest version\&. @@ -3578,6 +3842,22 @@ The module has no options\&. .SS "mod_mam" .sp This module implements XEP\-0313: Message Archive Management and XEP\-0441: Message Archive Management Preferences\&. Compatible XMPP clients can use it to store their chat history on the server\&. +.if n \{\ +.sp +.\} +.RS 4 +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBNote\fR +.ps -1 +.br +.sp +Mnesia backend for mod_mam is not recommended: it\(cqs limited to 2GB and often gets corrupted when reaching this limit\&. SQL backend is recommended\&. Namely, for small servers SQLite is a preferred choice because it\(cqs very easy to configure\&. +.sp .5v +.RE .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -3676,11 +3956,11 @@ When this option is disabled, for each individual subscriber a separate mucsub m \fIfalse\fR\&. .RE .RE -.SS "mod_matrix_gw" +.SS "mod_matrix_gw 🟤" .sp -\fINote\fR about this option: added in 24\&.02\&. +\fINote\fR about this option: improved in 25\&.03\&. .sp -Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. +Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -4349,9 +4629,12 @@ in order to accept their join in the room\&. The default value is Short description of the room\&. The default value is an empty string\&. .RE .PP -\fBenable_hats\fR: \fItrue | false\fR +\fBenable_hats 🟤\fR: \fItrue | false\fR .RS 4 -Allow extended roles as defined in XEP\-0317 Hats\&. The default value is +\fINote\fR +about this option: improved in 25\&.03\&. Allow extended roles as defined in XEP\-0317 Hats\&. Check the +\fI\&.\&./\&.\&./tutorials/muc\-hats\&.md|MUC Hats\fR +tutorial\&. The default value is \fIfalse\fR\&. .RE .PP @@ -4467,6 +4750,12 @@ A human\-readable title of the room\&. There is no default value A custom vCard for the room\&. See the equivalent mod_muc option\&.The default value is an empty string\&. .RE .PP +\fBvcard_xupdate\fR: \fIundefined | external | AvatarHash\fR +.RS 4 +Set the hash of the avatar image\&. The default value is +\fIundefined\fR\&. +.RE +.PP \fBvoice_request_min_interval\fR: \fINumber\fR .RS 4 Minimum interval between voice requests, in seconds\&. The default value is @@ -6308,9 +6597,7 @@ is .PP \fBaccess_from\fR: \fIAccessName\fR .RS 4 -By default, -\fIejabberd\fR -doesn\(cqt allow the client to register new accounts from s2s or existing c2s sessions\&. You can change it by defining access rule in this option\&. Use with care: allowing registration from s2s leads to uncontrolled massive accounts creation by rogue users\&. +By default, ejabberd doesn\(cqt allow the client to register new accounts from s2s or existing c2s sessions\&. You can change it by defining access rule in this option\&. Use with care: allowing registration from s2s leads to uncontrolled massive accounts creation by rogue users\&. .RE .PP \fBaccess_remove\fR: \fIAccessName\fR @@ -8085,7 +8372,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 24\&.12\&. +This section describes listeners options of ejabberd 25\&.03\&. .sp TODO .SH "AUTHOR" @@ -8093,13 +8380,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 24\&.12\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 25\&.03\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/24\&.12/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.03/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From f67743643701cfdf645d954fbd8187caf496cb20 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 28 Mar 2025 10:34:17 +0100 Subject: [PATCH 1069/1302] Update changelog to 25.03 --- CHANGELOG.md | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e1fc3bec..7c5363677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,117 @@ +## Version 25.03 + +#### Commands API +- `ejabberdctl`: New option `CTL_OVER_HTTP` ([#4340](https://github.com/processone/ejabberd/issues/4340)) +- `ejabberd_web_admin`: Support commands with tuple arguments +- `mod_adhoc_api`: New module to execute API Commands using Ad-Hoc Commands ([#4357](https://github.com/processone/ejabberd/issues/4357)) +- `mod_http_api`: Sort list elements in a command result +- Show warning when registering command with an existing name +- Fix commands unregistration +- `change_room_option`: Add forgotten support to set `enable_hats` room option +- `change_room_option`: Verify room option value before setting it ([#4337](https://github.com/processone/ejabberd/issues/4337)) +- `create_room_with_opts`: Recommend using `;` and `=` separators +- `list_cluster_detailed`: Fix crash when a node is down +- `mnesia_list_tables`: Allow using this internal command +- `mnesia_table_change_storage`: Allow using this internal command +- `status`: Separate command result with newline +- `update_sql`: Fix updating tables created by ejabberd internally +- `update_sql`: Fix MySQL support + +#### Configuration +- `acl`: Fix bug matching the acl `shared_group: NAME` +- `define_keyword`: New option to define keywords ([#4350](https://github.com/processone/ejabberd/issues/4350)) +- `define_macro`: Add option to `globals()` because it's useless inside `host_config` +- `ejabberd.yml.example`: Enable `mod_muc_occupantid` by default +- Add support to use keywords in toplevel, listener and modules +- Show warning also when deprecated listener option is set as disabled ([#4345](https://github.com/processone/ejabberd/issues/4345)) + +#### Container +- Bump versions to Erlang/OTP 27.3 and Elixir 1.18.3 +- Add `ERL_FLAGS` to compile elixir on qemu cross-platform +- Copy files to stable path, add ecs backwards compatibility +- Fix warning about relative workdir +- Improve entrypoint script: register account, or set random +- Link path to Mnesia spool dir for backwards compatibility +- Place `sockets/` outside `database/` +- Use again direct METHOD, qemu got fixed ([#4280](https://github.com/processone/ejabberd/issues/4280)) +- `ejabberd.yml.example`: Copy main example configuration file +- `ejabberd.yml.example`: Define and use macros in the default configuration file +- `ejabberd.yml.example`: Enable `CTL_OVER_HTTP` by default +- `ejabberd.yml.example`: Listen for webadmin in a port number lower than any other +- `ejabberdapi`: Compile during build +- `CONTAINER.md`: Include documentation for ecs container image + +#### Core and Modules +- `ejabberd_auth`: Add support for `auth_stored_password_types` +- `ejabberd_router`: Don't rewrite "self-addressed" privileged IQs as results ([#4348](https://github.com/processone/ejabberd/issues/4348)) +- `misc`: Fix json version of `json_encode_with_kv_list` for nested kv lists ([#4338](https://github.com/processone/ejabberd/issues/4338)) +- OAuth: Fix crashes when oauth is feed with invalid jid ([#4355](https://github.com/processone/ejabberd/issues/4355)) +- PubSub: Bubble up db errors in `nodetree_tree_sql:set_node` +- `mod_configure`: Add option `access` to let configure the access name +- `mod_mix_pam`: Remove `Channels` roster group of mix channels ([#4297](https://github.com/processone/ejabberd/issues/4297)) +- `mod_muc`: Document MUC room option vcard_xupdate +- `mod_privilege`: Accept non-privileged IQs from privileged components ([#4341](https://github.com/processone/ejabberd/issues/4341)) +- `mod_private`: Improve exception handling +- `mod_private`: Don't warn on conversion errors +- `mod_private`: Handle invalid PEP-native bookmarks +- `mod_private`: Don't crash on invalid bookmarks +- `mod_s2s_bidi`: Stop processing other handlers in s2s_in_handle_info ([#4344](https://github.com/processone/ejabberd/issues/4344)) +- `mod_s2s_bidi`: Fix issue with wrong namespace + +#### Dependencies +- `ex_doc`: Bump to 0.37.2 +- `stringprep`: Bump to 1.0.31 +- `provider_asn1`: Bump to 0.4.1 +- `xmpp` Bump to bring fix for ssdp hash calculation +- `xmpp` Bump to get support for webchat_url ([#3041](https://github.com/processone/ejabberd/issues/3041)) +- `xmpp` Bump to get XEP-0317 Hats namespaces version 0.2.0 +- `xmpp` Bump to bring SSDP to XEP version 0.4 +- `yconf` Bump to support macro inside string + +#### Development and Testing +- `mix.exs`: Keep debug info when building `dev` release +- `mix.exs`: The `ex_doc` dependency is only relevant for the `edoc` Mix environment +- `ext_mod`: add `$libdir/include` to include path +- `ext_mod`: fix greedy include path ([#4359](https://github.com/processone/ejabberd/issues/4359)) +- `gen_mod`: Support registering commands and `hook_subscribe` in `start/2` result +- `c2s_handle_bind`: New event in `ejabberd_c2s` ([#4356](https://github.com/processone/ejabberd/issues/4356)) +- `muc_disco_info_extras`: New event `mod_muc_room` useful for `mod_muc_webchat_url` ([#3041](https://github.com/processone/ejabberd/issues/3041)) +- VSCode: Fix compiling support +- Add tests for config features `define_macro` and `define_keyword` +- Allow test to run using `ct_run` +- Fixes to handle re-running test after `update_sql` +- Uninstall `mod_example` when the tests has finished + +#### Documentation +- Add XEPs that are indirectly supported and required by XEP-0479 +- Document that XEP-0474 0.4.0 was recently upgraded +- Don't use backtick quotes for ejabberd name +- Fix values allowed in db_type of mod_auth_fast documentation +- Reword explanation about ACL names and definitions +- Update moved or broken URLs in documentation + +#### Installers +- Bump Erlang/OTP 27.3 and Elixir 1.18.3 +- Bump OpenSSL 3.4.1 +- Bump crosstool-NG 1.27.0 +- Fix building Termcap and Linux-PAM + +#### Matrix Gateway +- Preserve XMPP message IDs in Matrix rooms +- Better Matrix room topic and room roles to MUC conversion, support room aliases in invites +- Add `muc#user` element to presences and an initial empty subject +- Fix `gen_iq_handler:remove_iq_handler` call +- Properly handle IQ requests +- Support Matrix room aliases +- Fix handling of 3PI events + +#### Unix Domain Socket +- Add support for socket relative path +- Use `/tmp` for temporary socket, as path is restricted to 107 chars +- Handle unix socket when logging remote client +- When stopping listener, delete Unix Domain Socket file +- `get_auto_url` option: Don't build auto URL if port is unix domain socket ([#4345](https://github.com/processone/ejabberd/issues/4345)) + ## Version 24.12 #### Miscelanea From 85d0e93af57ecce96eeb649dbca40944fd088095 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 28 Mar 2025 10:38:16 +0100 Subject: [PATCH 1070/1302] Set version to 25.03 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9e68cd390..5d1d0d68d 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 24.12` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.03` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From 0827a5116f15e161fe7110e8a476b9b0a2ad2ede Mon Sep 17 00:00:00 2001 From: Marcos de Vera Piquero Date: Fri, 28 Mar 2025 15:56:03 +0100 Subject: [PATCH 1071/1302] ejabberdctl: add new `kick_users host` command This new command accepts a host name and will kick all user sessions found for that given host. The result is the number of kicked sessions. --- src/ejabberd_sm.erl | 18 +++++++++++++++++- src/mod_admin_extra.erl | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index c1d970d84..3e7c2e7e3 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -63,6 +63,7 @@ kick_user/2, kick_user/3, kick_user_restuple/2, + kick_users/1, get_session_pid/3, get_session_sid/3, get_session_sids/2, @@ -1072,7 +1073,17 @@ get_commands_spec() -> args_example = [<<"user1">>, <<"example.com">>], result_desc = "The result text indicates the number of sessions that were kicked", result_example = {ok, <<"Kicked sessions: 2">>}, - result = {res, restuple}}]. + result = {res, restuple}}, + + #ejabberd_commands{name = kick_users, tags = [session], + desc = "Disconnect all given host users' active sessions", + module = ?MODULE, function = kick_users, + args = [{host, binary}], + args_desc = ["Server name"], + args_example = [<<"example.com">>], + result_desc = "Number of sessions that were kicked", + result_example = 3, + result = {num_sessions, integer}}]. -spec connected_users() -> [binary()]. @@ -1111,5 +1122,10 @@ kick_user_restuple(User, Server) -> NumberBin = integer_to_binary(kick_user(User, Server)), {ok, <<"Kicked sessions: ", NumberBin/binary>>}. +-spec kick_users(binary()) -> non_neg_integer(). +kick_users(Server) -> + length([kick_user(U, S, R) || {U, S, R} <-get_vh_session_list(Server)]). + + make_sid() -> {misc:unique_timestamp(), self()}. diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 15d65bb0b..b6eed30df 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2277,6 +2277,7 @@ web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) -> Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []), make_command(set_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []), make_command(kick_user, R, [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}]), + make_command(kick_users, R, [{<<"host">>, Host}], [{style, danger}]), make_command(kick_session, R, [{<<"user">>, User}, {<<"host">>, Host}], From 5008947e326f24fa88f9c06c970630047d6b1020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 31 Mar 2025 11:49:30 +0200 Subject: [PATCH 1072/1302] Make sqlite update_primary_key when copying data use list of columns and not * Since * depends on order of columns, if original table have different column layout we could get resulting columns in wrong order. This is fix for issue #4365 --- src/ejabberd_sql_schema.erl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_sql_schema.erl b/src/ejabberd_sql_schema.erl index f798af5b8..8d2830101 100644 --- a/src/ejabberd_sql_schema.erl +++ b/src/ejabberd_sql_schema.erl @@ -410,18 +410,21 @@ sqlite_table_copy_t(SchemaInfo, Table) -> NewTableName = <<"new_", TableName/binary>>, NewTable = Table#sql_table{name = NewTableName}, create_table_t(SchemaInfo, NewTable), - SQL2 = <<"INSERT INTO ", NewTableName/binary, - " SELECT * FROM ", TableName/binary>>, + Columns = lists:join(<<",">>, + lists:map(fun(C) -> escape_name(SchemaInfo, C#sql_column.name) end, + Table#sql_table.columns)), + SQL2 = [<<"INSERT INTO ">>, NewTableName, + <<" SELECT ">>, Columns, <<" FROM ">>, TableName], ?INFO_MSG("Copying table ~s to ~s:~n~s~n", [TableName, NewTableName, SQL2]), ejabberd_sql:sql_query_t(SQL2), SQL3 = <<"DROP TABLE ", TableName/binary>>, ?INFO_MSG("Droping old table ~s:~n~s~n", - [TableName, SQL2]), + [TableName, SQL3]), ejabberd_sql:sql_query_t(SQL3), SQL4 = <<"ALTER TABLE ", NewTableName/binary, " RENAME TO ", TableName/binary>>, - ?INFO_MSG("Renameing table ~s to ~s:~n~s~n", + ?INFO_MSG("Renaming table ~s to ~s:~n~s~n", [NewTableName, TableName, SQL4]), ejabberd_sql:sql_query_t(SQL4). From a8a5be7a3423f07a000401db801b9d87697248a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 06:07:45 +0000 Subject: [PATCH 1073/1302] build(deps): bump stun from 1.2.17 to 1.2.19 Bumps [stun](https://github.com/processone/stun) from 1.2.17 to 1.2.19. - [Changelog](https://github.com/processone/stun/blob/master/CHANGELOG.md) - [Commits](https://github.com/processone/stun/compare/1.2.17...1.2.19) --- updated-dependencies: - dependency-name: stun dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index c0c7f254f..3b2edb4ba 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, - "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, + "stun": {:hex, :stun, "1.2.19", "ff5bd2d2e3a0c2ade41fc71a7a069eebaa492ecdb35eca35350fff3c194b381a", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "66dc035ebf21de8abe51eccc2c3d4bbf63c78650f74c3afcaf2e4bb15c555927"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "xmpp": {:hex, :xmpp, "1.10.0", "68a6dff8db8987c4592b2d5dd71d3f947b4ebd15209c9acaca5909a642670630", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ceeae43b8fe97649d8f8546b3f7f2b38ecfc931c0cdd5c7445ffb3f80fcb7d85"}, "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, diff --git a/rebar.config b/rebar.config index 0d09d7f22..fa653a8a4 100644 --- a/rebar.config +++ b/rebar.config @@ -76,7 +76,7 @@ {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, - {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, + {stun, "~> 1.2.19", {git, "https://github.com/processone/stun", {tag, "1.2.19"}}}}, {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", {tag, "1.10.0"}}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 2c212b330..47b2e53b5 100644 --- a/rebar.lock +++ b/rebar.lock @@ -22,7 +22,7 @@ {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.10.0">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. @@ -51,7 +51,7 @@ {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, - {<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, + {<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, {<<"xmpp">>, <<"68A6DFF8DB8987C4592B2D5DD71D3F947B4EBD15209C9ACACA5909A642670630">>}, {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, @@ -79,7 +79,7 @@ {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, - {<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, + {<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, {<<"xmpp">>, <<"CEEAE43B8FE97649D8F8546B3F7F2B38ECFC931C0CDD5C7445FFB3F80FCB7D85">>}, {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} From ed6a11198230058fe40c8d388ef28353abff5bc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 06:16:42 +0000 Subject: [PATCH 1074/1302] build(deps): bump XMPP-Interop-Testing/xmpp-interop-tests-action Bumps [XMPP-Interop-Testing/xmpp-interop-tests-action](https://github.com/xmpp-interop-testing/xmpp-interop-tests-action) from 1.4.0 to 1.5.0. - [Commits](https://github.com/xmpp-interop-testing/xmpp-interop-tests-action/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: XMPP-Interop-Testing/xmpp-interop-tests-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28efecd1d..38323b2a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: - name: Run XMPP Interoperability Tests against CI server. if: matrix.otp == '27' continue-on-error: true - uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.4.0 + uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.5.0 with: domain: 'localhost' adminAccountUsername: 'admin' From 78093735b7a4f21b9640802c3a72c25b7071148b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 11:17:26 +0200 Subject: [PATCH 1075/1302] ejabberdctl: Add backward support for mnesia path with nodename (#4366) --- ejabberdctl.template | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 7858067d1..9d91e261c 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -71,6 +71,10 @@ done : "${SPOOL_DIR:="{{spool_dir}}"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" +# backward support for old mnesia spool dir path +: "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}" +[ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD" + # define erl parameters ERLANG_OPTS="+K $POLL +P $ERL_PROCESSES $ERL_OPTIONS" if [ -n "$FIREWALL_WINDOW" ] ; then From c343ef7aad28990959af09ed421c0a15316b1d10 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 13:41:59 +0200 Subject: [PATCH 1076/1302] Container: Apply recent ejabberdctl backward support code --- .github/container/ejabberdctl.template | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 2353040d5..228f8707c 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -71,6 +71,10 @@ done : "${SPOOL_DIR:="{{spool_dir}}"}" : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" +# backward support for old mnesia spool dir path +: "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}" +[ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD" + # define erl parameters ERLANG_OPTS="+K $POLL +P $ERL_PROCESSES $ERL_OPTIONS" if [ -n "$FIREWALL_WINDOW" ] ; then From 91542754312139de395e5d1b4460e3620f72c546 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 12:42:43 +0200 Subject: [PATCH 1077/1302] Revert "build(deps): bump stun from 1.2.17 to 1.2.19" This reverts commit a8a5be7a3423f07a000401db801b9d87697248a9. Don't upgrade to stun 1.2.19 yet, because esip still depends on stun 1.2.17: Because "the lock" depends on "esip 1.0.57" which depends on "stun 1.2.17", "the lock" requires "stun 1.2.17". And because "the lock" specifies "stun 1.2.19", no version of "the lock" is allowed. So, because "your app" depends on "the lock", version solving failed. ** (Mix) Hex dependency resolution failed --- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index 3b2edb4ba..c0c7f254f 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, - "stun": {:hex, :stun, "1.2.19", "ff5bd2d2e3a0c2ade41fc71a7a069eebaa492ecdb35eca35350fff3c194b381a", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "66dc035ebf21de8abe51eccc2c3d4bbf63c78650f74c3afcaf2e4bb15c555927"}, + "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "xmpp": {:hex, :xmpp, "1.10.0", "68a6dff8db8987c4592b2d5dd71d3f947b4ebd15209c9acaca5909a642670630", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ceeae43b8fe97649d8f8546b3f7f2b38ecfc931c0cdd5c7445ffb3f80fcb7d85"}, "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, diff --git a/rebar.config b/rebar.config index fa653a8a4..0d09d7f22 100644 --- a/rebar.config +++ b/rebar.config @@ -76,7 +76,7 @@ {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, - {stun, "~> 1.2.19", {git, "https://github.com/processone/stun", {tag, "1.2.19"}}}}, + {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", {tag, "1.10.0"}}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 47b2e53b5..2c212b330 100644 --- a/rebar.lock +++ b/rebar.lock @@ -22,7 +22,7 @@ {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.10.0">>},0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. @@ -51,7 +51,7 @@ {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, - {<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>}, + {<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, {<<"xmpp">>, <<"68A6DFF8DB8987C4592B2D5DD71D3F947B4EBD15209C9ACACA5909A642670630">>}, {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, @@ -79,7 +79,7 @@ {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, - {<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>}, + {<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, {<<"xmpp">>, <<"CEEAE43B8FE97649D8F8546B3F7F2B38ECFC931C0CDD5C7445FFB3F80FCB7D85">>}, {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} From 67aaf93157867b2e7253f8c5f994fdf7083d7615 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 13:23:39 +0200 Subject: [PATCH 1078/1302] Runtime: Pull images from ECR instead of Docker Hub Pull images from Amazon ECR (Elastic Container Registry) Public Gallery instead of Docker Hub to reduce consumption of the pull limits https://www.docker.com/blog/revisiting-docker-hub-policies-prioritizing-developer-experience/ --- .github/workflows/runtime.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 076798aba..e0b7469e4 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -38,7 +38,7 @@ jobs: rebar: 'rebar' runs-on: ubuntu-24.04 container: - image: erlang:${{ matrix.otp }} + image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }} steps: @@ -167,7 +167,7 @@ jobs: elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: - image: elixir:${{ matrix.elixir }} + image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} steps: @@ -290,7 +290,7 @@ jobs: elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: - image: elixir:${{ matrix.elixir }} + image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} steps: From d842d6772d4f17df73ae42ff4a7359852d0e419c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 15:27:33 +0200 Subject: [PATCH 1079/1302] CI: Pull Redis image from ECR instead of Docker Hub Pull images from Amazon ECR (Elastic Container Registry) Public Gallery instead of Docker Hub to reduce consumption of the pull limits https://www.docker.com/blog/revisiting-docker-hub-policies-prioritizing-developer-experience/ --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38323b2a6..86fffe591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-20.04 services: redis: - image: redis + image: public.ecr.aws/docker/library/redis ports: - 6379:6379 From 17b605a32befcfb9e880922aacdcc58530da4f11 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 3 Apr 2025 13:21:00 +0200 Subject: [PATCH 1080/1302] kick_users command: Move to "Online Users" page, disable auto-execution kick_users can be considered a "modifier" command with "informative" result as described in include/ejabberd_commands.hrl ejabberd_web_admin executes automatically a command if: - it returns something different than rescode or restuple - and all its arguments are provided - and force_execution is set to true or undefined --- src/ejabberd_sm.erl | 1 + src/ejabberd_web_admin.erl | 7 +++++-- src/mod_admin_extra.erl | 1 - 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 3e7c2e7e3..77b911eac 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -1078,6 +1078,7 @@ get_commands_spec() -> #ejabberd_commands{name = kick_users, tags = [session], desc = "Disconnect all given host users' active sessions", module = ?MODULE, function = kick_users, + note = "added in 25.xx", args = [{host, binary}], args_desc = ["Server name"], args_example = [<<"example.com">>], diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 96b232184..dfb42d57d 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -584,12 +584,15 @@ process_admin(Host, #request{path = [<<"users">> | RPath], lang = Lang} = R, AJI process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> Level = 3 + length(RPath), - Res = [make_command(connected_users_vhost, + Set = [make_command(kick_users, R, [{<<"host">>, Host}], + [{style, danger}, {force_execution, false}])], + timer:sleep(200), % small delay after kicking users before getting the updated list + Get = [make_command(connected_users_vhost, R, [{<<"host">>, Host}], [{table_options, {100, RPath}}, {result_links, [{sessions, user, Level, <<"">>}]}])], - make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Res, Host, R, AJID, Level); + make_xhtml([?XCT(<<"h1">>, ?T("Online Users"))] ++ Set ++ Get, Host, R, AJID, Level); process_admin(Host, #request{path = [<<"last-activity">>], q = Query, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index b6eed30df..15d65bb0b 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -2277,7 +2277,6 @@ web_page_hostuser(_, Host, User, #request{path = [<<"session">>]} = R) -> Set = [make_command(resource_num, R, [{<<"user">>, User}, {<<"host">>, Host}], []), make_command(set_presence, R, [{<<"user">>, User}, {<<"host">>, Host}], []), make_command(kick_user, R, [{<<"user">>, User}, {<<"host">>, Host}], [{style, danger}]), - make_command(kick_users, R, [{<<"host">>, Host}], [{style, danger}]), make_command(kick_session, R, [{<<"user">>, User}, {<<"host">>, Host}], From babd01a87f032016eb4aaefc9ebb078671c2e4d0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 2 Apr 2025 13:24:41 +0200 Subject: [PATCH 1081/1302] Container: Improve explanation of CTL_ON ignore prefix --- CONTAINER.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index 8badba852..077d5f0c6 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -266,15 +266,21 @@ and reads `CTL_ON_START` every time the container is started. Those variables can contain one ejabberdctl command, or several commands separated with the blankspace and `;` characters. -By default failure of any of commands executed that way would -abort start, this can be disabled by prefixing commands with `!` +If any of those commands returns a failure, the container starting gets aborted. +If there is a command with a result that can be ignored, +prefix that command with `!` -Example usage (or check the [full example](#customized-example)): +This example, registers an `admin@localhost` account when the container is first created. +Everytime the container starts, it shows the list of registered accounts, +checks that the admin account exists and password is valid, +changes the password of an account if it exists (ignoring any failure), +and shows the ejabberd starts (check also the [full example](#customized-example)): ```yaml environment: - - CTL_ON_CREATE=! register admin localhost asd + - CTL_ON_CREATE=register admin localhost asd - CTL_ON_START=stats registeredusers ; check_password admin localhost asd ; + ! change_password bot123 localhost qqq ; status ``` From c3af613db162a4f30035d625b523311bf7c70d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 4 Apr 2025 11:03:53 +0200 Subject: [PATCH 1082/1302] Catch errors from mod_shared_roster:create_group in srg_* commands --- src/mod_admin_extra.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 15d65bb0b..defe90cb4 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1899,16 +1899,20 @@ srg_create2(Group, Host, Label, Description, DisplayList) -> Opts = [{label, Label}, {displayed_groups, DisplayList}, {description, Description}], - {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts), - ok. + case mod_shared_roster:create_group(Host, Group, Opts) of + {atomic, _} -> ok; + {error, Err} -> Err + end. srg_add(Group, Host) -> Opts = [{label, <<"">>}, {description, <<"">>}, {displayed_groups, []} ], - {atomic, _} = mod_shared_roster:create_group(Host, Group, Opts), - ok. + case mod_shared_roster:create_group(Host, Group, Opts) of + {atomic, _} -> ok; + {error, Err} -> Err + end. srg_delete(Group, Host) -> {atomic, _} = mod_shared_roster:delete_group(Host, Group), From 780031847c08219d1a7b9851556215c2f5e0739c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 4 Apr 2025 11:07:41 +0200 Subject: [PATCH 1083/1302] Relax limits of shared groups names We want to normalize it, but we don't need to limit it to what nodeprep allows (like for example ':' that we see in use) --- src/mod_shared_roster.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 479956d59..1c9e6f88f 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -381,7 +381,7 @@ create_group(Host, Group) -> create_group(Host, Group, []). create_group(Host, Group, Opts) -> - case jid:nodeprep(Group) of + case jid:nameprep(Group) of error -> {error, invalid_group_name}; LGroup -> From 425504454c639132bbf2479e0472bf51b853cf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 8 Apr 2025 14:21:18 +0200 Subject: [PATCH 1084/1302] Allow passing multiple paths in external_beams --- src/ejabberd_config.erl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index fd4fac591..92ccae3a5 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -305,14 +305,21 @@ beams(external) -> end end, ExtMods), case application:get_env(ejabberd, external_beams) of - {ok, Path} -> - case lists:member(Path, code:get_path()) of - true -> ok; - false -> code:add_patha(Path) - end, - Beams = filelib:wildcard(filename:join(Path, "*\.beam")), - CustMods = [list_to_atom(filename:rootname(filename:basename(Beam))) - || Beam <- Beams], + {ok, Path0} -> + Paths = case Path0 of + [L|_] = V when is_list(L) -> V; + L -> [L] + end, + CustMods = lists:foldl( + fun(Path, CM) -> + case lists:member(Path, code:get_path()) of + true -> ok; + false -> code:add_patha(Path) + end, + Beams = filelib:wildcard(filename:join(Path, "*\.beam")), + CM ++ [list_to_atom(filename:rootname(filename:basename(Beam))) + || Beam <- Beams] + end, [], Paths), CustMods ++ ExtMods; _ -> ExtMods From c98739d5b5721d036b5bb9682890ed4c398cc516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 14 Apr 2025 16:42:00 +0200 Subject: [PATCH 1085/1302] Replace all occupand_id tags --- src/mod_muc_occupantid.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_occupantid.erl b/src/mod_muc_occupantid.erl index 338697b34..1e8eabee2 100644 --- a/src/mod_muc_occupantid.erl +++ b/src/mod_muc_occupantid.erl @@ -71,7 +71,7 @@ add_occupantid_packet(Packet, RoomJid) -> From = xmpp:get_from(Packet), OccupantId = calculate_occupantid(From, RoomJid), OccupantElement = #occupant_id{id = OccupantId}, - xmpp:set_subtag(Packet, OccupantElement). + xmpp:append_subtags(xmpp:remove_subtag(Packet, OccupantElement), [OccupantElement]). calculate_occupantid(From, RoomJid) -> Term = {jid:remove_resource(From), get_salt(RoomJid)}, From 602a42f5ce51158c3ef5036e144a2b306b15ee40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 16 Apr 2025 11:39:30 +0200 Subject: [PATCH 1086/1302] Add tests for duplicate occupant-id handling --- test/ejabberd_SUITE_data/ejabberd.mnesia.yml | 1 + test/ejabberd_SUITE_data/ejabberd.mssql.yml | 1 + test/ejabberd_SUITE_data/ejabberd.mysql.yml | 1 + test/ejabberd_SUITE_data/ejabberd.pgsql.yml | 1 + test/ejabberd_SUITE_data/ejabberd.redis.yml | 1 + test/ejabberd_SUITE_data/ejabberd.sqlite.yml | 4 ++ test/ejabberd_SUITE_data/ejabberd.yml | 1 + test/muc_tests.erl | 73 ++++++++++++++++++-- 8 files changed, 79 insertions(+), 4 deletions(-) diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml index 14bb2bff2..42ab23ab2 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml @@ -14,6 +14,7 @@ define_macro: mod_muc: db_type: internal vcard: VCARD + mod_muc_occupantid: [] mod_offline: db_type: internal mod_privacy: diff --git a/test/ejabberd_SUITE_data/ejabberd.mssql.yml b/test/ejabberd_SUITE_data/ejabberd.mssql.yml index 66bfee3f9..1458cafa4 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mssql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mssql.yml @@ -22,6 +22,7 @@ define_macro: db_type: sql ram_db_type: sql vcard: VCARD + mod_muc_occupantid: [] mod_offline: use_cache: true db_type: sql diff --git a/test/ejabberd_SUITE_data/ejabberd.mysql.yml b/test/ejabberd_SUITE_data/ejabberd.mysql.yml index abb0a6937..91705ee68 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mysql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mysql.yml @@ -22,6 +22,7 @@ define_macro: db_type: sql ram_db_type: sql vcard: VCARD + mod_muc_occupantid: [] mod_offline: use_cache: true db_type: sql diff --git a/test/ejabberd_SUITE_data/ejabberd.pgsql.yml b/test/ejabberd_SUITE_data/ejabberd.pgsql.yml index 014e1c058..16d8b1d27 100644 --- a/test/ejabberd_SUITE_data/ejabberd.pgsql.yml +++ b/test/ejabberd_SUITE_data/ejabberd.pgsql.yml @@ -22,6 +22,7 @@ define_macro: db_type: sql ram_db_type: sql vcard: VCARD + mod_muc_occupantid: [] mod_offline: use_cache: true db_type: sql diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml index 7065f0ffd..6ff7d7cdb 100644 --- a/test/ejabberd_SUITE_data/ejabberd.redis.yml +++ b/test/ejabberd_SUITE_data/ejabberd.redis.yml @@ -15,6 +15,7 @@ define_macro: mod_muc: db_type: internal vcard: VCARD + mod_muc_occupantid: [] mod_offline: db_type: internal mod_privacy: diff --git a/test/ejabberd_SUITE_data/ejabberd.sqlite.yml b/test/ejabberd_SUITE_data/ejabberd.sqlite.yml index 3e22f6a2d..11420ef6c 100644 --- a/test/ejabberd_SUITE_data/ejabberd.sqlite.yml +++ b/test/ejabberd_SUITE_data/ejabberd.sqlite.yml @@ -1,5 +1,8 @@ define_macro: SQLITE_CONFIG: + auth_stored_password_types: + - plain + - scram_sha256 sql_type: sqlite sql_pool_size: 1 auth_method: sql @@ -17,6 +20,7 @@ define_macro: db_type: sql ram_db_type: sql vcard: VCARD + mod_muc_occupantid: [] mod_offline: db_type: sql mod_privacy: diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 0abc35c2a..812bea841 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -123,6 +123,7 @@ modules: vcard: VCARD mod_muc: vcard: VCARD + mod_muc_occupantid: [] mod_muc_admin: [] mod_carboncopy: [] mod_jidprep: [] diff --git a/test/muc_tests.erl b/test/muc_tests.erl index 7a5d1d28f..053c62b99 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -304,7 +304,70 @@ master_slave_cases() -> master_slave_test(config_allow_voice_requests), master_slave_test(config_voice_request_interval), master_slave_test(config_visitor_nickchange), - master_slave_test(join_conflict)]}. + master_slave_test(join_conflict), + master_slave_test(duplicate_occupantid) + ]}. + +duplicate_occupantid_master(Config) -> + Room = muc_room_jid(Config), + PeerJID = ?config(slave, Config), + PeerNick = ?config(slave_nick, Config), + PeerNickJID = jid:replace_resource(Room, PeerNick), + MyNick = ?config(nick, Config), + MyNickJID = jid:replace_resource(Room, MyNick), + ok = join_new(Config), + wait_for_slave(Config), + Pres = ?match(#presence{from = PeerNickJID, type = available} = Pres, + recv_presence(Config), Pres), + ?match(#muc_user{items = [#muc_item{jid = PeerJID, + role = participant, + affiliation = none}]}, + xmpp:get_subtag(Pres, #muc_user{})), + OccupantId = ?match([#occupant_id{id = Id}], xmpp:get_subtags(Pres, #occupant_id{}), Id), + Pres2 = ?match(#presence{from = PeerNickJID, type = available} = Pres2, + recv_presence(Config), Pres2), + ?match([#occupant_id{id = OccupantId}], xmpp:get_subtags(Pres2, #occupant_id{})), + Body = xmpp:mk_text(<<"test-1">>), + Msg = ?match(#message{type = groupchat, from = PeerNickJID, + body = Body} = Msg, recv_message(Config), Msg), + ?match([#occupant_id{id = OccupantId}], xmpp:get_subtags(Msg, #occupant_id{})), + recv_muc_presence(Config, PeerNickJID, unavailable), + ok = leave(Config), + disconnect(Config). + +duplicate_occupantid_slave(Config) -> + Room = muc_room_jid(Config), + MyNick = ?config(slave_nick, Config), + MyNickJID = jid:replace_resource(Room, MyNick), + PeerNick = ?config(master_nick, Config), + PeerNickJID = jid:replace_resource(Room, PeerNick), + wait_for_master(Config), + send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}), + ?match(#presence{from = Room, type = available}, recv_presence(Config)), + OccupantId = case recv_presence(Config) of + #presence{from = MyNickJID, type = available} = Pres -> + recv_muc_presence(Config, PeerNickJID, available), + ?match([#occupant_id{id = Id}], xmpp:get_subtags(Pres, #occupant_id{}), Id); + #presence{from = PeerNickJID, type = available} -> + Pres2 = ?match(#presence{from = MyNickJID, type = available} = Pres2, + recv_presence(Config), Pres2), + ?match([#occupant_id{id = Id}], xmpp:get_subtags(Pres2, #occupant_id{}), Id) + end, + ?match(#message{type = groupchat, from = Room}, recv_message(Config)), + send(Config, #presence{to = Room, sub_els = [#occupant_id{id = <<"fake1">>}, + #occupant_id{id = <<"fake2">>}]}), + Pres3 = ?match(#presence{from = MyNickJID, type = available} = Pres3, + recv_presence(Config), Pres3), + ?match([#occupant_id{id = OccupantId}], xmpp:get_subtags(Pres3, #occupant_id{})), + Body = xmpp:mk_text(<<"test-1">>), + send(Config, #message{to = Room, type = groupchat, body = Body, + sub_els = [#occupant_id{id = <<"fake1">>}, + #occupant_id{id = <<"fake2">>}]}), + Msg = ?match(#message{type = groupchat, from = MyNickJID, + body = Body} = Msg, recv_message(Config), Msg), + ?match([#occupant_id{id = OccupantId}], xmpp:get_subtags(Msg, #occupant_id{})), + ok = leave(Config), + disconnect(Config). join_conflict_master(Config) -> ok = join_new(Config), @@ -1628,7 +1691,7 @@ join(Config, Role, Aff) when is_atom(Role), is_atom(Aff) -> join(Config, Role, #muc{} = SubEl) when is_atom(Role) -> join(Config, Role, none, SubEl). -join(Config, Role, Aff, SubEl) -> +join(Config, Role, Aff, SubEls) when is_list(SubEls) -> ct:comment("Joining existing room as ~s/~s", [Aff, Role]), MyJID = my_jid(Config), Room = muc_room_jid(Config), @@ -1636,7 +1699,7 @@ join(Config, Role, Aff, SubEl) -> MyNickJID = jid:replace_resource(Room, MyNick), PeerNick = ?config(peer_nick, Config), PeerNickJID = jid:replace_resource(Room, PeerNick), - send(Config, #presence{to = MyNickJID, sub_els = [SubEl]}), + send(Config, #presence{to = MyNickJID, sub_els = SubEls}), case recv_presence(Config) of #presence{type = error, from = MyNickJID} = Err -> xmpp:get_subtag(Err, #stanza_error{}); @@ -1667,7 +1730,9 @@ join(Config, Role, Aff, SubEl) -> {History, Subj} = recv_history_and_subject(Config), {empty, History, Subj, Codes} end - end. + end; +join(Config, Role, Aff, SubEl) -> + join(Config, Role, Aff, [SubEl]). leave(Config) -> leave(Config, muc_room_jid(Config)). From d791f6ceaae32d646cd48948b36c68fe283f8e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 16 Apr 2025 12:56:04 +0200 Subject: [PATCH 1087/1302] Update changelog --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c5363677..8a39bff35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## Version 25.04 + +#### Security fixes +- Fixes issue with handling of user provided occupant-id in messages and presences sent to muc room. Server was replacing + just first instance of occupant-id with it's own version, leaving other one untouched. That would mean that depending + on order in which clients seen occupant-id, they could see value provided by sender, and that could be used to spoof + as different sender. + +#### Commands API +- `kick_users`: New command that allow to kick all logged users for a given host + +#### Bugfixes +- Fix issue with sql schema auto upgrade when using `sqlite` database +- Fix problem with container update, that could ignore previous data stored in `mnesia` database +- Revert limit of allowed characters in shared roster group names, that will again allow using symbols like `:` + ## Version 25.03 #### Commands API From 4d3681aba1e29df035456a57b5cc1ad0129ef21d Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 11:58:45 +0200 Subject: [PATCH 1088/1302] Annotate kick_users version --- src/ejabberd_sm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 77b911eac..f12cd39e0 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -1078,7 +1078,7 @@ get_commands_spec() -> #ejabberd_commands{name = kick_users, tags = [session], desc = "Disconnect all given host users' active sessions", module = ?MODULE, function = kick_users, - note = "added in 25.xx", + note = "added in 25.04", args = [{host, binary}], args_desc = ["Server name"], args_example = [<<"example.com">>], From e43d86418433ffdc74594417a069cd27d30445a8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 15:39:17 +0200 Subject: [PATCH 1089/1302] CI: Bump ubuntu-24.04, and Erlang 25+ (#4281) Update postgresql preparation: - ubuntu-20.04 included PostgreSQL 14.13 - ubuntu-24.04 includes PostgreSQL 16.4 and in the meantime, PostgreSQL 15.0 revoked "the CREATE permission from all users except a database owner from the public (or default) schema." See https://www.postgresql.org/about/news/postgresql-15-released-2526/ --- .github/workflows/ci.yml | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86fffe591..fafa6eb65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,8 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20.0', '25', '26', '27'] - runs-on: ubuntu-20.04 + otp: ['25', '26', '27'] + runs-on: ubuntu-24.04 services: redis: image: public.ecr.aws/docker/library/redis @@ -50,13 +50,6 @@ jobs: with: otp-version: ${{ matrix.otp }} - - name: Get a compatible Rebar3 - if: matrix.otp < 24 - run: | - rm rebar3 - wget https://github.com/processone/ejabberd/raw/21.12/rebar3 - chmod +x rebar3 - - name: Install MS SQL Server run: | docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \ @@ -83,6 +76,8 @@ jobs: WITH PASSWORD 'ejabberd_test';" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" + sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;" + sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;" sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" @@ -107,10 +102,6 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - - name: Get old eredis for old Erlang - if: matrix.otp < 21 - run: ./rebar3 unlock eredis - - name: Compile run: | ./autogen.sh @@ -129,7 +120,6 @@ jobs: - run: make dialyzer - run: make test-eunit - run: make elvis - if: matrix.otp >= 23 - name: Check Production Release run: | @@ -267,6 +257,8 @@ jobs: sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ejabberd_test TO ejabberd_test;" + sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;" + sudo -u postgres psql -c "ALTER DATABASE ejabberd_test OWNER TO ejabberd_test;" sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ejabberd_test;" From d8016a647763831763db13798b800a2eab697322 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:14:17 +0200 Subject: [PATCH 1090/1302] Result of running "make format" --- src/ejabberd_web_admin.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index dfb42d57d..42ca212c0 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -584,7 +584,9 @@ process_admin(Host, #request{path = [<<"users">> | RPath], lang = Lang} = R, AJI process_admin(Host, #request{path = [<<"online-users">> | RPath], lang = Lang} = R, AJID) when is_binary(Host) -> Level = 3 + length(RPath), - Set = [make_command(kick_users, R, [{<<"host">>, Host}], + Set = [make_command(kick_users, + R, + [{<<"host">>, Host}], [{style, danger}, {force_execution, false}])], timer:sleep(200), % small delay after kicking users before getting the updated list Get = [make_command(connected_users_vhost, From 6fdb4674848e62b91cdc6ec5d11e4e023ea3eb42 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:21:14 +0200 Subject: [PATCH 1091/1302] Update man page to 25.04 --- man/ejabberd.yml.5 | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 90cffcc9a..2bb711cc7 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 03/28/2025 +.\" Date: 04/16/2025 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "03/28/2025" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "04/16/2025" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.03/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.04/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 25\&.03\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 25\&.04\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR .RS 4 @@ -469,7 +469,7 @@ format\&. You shouldn\(cqt change this if you already have passwords generated w \fIsha\fR\&. .RE .PP -\fBauth_stored_password_types 🟤\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +\fBauth_stored_password_types\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR .RS 4 \fINote\fR about this option: added in 25\&.03\&. List of password types that should be stored simultaneously for each user in database\&. When the user sets the account password, database will be updated to store the password in formats compatible with each type listed here\&. This can be used to migrate user passwords to a more secure format\&. If this option if set, it will override values set in @@ -707,7 +707,7 @@ Default volatile (in\-memory) storage for ejabberd\&. Modules and other componen \fImnesia\fR\&. .RE .PP -\fBdefine_keyword 🟤\fR: \fI{NAME: Value}\fR +\fBdefine_keyword\fR: \fI{NAME: Value}\fR .RS 4 \fINote\fR about this option: added in 25\&.03\&. Allows to define configuration @@ -734,7 +734,7 @@ sql_username: "prefix\&.@SQL_USERNAME@" .\} .RE .PP -\fBdefine_macro 🟤\fR: \fI{NAME: Value}\fR +\fBdefine_macro\fR: \fI{NAME: Value}\fR .RS 4 \fINote\fR about this option: improved in 25\&.03\&. Allows to define configuration @@ -1246,7 +1246,7 @@ This option can be used to tune tick time parameter of .RS 4 Whether to use the \fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/25\&.03/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/25\&.04/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -2043,7 +2043,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 25\&.03\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 25\&.04\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. @@ -2062,7 +2062,7 @@ Provide the Commands item in the Service Discovery\&. Default value: \fIfalse\fR\&. .RE .RE -.SS "mod_adhoc_api 🟤" +.SS "mod_adhoc_api" .sp \fINote\fR about this option: added in 25\&.03\&. .sp @@ -2808,7 +2808,7 @@ Please notice that all the ad\-hoc commands implemented by this module have an e \fBAvailable options:\fR .RS 4 .PP -\fBaccess 🟤\fR: \fIAccessName\fR +\fBaccess\fR: \fIAccessName\fR .RS 4 \fINote\fR about this option: added in 25\&.03\&. This option defines which access rule will be used to control who is allowed to access the features provided by this module\&. The default value is @@ -3956,7 +3956,7 @@ When this option is disabled, for each individual subscriber a separate mucsub m \fIfalse\fR\&. .RE .RE -.SS "mod_matrix_gw 🟤" +.SS "mod_matrix_gw" .sp \fINote\fR about this option: improved in 25\&.03\&. .sp @@ -4629,7 +4629,7 @@ in order to accept their join in the room\&. The default value is Short description of the room\&. The default value is an empty string\&. .RE .PP -\fBenable_hats 🟤\fR: \fItrue | false\fR +\fBenable_hats\fR: \fItrue | false\fR .RS 4 \fINote\fR about this option: improved in 25\&.03\&. Allow extended roles as defined in XEP\-0317 Hats\&. Check the @@ -8372,7 +8372,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 25\&.03\&. +This section describes listeners options of ejabberd 25\&.04\&. .sp TODO .SH "AUTHOR" @@ -8380,13 +8380,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 25\&.03\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 25\&.04\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.03/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.04/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 69695ffe27cada7f164a896b0a8de98f6278d6b5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:30:56 +0200 Subject: [PATCH 1092/1302] Update German translation (thanks to Nautilusx) --- priv/msgs/de.msg | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/priv/msgs/de.msg b/priv/msgs/de.msg index 8c97e427d..7247d5f55 100644 --- a/priv/msgs/de.msg +++ b/priv/msgs/de.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Jeder mit einem Präsenzabonnement von beiden oder davon darf Items abonnieren oder abrufen"}. {"Anyone with Voice","Jeder mit Stimme"}. {"Anyone","Jeder"}. +{"API Commands","API Befehle"}. {"April","April"}. +{"Arguments","Argumente"}. {"Attribute 'channel' is required for this request","Attribut 'channel' ist für diese Anforderung erforderlich"}. {"Attribute 'id' is mandatory for MIX messages","Attribut 'id' ist verpflichtend für MIX-Nachrichten"}. {"Attribute 'jid' is not allowed here","Attribut 'jid' ist hier nicht erlaubt"}. @@ -168,6 +170,7 @@ {"has been kicked because of an affiliation change","wurde wegen einer Änderung der Zugehörigkeit hinausgeworfen"}. {"has been kicked because the room has been changed to members-only","wurde hinausgeworfen weil der Raum zu Nur-Mitglieder geändert wurde"}. {"has been kicked","wurde hinausgeworfen"}. +{"Hash of the vCard-temp avatar of this room","Hash des vCard-temp Avatars dieses Raums"}. {"Hat title","Funktionstitel"}. {"Hat URI","Funktions-URI"}. {"Hats limit exceeded","Funktionslimit wurde überschritten"}. @@ -406,6 +409,7 @@ {"Restore binary backup immediately:","Stelle binäres Backup sofort wieder her:"}. {"Restore plain text backup immediately:","Stelle Klartext-Backup sofort wieder her:"}. {"Restore","Wiederherstellung"}. +{"Result","Ergebnis"}. {"Roles and Affiliations that May Retrieve Member List","Rollen und Zugehörigkeiten die Mitgliederliste abrufen dürfen"}. {"Roles for which Presence is Broadcasted","Rollen für welche die Präsenz übertragen wird"}. {"Roles that May Send Private Messages","Rollen die Privatnachrichten senden dürfen"}. @@ -550,6 +554,7 @@ {"Update message of the day on all hosts (don't send)","Aktualisiere Nachricht des Tages auf allen Hosts (nicht senden)"}. {"Update specs to get modules source, then install desired ones.","Aktualisieren Sie die Spezifikationen, um den Quellcode der Module zu erhalten und installieren Sie dann die gewünschten Module."}. {"Update Specs","Spezifikationen aktualisieren"}. +{"Updating the vCard is not supported by the vCard storage backend","Aktualisierung der vCard wird vom vCard-Speicher-Backend nicht unterstützt"}. {"Upgrade","Upgrade"}. {"URL for Archived Discussion Logs","URL für archivierte Diskussionsprotokolle"}. {"User already exists","Benutzer existiert bereits"}. @@ -578,6 +583,7 @@ {"Visitors are not allowed to send messages to all occupants","Besucher dürfen nicht an alle Teilnehmer Nachrichten versenden"}. {"Voice requests are disabled in this conference","Sprachrecht-Anforderungen sind in diesem Raum deaktiviert"}. {"Voice request","Sprachrecht-Anforderung"}. +{"Web client which allows to join the room anonymously","Web-Client, der es ermöglicht, dem Raum anonym beizutreten"}. {"Wednesday","Mittwoch"}. {"When a new subscription is processed and whenever a subscriber comes online","Sobald ein neues Abonnement verarbeitet wird und wann immer ein Abonnent sich anmeldet"}. {"When a new subscription is processed","Sobald ein neues Abonnement verarbeitet wird"}. From ce02fc485f1039736e47dcb22769a979742d98bd Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:31:18 +0200 Subject: [PATCH 1093/1302] Update Portuguese (Brazil) translation (thanks to Wellington Uemura) --- priv/msgs/pt-br.msg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/priv/msgs/pt-br.msg b/priv/msgs/pt-br.msg index 244138db5..9fbbb0096 100644 --- a/priv/msgs/pt-br.msg +++ b/priv/msgs/pt-br.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. +{"API Commands","Comandos API"}. {"April","Abril"}. +{"Arguments","Argumentos"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. {"Attribute 'jid' is not allowed here","O atributo 'jid' não é permitido aqui"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Aprovar esta assinatura."}. {"City","Cidade"}. {"Client acknowledged more stanzas than sent by server","O cliente reconheceu mais estrofes do que as enviadas pelo servidor"}. +{"Clustering","Agrupamento"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala de conferência não existe"}. {"Configuration of room ~s","Configuração para ~s"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. {"Restore plain text backup immediately:","Restaurar backup formato texto imediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. {"Roles and Affiliations that May Retrieve Member List","As funções e as afiliações que podem recuperar a lista dos membros"}. {"Roles for which Presence is Broadcasted","Para quem a presença será notificada"}. {"Roles that May Send Private Messages","Atribuições que talvez possam enviar mensagens privadas"}. @@ -584,6 +588,7 @@ {"Visitor","Visitante"}. {"Voice request","Requisição de voz"}. {"Voice requests are disabled in this conference","Requisições de voz estão desabilitadas nesta sala de conferência"}. +{"Web client which allows to join the room anonymously","Cliente da web que permite entrar na sala de forma anônima"}. {"Wednesday","Quarta"}. {"When a new subscription is processed and whenever a subscriber comes online","Quando uma nova assinatura é processada e sempre que um assinante fica online"}. {"When a new subscription is processed","Quando uma nova assinatura é processada"}. From da8d04a654e0119b51d101273bd3bddfb762ee4a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:31:54 +0200 Subject: [PATCH 1094/1302] =?UTF-8?q?Update=20Portuguese=20translation=20(?= =?UTF-8?q?thanks=20to=20Silv=C3=A9rio=20Santos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/pt.msg | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/priv/msgs/pt.msg b/priv/msgs/pt.msg index 4cc105458..358eb4858 100644 --- a/priv/msgs/pt.msg +++ b/priv/msgs/pt.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualquer pessoa com uma assinatura presente dos dois ou de ambos pode se inscrever e recuperar os itens"}. {"Anyone with Voice","Qualquer pessoa com voz"}. {"Anyone","Qualquer pessoa"}. +{"API Commands","Comandos API"}. {"April","Abril"}. +{"Arguments","Argumentos"}. {"Attribute 'channel' is required for this request","O atributo 'canal' é necessário para esta solicitação"}. {"Attribute 'id' is mandatory for MIX messages","O atributo 'id' é obrigatório para mensagens MIX"}. {"Attribute 'jid' is not allowed here","O atributo 'jid' não é permitido aqui"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Aprovar esta assinatura."}. {"City","Cidade"}. {"Client acknowledged more stanzas than sent by server","O cliente reconheceu mais estrofes do que as enviadas pelo servidor"}. +{"Clustering","Agrupamento"}. {"Commands","Comandos"}. {"Conference room does not exist","A sala não existe"}. {"Configuration of room ~s","Configuração para ~s"}. @@ -168,6 +171,7 @@ {"has been kicked because of an affiliation change","foi desconectado porque por afiliação inválida"}. {"has been kicked because the room has been changed to members-only","foi desconectado porque a política da sala mudou, só membros são permitidos"}. {"has been kicked","foi removido"}. +{"Hash of the vCard-temp avatar of this room","Hash do avatar do vCard-temp desta sala"}. {"Hat title","Título do chapéu"}. {"Hat URI","URI do chapéu"}. {"Hats limit exceeded","O limite dos chapéus foi excedido"}. @@ -223,6 +227,7 @@ {"leaves the room","Sair da sala"}. {"List of users with hats","Lista os utilizadores com chapéus"}. {"List users with hats","Lista os utilizadores com chapéus"}. +{"Logged Out","Desconectado"}. {"Logging","Registando no log"}. {"Make participants list public","Tornar pública a lista de participantes"}. {"Make room CAPTCHA protected","Tornar protegida a palavra-passe da sala"}. @@ -406,6 +411,7 @@ {"Restore binary backup immediately:","Restaurar imediatamente o backup binário:"}. {"Restore plain text backup immediately:","Restaurar backup formato texto imediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. {"Roles and Affiliations that May Retrieve Member List","As funções e as afiliações que podem recuperar a lista dos membros"}. {"Roles for which Presence is Broadcasted","Para quem a presença será notificada"}. {"Roles that May Send Private Messages","Atribuições que talvez possam enviar mensagens privadas"}. @@ -437,6 +443,7 @@ {"Set message of the day on all hosts and send to online users","Definir mensagem do dia em todos os hosts e enviar para os utilizadores online"}. {"Shared Roster Groups","Grupos Shared Roster"}. {"Show Integral Table","Mostrar Tabela Integral"}. +{"Show Occupants Join/Leave","Mostrar a entrada e a saída de ocupantes"}. {"Show Ordinary Table","Mostrar Tabela Ordinária"}. {"Shut Down Service","Parar Serviço"}. {"SOCKS5 Bytestreams","Bytestreams SOCKS5"}. @@ -534,6 +541,7 @@ {"Too many unacked stanzas","Quantidade excessiva de instâncias sem confirmação"}. {"Too many users in this conference","Há uma quantidade excessiva de utilizadores nesta conferência"}. {"Traffic rate limit is exceeded","Limite de banda excedido"}. +{"~ts's MAM Archive","Arquivo ~ts's MAM"}. {"~ts's Offline Messages Queue","~s's Fila de Mensagens Offline"}. {"Tuesday","Terça"}. {"Unable to generate a CAPTCHA","Impossível gerar um CAPTCHA"}. @@ -550,12 +558,14 @@ {"Update message of the day on all hosts (don't send)","Atualizar a mensagem do dia em todos os host (não enviar)"}. {"Update specs to get modules source, then install desired ones.","Atualize as especificações para obter a fonte dos módulos e instale os que desejar."}. {"Update Specs","Atualizar as especificações"}. +{"Updating the vCard is not supported by the vCard storage backend","A atualização do vCard não é compatível com o back-end de armazenamento do vCard"}. {"Upgrade","Atualização"}. {"URL for Archived Discussion Logs","A URL para o arquivamento dos registos da discussão"}. {"User already exists","Utilizador já existe"}. {"User (jid)","Utilizador (jid)"}. {"User JID","Utilizador JID"}. {"User Management","Gestão de utilizadores"}. +{"User not allowed to perform an IQ set on another user's vCard.","O utilizador não tem permissão para executar um conjunto de QI no vCard de outro utilizador."}. {"User removed","O utilizador foi removido"}. {"User session not found","A sessão do utilizador não foi encontrada"}. {"User session terminated","Sessão de utilizador terminada"}. @@ -578,6 +588,7 @@ {"Visitor","Visitante"}. {"Voice request","Requisição de voz"}. {"Voice requests are disabled in this conference","Requisições de voz estão desativadas nesta sala de conferência"}. +{"Web client which allows to join the room anonymously","Cliente da web que permite entrar na sala de forma anônima"}. {"Wednesday","Quarta"}. {"When a new subscription is processed and whenever a subscriber comes online","Quando uma nova assinatura é processada e sempre que um assinante fica online"}. {"When a new subscription is processed","Quando uma nova assinatura é processada"}. From fae4ab97fdee600e2ca50959ca87bfcc112275f6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:33:10 +0200 Subject: [PATCH 1095/1302] Update Albanian translation (thanks to Besnik Bleta) --- priv/msgs/sq.msg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/priv/msgs/sq.msg b/priv/msgs/sq.msg index 2cc43274d..de51a9614 100644 --- a/priv/msgs/sq.msg +++ b/priv/msgs/sq.msg @@ -28,7 +28,9 @@ {"Anyone may publish","Gjithkush mund të publikojë"}. {"Anyone with Voice","Cilido me Zë"}. {"Anyone","Cilido"}. +{"API Commands","Urdhra API"}. {"April","Prill"}. +{"Arguments","Argumente"}. {"Attribute 'channel' is required for this request","Atributi 'channel' është i domosdoshëm për këtë kërkesë"}. {"Attribute 'jid' is not allowed here","Atributi 'jid' s’lejohet këtu"}. {"Attribute 'node' is not allowed here","Atributi 'node' s’lejohet këtu"}. From f79b8e166ad181e4e3fbda66ce0c6ba7f815e87b Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:35:08 +0200 Subject: [PATCH 1096/1302] =?UTF-8?q?Update=20Ukrainian=20translation=20(t?= =?UTF-8?q?hanks=20to=20=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93?= =?UTF-8?q?=D0=BE=D1=80=D0=BF=D0=B8=D0=BD=D1=96=D1=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/uk.msg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 48f98b60d..520ec8a5e 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Будь-хто, хто має підписку на отримання інформації про присутність в обох випадках або може підписуватись та отримувати матеріали"}. {"Anyone with Voice","Будь-хто, хто має голос"}. {"Anyone","Будь-хто"}. +{"API Commands","Команди API"}. {"April","Квітень"}. +{"Arguments","Аргументи"}. {"Attribute 'channel' is required for this request","Атрибут \"канал\" є обов'язковим для цього запиту"}. {"Attribute 'id' is mandatory for MIX messages","Атрибут 'id' обов'язковий для MIX повідомлень"}. {"Attribute 'jid' is not allowed here","Атрибут 'jid' заборонений"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Виберіть, чи підтверджувати підписку."}. {"City","Місто"}. {"Client acknowledged more stanzas than sent by server","Клієнт підтвердив більше повідомлень, ніж було відправлено сервером"}. +{"Clustering","Кластеризація"}. {"Commands","Команди"}. {"Conference room does not exist","Кімната для переговорів відсутня"}. {"Configuration of room ~s","Конфігурація кімнати ~s"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","Відновити з бінарної резервної копії негайно:"}. {"Restore plain text backup immediately:","Відновити з текстової резервної копії негайно:"}. {"Restore","Відновлення з резервної копії"}. +{"Result","Результат"}. {"Roles and Affiliations that May Retrieve Member List","Ролі та зв’язки, які можуть отримати список учасників"}. {"Roles for which Presence is Broadcasted","Ролі для яких поширюється наявність"}. {"Roles that May Send Private Messages","Ролі, що можуть надсилати приватні повідомлення"}. @@ -584,6 +588,7 @@ {"Visitor","Відвідувач"}. {"Voice requests are disabled in this conference","Голосові запити відключені в цій конференції"}. {"Voice request","Голосовий запит"}. +{"Web client which allows to join the room anonymously","Веб-клієнт, який дозволяє анонімно приєднатися до кімнати"}. {"Wednesday","Середа"}. {"When a new subscription is processed and whenever a subscriber comes online","Коли обробляється нова підписка та щоразу, коли абонент виходить в Інтернет"}. {"When a new subscription is processed","Під час обробки нової підписки"}. From 629db496b57d000179156e3e8819771e65b82943 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:35:41 +0200 Subject: [PATCH 1097/1302] Update Chinese (Simplified) translation (thanks to Sketch6580) --- priv/msgs/zh.msg | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index ab7053b82..4f5688244 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -4,10 +4,10 @@ %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ {" (Add * to the end of field to match substring)"," (在字段末尾添加 * 以匹配子字符串)"}. -{" has set the subject to: "," 已将话题设置为: "}. +{" has set the subject to: "," 已将主题设置为: "}. {"# participants","# 参与者"}. {"A description of the node","节点的描述"}. -{"A friendly name for the node","节点的易记名称"}. +{"A friendly name for the node","节点的友好名称"}. {"A password is required to enter this room","需要密码才能进入此房间"}. {"A Web Page","网页"}. {"Accept","接受"}. @@ -25,7 +25,7 @@ {"Allow subscription","允许订阅"}. {"Allow this Jabber ID to subscribe to this pubsub node?","是否允许此 Jabber ID 订阅此 pubsub 节点?"}. {"Allow this person to register with the room?","是否允许此用户在房间注册?"}. -{"Allow users to change the subject","允许用户更改话题"}. +{"Allow users to change the subject","允许用户更改主题"}. {"Allow users to query other users","允许用户查询其他用户"}. {"Allow users to send invites","允许用户发送邀请"}. {"Allow users to send private messages","允许用户发送私信"}. @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","任何拥有 both 或 from 的在线状态订阅的用户都可以订阅和检索项目"}. {"Anyone with Voice","任何有发言权的人"}. {"Anyone","任何人"}. +{"API Commands","API 命令"}. {"April","四月"}. +{"Arguments","参数"}. {"Attribute 'channel' is required for this request","此请求要求“channel”属性"}. {"Attribute 'id' is mandatory for MIX messages","对于 MIX 消息,“id”属性是必需的"}. {"Attribute 'jid' is not allowed here","此处不允许“jid”属性"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","选择是否批准此实体的订阅。"}. {"City","城市"}. {"Client acknowledged more stanzas than sent by server","客户端确认的节数多于服务器发送的节数"}. +{"Clustering","集群"}. {"Commands","命令"}. {"Conference room does not exist","会议室不存在"}. {"Configuration of room ~s","房间 ~s 的配置"}. @@ -227,12 +230,12 @@ {"Logged Out","已登出"}. {"Logging","日志记录"}. {"Make participants list public","公开参与者列表"}. -{"Make room CAPTCHA protected","开启房间验证码保护"}. +{"Make room CAPTCHA protected","启用房间验证码保护"}. {"Make room members-only","将房间设为仅成员"}. -{"Make room moderated","开启房间发言审核"}. -{"Make room password protected","开启房间密码保护"}. +{"Make room moderated","启用房间发言审核"}. +{"Make room password protected","启用房间密码保护"}. {"Make room persistent","将房间设为持久"}. -{"Make room public searchable","将房间设为公开可搜索"}. +{"Make room public searchable","将房间设为可公开搜索"}. {"Malformed username","用户名格式不正确"}. {"MAM preference modification denied by service policy","服务策略拒绝修改 MAM 首选项"}. {"March","三月"}. @@ -324,7 +327,7 @@ {"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","自动清除项目前的秒数,`max` 表示除服务器强制规定的最大值外无其他特定限制"}. {"Occupants are allowed to invite others","允许使用者邀请他人"}. {"Occupants are allowed to query others","允许使用者查询他人"}. -{"Occupants May Change the Subject","使用者可以更改话题"}. +{"Occupants May Change the Subject","使用者可以更改主题"}. {"October","十月"}. {"OK","确定"}. {"Old Password:","旧密码:"}. @@ -335,8 +338,8 @@ {"Only or tags are allowed","仅允许 标签"}. {"Only element is allowed in this query","此查询中只允许 元素"}. {"Only members may query archives of this room","只有成员才能查询此房间的归档"}. -{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者更改此房间的话题"}. -{"Only moderators are allowed to change the subject in this room","只允许主持人更改此房间的话题"}. +{"Only moderators and participants are allowed to change the subject in this room","只允许主持人和参与者更改此房间的主题"}. +{"Only moderators are allowed to change the subject in this room","只允许主持人更改此房间的主题"}. {"Only moderators are allowed to retract messages","只允许主持人撤回消息"}. {"Only moderators can approve voice requests","只有主持人可以批准发言权请求"}. {"Only occupants are allowed to send messages to the conference","只允许使用者向会议发送消息"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","立即恢复二进制备份:"}. {"Restore plain text backup immediately:","立即恢复纯文本备份:"}. {"Restore","恢复"}. +{"Result","结果"}. {"Roles and Affiliations that May Retrieve Member List","可以检索成员列表的角色和从属关系"}. {"Roles for which Presence is Broadcasted","广播在线状态的角色"}. {"Roles that May Send Private Messages","可以发送私信的角色"}. @@ -456,7 +460,7 @@ {"Store plain text backup:","存储纯文本备份:"}. {"Stream management is already enabled","已启用流管理"}. {"Stream management is not enabled","未启用流管理"}. -{"Subject","话题"}. +{"Subject","主题"}. {"Submitted","已提交"}. {"Subscriber Address","订阅者地址"}. {"Subscribers may publish","订阅者可以发布"}. @@ -506,7 +510,7 @@ {"The presence states for which an entity wants to receive notifications","实体要接收通知的在线状态"}. {"The query is only allowed from local users","仅允许来自本地用户的查询"}. {"The query must not contain elements","查询不能包含 元素"}. -{"The room subject can be modified by participants","参与者可以修改房间话题"}. +{"The room subject can be modified by participants","参与者可以修改房间主题"}. {"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","节点中数据的语义类型信息,通常由有效负载的命名空间指定(如果有)"}. {"The sender of the last received message","最后收到的消息的发送者"}. {"The stanza MUST contain only one element, one element, or one element","节必须仅包含一个 元素、一个 元素或一个 元素"}. @@ -584,6 +588,7 @@ {"Visitor","参观者"}. {"Voice requests are disabled in this conference","此会议中禁用了发言权请求"}. {"Voice request","发言权请求"}. +{"Web client which allows to join the room anonymously","允许匿名加入房间的 Web 客户端"}. {"Wednesday","周三"}. {"When a new subscription is processed and whenever a subscriber comes online","处理新订阅时和订阅者上线时"}. {"When a new subscription is processed","处理新订阅时"}. From 4da8278e663c070c441a968d39e8896bb8157867 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:35:54 +0200 Subject: [PATCH 1098/1302] Update other translations --- priv/msgs/ca.msg | 5 +++++ priv/msgs/es.msg | 7 ++++++- priv/msgs/sv.msg | 2 -- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/priv/msgs/ca.msg b/priv/msgs/ca.msg index 2982b1211..951430a9b 100644 --- a/priv/msgs/ca.msg +++ b/priv/msgs/ca.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Qualsevol amb una subscripció de presencia de 'both' o 'from' pot subscriure's i publicar elements"}. {"Anyone with Voice","Qualsevol amb Veu"}. {"Anyone","Qualsevol"}. +{"API Commands","Comandaments API"}. {"April","Abril"}. +{"Arguments","Arguments"}. {"Attribute 'channel' is required for this request","L'atribut 'channel' és necessari per a aquesta petició"}. {"Attribute 'id' is mandatory for MIX messages","L'atribut 'id' es necessari per a missatges MIX"}. {"Attribute 'jid' is not allowed here","L'atribut 'jid' no està permès ací"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Tria si aproves aquesta entitat de subscripció."}. {"City","Ciutat"}. {"Client acknowledged more stanzas than sent by server","El client ha reconegut més paquets dels que ha enviat el servidor"}. +{"Clustering","Clustering"}. {"Commands","Comandaments"}. {"Conference room does not exist","La sala de conferències no existeix"}. {"Configuration of room ~s","Configuració de la sala ~s"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","Restaurar una còpia de seguretat binària ara mateix:"}. {"Restore plain text backup immediately:","Restaurar una còpia de seguretat en format de text pla ara mateix:"}. {"Restore","Restaurar"}. +{"Result","Resultat"}. {"Roles and Affiliations that May Retrieve Member List","Rols i Afiliacions que poden recuperar la llista de membres"}. {"Roles for which Presence is Broadcasted","Rols per als que sí se difon la seua presencia"}. {"Roles that May Send Private Messages","Rols que poden enviar missatges privats"}. @@ -584,6 +588,7 @@ {"Visitor","Visitant"}. {"Voice request","Petició de veu"}. {"Voice requests are disabled in this conference","Les peticions de veu es troben desactivades en aquesta conferència"}. +{"Web client which allows to join the room anonymously","Client web que permet entrar a la sala anonimament"}. {"Wednesday","Dimecres"}. {"When a new subscription is processed and whenever a subscriber comes online","Quan es processa una nova subscripció i un subscriptor es connecta"}. {"When a new subscription is processed","Quan es processa una nova subscripció"}. diff --git a/priv/msgs/es.msg b/priv/msgs/es.msg index a81091566..a914a43a1 100644 --- a/priv/msgs/es.msg +++ b/priv/msgs/es.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Cualquiera con una suscripción a la presencia de 'ambos' o 'de' puede suscribirse y recibir elementos"}. {"Anyone with Voice","Cualquiera con Voz"}. {"Anyone","Cualquiera"}. +{"API Commands","Comandos API"}. {"April","Abril"}. +{"Arguments","Argumentos"}. {"Attribute 'channel' is required for this request","El atributo 'channel' es necesario para esta petición"}. {"Attribute 'id' is mandatory for MIX messages","El atributo 'id' es necesario para mensajes MIX"}. {"Attribute 'jid' is not allowed here","El atributo 'jid' no está permitido aqui"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Decidir si aprobar la subscripción de esta entidad."}. {"City","Ciudad"}. {"Client acknowledged more stanzas than sent by server","El cliente ha reconocido más paquetes de los que el servidor ha enviado"}. +{"Clustering","Clustering"}. {"Commands","Comandos"}. {"Conference room does not exist","La sala de conferencias no existe"}. {"Configuration of room ~s","Configuración para la sala ~s"}. @@ -161,7 +164,7 @@ {"Get Pending","Obtener pendientes"}. {"Get User Last Login Time","Ver fecha de la última conexión de usuario"}. {"Get User Statistics","Ver estadísticas de usuario"}. -{"Given Name","Nombre"}. +{"Given Name","Nombre de pila"}. {"Grant voice to this person?","¿Conceder voz a esta persona?"}. {"has been banned","ha sido bloqueado"}. {"has been kicked because of a system shutdown","ha sido expulsado porque el sistema se va a detener"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","Restaurar inmediatamente copia de seguridad binaria:"}. {"Restore plain text backup immediately:","Restaurar copias de seguridad de texto plano inmediatamente:"}. {"Restore","Restaurar"}. +{"Result","Resultado"}. {"Roles and Affiliations that May Retrieve Member List","Roles y Afiliaciones que pueden obtener la lista de miembros"}. {"Roles for which Presence is Broadcasted","Roles para los que sí se difunde su Presencia"}. {"Roles that May Send Private Messages","Roles que pueden enviar mensajes privados"}. @@ -584,6 +588,7 @@ {"Visitor","Visitante"}. {"Voice request","Petición de voz"}. {"Voice requests are disabled in this conference","Las peticiones de voz están desactivadas en esta sala"}. +{"Web client which allows to join the room anonymously","Cliente web que permite entrar en la sala anonimamente"}. {"Wednesday","Miércoles"}. {"When a new subscription is processed and whenever a subscriber comes online","Cuando se procesa una nueva suscripción y cuando un suscriptor se conecta"}. {"When a new subscription is processed","Cuando se procesa una nueva suscripción"}. diff --git a/priv/msgs/sv.msg b/priv/msgs/sv.msg index 74337cd1f..8e454464e 100644 --- a/priv/msgs/sv.msg +++ b/priv/msgs/sv.msg @@ -34,7 +34,6 @@ {"Chatrooms","Chattrum"}. {"Choose a username and password to register with this server","Välj ett användarnamn och lösenord för att registrera mot denna server"}. {"Choose storage type of tables","Välj lagringstyp för tabeller"}. -{"Choose whether to approve this entity's subscription.","Välj om du vill godkänna hela denna prenumertion."}. {"City","Stad"}. {"Commands","Kommandon"}. {"Conference room does not exist","Rummet finns inte"}. @@ -196,7 +195,6 @@ {"Search users in ","Sök efter användare på "}. {"Send announcement to all online users on all hosts","Sänd meddelanden till alla inloggade användare på alla värdar"}. {"Send announcement to all online users","Sänd meddelanden till alla inloggade användare"}. -{"Send announcement to all users on all hosts","Sänd meddelanden till alla användare på alla värdar"}. {"Send announcement to all users","Sänd meddelanden till alla användare"}. {"September","September"}. {"Set message of the day and send to online users","Sätt dagens status meddelande och skicka till alla användare"}. From 2182cb60aeb43060f64ec88ef59319bb93de5115 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 13:42:16 +0200 Subject: [PATCH 1099/1302] CHANGELOG: Fix typos --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a39bff35..76d8b291b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,12 @@ #### Security fixes - Fixes issue with handling of user provided occupant-id in messages and presences sent to muc room. Server was replacing - just first instance of occupant-id with it's own version, leaving other one untouched. That would mean that depending - on order in which clients seen occupant-id, they could see value provided by sender, and that could be used to spoof - as different sender. + just first instance of occupant-id with its own version, leaving other ones untouched. That would mean that depending + on order in which clients send occupant-id, they could see value provided by sender, and that could be used to spoof + as different sender. #### Commands API -- `kick_users`: New command that allow to kick all logged users for a given host +- `kick_users`: New command to kick all logged users for a given host #### Bugfixes - Fix issue with sql schema auto upgrade when using `sqlite` database From ee3a0b8b1a1f8a76fd31652de9478a5a2ea4f12b Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 1 Apr 2025 12:26:53 +0200 Subject: [PATCH 1100/1302] Bump Erlang/OTP version to 27.3.2 27.3.3 was just release, but image is not yet published in https://hub.docker.com/_/erlang/tags?name=27.3 --- .github/container/Dockerfile | 2 +- CONTAINER.md | 2 +- tools/make-binaries | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 2a271215d..e92ffe8dd 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,5 @@ #' Define default build variables -ARG OTP_VSN='27.3' +ARG OTP_VSN='27.3.2' ARG ELIXIR_VSN='1.18.3' ARG UID='9000' ARG USER='ejabberd' diff --git a/CONTAINER.md b/CONTAINER.md index 077d5f0c6..5b46ffb5d 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -926,7 +926,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.2-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included | diff --git a/tools/make-binaries b/tools/make-binaries index eaf0b1557..e0847cc0c 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.4.1' -otp_vsn='27.3' +otp_vsn='27.3.2' elixir_vsn='1.18.3' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' From ef754939c49526b8b5e749b93e0968d64e49f255 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 17:58:37 +0200 Subject: [PATCH 1101/1302] Set version to 25.04 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5d1d0d68d..b7ade05cb 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.03` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" From 45e7d8426d94090bf5bb2a2b8482d12502123e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 17 Apr 2025 14:21:25 +0200 Subject: [PATCH 1102/1302] Make delete_old_mam_messages_batch work with sqlite --- src/mod_mam_sql.erl | 46 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index de97eaaa5..2747254ac 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -213,21 +213,37 @@ count_messages_to_delete(ServerHost, TimeStamp, Type) -> delete_old_messages_batch(ServerHost, TimeStamp, Type, Batch) -> TS = misc:now_to_usec(TimeStamp), Res = - case Type of - all -> - ejabberd_sql:sql_query( - ServerHost, - ?SQL("delete from archive" - " where timestamp < %(TS)d and %(ServerHost)H limit %(Batch)d")); - _ -> - SType = misc:atom_to_binary(Type), - ejabberd_sql:sql_query( - ServerHost, - ?SQL("delete from archive" - " where timestamp < %(TS)d" - " and kind=%(SType)s" - " and %(ServerHost)H limit %(Batch)d")) - end, + case Type of + all -> + ejabberd_sql:sql_query( + ServerHost, + fun(sqlite, _) -> + ejabberd_sql:sql_query_t( + ?SQL("delete from archive where rowid in " + "(select rowid from archive where timestamp < %(TS)d and %(ServerHost)H limit %(Batch)d)")); + (_, _) -> + ejabberd_sql:sql_query_t( + ?SQL("delete from archive" + " where timestamp < %(TS)d and %(ServerHost)H limit %(Batch)d")) + end); + _ -> + SType = misc:atom_to_binary(Type), + ejabberd_sql:sql_query( + ServerHost, + fun(sqlire,_)-> + ejabberd_sql:sql_query_t( + ?SQL("delete from archive where rowid in (" + " select rowid from archive where timestamp < %(TS)d" + " and kind=%(SType)s" + " and %(ServerHost)H limit %(Batch)d)")); + (_,_)-> + ejabberd_sql:sql_query_t( + ?SQL("delete from archive" + " where timestamp < %(TS)d" + " and kind=%(SType)s" + " and %(ServerHost)H limit %(Batch)d")) + end) + end, case Res of {updated, Count} -> {ok, Count}; From 7167df79795fd157972915febc599d41c0341713 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 11:05:12 +0200 Subject: [PATCH 1103/1302] mysql.sql: Fix typo in commit 7862c6a when creating users table --- sql/mysql.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysql.sql b/sql/mysql.sql index 210f41713..630c4a557 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -18,7 +18,7 @@ CREATE TABLE users ( username varchar(191) NOT NULL, - type smallint NOT NULL,, + type smallint NOT NULL, password text NOT NULL, serverkey varchar(128) NOT NULL DEFAULT '', salt varchar(128) NOT NULL DEFAULT '', From 82ec0a4837ddc7848abce94b64b8f267591f8e6f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 21:13:47 +0200 Subject: [PATCH 1104/1302] Remove unused MyNick variables --- test/muc_tests.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/muc_tests.erl b/test/muc_tests.erl index 053c62b99..ae249691d 100644 --- a/test/muc_tests.erl +++ b/test/muc_tests.erl @@ -313,8 +313,6 @@ duplicate_occupantid_master(Config) -> PeerJID = ?config(slave, Config), PeerNick = ?config(slave_nick, Config), PeerNickJID = jid:replace_resource(Room, PeerNick), - MyNick = ?config(nick, Config), - MyNickJID = jid:replace_resource(Room, MyNick), ok = join_new(Config), wait_for_slave(Config), Pres = ?match(#presence{from = PeerNickJID, type = available} = Pres, From 54796f888eb5fe15b250eed969427e4c8aac9a0f Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 11:24:08 +0200 Subject: [PATCH 1105/1302] Raise Erlang/OTP minimum requirement to 25.0 (#4281) --- .github/workflows/runtime.yml | 5 +++++ COMPILE.md | 2 +- configure.ac | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index e0b7469e4..cc4829911 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -44,6 +44,11 @@ jobs: - uses: actions/checkout@v4 + - name: Temporarily reenable compilation with Erlang/OTP 20 + if: matrix.otp < 25 + run: | + sed -i "s|13.0 |9.0.5 |g" configure.ac + - name: Get compatible Rebar binaries if: matrix.otp < 24 run: | diff --git a/COMPILE.md b/COMPILE.md index 67277051c..49e9be269 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -19,7 +19,7 @@ To compile ejabberd you need: - GCC - Libexpat ≥ 1.95 - Libyaml ≥ 0.1.4 -- Erlang/OTP ≥ 20.0 +- Erlang/OTP ≥ 25.0 - OpenSSL ≥ 1.0.0 Other optional libraries are: diff --git a/configure.ac b/configure.ac index b7ade05cb..163f027aa 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) -REQUIRE_ERLANG_MIN="9.0.5 (Erlang/OTP 20.0)" +REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) From 05b0037462343f68a01413f50520d46e8fa4017a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 18:10:43 +0200 Subject: [PATCH 1106/1302] Raise the minimum Elixir tested version to 1.14.0 (#4281) Cannot test with Elixir 1.13.4 because its container image includes Erlang/OTP 24.3 that ejabberd does not support anymore. --- .github/workflows/runtime.yml | 5 ++--- COMPILE.md | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index cc4829911..da9fc9c03 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -169,7 +169,7 @@ jobs: strategy: fail-fast: false matrix: - elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] + elixir: ['1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} @@ -292,7 +292,7 @@ jobs: strategy: fail-fast: false matrix: - elixir: ['1.13', '1.14', '1.15', '1.16', '1.17', '1.18'] + elixir: ['1.14', '1.15', '1.16', '1.17', '1.18'] runs-on: ubuntu-24.04 container: image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }} @@ -349,7 +349,6 @@ jobs: - run: make dialyzer - run: make edoc - if: matrix.elixir >= '1.14' - name: Run rel run: | diff --git a/COMPILE.md b/COMPILE.md index 49e9be269..42f30a6f2 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -28,8 +28,7 @@ Other optional libraries are: - PAM library, for Pluggable Authentication Modules (PAM) - ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA challenges -- Elixir ≥ 1.10.3, for Elixir support. It is recommended Elixir 1.13.4 or higher - and Erlang/OTP 23.0 or higher. +- Elixir ≥ 1.10.3, for Elixir support. It is recommended Elixir 1.14.0 or higher If your system splits packages in libraries and development headers, install the development packages too. From 826123db56eb8c910295df43eb0d2ec864338e87 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 11:09:26 +0200 Subject: [PATCH 1107/1302] Bump Erlang/OTP version to 27.3.3 --- .github/container/Dockerfile | 2 +- CONTAINER.md | 2 +- tools/make-binaries | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index e92ffe8dd..ccf1af5a6 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,5 @@ #' Define default build variables -ARG OTP_VSN='27.3.2' +ARG OTP_VSN='27.3.3' ARG ELIXIR_VSN='1.18.3' ARG UID='9000' ARG USER='ejabberd' diff --git a/CONTAINER.md b/CONTAINER.md index 5b46ffb5d..de28e9fb3 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -926,7 +926,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.2-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.3-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included | diff --git a/tools/make-binaries b/tools/make-binaries index e0847cc0c..2d3e9adb1 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.6.4' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.4.1' -otp_vsn='27.3.2' +otp_vsn='27.3.3' elixir_vsn='1.18.3' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' From 67cc0c528667a5bcdeab0cf63cf8aa0d42c6f0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 22 Apr 2025 12:30:55 +0200 Subject: [PATCH 1108/1302] Handle objects that don't need conversion in mod_mam_mnesia:transform() This should fix issue #4374 --- src/mod_mam_mnesia.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 0895c994f..68edb1c5e 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -365,4 +365,6 @@ transform({archive_msg, US, ID, Timestamp, Peer, BarePeer, packet = Packet, nick = Nick, type = Type, - origin_id = <<"">>}. + origin_id = <<"">>}; +transform(Other) -> + Other. From 3874e719715beb2539f1c53f6b86376b354509aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Apr 2025 12:58:59 +0200 Subject: [PATCH 1109/1302] Better lists:uniq substitute in ejabberd_config Original version didn't keep original order of modules, which could break ability to override of options by external modules. --- src/ejabberd_config.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 92ccae3a5..d9c053da8 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -582,7 +582,14 @@ callback_modules(all) -> -ifdef(OTP_BELOW_25). lists_uniq(List) -> - lists:usort(List). + {Res, _} = lists:foldr( + fun(El, {Result, Existing} = Acc) -> + case maps:is_key(El, Existing) of + true -> Acc; + _ -> {[El | Result], Existing#{El => true}} + end + end, {[], #{}}, List), + Res. -else. lists_uniq(List) -> lists:uniq(List). From e7997244aff4cd23336af86d6f3c98fe441b38da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 28 Apr 2025 13:58:40 +0200 Subject: [PATCH 1110/1302] Allow to specify minimal erlang version using --with-min-erlang in configure --- configure.ac | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 163f027aa..12e3ae463 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,16 @@ AC_PREREQ(2.59) AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) -REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)" + +AC_ARG_WITH(min-erlang, + AS_HELP_STRING([--with-min-erlang=version],[set minimal required erlang version, default to OTP25]), +[if test "X$withval" = "X"; then + REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)" +else + REQUIRE_ERLANG_MIN="$withval" +fi +], [REQUIRE_ERLANG_MIN="13.0 (Erlang/OTP 25.0)"]) + REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) From 838bbd70ef1c8de7d7ea55c95633e24564b5a19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 29 Apr 2025 10:33:17 +0200 Subject: [PATCH 1111/1302] Strip query data when returning errors in mod_register --- src/mod_register.erl | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index b811dcff0..6c422ca51 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -87,7 +87,7 @@ c2s_unauthenticated_packet(#{ip := IP, server := Server} = State, catch _:{xmpp_codec, Why} -> Txt = xmpp:io_format_error(Why), Lang = maps:get(lang, State), - Err = xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)), + Err = make_stripped_error(IQ, xmpp:err_bad_request(Txt, Lang)), {stop, ejabberd_c2s:send(State, Err)} end; c2s_unauthenticated_packet(State, _) -> @@ -116,7 +116,7 @@ process_iq(#iq{type = set, lang = Lang, sub_els = [#register{remove = true}]} = IQ, _Source, _IsCaptchaEnabled, _AllowRemove = false) -> Txt = ?T("Access denied by service policy"), - xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)); + make_stripped_error(IQ, xmpp:err_forbidden(Txt, Lang)); process_iq(#iq{type = set, lang = Lang, to = To, from = From, sub_els = [#register{remove = true, username = User, @@ -141,12 +141,12 @@ process_iq(#iq{type = set, lang = Lang, to = To, from = From, ignore; false -> Txt = ?T("Incorrect password"), - xmpp:make_error( + make_stripped_error( IQ, xmpp:err_forbidden(Txt, Lang)) end; true -> Txt = ?T("No 'password' found in this query"), - xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) + make_stripped_error(IQ, xmpp:err_bad_request(Txt, Lang)) end end; true -> @@ -158,7 +158,7 @@ process_iq(#iq{type = set, lang = Lang, to = To, from = From, ignore; _ -> Txt = ?T("The query is only allowed from local users"), - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)) + make_stripped_error(IQ, xmpp:err_not_allowed(Txt, Lang)) end end; process_iq(#iq{type = set, to = To, @@ -186,17 +186,17 @@ process_iq(#iq{type = set, to = To, User, Server, Password, IQ, Source, true); _ -> Txt = ?T("Incorrect data form"), - xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) + make_stripped_error(IQ, xmpp:err_bad_request(Txt, Lang)) end; {error, malformed} -> Txt = ?T("Incorrect CAPTCHA submit"), - xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); + make_stripped_error(IQ, xmpp:err_bad_request(Txt, Lang)); _ -> ErrText = ?T("The CAPTCHA verification has failed"), - xmpp:make_error(IQ, xmpp:err_not_allowed(ErrText, Lang)) + make_stripped_error(IQ, xmpp:err_not_allowed(ErrText, Lang)) end; process_iq(#iq{type = set} = IQ, _Source, _IsCaptchaEnabled, _AllowRemove) -> - xmpp:make_error(IQ, xmpp:err_bad_request()); + make_stripped_error(IQ, xmpp:err_bad_request()); process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ, Source, IsCaptchaEnabled, _AllowRemove) -> Server = To#jid.lserver, @@ -248,11 +248,11 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ, sub_els = [Xdata | CaptchaEls2]}); {error, limit} -> ErrText = ?T("Too many CAPTCHA requests"), - xmpp:make_error( + make_stripped_error( IQ, xmpp:err_resource_constraint(ErrText, Lang)); _Err -> ErrText = ?T("Unable to generate a CAPTCHA"), - xmpp:make_error( + make_stripped_error( IQ, xmpp:err_internal_server_error(ErrText, Lang)) end; true -> @@ -277,14 +277,14 @@ try_register_or_set_password(User, Server, Password, ok -> xmpp:make_iq_result(IQ); {error, Error} -> - xmpp:make_error(IQ, Error) + make_stripped_error(IQ, Error) end; deny -> Txt = ?T("Access denied by service policy"), - xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)) + make_stripped_error(IQ, xmpp:err_forbidden(Txt, Lang)) end; _ -> - xmpp:make_error(IQ, xmpp:err_not_allowed()) + make_stripped_error(IQ, xmpp:err_not_allowed()) end. try_set_password(User, Server, Password) -> @@ -307,15 +307,15 @@ try_set_password(User, Server, Password, #iq{lang = Lang, meta = M} = IQ) -> xmpp:make_iq_result(IQ); {error, not_allowed} -> Txt = ?T("Changing password is not allowed"), - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); + make_stripped_error(IQ, xmpp:err_not_allowed(Txt, Lang)); {error, invalid_jid = Why} -> - xmpp:make_error(IQ, xmpp:err_jid_malformed(format_error(Why), Lang)); + make_stripped_error(IQ, xmpp:err_jid_malformed(format_error(Why), Lang)); {error, invalid_password = Why} -> - xmpp:make_error(IQ, xmpp:err_not_allowed(format_error(Why), Lang)); + make_stripped_error(IQ, xmpp:err_not_allowed(format_error(Why), Lang)); {error, weak_password = Why} -> - xmpp:make_error(IQ, xmpp:err_not_acceptable(format_error(Why), Lang)); + make_stripped_error(IQ, xmpp:err_not_acceptable(format_error(Why), Lang)); {error, db_failure = Why} -> - xmpp:make_error(IQ, xmpp:err_internal_server_error(format_error(Why), Lang)) + make_stripped_error(IQ, xmpp:err_internal_server_error(format_error(Why), Lang)) end. try_register(User, Server, Password, SourceRaw, Module) -> @@ -562,6 +562,11 @@ is_strong_password2(Server, Password) -> ejabberd_auth:entropy(Password) >= Entropy end. +make_stripped_error(#iq{} = IQ, Err) -> + xmpp:make_error(xmpp:remove_subtag(IQ, #register{}), Err); +make_stripped_error(Pkt, Err) -> + xmpp:make_error(Pkt, Err). + %%% %%% ip_access management %%% From bd5f9537c515bc503bba0146b0a5a223d62fed6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 29 Apr 2025 11:04:23 +0200 Subject: [PATCH 1112/1302] Normalize username when determining if user want to change pass in mod_register Should fix issue #4377 --- src/mod_register.erl | 6 ++++-- test/ejabberd_SUITE.erl | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 6c422ca51..dfe9ffe76 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -267,8 +267,10 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ, try_register_or_set_password(User, Server, Password, #iq{from = From, lang = Lang} = IQ, Source, CaptchaSucceed) -> - case From of - #jid{user = User, lserver = Server} -> + case {jid:nodeprep(User), From} of + {error, _} -> + make_stripped_error(IQ, {error, invalid_jid}); + {UserP, #jid{user = User2, lserver = Server}} when UserP == User2 -> try_set_password(User, Server, Password, IQ); _ when CaptchaSucceed -> case check_from(From, Server) of diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index eb5f724f4..b0e47cde6 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -436,6 +436,7 @@ db_tests(DB) when DB == mnesia; DB == redis -> mam_tests:single_cases(), csi_tests:single_cases(), push_tests:single_cases(), + test_pass_change, test_unregister]}, muc_tests:master_slave_cases(), privacy_tests:master_slave_cases(), @@ -465,6 +466,7 @@ db_tests(DB) -> offline_tests:single_cases(), mam_tests:single_cases(), push_tests:single_cases(), + test_pass_change, test_unregister]}, muc_tests:master_slave_cases(), privacy_tests:master_slave_cases(), @@ -683,6 +685,25 @@ register(Config) -> password = ?config(password, Config)}]}), Config. +test_pass_change(Config) -> + case ?config(register, Config) of + true -> + #iq{type = result, sub_els = []} = + send_recv( + Config, + #iq{type = set, + sub_els = [#register{username = ?config(user, Config), + password = ?config(password, Config)}]}), + #iq{type = result, sub_els = []} = + send_recv( + Config, + #iq{type = set, + sub_els = [#register{username = str:to_upper(?config(user, Config)), + password = ?config(password, Config)}]}); + _ -> + {skipped, 'registration_not_available'} + end. + test_unregister(Config) -> case ?config(register, Config) of true -> From f046aeeaa2eb42ea041f6fa4e06641ae8aa4bcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 29 Apr 2025 12:20:13 +0200 Subject: [PATCH 1113/1302] Fix dialyzer warning in last commit --- src/mod_register.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index dfe9ffe76..3c32bc362 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -269,7 +269,8 @@ try_register_or_set_password(User, Server, Password, Source, CaptchaSucceed) -> case {jid:nodeprep(User), From} of {error, _} -> - make_stripped_error(IQ, {error, invalid_jid}); + Err = xmpp:err_jid_malformed(format_error(invalid_jid), Lang), + make_stripped_error(IQ, Err); {UserP, #jid{user = User2, lserver = Server}} when UserP == User2 -> try_set_password(User, Server, Password, IQ); _ when CaptchaSucceed -> @@ -564,10 +565,8 @@ is_strong_password2(Server, Password) -> ejabberd_auth:entropy(Password) >= Entropy end. -make_stripped_error(#iq{} = IQ, Err) -> - xmpp:make_error(xmpp:remove_subtag(IQ, #register{}), Err); -make_stripped_error(Pkt, Err) -> - xmpp:make_error(Pkt, Err). +make_stripped_error(IQ, Err) -> + xmpp:make_error(xmpp:remove_subtag(IQ, #register{}), Err). %%% %%% ip_access management From cbb88638d22fe898af9c5821bf5e6e5993febcb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 30 Apr 2025 14:31:05 +0200 Subject: [PATCH 1114/1302] Add mssql specific implementation of delete_old_mam_messages --- src/mod_mam_sql.erl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 2747254ac..71420c9ea 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -221,6 +221,10 @@ delete_old_messages_batch(ServerHost, TimeStamp, Type, Batch) -> ejabberd_sql:sql_query_t( ?SQL("delete from archive where rowid in " "(select rowid from archive where timestamp < %(TS)d and %(ServerHost)H limit %(Batch)d)")); + (mssql, _) -> + ejabberd_sql:sql_query_t( + ?SQL("delete top(%(Batch)d)§ from archive" + " where timestamp < %(TS)d and %(ServerHost)H")); (_, _) -> ejabberd_sql:sql_query_t( ?SQL("delete from archive" @@ -236,6 +240,12 @@ delete_old_messages_batch(ServerHost, TimeStamp, Type, Batch) -> " select rowid from archive where timestamp < %(TS)d" " and kind=%(SType)s" " and %(ServerHost)H limit %(Batch)d)")); + (mssql, _)-> + ejabberd_sql:sql_query_t( + ?SQL("delete top(%(Batch)d) from archive" + " where timestamp < %(TS)d" + " and kind=%(SType)s" + " and %(ServerHost)H")); (_,_)-> ejabberd_sql:sql_query_t( ?SQL("delete from archive" From 128103b7b221c188629528b8320a41d1ac10defc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 30 Apr 2025 14:44:19 +0200 Subject: [PATCH 1115/1302] Typo --- src/mod_mam_sql.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 71420c9ea..01efa313b 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -234,7 +234,7 @@ delete_old_messages_batch(ServerHost, TimeStamp, Type, Batch) -> SType = misc:atom_to_binary(Type), ejabberd_sql:sql_query( ServerHost, - fun(sqlire,_)-> + fun(sqlite,_)-> ejabberd_sql:sql_query_t( ?SQL("delete from archive where rowid in (" " select rowid from archive where timestamp < %(TS)d" From d65cafae647fd7a6b1179ddb0194d7f1e501ccde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 12 May 2025 18:47:18 +0200 Subject: [PATCH 1116/1302] Update code for switching to new schema type to users table changes --- sql/pg.new.sql | 2 +- src/mod_admin_update_sql.erl | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 5bd107d97..1e59ec571 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -20,7 +20,7 @@ -- ALTER TABLE users ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE users DROP CONSTRAINT users_pkey; --- ALTER TABLE users ADD PRIMARY KEY (server_host, username); +-- ALTER TABLE users ADD PRIMARY KEY (server_host, username, "type"); -- ALTER TABLE users ALTER COLUMN server_host DROP DEFAULT; -- ALTER TABLE last ADD COLUMN server_host text NOT NULL DEFAULT ''; diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index ba33f7e50..105828f7d 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -130,7 +130,7 @@ update_tables(State) -> case add_sh_column(State, "users") of true -> drop_pkey(State, "users"), - add_pkey(State, "users", ["server_host", "username"]), + add_pkey(State, "users", ["server_host", "username", "type"]), drop_sh_default(State, "users"); false -> ok @@ -443,7 +443,8 @@ drop_pkey(#state{dbtype = mysql} = State, Table) -> ["ALTER TABLE ", Table, " DROP PRIMARY KEY;"]). add_pkey(#state{dbtype = pgsql} = State, Table, Cols) -> - SCols = string:join(Cols, ", "), + Cols2 = lists:map(fun("type") -> "\"type\""; (V) -> V end, Cols), + SCols = string:join(Cols2, ", "), sql_query( State#state.host, ["ALTER TABLE ", Table, " ADD PRIMARY KEY (", SCols, ");"]); From bf3f904fe921ecff8394b261cb98f31383a04a9b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 19:07:09 +0200 Subject: [PATCH 1117/1302] Runtime: Don't test rebar2 + OTP 28 because "make rel" fails When running "make rel": ./rebar generate ==> rel (generate) ERROR: generate failed while processing /__w/ejabberd/ejabberd/rel: {'EXIT',{{badmatch,{error,"Application et is used in release \"ejabberd\" and cannot be excluded"}}, [{rebar_reltool,generate,2, [{file,"src/rebar_reltool.erl"},{line,53}]}, {rebar_core,run_modules,4,[{file,"src/rebar_core.erl"},{line,493}]}, {rebar_core,execute,6,[{file,"src/rebar_core.erl"},{line,418}]}, {rebar_core,maybe_execute,8, [{file,"src/rebar_core.erl"},{line,302}]}, {rebar_core,process_dir1,7,[{file,"src/rebar_core.erl"},{line,261}]}, {rebar_core,process_each,5,[{file,"src/rebar_core.erl"},{line,351}]}, {rebar_core,process_dir1,7,[{file,"src/rebar_core.erl"},{line,253}]}, {rebar_core,process_commands,2, [{file,"src/rebar_core.erl"},{line,93}]}]}} make: *** [Makefile:570: prod] Error 1 --- .github/workflows/runtime.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index da9fc9c03..87faf076d 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -36,6 +36,8 @@ jobs: exclude: - otp: '27' rebar: 'rebar' + - otp: '28' + rebar: 'rebar' runs-on: ubuntu-24.04 container: image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }} From 354009033a3473b5e36ef0f2a2af11f97db0cf09 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 18:54:12 +0200 Subject: [PATCH 1118/1302] CI: Don't run "make options" with Erlang/OTP 28 yet because it crashes (#4352) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fafa6eb65..8edbd2ec7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,6 +116,7 @@ jobs: - run: make install -s - run: make hooks - run: make options + if: matrix.otp <= '27' - run: make xref - run: make dialyzer - run: make test-eunit From 30c8088d73274c6273efe2f977eb0949bee3b745 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Apr 2025 21:08:39 +0200 Subject: [PATCH 1119/1302] Fix crash in "rebar3 cover" with Erlang/OTP 28 (#4353) --- rebar.config | 1 + 1 file changed, 1 insertion(+) diff --git a/rebar.config b/rebar.config index 0d09d7f22..5d29a5b7f 100644 --- a/rebar.config +++ b/rebar.config @@ -255,6 +255,7 @@ {cover_enabled, true}. {cover_export_enabled, true}. +{cover_excl_mods, [eldap_filter_yecc]}. {coveralls_coverdata, "_build/test/cover/ct.coverdata"}. {coveralls_service_name, "github"}. From 010eab6e306b8eeff0f348f8d9cb2d7214cba7c0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 8 May 2025 13:15:25 +0200 Subject: [PATCH 1120/1302] When encoding JSON, handle term that is key-value list (#4379) --- src/misc.erl | 10 ++++------ src/mod_matrix_gw.erl | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/misc.erl b/src/misc.erl index 46877538b..ca7e43486 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -42,7 +42,7 @@ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4, get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, - json_encode/1, json_decode/1, json_encode_with_kv_lists/1, + json_encode/1, json_decode/1, set_proc_label/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). @@ -132,20 +132,18 @@ crypto_hmac(Type, Key, Data, MacL) -> crypto:macN(hmac, Type, Key, Data, MacL). -endif. -ifdef(OTP_BELOW_27). -json_encode_with_kv_lists(Term) -> - jiffy:encode(Term). json_encode(Term) -> jiffy:encode(Term). json_decode(Bin) -> jiffy:decode(Bin, [return_maps]). -else. -json_encode_with_kv_lists(Term) -> +json_encode({[{_Key, _Value} | _]} = Term) -> iolist_to_binary(json:encode(Term, fun({Val}, Encoder) when is_list(Val) -> json:encode_key_value_list(Val, Encoder); (Val, Encoder) -> - json:encode_value(Val, Encoder) - end)). + json:encode_value(Val, Encoder) + end)); json_encode(Term) -> iolist_to_binary(json:encode(Term)). json_decode(Bin) -> diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 3eeaa47dc..f8e208ebe 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -704,7 +704,7 @@ get_pruned_event_id(PrunedEvent) -> encode_canonical_json(JSON) -> JSON2 = sort_json(JSON), - misc:json_encode_with_kv_lists(JSON2). + misc:json_encode(JSON2). sort_json(#{} = Map) -> Map2 = maps:map(fun(_K, V) -> From 18e7805ef51e31f6d32f9627c1112609dcfa5eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 16 May 2025 13:33:24 +0200 Subject: [PATCH 1121/1302] Present mam full text search in xep-431 compatible way Also offer those fields only on mysql, where full text search is available --- src/mod_mam.erl | 19 +++++++++++++++---- src/mod_mam_mnesia.erl | 4 ++-- src/mod_mam_sql.erl | 15 ++++++++++++--- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 8df6900ee..33f361f47 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -31,6 +31,7 @@ -protocol({xep, 424, '0.4.2', '24.02', "partial", "Tombstones not implemented"}). -protocol({xep, 425, '0.3.0', '24.06', "complete", ""}). -protocol({xep, 441, '0.2.0', '15.06', "complete", ""}). +-protocol({xep, 431, '0.2.0', '24.12', "complete", ""}). -behaviour(gen_mod). @@ -78,7 +79,7 @@ -callback delete_old_messages(binary() | global, erlang:timestamp(), all | chat | groupchat) -> any(). --callback extended_fields() -> [mam_query:property() | #xdata_field{}]. +-callback extended_fields(binary()) -> [mam_query:property() | #xdata_field{}]. -callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat, jid(), binary(), recv | send, integer(), binary(), {true, binary()} | false) -> ok | any(). @@ -605,8 +606,18 @@ parse_query(#mam_query{xdata = #xdata{}} = Query, Lang) -> #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [?NS_MAM_1]}, Query#mam_query.xdata), - try mam_query:decode(X#xdata.fields) of - Form -> {ok, Form} + {Fields, WithText} = case lists:keytake(<<"{urn:xmpp:fulltext:0}fulltext">>, #xdata_field.var, X#xdata.fields) of + false -> {X#xdata.fields, <<>>}; + {value, #xdata_field{values = [V]}, F} -> {F, V}; + {value, _, F} -> {F, <<>>} + end, + try mam_query:decode(Fields) of + Form -> + if WithText /= <<>> -> + {ok, lists:keystore(withtext, 1, Form, {withtext, WithText})}; + true -> + {ok, Form} + end catch _:{mam_query, Why} -> Txt = mam_query:format_error(Why), {error, xmpp:err_bad_request(Txt, Lang)} @@ -818,7 +829,7 @@ process_iq(LServer, #iq{sub_els = [#mam_query{xmlns = NS}]} = IQ) -> CommonFields = [{with, undefined}, {start, undefined}, {'end', undefined}], - ExtendedFields = Mod:extended_fields(), + ExtendedFields = Mod:extended_fields(LServer), Fields = mam_query:encode(CommonFields ++ ExtendedFields), X = xmpp_util:set_xdata_field( #xdata_field{var = <<"FORM_TYPE">>, type = hidden, values = [NS]}, diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl index 68edb1c5e..4f59fa1fc 100644 --- a/src/mod_mam_mnesia.erl +++ b/src/mod_mam_mnesia.erl @@ -28,7 +28,7 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/6, + extended_fields/1, store/10, write_prefs/4, get_prefs/2, select/6, remove_from_archive/3, is_empty_for_user/2, is_empty_for_room/3, delete_old_messages_batch/5, transform/1]). @@ -185,7 +185,7 @@ delete_old_messages_batch(LServer, TimeStamp, Type, Batch, LastUS) -> {error, Err} end. -extended_fields() -> +extended_fields(_) -> []. store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, TS, diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index 01efa313b..8a1d8e02f 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -29,7 +29,7 @@ %% API -export([init/2, remove_user/2, remove_room/3, delete_old_messages/3, - extended_fields/0, store/10, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, + extended_fields/1, store/10, write_prefs/4, get_prefs/2, select/7, export/1, remove_from_archive/3, is_empty_for_user/2, is_empty_for_room/3, select_with_mucsub/6, delete_old_messages_batch/4, count_messages_to_delete/3]). -export([sql_schemas/0]). @@ -280,8 +280,17 @@ delete_old_messages(ServerHost, TimeStamp, Type) -> end, ok. -extended_fields() -> - [{withtext, <<"">>}]. +extended_fields(LServer) -> + case ejabberd_option:sql_type(LServer) of + mysql -> + [{withtext, <<"">>}, + #xdata_field{var = <<"{urn:xmpp:fulltext:0}fulltext">>, + type = 'text-single', + label = <<"Search the text">>, + values = []}]; + _ -> + [] + end. store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS, OriginID, Retract) -> From c38b2bfc2161b1272a7230d30ffd97b370cc7045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 21 May 2025 15:37:49 +0200 Subject: [PATCH 1122/1302] Add options that allow to configure proxy used by rest module --- src/ejabberd_option.erl | 32 ++++++++++++++++++++++++++++++++ src/ejabberd_options.erl | 12 ++++++++++++ src/ejabberd_options_doc.erl | 16 ++++++++++++++++ src/rest.erl | 20 +++++++++++++++++--- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 2a5c36963..d9ded9bdf 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -119,6 +119,10 @@ -export([redis_server/0]). -export([registration_timeout/0]). -export([resource_conflict/0, resource_conflict/1]). +-export([rest_proxy/0, rest_proxy/1]). +-export([rest_proxy_password/0, rest_proxy_password/1]). +-export([rest_proxy_port/0, rest_proxy_port/1]). +-export([rest_proxy_username/0, rest_proxy_username/1]). -export([router_cache_life_time/0]). -export([router_cache_missed/0]). -export([router_cache_size/0]). @@ -833,6 +837,34 @@ resource_conflict() -> resource_conflict(Host) -> ejabberd_config:get_option({resource_conflict, Host}). +-spec rest_proxy() -> binary(). +rest_proxy() -> + rest_proxy(global). +-spec rest_proxy(global | binary()) -> binary(). +rest_proxy(Host) -> + ejabberd_config:get_option({rest_proxy, Host}). + +-spec rest_proxy_password() -> string(). +rest_proxy_password() -> + rest_proxy_password(global). +-spec rest_proxy_password(global | binary()) -> string(). +rest_proxy_password(Host) -> + ejabberd_config:get_option({rest_proxy_password, Host}). + +-spec rest_proxy_port() -> char(). +rest_proxy_port() -> + rest_proxy_port(global). +-spec rest_proxy_port(global | binary()) -> char(). +rest_proxy_port(Host) -> + ejabberd_config:get_option({rest_proxy_port, Host}). + +-spec rest_proxy_username() -> string(). +rest_proxy_username() -> + rest_proxy_username(global). +-spec rest_proxy_username(global | binary()) -> string(). +rest_proxy_username(Host) -> + ejabberd_config:get_option({rest_proxy_username, Host}). + -spec router_cache_life_time() -> 'infinity' | pos_integer(). router_cache_life_time() -> ejabberd_config:get_option({router_cache_life_time, global}). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 8dc88cd23..489320840 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -333,6 +333,14 @@ opt_type(registration_timeout) -> econf:timeout(second, infinity); opt_type(resource_conflict) -> econf:enum([setresource, closeold, closenew, acceptnew]); +opt_type(rest_proxy) -> + econf:domain(); +opt_type(rest_proxy_port) -> + econf:port(); +opt_type(rest_proxy_username) -> + econf:string(); +opt_type(rest_proxy_password) -> + econf:string(); opt_type(router_cache_life_time) -> econf:timeout(second, infinity); opt_type(router_cache_missed) -> @@ -652,6 +660,10 @@ options() -> {redis_server, "localhost"}, {registration_timeout, timer:seconds(600)}, {resource_conflict, acceptnew}, + {rest_proxy, <<>>}, + {rest_proxy_port, 0}, + {rest_proxy_username, ""}, + {rest_proxy_password, ""}, {router_cache_life_time, fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end}, {router_cache_missed, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 36bebcd5c..cbbc4be9f 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -1194,6 +1194,22 @@ doc() -> "uses old Jabber Non-SASL authentication (XEP-0078), " "then this option is not respected, and the action performed " "is 'closeold'.")}}, + {rest_proxy, + #{value => "Host", + desc => ?T("Address of a HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_port, + #{value => "1..65535", + desc => ?T("Port of a HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_username, + #{value => "string()", + desc => ?T("Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, + {rest_proxy_password, + #{value => "string()", + desc => ?T("Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls " + "(like ejabberd_oauth_rest)")}}, {router_cache_life_time, #{value => "timeout()", desc => diff --git a/src/rest.erl b/src/rest.erl index 7fde9e710..6134c0557 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -38,7 +38,14 @@ start(Host) -> application:start(inets), Size = ejabberd_option:ext_api_http_pool_size(Host), - httpc:set_options([{max_sessions, Size}]). + Proxy = case {ejabberd_option:rest_proxy(Host), + ejabberd_option:rest_proxy_port(Host)} of + {<<>>, _, _} -> + []; + {Host, Port} -> + [{proxy, {{binary_to_list(Host), Port}, []}}] + end, + httpc:set_options([{max_sessions, Size}] ++ Proxy). stop(_Host) -> ok. @@ -87,8 +94,15 @@ request(Server, Method, Path, Params, Mime, Data) -> _ -> {Params, []} end, URI = to_list(url(Server, Path, Query)), - HttpOpts = [{connect_timeout, ?CONNECT_TIMEOUT}, - {timeout, ?HTTP_TIMEOUT}], + HttpOpts = case {ejabberd_option:rest_proxy_username(Server), + ejabberd_option:rest_proxy_password(Server)} of + {"", _} -> [{connect_timeout, ?CONNECT_TIMEOUT}, + {timeout, ?HTTP_TIMEOUT}]; + {User, Pass} -> + [{connect_timeout, ?CONNECT_TIMEOUT}, + {timeout, ?HTTP_TIMEOUT}, + {proxy_auth, {User, Pass}}] + end, Hdrs = [{"connection", "keep-alive"}, {"Accept", "application/json"}, {"User-Agent", "ejabberd"}] From 9d1d57cd82edbf3b330c91f9e8e0aecdd2c406d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 21 May 2025 15:53:13 +0200 Subject: [PATCH 1123/1302] Fix typo in last commit --- src/rest.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest.erl b/src/rest.erl index 6134c0557..b456fdaac 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -40,7 +40,7 @@ start(Host) -> Size = ejabberd_option:ext_api_http_pool_size(Host), Proxy = case {ejabberd_option:rest_proxy(Host), ejabberd_option:rest_proxy_port(Host)} of - {<<>>, _, _} -> + {<<>>, _} -> []; {Host, Port} -> [{proxy, {{binary_to_list(Host), Port}, []}}] From 038491d2ecee6a41ad824956f1dae0e448ff0e25 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 26 May 2025 18:11:45 +0300 Subject: [PATCH 1124/1302] Support older Matrix rooms versions starting from version 4 --- include/mod_matrix_gw.hrl | 7 ++ src/mod_matrix_gw.erl | 47 +++++++++-- src/mod_matrix_gw_room.erl | 164 +++++++++++++++++++++++++++++++++---- src/mod_matrix_gw_s2s.erl | 25 ++++-- 4 files changed, 210 insertions(+), 33 deletions(-) diff --git a/include/mod_matrix_gw.hrl b/include/mod_matrix_gw.hrl index 18efbf252..3b4b6a41f 100644 --- a/include/mod_matrix_gw.hrl +++ b/include/mod_matrix_gw.hrl @@ -21,6 +21,13 @@ -record(room_version, {id :: binary(), %% use the same field names as in Synapse + enforce_key_validity :: boolean(), + special_case_aliases_auth :: boolean(), + strict_canonicaljson :: boolean(), + limit_notifications_power_levels :: boolean(), + knock_join_rule :: boolean(), + restricted_join_rule :: boolean(), + restricted_join_rule_fix :: boolean(), knock_restricted_join_rule :: boolean(), enforce_int_power_levels :: boolean(), implicit_room_creator :: boolean(), diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index f8e208ebe..bfd3951b8 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -41,6 +41,7 @@ handle_info/2, terminate/2, code_change/3, depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]). -export([parse_auth/1, encode_canonical_json/1, + is_canonical_json/1, get_id_domain_exn/1, base64_decode/1, base64_encode/1, prune_event/2, get_event_id/2, content_hash/1, @@ -155,8 +156,8 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID], %% TODO: check type and userid Host = ejabberd_config:get_myname(), PrunedEvent = prune_event(Event, RoomVersion), - ?DEBUG("invite ~p~n", [{RoomID, EventID, Event, RoomVer, catch mod_matrix_gw_s2s:check_signature(Host, PrunedEvent), get_pruned_event_id(PrunedEvent)}]), - case mod_matrix_gw_s2s:check_signature(Host, PrunedEvent) of + %?DEBUG("invite ~p~n", [{RoomID, EventID, Event, RoomVer, catch mod_matrix_gw_s2s:check_signature(Host, PrunedEvent, RoomVersion), get_pruned_event_id(PrunedEvent)}]), + case mod_matrix_gw_s2s:check_signature(Host, PrunedEvent, RoomVersion) of true -> case get_pruned_event_id(PrunedEvent) of EventID -> @@ -629,9 +630,15 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event, Content2 = case Type of <<"m.room.member">> -> - C3 = maps:with([<<"membership">>, - <<"join_authorised_via_users_server">>], - Content), + C3 = + case RoomVersion#room_version.restricted_join_rule_fix of + true -> + maps:with([<<"membership">>, + <<"join_authorised_via_users_server">>], + Content); + false -> + maps:with([<<"membership">>], Content) + end, case RoomVersion#room_version.updated_redaction_rules of false -> C3; @@ -653,7 +660,12 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event, Content end; <<"m.room.join_rules">> -> - maps:with([<<"join_rule">>, <<"allow">>], Content); + case RoomVersion#room_version.restricted_join_rule of + false -> + maps:with([<<"join_rule">>], Content); + true -> + maps:with([<<"join_rule">>, <<"allow">>], Content) + end; <<"m.room.power_levels">> -> case RoomVersion#room_version.updated_redaction_rules of false -> @@ -677,6 +689,8 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event, true -> maps:with([<<"redacts">>], Content) end; + <<"m.room.aliases">> when RoomVersion#room_version.special_case_aliases_auth -> + maps:with([<<"aliases">>], Content); _ -> #{} end, Event2#{<<"content">> := Content2}. @@ -716,6 +730,27 @@ sort_json(List) when is_list(List) -> sort_json(JSON) -> JSON. +is_canonical_json(N) when is_integer(N), + -16#1FFFFFFFFFFFFF =< N, + N =< 16#1FFFFFFFFFFFFF -> + true; +is_canonical_json(B) when is_binary(B) -> + true; +is_canonical_json(B) when is_boolean(B) -> + true; +is_canonical_json(Map) when is_map(Map) -> + maps:fold( + fun(_K, V, true) -> + is_canonical_json(V); + (_K, _V, false) -> + false + end, true, Map); +is_canonical_json(List) when is_list(List) -> + lists:all(fun is_canonical_json/1, List); +is_canonical_json(_) -> + false. + + base64_decode(B) -> Fixed = case size(B) rem 4 of diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 002704f69..1d409716f 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -103,6 +103,7 @@ -define(ROOM_MESSAGE, <<"m.room.message">>). -define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>). -define(ROOM_TOPIC, <<"m.room.topic">>). +-define(ROOM_ALIASES, <<"m.room.aliases">>). -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). -define(MAX_TXN_RETRIES, 5). @@ -650,9 +651,7 @@ handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, D Host, get, MatrixServer, [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, RoomID, UserID], - [{<<"ver">>, <<"9">>}, - {<<"ver">>, <<"10">>}, - {<<"ver">>, <<"11">>}], + [{<<"ver">>, V} || V <- supported_versions()], none, [{timeout, 5000}], [{sync, true}, @@ -770,9 +769,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> Host, get, MatrixServer, [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, RoomID, UserID], - [{<<"ver">>, <<"9">>}, - {<<"ver">>, <<"10">>}, - {<<"ver">>, <<"11">>}], + [{<<"ver">>, V} || V <- supported_versions()], none, [{timeout, 5000}], [{sync, true}, @@ -1280,7 +1277,7 @@ check_event_auth(Event, StateMap, Data) -> <<"ban">> -> check_event_auth_ban( Event, StateMap, Data); - <<"knock">> -> + <<"knock">> when (Data#data.room_version)#room_version.knock_join_rule -> check_event_auth_knock( Event, StateMap, Data); _ -> @@ -1289,6 +1286,18 @@ check_event_auth(Event, StateMap, Data) -> _ -> false end; + ?ROOM_ALIASES when (Data#data.room_version)#room_version.special_case_aliases_auth -> + case Event#event.state_key of + undefined -> + false; + StateKey -> + case mod_matrix_gw:get_id_domain_exn(Event#event.sender) of + StateKey -> + true; + _ -> + false + end + end; _ -> Sender = Event#event.sender, case maps:find({?ROOM_MEMBER, Sender}, StateMap) of @@ -1372,8 +1381,11 @@ check_event_auth_join(Event, StateMap, Data) -> case {JoinRule, SenderMembership} of {<<"public">>, _} -> true; {<<"invite">>, <<"invite">>} -> true; - {<<"knock">>, <<"invite">>} -> true; - {<<"restricted">>, <<"invite">>} -> + {<<"knock">>, <<"invite">>} + when (Data#data.room_version)#room_version.knock_join_rule -> + true; + {<<"restricted">>, <<"invite">>} + when (Data#data.room_version)#room_version.restricted_join_rule -> %% TODO true; {<<"knock_restricted">>, <<"invite">>} @@ -1442,7 +1454,7 @@ check_event_auth_leave(Event, StateMap, Data) -> case SenderMembership of <<"invite">> -> true; <<"join">> -> true; - <<"knock">> -> true; + <<"knock">> when (Data#data.room_version)#room_version.knock_join_rule -> true; _ -> false end; _ -> @@ -1609,6 +1621,13 @@ check_event_auth_power_levels(Event, StateMap, Data) -> try case Event#event.json of #{<<"content">> := NewPL = #{<<"users">> := Users}} when is_map(Users) -> + CheckKeys = + case (Data#data.room_version)#room_version.limit_notifications_power_levels of + false -> + [<<"events">>, <<"users">>]; + true -> + [<<"events">>, <<"users">>, <<"notifications">>] + end, case (Data#data.room_version)#room_version.enforce_int_power_levels of true -> lists:foreach( @@ -1632,7 +1651,7 @@ check_event_auth_power_levels(Event, StateMap, Data) -> end end, [], NewMap) end, - [<<"events">>, <<"users">>, <<"notifications">>]); + CheckKeys); false -> ok end, @@ -1677,7 +1696,7 @@ check_event_auth_power_levels(Event, StateMap, Data) -> end end, [], maps:merge(OldMap, NewMap)) end, - [<<"events">>, <<"users">>, <<"notifications">>]), + CheckKeys), true; _ -> true @@ -1772,7 +1791,7 @@ fill_event(JSON, Data) -> _ -> [] end end, - compute_event_auth_keys(JSON))), + compute_event_auth_keys(JSON, Data#data.room_version))), {JSON#{<<"auth_events">> => AuthEvents, <<"depth">> => Depth2, <<"origin">> => MatrixServer, @@ -1923,7 +1942,8 @@ get_latest_events(Pid) -> check_event_signature(Host, Event) -> PrunedEvent = mod_matrix_gw:prune_event(Event#event.json, Event#event.room_version), - mod_matrix_gw_s2s:check_signature(Host, PrunedEvent). + mod_matrix_gw_s2s:check_signature(Host, PrunedEvent, + Event#event.room_version). find_event(Pid, EventID) -> gen_statem:call(Pid, {find_event, EventID}). @@ -2526,8 +2546,85 @@ find_power_level_event(EventID, Data) -> end, undefined, Event#event.auth_events). +binary_to_room_version(<<"4">>) -> + #room_version{id = <<"4">>, + enforce_key_validity = false, + special_case_aliases_auth = true, + strict_canonicaljson = false, + limit_notifications_power_levels = false, + knock_join_rule = false, + restricted_join_rule = false, + restricted_join_rule_fix = false, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"5">>) -> + #room_version{id = <<"5">>, + enforce_key_validity = true, + special_case_aliases_auth = true, + strict_canonicaljson = false, + limit_notifications_power_levels = false, + knock_join_rule = false, + restricted_join_rule = false, + restricted_join_rule_fix = false, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"6">>) -> + #room_version{id = <<"6">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = false, + restricted_join_rule = false, + restricted_join_rule_fix = false, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"7">>) -> + #room_version{id = <<"7">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = false, + restricted_join_rule_fix = false, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; +binary_to_room_version(<<"8">>) -> + #room_version{id = <<"8">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = false, + knock_restricted_join_rule = false, + enforce_int_power_levels = false, + implicit_room_creator = false, + updated_redaction_rules = false + }; binary_to_room_version(<<"9">>) -> #room_version{id = <<"9">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = true, knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, @@ -2535,6 +2632,13 @@ binary_to_room_version(<<"9">>) -> }; binary_to_room_version(<<"10">>) -> #room_version{id = <<"10">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = true, knock_restricted_join_rule = true, enforce_int_power_levels = true, implicit_room_creator = false, @@ -2542,6 +2646,13 @@ binary_to_room_version(<<"10">>) -> }; binary_to_room_version(<<"11">>) -> #room_version{id = <<"11">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = true, knock_restricted_join_rule = true, enforce_int_power_levels = true, implicit_room_creator = true, @@ -2550,6 +2661,10 @@ binary_to_room_version(<<"11">>) -> binary_to_room_version(_) -> false. +supported_versions() -> + [<<"4">>, <<"5">>, <<"6">>, <<"7">>, <<"8">>, <<"9">>, + <<"10">>, <<"11">>]. + json_to_event(#{<<"type">> := Type, <<"room_id">> := RoomID, <<"depth">> := Depth, @@ -2562,6 +2677,17 @@ json_to_event(#{<<"type">> := Type, is_list(AuthEvents) -> StateKey = maps:get(<<"state_key">>, JSON, undefined), EventID = mod_matrix_gw:get_event_id(JSON, RoomVersion), + case RoomVersion#room_version.strict_canonicaljson of + true -> + case mod_matrix_gw:is_canonical_json(JSON) of + true -> + ok; + false -> + throw(non_canonical_json) + end; + false -> + ok + end, #event{id = EventID, room_version = RoomVersion, room_id = RoomID, @@ -3162,12 +3288,13 @@ new_room_id() -> MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), <<$!, S/binary, $:, MatrixServer/binary>>. -compute_event_auth_keys(#{<<"type">> := ?ROOM_CREATE}) -> +compute_event_auth_keys(#{<<"type">> := ?ROOM_CREATE}, _RoomVersion) -> []; compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, <<"sender">> := Sender, <<"content">> := #{<<"membership">> := Membership} = Content, - <<"state_key">> := StateKey}) -> + <<"state_key">> := StateKey}, + RoomVersion) -> Common = [{?ROOM_CREATE, <<"">>}, {?ROOM_POWER_LEVELS, <<"">>}, {?ROOM_MEMBER, Sender}, @@ -3175,7 +3302,8 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, case Membership of <<"join">> -> case Content of - #{<<"join_authorised_via_users_server">> := AuthUser} -> + #{<<"join_authorised_via_users_server">> := AuthUser} + when RoomVersion#room_version.restricted_join_rule -> [{?ROOM_MEMBER, AuthUser}, {?ROOM_JOIN_RULES, <<"">>} | Common]; _ -> [{?ROOM_JOIN_RULES, <<"">>} | Common] @@ -3192,7 +3320,7 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, _ -> Common end; -compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}) -> +compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}, _RoomVersion) -> [{?ROOM_CREATE, <<"">>}, {?ROOM_POWER_LEVELS, <<"">>}, {?ROOM_MEMBER, Sender}]. diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 5065eba8d..d450c541a 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -28,7 +28,7 @@ %% API -export([start_link/2, supervisor/1, create_db/0, - get_connection/2, check_auth/5, check_signature/2, + get_connection/2, check_auth/5, check_signature/3, get_matrix_host_port/2]). %% gen_statem callbacks @@ -38,6 +38,7 @@ -include("logger.hrl"). -include("ejabberd_http.hrl"). -include_lib("kernel/include/inet.hrl"). +-include("mod_matrix_gw.hrl"). -record(matrix_s2s, {to :: binary(), @@ -169,23 +170,29 @@ check_auth(Host, MatrixServer, AuthParams, Content, Request) -> false end. -check_signature(Host, JSON) -> +check_signature(Host, JSON, RoomVersion) -> case JSON of #{<<"sender">> := Sender, - <<"signatures">> := Sigs} -> + <<"signatures">> := Sigs, + <<"origin_server_ts">> := OriginServerTS} -> MatrixServer = mod_matrix_gw:get_id_domain_exn(Sender), case Sigs of #{MatrixServer := #{} = KeySig} -> case maps:next(maps:iterator(KeySig)) of {KeyID, _Sig, _} -> case catch get_key(Host, MatrixServer, KeyID) of - {ok, VerifyKey, _ValidUntil} -> - %% TODO: check ValidUntil - case check_signature(JSON, MatrixServer, KeyID, VerifyKey) of + {ok, VerifyKey, ValidUntil} -> + if + not RoomVersion#room_version.enforce_key_validity or + OriginServerTS =< ValidUntil -> + case check_signature(JSON, MatrixServer, KeyID, VerifyKey) of + true -> + true; + false -> + ?WARNING_MSG("Failed authentication: ~p", [JSON]), + false + end; true -> - true; - false -> - ?WARNING_MSG("Failed authentication: ~p", [JSON]), false end; _ -> From ffa7c32d809f916b11d3e4fa54466a9446fb07e0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 18 Feb 2025 11:50:04 +0100 Subject: [PATCH 1125/1302] Rebar/Rebar3: Update binaries to work with Erlang/OTP 25-28 (#4354) They are compiled from their git repositories, main branches, using erlang:25-slim docker image. To compile ejabberd using rebar/rebar3 and Erlang 20.0 up to 23.3, you can download the old binaries from ejabberd 21.12, available at: https://github.com/processone/ejabberd/raw/21.12/rebar https://github.com/processone/ejabberd/raw/21.12/rebar3 To compile ejabberd using rebar/rebar3 and Erlang 24.0 up to 24.3, you can download the old binaries from ejabberd 24.12, available at: https://github.com/processone/ejabberd/raw/24.12/rebar https://github.com/processone/ejabberd/raw/24.12/rebar3 --- .github/workflows/runtime.yml | 12 +++++++++++- rebar | Bin 203608 -> 205504 bytes rebar3 | Bin 796043 -> 835323 bytes 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 87faf076d..ab0d3684c 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -51,7 +51,7 @@ jobs: run: | sed -i "s|13.0 |9.0.5 |g" configure.ac - - name: Get compatible Rebar binaries + - name: Get old compatible Rebar binaries if: matrix.otp < 24 run: | rm rebar @@ -61,6 +61,16 @@ jobs: chmod +x rebar chmod +x rebar3 + - name: Get recent compatible Rebar binaries + if: matrix.otp > 23 && matrix.otp < 25 + run: | + rm rebar + rm rebar3 + wget https://github.com/processone/ejabberd/raw/24.12/rebar + wget https://github.com/processone/ejabberd/raw/24.12/rebar3 + chmod +x rebar + chmod +x rebar3 + - name: Prepare libraries run: | apt-get -qq update diff --git a/rebar b/rebar index d0f5766d62a56a3725e4d1e17fc25efe5c3f4a4a..3f6203bcff5deab0cfbf5d1a0ff6dde3e533f014 100755 GIT binary patch delta 184281 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≶L&LZ!_gW0Ojikf5a;y#=PFGy-5mCsw8YG7_} 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 jZpCk!rL$LLH^XsB!8Tb-X%3 zov6-H)781^EHzD?q)t(%f2uRo#p)00QgykSq0UzqsGHO*HCz2f%~SWQ`RXC{uzFNI zr53Aa)pP1;b%mO#uEMYJKlr`Msl4ihui+AW317yA_yj(QcjG;H0bYnl<1siMC*a{Y z0{6!Q@gO`H55VEL9aeBl+)7+6t`XOY>%@iPB5|>p#5MeX+8Bl%H5@Ze|8GcUfzbH9 zqrcwMpUhQ9cvKzXxc>rBO9KQH0000809Zo0T6UfQHd_w>0MHiz04A4lKLHh&vLpc& ze>e+t6W5h9eq-s4@Px5O*v9EQ!$wLfR0P zketx=B!o>`rsa16DdE6w++<1iG$e=SSD@P*@@c!-Zg#ySIZ2uXlD2HNNeO%3=!vM_ zbal@2+;`vo|K51oI@i>2T=9kG)~5C*e`9Su#|axajvFe{wZ4!Z)b!ZSh!G2h6T``9 zOw;WQ5Wq3{p86s%X4FAq;Ard#zA)_FxK9Mw11sNsbM7%#bfIziev zgoAn_VK{~pk#tN8YIhj+MB1QdqOOJ0dMdhIbEFb!J!~D0V-ka4qMezVzhf{O9xR9_ z;y?k0MB@Ybnyx4G+fqg-9tr7@f1tG$Gv34!0|-`rs1F>mr?y6uNiE`tB(eb)sDrk2 zoCZxygTup`KA_p6iC`?+r(|UGg;LtB1mVQippj0-v~iZ2418NQj>(Xo()4l6zj$a^ z!~MWz0aOeHY(N}}57+=bU<~qJTguSE3tKAP2apukv`8we&Q7_(5-Ao+e;Gj#8|{y3 z5fqB=9ItErny$seT7-rtWrrYA=??Hg*OIYN7<|)=6pCv*#?eaAV%o44H^xP@SQfS$ z)XkJ3kC9cAr)zd5#1?{x#UuF#& ziD+D+Hdce-(A0?oY>ol5Z^X=C%m1 z(}OziH==93n05>ppp}k2|JvT$l8|j9)CYl~fwY0;A${N*88YS;e|ocK)mmz5ym?hs zNKePd9{9X4Se8P+bDX0LK$*+@o`kg-RxvM|a-MU#%BuxF`Ao4YxCru?svzG>kjwPh zIgTK~Bz8`=%W73C+9S{Rak9**GIyFo#j=OE+~j})oGy>qW%v1joe1cDRW`TSWl}X4 z?SZXRR(Rhs#1oUif8Q=Mndf|6<+oI=MOUiUD$_}TQ%FEY=I#!)i%**S^PG-CncrO? zgGsVL^4!ExsF=s>zAe<65kZ!EPYK984?u_ik#$HWf^xsikwRo%wyQ~5m0M9Ba&ij4 z|CYV-X;}ebr;uWfVs*F7A2UA%Jw+-%QlP3t5vaph7UW-pe=vgF#D%bLzsjFCaUsx6 zl=;gG(aXi7j#A0xm?SBV$&%ACMJjYmm1M`X>kjojev|}Mr0}1TJ@6OHB;cdZLJ}bK z=_G+J`cz0BeYi+q3w=&7u@mH%5gr#65JKt%;&u_mLy(_1rXv$8cKKM9RE!)R5=cTM zW`KaY`w2%Je>36KWhWyho`AXY9FL2M0Cp?H0|!NdPpYhvN@xP=XqG$*@uPeqm4c=c z;;2MU0_rE4xERz|0kX6pNzN4e1eb@@`H8!QC_My4NS#6)r690GP|wor`e8p6_7knW z%i2$deW|qPHgKlI0c?mqEqMf@~1KoAkixbU1Z3NN&`)4yn@2;w`E)%_mJJ(j*>FYDSz=CCyY0 zG$S97e=8&c9B%BxN|MKu>1i`k-3t}qgRDqX4@gsZJS9u$VT4ngk&KADj+T^vY4&sm z?!qoOokqpn5>a+QQhBVB1IU?#+>}w)25CB4087od?S|y%$eGFHOqY+zR&?tS4Fd&x z(ElYbXXNgMh^d@$K=Sa|W9geg#?sGX^fMt;e=87=%S~JX)#8_VJgb0{OiCxLQypFU==R15l^pQm|~ciEBV1flzvLTDum6 ze@~Kq@`*ZHc?b-IZn3Pf%D2u=@MJn(u;0!>8bPQFL1j&)ad+ieBSAsdpaI2mp}VRx zied1Wcpgwr!!lOE;W;Lr5BisZ!`0w0aSKGT{B@|Hr=GS+;22csNF52BS3qqU7385M z3Z>rwE)f$i0BfgHCKqe3g;slgP1gu?e~sCi0J^|sAY286i>!*zk}ZacpJ(DFAkGcO zgP9&W;V}Wkj4H_oLs`g#+GnMp3GnJEPsUNz#Ld7l^VUSn2d^QlH6{lRt_ELgi4%3m zxSYpz863plPt63hif8e7E|>uLZotp0mlmJ`Rc=BqfyP$G6;Or^P=;%z`aRNoe;&`b z!YNaesb>PRZVp@z=zPF|<_4NTTIan8qFo7?1wgP6!U0Ow!M3qpT7)E(K-yhR2np6u zfXM>ds@QZyF3>^TC>y}6nniwTF`dYZvpP0V*D86u1hN9+H-dOlFSQ`aw_CLeNAZ9P zm!%>wiYz^?flm>HaVhAVO#)a}e@xs0A@nv#ZP3PTNM#M*W_4fl0jY_{P1cAAV7TLO zD-h3tGF&7r<#98~6L`Foj&9ejnzsrh*X|a-)JpAc9cy`}GcM7>p9I&o-#hSs(u$qL_f3Gz0a^ML8 z&#DG#C3v^e@(!x84x&pGIs;6+qJv=Qgm&{h5Qm`*I_jlv)C5<+d})nxt3RQ)z^EE1 zh}Bj>bnTHkdE7~B4la4n(Pf3el_`&oT>n=ShnB5|H8wcpakpRUp&{uR8n$@Hq;<*ySZXFTfAr-dSNb}T*vsSferW@hxM8fsjb;}}8~_(L6>Nd&1~z)2 z16qi?hX%~*-IZ4F`bmqDnE|WVRocR?(sp*0()|?JcUUt5J88St=DfiWn1C&m3dB36 z0bwV-W0M(!rlCwBVg;p$3yP+W-f}J71ZI~IwywJ_yc^eT;qkXjf4mxu9)e5%_EU-* z-id5PDnjr=*lp-N^aXMGa1x$*27rE8^hjXuj`^@`45oN^N91@|4p`$Dj?xq`{CM`> znFq@WOJOvW-o@|~p1|N@SS5^xE;o}?4r>;(gUYX9Hc;N#jE3@hbL^SJWK`$akd;I6 z(;1G!Q1o0)4qVIhf6Rq7kI9@5t3C&AVEhYUEo8ilU@gwEVF{z5a+-4R&CHjjuv(Ze z6mDho6mH9b1B`b$tQCxx%2~i%5?AK}JJk9)*Ql@1V73f7KnOU(IO!4CYvFB*tfB zPiwWMzVg++XOEY(@UvHKKBXKq zMqkocW>zB=x)$Df%`%!(DK4)z{h{Q8ExOT9n&%_aQsCkG!m zR$d(|f4$_bDtaIqZg1N3;f}SXGiUzW&fw2}_ui8)j;{FW$D0n{ylZ%{%-GfIS=4-? z;ryuD`GL2>+!z|a_n8|H{`|qK!Sip0x~K2Cd-Y$wsy`!qer{W8yZ4ckk{TcVFPG2cEj?(Y=2Z|NHrYKTC7ouwCWaHG|g4lzU>d5(e`koUFy^fAx`I zf4>$o(z=!!q6M=m5vDcz0hgJDxiyqqul=kxdtrqc+%cvWbL%*_b~iGZz*@)PMp)fU z&OAn!osERico`kVSF)a?^mJZUF*{3Pl`*&o)WP$!rwCM}Z(35699GEy9WEctq=u#rQN~h9c`Ukxd{VNL$&Pi)-`wsfBNP{ z>U<6UTZElGU5qfVcJ2_>Zp(bZ+{#r{18cSQ5wSX@hpWH&lkXfr3~?=e19Tn^-a{w$ z&>e@STx@c@;ByI=Uvpe(emwA5%Ta&b@bS-1`uB}~()z;(pSkU&M}G80-^KU)t}Wd5 z#u4wTv!9gB``*19AAITJ_o`6If35{5%3r-0zUtccVaKm7zI^kcl11lwo{?L(9&Nw= zS^h@{%KmW`#0oV1mizj*nay0@DaOQMF)k*=ZQ?HRUhzKh@5P73N5mh92gM(WC&b6Z zlj0NNi1?)V&*IO-7sLzVzlgsQUlo5TUJ~CFe3?9ar6CpyR0y)88)=%nwrSHOz9b0jsGNf+$Q%eFKEZ*G zI#7<_{C7m>umcAkaX36W*xX_B9398wgl(wDoyqR^`ijwW)^l!ibMNoo?|1)vN#3I6 zE9yCpZtU>4FKy4PYUVieMULZt0#j6_FRX@?bVQA%GKy+5wAcp4aD`K;P&N~br!mtK z;SnWIQRCsnKz=xr90_HTp?EBv$v5J3K>m1SB$?=s4UhpP6N)Dz;kdOw7FXi2KAVzA zXH_K>jj873aKS`mED?#b)6$sXSRQ^T8B3U}tI>v7Dleic;f!KYR5hu8PML^F55h@_ z0R(5~fu5e=)+kWPS%R~SzYAdpGp@ANKN!hK0KGa1N6^*|`AKS^$A z2)vUbgTPq{jU=OrB@$PE!U;N_Ku76WCPkIF0!J(1$RN++_(3I}B1{W0%^XYGSzp;m z`jqfUnpc1~AAyWkD4Mla@4K=tqlOE=(_!6Y=}*R^aa%lTuuS7SWASJN>LWLpTOkRX zf?cNt!-U3VfLRRIC6Q5MN}2~fZAqu%v5bYO^;T6`tEg$kl1)Q@6L5A|SFKwm$<*BSLe-f*!#ddpd=I(Hc%DZHwp!Be>MRKj#~(w z;JCaeVaF*q{`V`81t5ZnYSdgqI)j>M6*(CNTTY?YQ%LLx(oVS( zX_A%FjvyD5gMy5Id~&B#7Lcq8nrxLs8M(9`E0wBWL}T(F1ljDAgGlRT9i3xizfaIh4)h`oQ>(bqN_WV%>0s02 zaRGyFq<#lLk%0U@g#0`Lt`hI?X}Hiz4=}nefC^UeSc@O+&#p#PvhHQio(PzX=)nkew#?-4H zT4JS__U}gG7@Q^S-z{9S&FTbhBh29RELJkAg0CI99I{OspH?Mw1g6Ya+e=X392Ggg%M6Jwfy;?S?2t{k1b`6g(y$Be2U}^j z(7e9Svct7S6g51Ht+nhB^yS5~jqT-;1+E1!DD~N=B3L`Ygr{kE4)F2<@#*;-W%tPD z6Es}K+SMenD=bp_3?~$GZ24eS;duI| zKYjxo2o;LZ1!!hHoZbpum<3(Xum^IL?zhh#Utv@TpImr0+mAUK_5!jEkX1U;2U+J@ zsJ#)|ag&A{z&Q_`b9HAUq?yKgYG!rT0Hj*OO#oQ{b=EZ7Yf(G$BA?wYfUI`Hg35j` zz`X)AvBrem`7l~E-U$R&4L3s_`hq!soW@822>q+|*{hLWY0EN-r<-t{hFjqv8MsI~ zmp1kp0wDzm7HI}hsn<9Fbb#iO3lT(>nXq@<20=cC1+$1}ns9@L=YhQw?2XO#CX`QU zI|5#TE3kY$hj|1|Xrw*#pa886VCom3v2d$!6G$`68-o2%a!a$l6~TDNN865n1YY0z z`LM1PGVe8}=5THisq6(u4LDj@xxA6D)n{*GYuiR8eU7$8j5w^@JXlINBmk|L-(sJK zT0unkT#yCc_=HA>5%g~Xd^426Cp{K>DB`%488yhhpt?p<@M^jjR1pL`91mg!v4G@( zm_RTHG5j`=Nrrtg$dnvBHS_|1kU|5`Vv7vD7^K9&l!8n%^03$$hF%6T)4(uJ6$YnD z5U26I$}p~+*x3f2+2*xQxcbD|$@t;1o_Lb-y-)`G+^5MGs{{C?N zn~VOkxT@>gm*!n7k43tFE0PsI+4TBvJQshreZk>|Il`cJ;rX{NuV40scy!*;--kSJ zO#0}f^yrKY-0|{Br7KU5yp?vluU3Bar@I#Ic;_1bLGS0b70Qt{miJpOAH6pD{k_Xq z9JH*x*j}5F_6(cv;Xf&N2!k&?zxn!CcQ+0s-`W1pZDsqa9y+&w^YLvp+Z6FgchTU> zPp+_B<=?J3cJc77XZJnT;lF>^(|b4hx^BKOlHm3f9f&p*w0f3&ysG~F<5xc2ARKx1 zaQ)=3*E|}2!gg&|U#a@wt^cNfJsUlF{_P8A5B1DAQ#SLt^VzLy9y^$Q`d?oJ@7*gN z`{q8|(f{1O^|ZQw`omA2c+c^}*Gd*F$$s{;FG}st41B!sg~ea)`(s@bK3%m`$%NV7 zY>KnV44R`Hg-4FQxB@>o;W+~yPtrp};Ob6B*!+$dbIqNf-`G#KgPq2F8;zY|I(%>G zzc(4C24tRLXZN7Z(Amzk8u~0_hmmp58V$YN&?oMc*T7GI2dOjc>@K?vdr^BPqq5xJ z)`r%m6Z)G37Ir22!TFVrM+|Rezv5%b#4u-VuJzP5 z*7|g75}pf_i8O=dF@q@v0QOLkjVe)&WD`-PKbBCUx-SYXWseW(R5$`%&8$x;=|ME^ zuyw5J>h27G1-q8|hq);p$J<|DkpJ%FeXg#v})S?p@p8It~S{cJB9iM}BdC^_RY_mp=3F+5Yn6le-`Ly6?;< zeV@18asKD-?iW9+Xxz5>w(Td+Y^z2kcefs#^Uj&bKOA>lUUv4(sp}7w%zrKTGW8Gd zUwZAT<yJ9LNbBUYOl6vAH&7_IUBQj|x9b_T#lPT%2!E$X}ZQrsv%qy3c9^EZ7NBl0StU;5`w59DvFMmPA6Rn4Sln^8-&pj@9{bq zMg1ISz`F<{-bZJ4mt}X)zU+ffyAONL9$)5VANII&cF);|J!kg!|EfC)2<~uB`m6eX z|NFhzy5qJUO@F)oOnP|mmciN`{hGGuzco$!mqorek@YiPy_B!D2ES9;Le+~*mW!2q z!ON6Og#(cQX|i0J%1?);|!4n9MZr{YcHL)|ziE<-H97^;*7A?U*ch z*;2iN>ztRVWNW*PnqT)?^5wRvvR};BS`~s7kW7(yMSs`o7c>5pkuOb^jon_M(v~l0 z3i*lFLcUt7hNcSHTFoo9({+Zf+x>c}VbO$_EmqqrS-+OA<;$fusG2QJwe%!=oeHo*lnDpx(AR$+B^s^&F()n4(-6(zr&{IM@vsC$O@AVg*-%6@H; zSE|;1FMpHE`&CYcl(I$7*i+7zLi7gZ!}A#2o5+{wpkc$(C}NVmVhYcvYLARj=Sp*1TM%TF(Qcty=T*rRla>c`tNP zVQpThqg5R-f&Y5rYFYE{4SzkdL;Cqh2U)XJG`tz5KxAeD>xgI+^& zd%cwNrt&2(*BM*|@J!jyd4AiZf1pw;NBn?uo`Q5qlU=@4^QI9l&-crI+e9{(^{0PU ztpPRb=Q0Y~9>lv)o@V&#IA3;8712XVBT-fTLO4RjB%feiyuxr&{Yo)_Klt;G?4FPkT!ceafNp+|m|+?H`l%M95KOAz9jn5}eG|E`Zm2u0q~} z6{u0QF92i+yGjzOjli}Bb~se!hsh z+UHfnLFN+FY$SG}oXvT;aB(WL-_KT1RDT^)_0l9&VMCsmYn>_=a)nC*+Bs%RE56sW zZo=)&oOgjrD|tTr;Tt5#n&a0H&>7$d!3d%{SIp`vR1K=?XJmeWT&@Imqtqu*=;3*& zsxorAjWnv&tMC^%dGqBz=~b&7rCo{F|1OR(LgunAZinEtUP2vIFGF1N6 zMRXhwcDknB&_i(Uym5)DVS6+*5TY7A&?K|7BuMEsdbQpJdv z7`gmpt$oTz0+KbMMizE8A{;OvjR1fFd?J5aRgEqLQFJbAGRi0(4SY(XOZI2s=6VTT zH>9Oe`Jz{@*D{1m2@IoGR+(w?@N>CdZoKJiOfs7$u)n; zi+R7hN1^m4RmK5&#ZRFYCgbZs^O-~>J@{yuS))C;58)m;Ot4Zs#dv3r4H#* z+IMk^UYxzj#re`?p?-eGqu)0iba8+Bz_u01b4sb(U2wNLd4UQ&2V-UOLQQKfN-e&} zurq{8Wz{Qpw1thkkZ8MNroDe`Qmszif2#qFIX2Y;LU&YmY4-R zdwvol;L(+w+YK9D?o;U*rluPh_+yk_#r((cz!2`2%QZ?$`y7P8vGI!sCSNMhqNDoA zMS`;s*#L~Y7T~O1Ozl}#r3;unkY5HQEH|dtNS~}M_x)X7&lUca(o-p$K;{yhCVLG@LZ9H zTRQ*b=!YPN$z!9I&=1SgEuG;`M?aQ0!ov1O`$(7g(?6{FQa>$51Z#h@{-~dcXqp%q zgSc@f&xD+@kTu?F{ZZ4uliz`~%hJEM9BY`3RDg1b$?r!USN}o&fV-ux&aFtrJ=rCU zXgnoiL&DxESWb!OR>-PY*Sq9E$Ffdnb{CwGI?im#J3`K(G}Gebs1#5;4J*mhQOm~d z$u27@xgBy23r5D`JuZKFG~^tk_Z%}Ev-da=Ee4SxQnp|*fF)mIxMmDaiXR`sTNiF2 z53H6VB$owv;FVIi%R=sHD09^8g8wC74wQO&NCVE0x?ce)dk`|oFICjh7zK~@A==Tn z!niCX&ZJWosh5&`WytytV7%lcUu9TknUp;n6X7*1XRs);PHIgU6F@;5U^$FdeCmh5|VGGcWX>JC(4Fd zJVpg)P^U@?onK4D3A5Xd&lR*#6x3LT5En#8;f{alyxVfL;W)Lw?;vOpNo4`*TCPjX z?HD}JBY7H@Y=*ykBd$aF0JWkbEZ~YkIABZAa1&-9LuXyD{rS(fh{k6eOLTze5X@Un z^$GnPlVZ@9FjFjyu(M7V{8PK*<~qrTq2h{soG_yb6P{bQC!R2`+r_N#FitzXm9#Zl z`b2+t7#e>cLk}0>5eV5D5>zT9T;rF)`PUUl@#`hu2F15S@qW`0lqqOYu*+?a*~iyQ zej`M#3|QJT$cvF5z-oKQah0P{Y&RjS{{f^`t(N(p;0v0Y!EqE$-ms{ZJG+?EvL$71 zjEP=*z`Q}{8v$Kyw?Oy}Yt4ZzXA|Zo`>cP?2LRZFyl%71f7khD$!`IdTVbFusb^c% zvt6in!eU((Q#CdUx2*}>rc~L1mdq_4gBF95kAZ$C=+h~4$WEDC$lNUv@IY!4^{uGT zt&-mc{qG5wAhL1zeu$7_l>d_7jvCvxK*UanjKk7ePwn6h7MU25Q+a=FH&3mBcO%Z?LQZHkvw2fXB(~PuD28~1hzop}rO;tuEmj&Qo z8)`)3q=vx+5--JCg?S6&f3xHhV0teeZ|OI0WsyT7u)S}mh~Sy-4k&v)pD`+ggtP30Q}b`%yBltJ4JZf7OP{yg&%Tx5?e!t zwubx8n)mAb-Z`n-3BhQWG6u~1b$;Ih_*ekf`Tc5tpzMWfsuZ~g&YBgS zS75~w1k2a?1EK}d@kO|%W_vW~77+M20^Zl`i^ssU3RV=7n&f{^K+Zm-Rz2cs%2sIo zB(i$HfH4uMb-X>y7yLrMqX!qg=XsIAm=%#-Y@y{z{50p0mlZ(Uxa?I z>il3jN>%eKsQG{Qs>TkJ-c-8u@)A1BH>%d)fmz@@LUyDL%~sX-GQI%-e5kqS{zCFs zpx)z9?;%uoP`*ciaZ>VEVQdU~Ee$>#(8nIeC#V{sz>&fr5VqSEOjqm;BdI=RG)W(~x;c zjhZ*1*Z{?ZUVhU^@8y)<=;dl=jPMnV4I8Y{kBDHC&JS(Ij`~|XZ$4^1ijNd*u~@|( zRVx`Fwi|vpg;yFD37nW7P7so3Am=D_!S*^}KBn_WMaz)R9~0qcad8X`#d`B`uy}eG zbDddofg69cvO{QPG*(hWB6r zA5WN1v+%GOg4dp)Z&}Bc{B5#QO-6e-YR71jjqpqDAQbOVC>AuP!)Q$Ev)Q0UP4age zfk19P0{=gWECBDPpbeGZ)3AlgV@C4#5TWPM3Z8#iZ_d~o@aIGvbdxAt19!fUfIOFu zYEcxu4vRR^CPI5miz<|w=_CYBSD^({3OI5 zhJw#E_V^Kg83cU{?S~ZmQ=l3S<_}$b(r9dI|LFkT^ODmi`pdB3g=QQ4G>H3)b8&xB z#XWz8v-wHz8F#-2^^47M^DD_e2b)&_JKSTw!qk6FQ2oX~iITkV5fFU=t+qnRkE84~ zocB_f`7$zLmW_p9gB(QFK~!gB;UoC9cNyZ8k!FG|R-I#Zsri!5UtVs$wCqZ2iREgi z4AsqFMDYO+YC4dcu^u*qAQDZ$PQv zje=O`vYl^3k9lL4ZH3=d|0ls$&NrEFRN*!n!5a-%x6_CCxA4`ClK=kUR*i4pWsG%D z#=H(Gzm^*09fJ~YicSV0H|hL00SvdlC8q%F@SAmh6fp+;ZJ2N@*wEfW0s`_5qVj+C zdh;D*{yQr3WAn8!>hfE3{!W{fjOzSdls@p^1Lybp%@0(#)|>AmAK!1}Beha&UyQcN zv_Wf^ffS=Rte@01s8!pvunpUcbvjVueR%FYoqv!rKO~(#Y!1-($(m7}A4j`_&<`N= zBcLAE`3W>cKt2TIWWV_}i-_PqsOEn*-#--Z0V-=G3uW@y&S};y4^pimU8$w@eeoE++XCDK0CSdUyU^@R) z^kQ-RT<4#u`%}38TjHbh&)1q?82BRjlHrTw3o)A-Fu&6ImzL0mqiBpNMje0Mv4bsE zrNLR12H{U^Wz64!&22jW3d3AYZ8ZyM=R`AAMe#NMeup^&mz+iGu#0RR7OR|!}Y#}b}l7MMW>M|Kra(N$3C;Nl7^S>zE%5Vhl( z0A4|nV^stegvA5zD`?c181sLg8!w`$ct%Z>XjBwIMHKJ?)F_E}yzzO*<5llAaYEjF z-~VmRKUM!!S9jO+%4#3ko3rx8&JrIfs{&<>x9p>Kl*I#jmj6Y~^84YLgS*R*GRq$@ ziifx!TsRjj7KV2NXv4yp@J?X3{Ny@lkQ}a(IgK0$f(BXLQw{{#5@&xc|4AqDAXg;& z!0l0ZOBBl=lvV2ak}PY0ItCumeqs@~RMrH_KHjoHEvq<^0KBo#34U4rkU>1mL7Gll zaWBgsmh@5{=b?4kK=K|S@dH@C2!#4@EPuqxFM(|LNGxA$6iYZ)N$SC=IF%$8OIj&R z#i%3H9Oo{9anb)=v^hPtX4tjv4)|?oq%NdQiFJc zgWD7D6S8MhX5~Kz*`Go7q)|M@xoRN`jS9v~s=UdT&q-;Jl&AL4YE(8^E$EyAlw0}Z z5V{wtUlA;x;kp5|qAcrWGEs&wj?sj1k}5*wz(r^{wcML;3srw8b=K;kYF7RPyr2V+ z?bKGj3@SU=Ppsrz_RA`7S)-N>fm=PaDue8j3a%%2xg5|B2^=MJ>QM1C%U3pZN&%-+ z@10owGz>uYG0fLXR=!-N6~{tdBA~jqnw)Xc>fBlWtd*~T{13v=&$%+-SV=SThKx>= zj2b!eT1bg=XCQy-yp=bDdm*@28O0hp0o5=8)oOj1n(+=(OKLy3VO=G?>`i8)io8`t zXQCQrqKf5f4B`cn;f01XanZ`hf&L-Tzho4DwXX+GMRKxF!zHEKuYk)|eiTF;hKMUh zvG$)4SCkR8fZweA8Hgy-vi$GC;#HWktDIK)P|rvbqm_TOFlF499h`>*YNH5tPVD}N5y5@2rzi?>1UHm7y9>8NaTe4gwnKk*htAa7zQI6@ZLA*y2 zx!16m_pN_?Ul2SFf)9-1!}?|I%<#kPrFlTtv`X{Wntlj)WRE)madk%Vv0_u!*>;xn zerG)f{MX8dfO{FZKQW3=74A=zX+AZG&q$ij8m9T&%GW^rNhsh8qxgz*)x$paso#LM z+Px(Gi1Zm*<1=3?!CHQylzwS{=m<3I!&kukY2|;rg61j6>9tXOqgWN5_MyL4#=kL$ zZ%Jxz8>aTo%6ouJxy1T1Mjfk=8E%u&F$x)9)}V6$8DED6GQKctO+ls-WK>3-qjF~H zw1SjMDdlL;IT0zRhEmQ}-W{Y)gA{Mn35tw9gH7wBcGl+F<#?r>V9>b`IhTfVjjVhV zkUM`1a;~ubsRWN;dAH?Q+n)Co?fc~%__&4YMAo;leZ`*#LJHM2W__F3n_!ie^>q)` zHDP_BcjN(44IY|Motk5qNJ~Z(0~bzBhhG)3V*ri-CjfXb&H$d;E`UY=79axL01f~N z(1h|D1>Y3VjPfMbliGxDL48^RS}ANVYPWv|w4v`MwickGc3Z0B4e(L;v}f!Wc~=Lj z;|oC4{s7>o$fuLS-=ErgKo^A_K>djwNbMlNhl-p(qI?)2T*3FGd>>L`nty$6IGPYLnQ33Y|fUdVZ>~BNegH3Vw(pc9H<1wT?zpHY;L1H>!FK3b96SXzH` zqLV~z6TnP$NZfepPwrFcFR4Q&<;fb$qOnACBCQFrCs8{aFopV015BrUeLYF+Ou#J4 z&sNwZx4HDaWF37@c`}w9s#(7_Seo;EY7_s3)LsnuQej8a_Y!-FLg#Dd{d!xfsMWs} zbJ1SGub}y?0<2c>YZU(9EBNmzPwIb=r>I+m!eMq?Plc`=@DoOJ1&f4hLO5GFO`0Cm}Iqok^(vc55GY9_PMbQBnJ_ zbr&uSPn$C4mz)Q!J3ou_f2)6f`E~I1j*m8HkFI_;J78D#pIOz%wrt*>vDD`hUb=PF z;%~n7kM8B1+fpvm{p^;m`}vCTLKQ_0+gh5TOpRerJ{><)KCp#pXy`nm_ z-XwoCsa5Sqemh25_Xay;yPnQT%g>c><(-#9c7OI~XrI^K!~NE+|KooqVLB3L-!x39y<+f`(>*5 ztna&3i~sX>+q8n9-7l9n&CYH0G_d;AHqSfZjvrraQM+aFr&%v&BpYU?cd&Zqhqc@{ z>d=sGm9=lrzOAaf=(c}9Wz&fAoryz|=Cr8s-@DekW9M0)?!6l5xG*K5Zbeey{k=hz zF;kXAdoFlBeS%9xVdmklSEwUme`SnK3q=cQwIXUZ4 zR8Et~QER={=bRW?Q`Y;F*q9gPv*%Tv|FyJW@wg_R4e}@o8CieQXGOu(E<+P{R^MD( zIa$A=WTrz#!<(a>JG_hiRb_8nwyf*SrIsMVJ-6hLbzVn%0 zUa`jcVeJQvBlR6}QU=GAe|JpNQ9QPxAi6ND<#0{A;(z@(P3u;*tlXj)F zd}{LnLr(Xc=6B6!vdZe*X11reb1AV^R00)r+!$Gp=;6KWFg;3 zuQ@4X*Nt^ZWF(V0EDaBkjEh06XY=iAFy4 z+1h`gzpkhD`UZVe**mS&XF0XWMZVF?DE}4U8ydTq-V>gj2b4W~9_42M=1_YopdGa* z(|dA}v&{r*lQT~S^+^MyQ_XSop4jAkrToq)zeR~O_A@{%waIyJ1dSzU!Wde&{?w1e zlJE3TYLoNRVCqlSWEADe`SlZO%YXq?ryGCZW9n}Nbf-2s8;4SV9Uz3}<467K&jDSj zKRNdrs6RQQbfz_FL*vOs&i@^$P5v)5r~YJLcu-BZ9u^C!VVZ64Igq`_O~^FcHlkT# z&ibA_G-93W&ZrFq-ACTtJ7XMi;1iSLQYL4cGTv`jb6RX_Qf6k7c}#3VTuMqNA)9}v zXPCNljn@annzGDEme}-+q|_u!(gaf+2fH~Oo5?x&iS4}{@pqnJ%E(MgGbc07M*jf+ zAb*36O-r``nMvH72*D!UN6tKL(sCEqV8K z^U(cK1yV%vn*LAg9Pch|yS*RO2WILb^Vh|ab59L1+=XaiurO2@CJYxw3b8_rkSLgh zWWg+q7czvUf>l@{tQJ-YYlMHbLY}Ze*d*i&TZFB`55jg~hpKP$rxd z%7qHyv`{5f3pK(Op;q`!_+7XrTo>*LcZGYxL*bF|Sa>eH5MBvy5R04dxd=Ww)pkC+`Gz<+#F(?*|LJ24yjYdf*8KtA~C=*%GL^OW^WuvKRI+}sz zp!w(vvh7>UiOcglCgdL{>Dg5Lx%qcW9w&sXS5*yV3_{_P)h>@6aWAK z2mn|@x?0}qlDSw5004*)001D9aY_`I`UwLMm%(5G7=LBik?lBH+ffvUICgA@PzuO8 z*0y3<6022?wv-IlBm{_|w6Ktp5U!><2xgejDThk~#Y5Xkr=?{|h6bi7PzWTH4il!3 z{%^Iil>{{NYoGr8{{R2}5&D^25G8RyCqLHMUvfrf$QFH>BP&W=nH83!vMT$jk$;(@a?^@jITn{=>g=Q%h=qZbUq_R4 z$Y^{Rf#d{&aYdcCRf&KDJ=MsNKNODvH#O-GM}~%Eg^$E3X}=ox2P3h7vK?Fm!aCYI zV_1;I0t^+uUYc3kLh;1*^g0*#533_qnq*p&GZcs>WwWZJWE>16^_=inU{t0KtdXQ& zk$;C%(STx=W66}FYt7W3Q8t}>jyj^mx8_DDTV0eCk||p;XM_bl7wy{EUVwZ*CXhnMv$vmUgJ%+a-J3QcwRzgO|&pf za|*Sjkl2A3p;br~Nqyd6BkPpR=x`Bl)|{3!QZn;gk7O2LeGmy+uSFEKLC_UfZ$uLS z&f&RAo|Ry=lV`L9+*!$sycd}S$;TjVheZTWlH}tBi`ZK&BcCWuAR8mFz1rX!#D5NI zJK#H^iE9zNLmM~QtTvGycRz^$C9wPRL{SpV60&RiDdRJ!d^OPl5h+04lo?IaiQ4-MOa%0AAH#EA~XqZ&>?CSGE^ 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 W@&e#PIW?q^8iWbY1%jFz=KldM+rEVW diff --git a/rebar3 b/rebar3 index ab1c1369d94f38f86cfc48c977336127c8218339..deaea1f90e555cc0177079447f192e2692ad25f6 100755 GIT binary patch delta 792610 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

7CQFTE@AOnpOu6Nc#^k z|NhrYnfQNc!V89oAZ%-(V{c%KE2(3tXKY~i{O8}8?-4=eb<7Q|Z1w5TX;FXRC%k}v zw50S~=@%gF*(Xu3nf>UbFu(qw$@#+zWMakz{D|@AL_ynKObsoSC zam%f76SK+aI`hVCE4A`{rmw5xXPc;hsr?7&+f?)0r83|1H=Z|NzI~*T_Db@qzBdxE zZH5Av4Bj)N96N|i#C-F~b7Rf&e!5^ins8RYOt(()VfSm3c}?gUOnCBoin&SBwX+DV z;6%v^G^gOZd-`};k|v|}Ug{dNU7@Zn+{|(ugGCXrhFqz_Q$HaV|Gn*g9}xk6BPm<* z6pdyH4AKw{e7CAki8F`GvVQI0LQ)poNw;w*v+fMae4OF32aq)ypDFdwmz2ACnszk5 zrF<4lTFp@-tZFD!;u^9ldJ{a#JidXn@ao#Z|+Yu55 zCm(>RtnGnzLyeHgOyqzXrmfGJ^DLb1lyx}kZM*!dW*%OHC_Tves ze?-Y4YvTS4J^lD!0(gj%2_gU5QrSlH3Q4{<05KA&eH*O`LzzSoKsOI%+0YlbiJXX3 z#Y)*_lb-O=JJb#7)XBDg<;`es&2^ZvldF&sOw__wrnqy_w`qXSOLef|FvxR5T{|9M ze_Uyu?0!ryjihMcO1hr516L8U{Fr(^MKTsQ%i?E?IcB$|DWi&+eaDo$4OmLLH+^Qp+}rf?C?l32yx4pTapY2?@M^rkwufYGhBkyE%}c zwnkLI<-JD3UNmG@=eBWy1adig62!JVWprZ}II07bL4jKzK`nY4>HBt!r(p`+qg*%e z4~a%$ZvY33K}_}V9Du!pLM(kc-=7xdQf-~*5yA=k^$wheyQq|dq->#+&!hoP#d;h7 zi}fDQZ5IM^UnnJi^BLiyfs3kCrYA zh?uav;no_gFYHpW*;AJ0kZmFEx438D);9*A3;I4pm%G2;uYO}<2;;joFxXA?QPvCZ zE0g?t5HZb+l12<|`xk(ZeFpfbXMq37zY6y{1w%PK0*`)`4YWDm$wA}y0I&O7+xTaF z9t0xPTN5LHlNXeGNkadP*$5HtC6%rJeCRuGjGmPR9p3kntY>&c{o6NwS&;Oj$o2*&j*nv zwzOuzx#SS!@O#tI@;q8Iij>h+6m80lW34)?KH4X}vt@WcjwpyoyXtcR>9=FYX>Pqi z1B-osLNGl*fU8GI^XW)asz3HCp-u|ZA$YImk=o1V!-3I}5+**aN*BWu$9~a5LuKU* zg|`u~uj-`>0P{*GgRUBzr32EM+swQ4LkqE*OA(P?3w6y9*+3D) z=cZV5X~Ertu6%e9qpgY+efSw>7r|_)ETVyb-3TS7n#-_3?KPYX=Q-#FlNtHiUd*bB z<#z5AWg0E#k~e2bv)3ZM#rQ&^E*z=Wn}j{D-VvczH-W6F zWo2uA zmDY%N_hVui4ubTbfw@U%-o41I6{9;EyDKXjD11}j7(r>&vCQh*eX@8`fvN^8cL|m;GUoB WKET}FJ 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[sxU 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 z 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 z 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

Ou1f~)+WJxNw;B`segJ0 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

$2JO$}iD_Zg!dOyg~ee&0-UW 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

;|aBvY+$=;Q*h87mQGgW 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

+H#e(?r)0Z9CziQEIH?74YIiZ?x>{E2@p@ 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 tb-$?E8N@u6 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⋙v}Ozq~f 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�Ij`317}{BmkN$KN)k@#k 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

D0XZ`C1@l4s3U=szY*$vJ<|DQd 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 From 9bc991cb7dd2c542338325e83542d6dc3671c358 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Apr 2025 16:57:35 +0200 Subject: [PATCH 1126/1302] CI and Runtime: Add Erlang/OTP 28 to the versions matrix --- .github/workflows/ci.yml | 2 +- .github/workflows/runtime.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8edbd2ec7..8cc8834cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['25', '26', '27'] + otp: ['25', '26', '27', '28'] runs-on: ubuntu-24.04 services: redis: diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index ab0d3684c..d0957a432 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20', '25', '26', '27'] + otp: ['20', '25', '26', '27', '28'] rebar: ['rebar', 'rebar3'] exclude: - otp: '27' From c10e6ded78bcd02526424176fa35184941f9cccc Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 26 May 2025 15:07:31 +0200 Subject: [PATCH 1127/1302] Runtime: Use --with-min-erlang to bypass Erlang 25 soft requirement --- .github/workflows/runtime.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index d0957a432..1cb5a0602 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -46,11 +46,6 @@ jobs: - uses: actions/checkout@v4 - - name: Temporarily reenable compilation with Erlang/OTP 20 - if: matrix.otp < 25 - run: | - sed -i "s|13.0 |9.0.5 |g" configure.ac - - name: Get old compatible Rebar binaries if: matrix.otp < 24 run: | @@ -94,6 +89,7 @@ jobs: ./autogen.sh ./configure --with-rebar=./${{ matrix.rebar }} \ --prefix=/tmp/ejabberd \ + --with-min-erlang=9.0.5 \ --enable-all \ --disable-elixir \ --disable-tools \ From f1de7b008b473e84b073c22c0090a1e53bf0e791 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 27 May 2025 16:45:04 +0200 Subject: [PATCH 1128/1302] Use xmpp and p1_acme patched with Erlang/OTP 28 support --- mix.exs | 4 ++-- mix.lock | 4 ++-- rebar.config | 4 ++-- rebar.lock | 14 ++++++++------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/mix.exs b/mix.exs index 69f4a7455..e9c6f6bce 100644 --- a/mix.exs +++ b/mix.exs @@ -125,12 +125,12 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, "~> 1.0"}, + {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2", override: true}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, "~> 1.10.0"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "e2fa1f27b66099fc44ec717636c242aaac5e40d4", override: true}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index c0c7f254f..496a2cfe6 100644 --- a/mix.lock +++ b/mix.lock @@ -24,7 +24,7 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "p1_acme": {:hex, :p1_acme, "1.0.25", "db91f0d6c193cd1d5c0b0fa3939a898dbf56a6075db4347cde26e802715de50c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a7b55b47495ddb4f98a15e65451ec3ad43f4637b955c74cd695d98e6a645d08c"}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2", [ref: "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.32", "3f95d7e3413fc8f0be80abb4be1a0d7f67066a36905085cd5a423145598b0cb0", [:rebar3], [{:xmpp, "~> 1.10.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "268b01e8f4eb75c211a31495a25c2815c549aecce2f0df1a161c6e0a2cde061e"}, @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:hex, :xmpp, "1.10.0", "68a6dff8db8987c4592b2d5dd71d3f947b4ebd15209c9acaca5909a642670630", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "ceeae43b8fe97649d8f8546b3f7f2b38ecfc931c0cdd5c7445ffb3f80fcb7d85"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "e2fa1f27b66099fc44ec717636c242aaac5e40d4", [ref: "e2fa1f27b66099fc44ec717636c242aaac5e40d4"]}, "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, } diff --git a/rebar.config b/rebar.config index 5d29a5b7f..4ba5dec8e 100644 --- a/rebar.config +++ b/rebar.config @@ -64,7 +64,7 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.17", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", {tag, "1.0.25"}}}, + {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", {tag, "1.10.0"}}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "e2fa1f27b66099fc44ec717636c242aaac5e40d4"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 2c212b330..de6a53fe5 100644 --- a/rebar.lock +++ b/rebar.lock @@ -14,7 +14,10 @@ {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, - {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.25">>},0}, + {<<"p1_acme">>, + {git,"https://github.com/processone/p1_acme", + {ref,"8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"}}, + 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.32">>},0}, @@ -24,7 +27,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.10.0">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"e2fa1f27b66099fc44ec717636c242aaac5e40d4"}}, + 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ {pkg_hash,[ @@ -43,7 +49,6 @@ {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, - {<<"p1_acme">>, <<"DB91F0D6C193CD1D5C0B0FA3939A898DBF56A6075DB4347CDE26E802715DE50C">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_pgsql">>, <<"3F95D7E3413FC8F0BE80ABB4BE1A0D7F67066A36905085CD5A423145598B0CB0">>}, @@ -53,7 +58,6 @@ {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, {<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, - {<<"xmpp">>, <<"68A6DFF8DB8987C4592B2D5DD71D3F947B4EBD15209C9ACACA5909A642670630">>}, {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -71,7 +75,6 @@ {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, - {<<"p1_acme">>, <<"A7B55B47495DDB4F98A15E65451EC3AD43F4637B955C74CD695D98E6A645D08C">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_pgsql">>, <<"268B01E8F4EB75C211A31495A25C2815C549AECCE2F0DF1A161C6E0A2CDE061E">>}, @@ -81,6 +84,5 @@ {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, {<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, - {<<"xmpp">>, <<"CEEAE43B8FE97649D8F8546B3F7F2B38ECFC931C0CDD5C7445FFB3F80FCB7D85">>}, {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} ]. From 573d5525ec6915ae37c176c78cc1255caef779f2 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 30 May 2025 17:02:19 +0300 Subject: [PATCH 1129/1302] Fix key validation in mod_matrix_gw_s2s:check_signature --- src/mod_matrix_gw_s2s.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index d450c541a..e9ea5bb99 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -184,7 +184,7 @@ check_signature(Host, JSON, RoomVersion) -> {ok, VerifyKey, ValidUntil} -> if not RoomVersion#room_version.enforce_key_validity or - OriginServerTS =< ValidUntil -> + (OriginServerTS =< ValidUntil) -> case check_signature(JSON, MatrixServer, KeyID, VerifyKey) of true -> true; From 9569e407b5eb140ab71ef54dd2c7eab2b35bf375 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 30 May 2025 17:11:18 +0300 Subject: [PATCH 1130/1302] Don't send empty messages in Matrix rooms (#4385) --- src/mod_matrix_gw_room.erl | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 1d409716f..6ad3b7c3c 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -222,35 +222,39 @@ route(#message{from = From, to = #jid{luser = <>} = To, when C == $!; C == $# -> Host = ejabberd_config:get_myname(), - case room_id_from_xmpp(Host, To#jid.luser) of - {ok, RoomID} -> - case From#jid.lserver of - Host -> - case user_id_from_jid(From, Host) of - {ok, UserID} -> - case get_existing_room_pid(Host, RoomID) of - {ok, Pid} -> - Text = xmpp:get_text(Body), - JSON = - #{<<"content">> => - #{<<"body">> => Text, - <<"msgtype">> => <<"m.text">>, - <<"net.process-one.xmpp-id">> => MsgID}, - <<"sender">> => UserID, - <<"type">> => ?ROOM_MESSAGE}, - gen_statem:cast(Pid, {add_event, JSON}), - ok; - _ -> + case xmpp:get_text(Body) of + <<"">> -> + ok; + Text -> + case room_id_from_xmpp(Host, To#jid.luser) of + {ok, RoomID} -> + case From#jid.lserver of + Host -> + case user_id_from_jid(From, Host) of + {ok, UserID} -> + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + JSON = + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>, + <<"net.process-one.xmpp-id">> => MsgID}, + <<"sender">> => UserID, + <<"type">> => ?ROOM_MESSAGE}, + gen_statem:cast(Pid, {add_event, JSON}), + ok; + _ -> + ok + end; + error -> ok end; - error -> + _ -> ok end; - _ -> + error -> ok - end; - error -> - ok + end end; route(#message{from = From, to = To, body = Body} = _Pkt) -> Host = ejabberd_config:get_myname(), From 250af8f06ab89a7379423cb1bb3b986850fd7225 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 29 May 2025 13:36:34 +0200 Subject: [PATCH 1131/1302] Fix "make options" in Erlang/OTP 28 (thanks to Alexey Shchepin)(#4352) --- .github/workflows/ci.yml | 1 - tools/opt_types.sh | 12 ++++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cc8834cd..a39b5aadc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,7 +116,6 @@ jobs: - run: make install -s - run: make hooks - run: make options - if: matrix.otp <= '27' - run: make xref - run: make dialyzer - run: make test-eunit diff --git a/tools/opt_types.sh b/tools/opt_types.sh index 5c8761f82..bf8f99da6 100755 --- a/tools/opt_types.sh +++ b/tools/opt_types.sh @@ -11,6 +11,7 @@ mod_specs = #{} :: map()}). main([Mod|Paths]) -> + put(otp_version, list_to_integer(erlang:system_info(otp_release))), State = fold_beams( fun(File, Form, StateAcc) -> append(Form, File, StateAcc) @@ -453,6 +454,17 @@ t_from_form(Spec) -> T. t_remote(Mod, Type) -> + case get(otp_version) >= 28 of + true -> + t_remote_newopt(Mod, Type); + false -> + t_remote_oldopt(Mod, Type) + end. + +t_remote_newopt(Mod, Type) -> + erl_types:t_nominal({Mod, Type, 0, opaque}, opaque). + +t_remote_oldopt(Mod, Type) -> D = maps:from_list([{{opaque, Type, []}, {{Mod, 1, 2, []}, type}}]), [T] = erl_types:t_opaque_from_records(D), From 7755fcc846f1ade8543cb301e373f7dbfc33c717 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 30 May 2025 13:20:57 +0200 Subject: [PATCH 1132/1302] Bump fast_xml and xmpp for improved Erlang/OTP 28 support --- mix.exs | 4 ++-- mix.lock | 4 ++-- rebar.config | 6 +++--- rebar.lock | 9 +++++---- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index e9c6f6bce..509cf0669 100644 --- a/mix.exs +++ b/mix.exs @@ -121,7 +121,7 @@ defmodule Ejabberd.MixProject do {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, {:fast_tls, "~> 1.1.22"}, - {:fast_xml, "~> 1.1.53"}, + {:fast_xml, git: "https://github.com/processone/fast_xml", ref: "72e1c1b2eef84804399095704f2d729d5df8f02e", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "e2fa1f27b66099fc44ec717636c242aaac5e40d4", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "ee9fd6583bbaf5d05ba16e1b2804750275a46f42", override: true}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 496a2cfe6..99b22f712 100644 --- a/mix.lock +++ b/mix.lock @@ -12,7 +12,7 @@ "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, - "fast_xml": {:hex, :fast_xml, "1.1.55", "ace020f2521f2a484ac8467d2822af85534a346e2aae03ffcbc34f29318befaf", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "83f3e23a780ed5f567cdec73953f06c95b838d709dbfa86b59a98a8d23c99f85"}, + "fast_xml": {:git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e", [ref: "72e1c1b2eef84804399095704f2d729d5df8f02e"]}, "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "e2fa1f27b66099fc44ec717636c242aaac5e40d4", [ref: "e2fa1f27b66099fc44ec717636c242aaac5e40d4"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "ee9fd6583bbaf5d05ba16e1b2804750275a46f42", [ref: "ee9fd6583bbaf5d05ba16e1b2804750275a46f42"]}, "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, } diff --git a/rebar.config b/rebar.config index 4ba5dec8e..60521c23a 100644 --- a/rebar.config +++ b/rebar.config @@ -45,7 +45,7 @@ {if_var_true, zlib, {ezlib, "~> 1.0.13", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, - {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", {tag, "1.1.55"}}}, + {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e"}}, {fast_yaml, "~> 1.0.37", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", @@ -70,14 +70,14 @@ {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, {p1_pgsql, "~> 1.1.32", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.32"}}}}, - {p1_utils, "~> 1.0.27", {git, "https://github.com/processone/p1_utils", {tag, "1.0.27"}}}, + {p1_utils, "~> 1.0.26", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "e2fa1f27b66099fc44ec717636c242aaac5e40d4"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "ee9fd6583bbaf5d05ba16e1b2804750275a46f42"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index de6a53fe5..718196335 100644 --- a/rebar.lock +++ b/rebar.lock @@ -7,7 +7,10 @@ {<<"esip">>,{pkg,<<"esip">>,<<"1.0.57">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, - {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.55">>},0}, + {<<"fast_xml">>, + {git,"https://github.com/processone/fast_xml", + {ref,"72e1c1b2eef84804399095704f2d729d5df8f02e"}}, + 0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, @@ -29,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"e2fa1f27b66099fc44ec717636c242aaac5e40d4"}}, + {ref,"ee9fd6583bbaf5d05ba16e1b2804750275a46f42"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ @@ -42,7 +45,6 @@ {<<"esip">>, <<"4B14E4832D08B9FFC10D855B5D10B3083232B1D53DEB4C046679496CE85569C4">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, - {<<"fast_xml">>, <<"ACE020F2521F2A484AC8467D2822AF85534A346E2AAE03FFCBC34F29318BEFAF">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, @@ -68,7 +70,6 @@ {<<"esip">>, <<"19C357E1817B1E04792EF359BF900400F3E6D0E5ADE929FD72F88EA9B44AF2ED">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, - {<<"fast_xml">>, <<"83F3E23A780ED5F567CDEC73953F06C95B838D709DBFA86B59A98A8D23C99F85">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, From b4a917db097824972bf48023389b54d1fa3c8ef7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 30 May 2025 17:05:14 +0200 Subject: [PATCH 1133/1302] Runtime: Fix step name --- .github/workflows/runtime.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 1cb5a0602..4236a22be 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -80,7 +80,7 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{hashFiles('rebar.config')}} - - name: Get compatible Rebar binaries + - name: Unlock eredis dependency if: matrix.rebar == 'rebar3' && matrix.otp < 21 run: rebar3 unlock eredis From 591e15f0f6e8a5992da33fa0892c3d6dde2ff5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Jun 2025 18:19:03 +0200 Subject: [PATCH 1134/1302] Fix mnesia to sql exporter after changes to auth tables Conversion functions used by ej2sql module was not updated after change that did allow storing multiple passwords per user, which made us skip passwords that were updated to new format, this fixes this problem. This fixes issue #4391 --- src/ejabberd_auth_sql.erl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index b8ff98615..747a42598 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -406,40 +406,44 @@ which_users_exists(LServer, LUsers) -> export(_Server) -> [{passwd, - fun(Host, #passwd{us = {LUser, LServer}, password = Password}) + fun(Host, #passwd{us = {LUser, LServer, plain}, password = Password}) when LServer == Host, is_binary(Password) -> - [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), + [?SQL("delete from users where username=%(LUser)s and type=1 and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", + "type=1", "password=%(Password)s"])]; - (Host, {passwd, {LUser, LServer}, - {scram, StoredKey1, ServerKey, Salt, IterationCount}}) + (Host, {passwd, {LUser, LServer, _}, + {scram, StoredKey, ServerKey, Salt, IterationCount}}) when LServer == Host -> Hash = sha, - StoredKey = scram_hash_encode(Hash, StoredKey1), - [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), + Type = hash_to_num(Hash), + [?SQL("delete from users where username=%(LUser)s and type=%(Type)d and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", + "type=%(Type)d", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", "iterationcount=%(IterationCount)d"])]; - (Host, #passwd{us = {LUser, LServer}, password = #scram{} = Scram}) + (Host, #passwd{us = {LUser, LServer, _}, password = #scram{} = Scram}) when LServer == Host -> - StoredKey = scram_hash_encode(Scram#scram.hash, Scram#scram.storedkey), + StoredKey = Scram#scram.storedkey, ServerKey = Scram#scram.serverkey, Salt = Scram#scram.salt, IterationCount = Scram#scram.iterationcount, - [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), + Type = hash_to_num(Scram#scram.hash), + [?SQL("delete from users where username=%(LUser)s and type=%(Type)d and %(LServer)H;"), ?SQL_INSERT( "users", ["username=%(LUser)s", "server_host=%(LServer)s", + "type=%(Type)d", "password=%(StoredKey)s", "serverkey=%(ServerKey)s", "salt=%(Salt)s", From 167bbc768a50ba7aa5bb22f8d22d2a93edc8cecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 2 Jun 2025 18:39:51 +0200 Subject: [PATCH 1135/1302] Remove unused function --- src/ejabberd_auth_sql.erl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 747a42598..8ce78bc18 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -258,13 +258,6 @@ drop_password_type(LServer, Hash) -> ?SQL("delete from users" " where type=%(Type)d and %(LServer)H")). -scram_hash_encode(Hash, StoreKey) -> - case Hash of - sha -> StoreKey; - sha256 -> <<"sha256:", StoreKey/binary>>; - sha512 -> <<"sha512:", StoreKey/binary>> - end. - set_password_scram_t(LUser, LServer, Hash, StoredKey, ServerKey, Salt, IterationCount) -> Type = hash_to_num(Hash), From 363351b18c95b304b491c4d9543bc26039a1201c Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 3 Jun 2025 21:13:29 +0200 Subject: [PATCH 1136/1302] Add workaround for Jose 1.11.10 not supporting OTP 28 ecPrivkeyVer1 (#4393) --- src/ejabberd_acme.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 8b16fc727..a3dcbbf91 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -318,7 +318,11 @@ read_account_key() -> case pkix:read_file(Path) of {ok, _, KeyMap} -> case maps:keys(KeyMap) of - [#'ECPrivateKey'{} = Key|_] -> {ok, Key}; + [#'ECPrivateKey'{version = ecPrivkeyVer1} = Key|_] -> + %% Erlang/OTP 28 generates this, but Jose doesn't support it + {ok, Key#'ECPrivateKey'{version = 1}}; + [#'ECPrivateKey'{} = Key|_] -> + {ok, Key}; _ -> ?WARNING_MSG("File ~ts doesn't contain ACME account key. " "Trying to create a new one...", From c51b044b3fb181ac4c07d523da8ee9c5e4c94bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Thu, 5 Jun 2025 14:20:11 +0200 Subject: [PATCH 1137/1302] Fix macro used in string options when defined in env var Configuration macro defined as EJABBERD_MACRO_* environment variable couldn't be used inside string options, like this: captcha_cmd: "tools/@SCRIPT@" --- src/ejabberd_config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index d9c053da8..c4e6b3a62 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -663,7 +663,7 @@ get_additional_macros() -> parse_macro_string(MacroString) -> [NameString, ValueString] = string:split(MacroString, "="), - {ok, [ValueDecoded]} = fast_yaml:decode(ValueString, [plain_as_atom]), + {ok, [ValueDecoded]} = fast_yaml:decode(ValueString), {list_to_atom(NameString), ValueDecoded}. read_yaml_files(Files, Opts) -> From 38cc3ccb1e5cd094a93af6e649ee4be16dedc684 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 5 Jun 2025 16:38:29 +0200 Subject: [PATCH 1138/1302] Avoid using the "else" conditional compilation macro directive Even if "-else." is a valid directive: https://www.erlang.org/docs/28/system/macros.html#conditional-compilation there is a bug in rebar3_format that annoyingly rewrites it as "- else ." https://github.com/AdRoll/rebar3_format/issues/337 as a quick workaround, rewrite directives to not use "-else." --- src/ejabberd_web_admin.erl | 4 +++- src/mod_adhoc_api.erl | 4 +++- test/json_test.erl | 10 +++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 42ca212c0..0e122d451 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1925,7 +1925,9 @@ lists_zipwith3(Combine, [E1 | List1], [E2 | List2], [], DefX, DefY, DefZ, Res) - E123 = Combine(E1, E2, DefZ), lists_zipwith3(Combine, List1, List2, [], DefX, DefY, DefZ, [E123 | Res]). --else. +-endif. + +-ifndef(OTP_BELOW_26). lists_zipwith3(Combine, List1, List2, List3, How) -> lists:zipwith3(Combine, List1, List2, List3, How). diff --git a/src/mod_adhoc_api.erl b/src/mod_adhoc_api.erl index 2e478333c..59a4b4b5f 100644 --- a/src/mod_adhoc_api.erl +++ b/src/mod_adhoc_api.erl @@ -664,7 +664,9 @@ lists_zip3_pad([A | As], Nil, Nil, Xs) when (Nil == none) or (Nil == []) -> lists_zip3_pad([], Nil, Nil, Xs) when (Nil == none) or (Nil == []) -> lists:reverse(Xs). --else. +-endif. + +-ifndef(OTP_BELOW_26). lists_zip3_pad(As, Bs, Cs) -> lists:zip3(As, Bs, Cs, {pad, {error_missing_args_def, "", ""}}). diff --git a/test/json_test.erl b/test/json_test.erl index 49fad0f25..7f16cf7a7 100644 --- a/test/json_test.erl +++ b/test/json_test.erl @@ -11,7 +11,6 @@ encode_binary_test() -> Encoded = <<"\"This is an error text.\"">>, ?assertMatch(Encoded, misc:json_encode(Binary)). --ifdef(OTP_BELOW_27). -ifdef(OTP_BELOW_26). %% OTP 25 or lower @@ -24,7 +23,11 @@ encode_map_test() -> <<"{\"service\":\"conference\",\"name\":\"room\",\"jid\":\"user@server\",\"affiliation\":\"member\"}">>, ?assertMatch(Encoded, misc:json_encode(Map)). --else. +-endif. + +-ifdef(OTP_BELOW_27). + +-ifndef(OTP_BELOW_26). %% OTP 26 encode_map_test() -> @@ -37,8 +40,9 @@ encode_map_test() -> ?assertMatch(Encoded, misc:json_encode(Map)). -endif. +-endif. --else. +-ifndef(OTP_BELOW_27). %% OTP 27 or higher or higher encode_map_test() -> From 0bb99bb3712457cbd8753ecb7a41238285bc9559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Fri, 6 Jun 2025 11:57:27 +0200 Subject: [PATCH 1139/1302] Fix supported version of XEP-0485 --- src/ejabberd.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index b49d11f7f..e410fb016 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -42,7 +42,7 @@ -protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.03"}). --protocol({xep, 485, '0.2.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). +-protocol({xep, 485, '0.1.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). From 95a083a6f4ac29786e1d65593e2d5b7354744d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Fri, 6 Jun 2025 16:52:28 +0200 Subject: [PATCH 1140/1302] Replace csplit with perl in rebar3-format.sh Bsd csplit doesn't offer some options that we require, so let's use perl that should work everywhere --- tools/rebar3-format.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/rebar3-format.sh b/tools/rebar3-format.sh index d83d8e803..2181b6870 100755 --- a/tools/rebar3-format.sh +++ b/tools/rebar3-format.sh @@ -17,7 +17,7 @@ FPATH=$1 ERLS=$(git grep --name-only @format-begin "$FPATH"/) for ERL in $ERLS; do - csplit --quiet --prefix=$ERL-format- $ERL /@format-/ "{*}" + perl -n -e 'sub o { open(OUT, ">", sprintf("%s-format-%02d", $f, $n++));}; BEGIN{($f)=@ARGV;o()}; o() if /\@format-/; print OUT $_;' $ERL done EFMTS=$(find "$FPATH"/*-format-* -type f -exec grep --files-with-matches "@format-begin" '{}' ';') From 8855a304ccbf9ecaee617d4a3eb9dc330722e051 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 9 Jun 2025 17:47:27 +0200 Subject: [PATCH 1141/1302] ext_mod: Support library path installed from Debian (processone/ejabberd-contrib#363) --- src/ext_mod.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 2d34a91f9..51139ae9d 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -645,6 +645,8 @@ compile_options() -> ++ maybe_define_lager_macro() ++ [{i, filename:join(app_dir(App), "include")} || App <- [fast_xml, xmpp, p1_utils, ejabberd]] + ++ [{i, filename:join(app_dir(App), "include")} + || App <- [p1_xml, p1_xmpp]] % paths used in Debian packages ++ [{i, filename:join(mod_dir(Mod), "include")} || Mod <- installed()]. From ed846c4a8891491442c2aa5744b3d809a95dc40d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 9 Jun 2025 18:02:40 +0200 Subject: [PATCH 1142/1302] ext_mod: Recommend to write README.md instead txt (processone/ejabberd-contrib#363) --- src/ext_mod.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 51139ae9d..3593c2896 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -543,7 +543,7 @@ check_sources(Module) -> true -> Acc; false -> [{missing, Name}|Acc] end - end, HaveSrc, [{is_file, "README.txt"}, + end, HaveSrc, [{is_file, "README.md"}, {is_file, "COPYING"}, {is_file, SpecFile}]), SpecCheck = case consult(SpecFile) of From 38f365ffebda70cbdd2e43ba8661e7c0aaf5f309 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 14 Jun 2025 19:42:26 +0200 Subject: [PATCH 1143/1302] Move ecPrivkeyVer1 workaround for Jose from ejabberd to p1_acme Revert "Add workaround for Jose 1.11.10 not supporting OTP 28 ecPrivkeyVer1 (#4393)" This reverts commit 363351b18c95b304b491c4d9543bc26039a1201c. --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- src/ejabberd_acme.erl | 6 +----- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mix.exs b/mix.exs index 509cf0669..4a677be5f 100644 --- a/mix.exs +++ b/mix.exs @@ -125,7 +125,7 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2", override: true}, + {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "27a590789add30ff507a49ffd440eeeb28c96ce5", override: true}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index 99b22f712..16e15d46c 100644 --- a/mix.lock +++ b/mix.lock @@ -24,7 +24,7 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2", [ref: "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"]}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "27a590789add30ff507a49ffd440eeeb28c96ce5", [ref: "27a590789add30ff507a49ffd440eeeb28c96ce5"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.32", "3f95d7e3413fc8f0be80abb4be1a0d7f67066a36905085cd5a423145598b0cb0", [:rebar3], [{:xmpp, "~> 1.10.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "268b01e8f4eb75c211a31495a25c2815c549aecce2f0df1a161c6e0a2cde061e"}, diff --git a/rebar.config b/rebar.config index 60521c23a..1c076e42a 100644 --- a/rebar.config +++ b/rebar.config @@ -64,7 +64,7 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.17", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", "8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"}}, + {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", "27a590789add30ff507a49ffd440eeeb28c96ce5"}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, diff --git a/rebar.lock b/rebar.lock index 718196335..964427999 100644 --- a/rebar.lock +++ b/rebar.lock @@ -19,7 +19,7 @@ {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, {<<"p1_acme">>, {git,"https://github.com/processone/p1_acme", - {ref,"8471432f4df79cc8f18dbb1f2da5af11e7bf59e2"}}, + {ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}}, 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index a3dcbbf91..8b16fc727 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -318,11 +318,7 @@ read_account_key() -> case pkix:read_file(Path) of {ok, _, KeyMap} -> case maps:keys(KeyMap) of - [#'ECPrivateKey'{version = ecPrivkeyVer1} = Key|_] -> - %% Erlang/OTP 28 generates this, but Jose doesn't support it - {ok, Key#'ECPrivateKey'{version = 1}}; - [#'ECPrivateKey'{} = Key|_] -> - {ok, Key}; + [#'ECPrivateKey'{} = Key|_] -> {ok, Key}; _ -> ?WARNING_MSG("File ~ts doesn't contain ACME account key. " "Trying to create a new one...", From 6c1452435df630f805e2c414b95fe4a8ab1aee33 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Jun 2025 12:23:33 +0200 Subject: [PATCH 1144/1302] Bump xmpp to get: Add Unified Push support (via Conversations/up) (processone/xmpp#101) --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 4a677be5f..c8d6d82bc 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "ee9fd6583bbaf5d05ba16e1b2804750275a46f42", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "74ed2d87222d3bd5e86f7c41daaa28fae59d4995", override: true}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 16e15d46c..ef4de2752 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "ee9fd6583bbaf5d05ba16e1b2804750275a46f42", [ref: "ee9fd6583bbaf5d05ba16e1b2804750275a46f42"]}, + "xmpp": {:git, "https://github.com/processone/xmpp", "74ed2d87222d3bd5e86f7c41daaa28fae59d4995", [ref: "74ed2d87222d3bd5e86f7c41daaa28fae59d4995"]}, "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, } diff --git a/rebar.config b/rebar.config index 1c076e42a..7f07b4413 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "ee9fd6583bbaf5d05ba16e1b2804750275a46f42"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 964427999..973df5a07 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"ee9fd6583bbaf5d05ba16e1b2804750275a46f42"}}, + {ref,"74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ From aff8b47b6c4f12704f6e29ac2b29966eb6e4f160 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Jun 2025 12:48:34 +0200 Subject: [PATCH 1145/1302] Add dialyzer spec to try to dix warnings --- src/mod_mix_mnesia.erl | 1 + src/mod_mix_sql.erl | 1 + 2 files changed, 2 insertions(+) diff --git a/src/mod_mix_mnesia.erl b/src/mod_mix_mnesia.erl index 2ffd32bee..706e317f3 100644 --- a/src/mod_mix_mnesia.erl +++ b/src/mod_mix_mnesia.erl @@ -128,6 +128,7 @@ set_participant(_LServer, Channel, Service, JID, ID, Nick) -> nick = Nick, created_at = erlang:timestamp()}). +-spec get_participant(binary(), binary(), binary(), jid()) -> {ok, {binary(), binary()}} | {error, notfound}. get_participant(_LServer, Channel, Service, JID) -> {User, Domain, _} = jid:tolower(JID), case mnesia:dirty_read(mix_participant, {User, Domain, Channel, Service}) of diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl index 5d0c3d95d..fcc5b3d97 100644 --- a/src/mod_mix_sql.erl +++ b/src/mod_mix_sql.erl @@ -167,6 +167,7 @@ set_participant(LServer, Channel, Service, JID, ID, Nick) -> _Err -> {error, db_failure} end. +-spec get_participant(binary(), binary(), binary(), jid()) -> {ok, {binary(), binary()}} | {error, notfound | db_failure}. get_participant(LServer, Channel, Service, JID) -> {User, Domain, _} = jid:tolower(JID), case ejabberd_sql:sql_query( From f03b5f4c44fd2891c6ef1d7cc00062bfe0f69ad5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Jun 2025 12:57:02 +0200 Subject: [PATCH 1146/1302] Support list of IDs in pubsub-items-retract (processone/xmpp#100) --- src/mod_mix.erl | 2 +- src/mod_muc_rtbl.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_mix.erl b/src/mod_mix.erl index c6aa60ef6..ced8ada75 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -626,7 +626,7 @@ notify_participant_joined(Mod, LServer, To, From, ID, Nick) -> notify_participant_left(Mod, LServer, To, ID) -> {Chan, Host, _} = jid:tolower(To), Items = #ps_items{node = ?NS_MIX_NODES_PARTICIPANTS, - retract = ID}, + retract = [ID]}, Event = #ps_event{items = Items}, Msg = #message{from = jid:remove_resource(To), id = p1_rand:get_string(), diff --git a/src/mod_muc_rtbl.erl b/src/mod_muc_rtbl.erl index d7504ab7c..94186e890 100644 --- a/src/mod_muc_rtbl.erl +++ b/src/mod_muc_rtbl.erl @@ -151,9 +151,9 @@ pubsub_event_handler(#message{from = #jid{luser = <<>>, lserver = SServer}, SNode = mod_muc_rtbl_opt:rtbl_node(Server), if SServer == SServer2 -> case xmpp:get_subtag(Msg, #ps_event{}) of - #ps_event{items = #ps_items{node = Node, retract = Retract}} when Node == SNode, + #ps_event{items = #ps_items{node = Node, retract = [Retract | _] = RetractList}} when Node == SNode, is_binary(Retract) -> - mnesia:dirty_delete(muc_rtbl, {Server, Retract}); + [mnesia:dirty_delete(muc_rtbl, {Server, R1}) || R1 <- RetractList]; #ps_event{items = #ps_items{node = Node, items = Items}} when Node == SNode -> Added = lists:foldl( fun(#ps_item{id = ID}, Acc) -> From 71f623ddbf89550736397d45fa2bab33630f6463 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Jun 2025 13:14:07 +0200 Subject: [PATCH 1147/1302] Fix previous commits --- src/mod_mix_mnesia.erl | 2 +- src/mod_mix_sql.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_mix_mnesia.erl b/src/mod_mix_mnesia.erl index 706e317f3..0d3a4d20c 100644 --- a/src/mod_mix_mnesia.erl +++ b/src/mod_mix_mnesia.erl @@ -128,7 +128,7 @@ set_participant(_LServer, Channel, Service, JID, ID, Nick) -> nick = Nick, created_at = erlang:timestamp()}). --spec get_participant(binary(), binary(), binary(), jid()) -> {ok, {binary(), binary()}} | {error, notfound}. +-spec get_participant(binary(), binary(), binary(), jid:jid()) -> {ok, {binary(), binary()}} | {error, notfound}. get_participant(_LServer, Channel, Service, JID) -> {User, Domain, _} = jid:tolower(JID), case mnesia:dirty_read(mix_participant, {User, Domain, Channel, Service}) of diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl index fcc5b3d97..be3b28124 100644 --- a/src/mod_mix_sql.erl +++ b/src/mod_mix_sql.erl @@ -167,7 +167,7 @@ set_participant(LServer, Channel, Service, JID, ID, Nick) -> _Err -> {error, db_failure} end. --spec get_participant(binary(), binary(), binary(), jid()) -> {ok, {binary(), binary()}} | {error, notfound | db_failure}. +-spec get_participant(binary(), binary(), binary(), jid:jid()) -> {ok, {binary(), binary()}} | {error, notfound | db_failure}. get_participant(LServer, Channel, Service, JID) -> {User, Domain, _} = jid:tolower(JID), case ejabberd_sql:sql_query( From 2d2b98e525f90b06fcc9b5a77aec11256ff84377 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Jun 2025 13:41:30 +0200 Subject: [PATCH 1148/1302] Update Elvis to 4.1.1, fix some warnings and enable their tests --- .github/workflows/ci.yml | 1 + elvis.config | 6 +++--- rebar.config | 2 +- src/ejabberd_auth.erl | 7 +++---- src/ejabberd_http.erl | 5 ++--- src/ejabberd_listener.erl | 6 +++--- src/mod_muc_admin.erl | 5 ++--- src/mod_scram_upgrade.erl | 6 ++---- src/pubsub_db_sql.erl | 2 +- 9 files changed, 18 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a39b5aadc..6c747e16b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,6 +120,7 @@ jobs: - run: make dialyzer - run: make test-eunit - run: make elvis + if: matrix.otp >= '26' - name: Check Production Release run: | diff --git a/elvis.config b/elvis.config index 074e13010..fb0f8b973 100644 --- a/elvis.config +++ b/elvis.config @@ -16,17 +16,17 @@ {elvis_style, function_naming_convention, disable}, {elvis_style, god_modules, #{limit => 300}}, {elvis_style, invalid_dynamic_call, disable}, - {elvis_style, macro_module_names, disable}, - {elvis_style, macro_names, disable}, {elvis_style, max_function_arity, disable}, % #{max_arity => 15}}, {elvis_style, nesting_level, disable}, {elvis_style, no_author, disable}, + {elvis_style, no_boolean_in_comparison, disable}, {elvis_style, no_catch_expressions, disable}, {elvis_style, no_debug_call, disable}, {elvis_style, no_if_expression, disable}, {elvis_style, no_import, disable}, - {elvis_style, no_match_in_condition, disable}, {elvis_style, no_nested_try_catch, disable}, + {elvis_style, no_operation_on_same_value, disable}, + {elvis_style, no_receive_without_timeout, disable}, {elvis_style, no_single_clause_case, disable}, {elvis_style, no_spec_with_records, disable}, {elvis_style, no_throw, disable}, diff --git a/rebar.config b/rebar.config index 7f07b4413..63c51b8fb 100644 --- a/rebar.config +++ b/rebar.config @@ -165,7 +165,7 @@ }]}}. {if_rebar3, {project_plugins, [configure_deps, {if_var_true, tools, rebar3_format}, - {if_var_true, tools, rebar3_lint} + {if_var_true, tools, {rebar3_lint, "4.1.1"}} ]}}. {if_not_rebar3, {plugins, [ deps_erl_opts, override_deps_versions2, override_opts, configure_deps diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 419aad54d..f29ccbb96 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -423,9 +423,8 @@ count_users(Server, Opts) -> -spec get_password(binary(), binary()) -> false | [password()]. get_password(User, Server) -> - case get_password_with_authmodule(User, Server) of - {Passwords, _} -> Passwords - end. + {Passwords, _} = get_password_with_authmodule(User, Server), + Passwords. -spec get_password_s(binary(), binary()) -> password(). get_password_s(User, Server) -> @@ -815,7 +814,7 @@ db_user_exists(User, Server, Mod) -> end, case Val of {ok, _} -> - {true, Mod /= ejabberd_auth_anonymous} ; + {true, Mod /= ejabberd_auth_anonymous}; not_found -> {false, Mod /= ejabberd_auth_anonymous}; error -> diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 8a3a45f54..aad7f7897 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -170,9 +170,8 @@ send_file(State, Fd, Size, FileName) -> try case State#state.sockmod of gen_tcp -> - case file:sendfile(Fd, State#state.socket, 0, Size, []) of - {ok, _} -> ok - end; + {ok, _} = file:sendfile(Fd, State#state.socket, 0, Size, []), + ok; _ -> case file:read(Fd, ?SEND_BUF) of {ok, Data} -> diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 4fd7115a7..a3a9cc3ed 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -297,11 +297,11 @@ maybe_delete_udsocket_file(_Port) -> split_opts(Transport, Opts) -> maps:fold( fun(Opt, Val, {ModOpts, SockOpts}) -> - case OptVal = {Opt, Val} of + case {Opt, Val} of {ip, _} -> - {ModOpts, [OptVal|SockOpts]}; + {ModOpts, [{Opt, Val} | SockOpts]}; {backlog, _} when Transport == tcp -> - {ModOpts, [OptVal|SockOpts]}; + {ModOpts, [{Opt, Val} | SockOpts]}; {backlog, _} -> {ModOpts, SockOpts}; _ -> diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 1eadea684..77b46b275 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -2230,9 +2230,8 @@ find_services_validate(Global, _Name) when Global == global; Global == <<"global">> -> find_services(Global); find_services_validate(Service, Name) -> - case validate_muc(Service, Name) of - Service2 -> find_services(Service2) - end. + Service2 = validate_muc(Service, Name), + find_services(Service2). find_services(Global) when Global == global; Global == <<"global">> -> diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index f08c28631..7699006de 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -112,10 +112,8 @@ c2s_handle_sasl2_task_data({_, #{user := User, server := Server, serverkey = ServerKey, storedkey = StoredKey}), State2 = maps:remove(scram_upgrade, State), InlineEls2 = lists:keydelete(sasl_upgrade, 1, InlineEls), - case ejabberd_c2s:handle_sasl2_inline(InlineEls2, State2) of - {State3, NewEls, Results} -> - {success, NewEls, Results, State3} - end; + {State3, NewEls, Results} = ejabberd_c2s:handle_sasl2_inline(InlineEls2, State2), + {success, NewEls, Results, State3}; _ -> {abort, State} end. diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl index a67654c7c..7a789e9ea 100644 --- a/src/pubsub_db_sql.erl +++ b/src/pubsub_db_sql.erl @@ -56,7 +56,7 @@ delete_subscription(SubID) -> "where subid = %(SubID)s")), ok. --spec update_subscription(#pubsub_subscription{}) -> ok . +-spec update_subscription(#pubsub_subscription{}) -> ok. update_subscription(#pubsub_subscription{subid = SubId} = Sub) -> delete_subscription(SubId), add_subscription(Sub). From 064b005ec5cbdb73f5fcbed378420c7c8a343a2f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 20 May 2025 17:34:43 +0200 Subject: [PATCH 1149/1302] Define some example glossary terms --- src/mod_adhoc.erl | 6 ++++-- src/mod_adhoc_api.erl | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 39f03ba53..231480b3d 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -252,9 +252,11 @@ mod_options(_Host) -> mod_doc() -> #{desc => - ?T("This module implements https://xmpp.org/extensions/xep-0050.html" + [?T("def:ad-hoc command"), "", + ?T(": Command that can be executed by an XMPP client using XEP-0050."), "", + ?T("This module implements https://xmpp.org/extensions/xep-0050.html" "[XEP-0050: Ad-Hoc Commands]. It's an auxiliary module and is " - "only needed by some of the other modules."), + "only needed by some of the other modules.")], opts => [{report_commands_node, #{value => "true | false", diff --git a/src/mod_adhoc_api.erl b/src/mod_adhoc_api.erl index 59a4b4b5f..73c95bc10 100644 --- a/src/mod_adhoc_api.erl +++ b/src/mod_adhoc_api.erl @@ -90,7 +90,7 @@ depends(_Host, _Opts) -> mod_doc() -> #{desc => - ?T("Execute https://docs.ejabberd.im/developer/ejabberd-api/[API Commands] " + ?T("Execute (def:API commands) " "in a XMPP client using " "https://xmpp.org/extensions/xep-0050.html[XEP-0050: Ad-Hoc Commands]. " "This module requires _`mod_adhoc`_ (to execute the commands), " @@ -102,7 +102,7 @@ mod_doc() -> desc => ?T("What API version to use. " "If setting an ejabberd version, it will use the latest API " - "version that was available in that ejabberd version. " + "version that was available in that (def:c2s) ejabberd version. " "For example, setting '\"24.06\"' in this option implies '2'. " "The default value is the latest version.")}}], example => From 31967793085bcd8d537710e58eb9f20f7335dc62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 09:48:46 +0000 Subject: [PATCH 1150/1302] build(deps-dev): bump ex_doc from 0.37.3 to 0.38.2 Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.37.3 to 0.38.2. - [Release notes](https://github.com/elixir-lang/ex_doc/releases) - [Changelog](https://github.com/elixir-lang/ex_doc/blob/main/CHANGELOG.md) - [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.37.3...v0.38.2) --- updated-dependencies: - dependency-name: ex_doc dependency-version: 0.38.2 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index ef4de2752..8df7f78f8 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.57", "4b14e4832d08b9ffc10d855b5d10b3083232b1d53deb4c046679496ce85569c4", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.17", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "19c357e1817b1e04792ef359bf900400f3e6d0e5ade929fd72f88ea9b44af2ed"}, - "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, + "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, From c78e99dd543bee1b4bf7e3b3837c5ccd1baf1900 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Jun 2025 11:53:51 +0200 Subject: [PATCH 1151/1302] Use auxiliary function to get HOME, use Mnesia directory when not set (#4402) --- src/ejabberd_app.erl | 5 +++++ src/ejabberd_config.erl | 2 +- src/ejabberd_listener.erl | 2 +- src/ejabberd_sql.erl | 2 +- src/ext_mod.erl | 4 +--- src/misc.erl | 20 ++++++++++++++++++++ src/mod_http_upload.erl | 2 +- 7 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 9f9324494..5d15d98fd 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -44,6 +44,7 @@ start(normal, _Args) -> ejabberd_logger:start(), write_pid_file(), start_included_apps(), + misc:warn_unset_home(), start_elixir_application(), setup_if_elixir_conf_used(), case ejabberd_config:load() of @@ -176,6 +177,10 @@ file_queue_init() -> Err -> throw({?MODULE, Err}) end. +%%% +%%% Elixir +%%% + -ifdef(ELIXIR_ENABLED). is_using_elixir_config() -> Config = ejabberd_config:path(), diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index c4e6b3a62..3a556d7f6 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -503,7 +503,7 @@ get_predefined_keywords(Host) -> _ -> [{<<"HOST">>, Host}] end, - {ok, [[Home]]} = init:get_argument(home), + Home = misc:get_home(), HostList ++ [{<<"HOME">>, list_to_binary(Home)}, {<<"SEMVER">>, ejabberd_option:version()}, diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index a3a9cc3ed..05e1c1661 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -222,7 +222,7 @@ setup_provisional_udsocket_dir(DefinitivePath) -> get_provisional_udsocket_path(Path) -> PathBase64 = misc:term_to_base64(Path), - PathBuild = filename:join(os:getenv("HOME"), PathBase64), + PathBuild = filename:join(misc:get_home(), PathBase64), %% Shorthen the path, a long path produces a crash when opening the socket. binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}). diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 36db77fee..0c29f039e 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -1376,7 +1376,7 @@ write_file_if_new(File, Payload) -> tmp_dir() -> case os:type() of - {win32, _} -> filename:join([os:getenv("HOME"), "conf"]); + {win32, _} -> filename:join([misc:get_home(), "conf"]); _ -> filename:join(["/tmp", "ejabberd"]) end. diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 3593c2896..8af0300aa 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -371,8 +371,6 @@ geturl(Url) -> {error, Reason} end. -getenv(Env) -> - getenv(Env, ""). getenv(Env, Default) -> case os:getenv(Env) of false -> Default; @@ -453,7 +451,7 @@ delete_path(Path, Package) -> delete_path(filename:join(filename:dirname(Path), Package)). modules_dir() -> - DefaultDir = filename:join(getenv("HOME"), ".ejabberd-modules"), + DefaultDir = filename:join(misc:get_home(), ".ejabberd-modules"), getenv("CONTRIB_MODULES_PATH", DefaultDir). sources_dir() -> diff --git a/src/misc.erl b/src/misc.erl index ca7e43486..0ddc85996 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -36,6 +36,7 @@ l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1, now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2, compile_exprs/2, join_atoms/2, try_read_file/1, get_descr/2, + get_home/0, warn_unset_home/0, css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0, read_css/1, read_img/1, read_js/1, read_lua/1, intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0, @@ -470,6 +471,25 @@ get_descr(Lang, Text) -> Copyright = ejabberd_config:get_copyright(), <>. +-spec get_home() -> string(). +get_home() -> + case init:get_argument(home) of + {ok, [[Home]]} -> + Home; + error -> + mnesia:system_info(directory) + end. + +warn_unset_home() -> + case init:get_argument(home) of + {ok, [[_Home]]} -> + ok; + error -> + ?INFO_MSG("The 'HOME' environment variable is not set, " + "ejabberd will use as HOME the Mnesia directory: ~s.", + [mnesia:system_info(directory)]) + end. + -spec intersection(list(), list()) -> list(). intersection(L1, L2) -> lists:filter( diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index fddf27f46..e47da035e 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -717,7 +717,7 @@ get_proc_name(ServerHost, ModuleName, PutURL) -> -spec expand_home(binary()) -> binary(). expand_home(Input) -> - {ok, [[Home]]} = init:get_argument(home), + Home = misc:get_home(), misc:expand_keyword(<<"@HOME@">>, Input, Home). -spec expand_host(binary(), binary()) -> binary(). From a7c15eaccfb835c3072c528caf7173a0e3bd7ac7 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Thu, 17 Apr 2025 10:06:06 +0200 Subject: [PATCH 1152/1302] mod_antispam: initial import from ejabberd-contrib/mod_spam_filter --- src/mod_antispam.erl | 1072 +++++++++++++++++++++++++++++++++++++ src/mod_antispam_rtbl.erl | 132 +++++ 2 files changed, 1204 insertions(+) create mode 100644 src/mod_antispam.erl create mode 100644 src/mod_antispam_rtbl.erl diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl new file mode 100644 index 000000000..986f21066 --- /dev/null +++ b/src/mod_antispam.erl @@ -0,0 +1,1072 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_antispam.erl +%%% Author : Holger Weiss +%%% Author : Stefan Strigler +%%% Purpose : Filter spam messages based on sender JID and content +%%% Created : 31 Mar 2019 by Holger Weiss +%%% +%%% +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_antispam). +-author('holger@zedat.fu-berlin.de'). +-author('stefan@strigler.de'). + +-behaviour(gen_server). +-behaviour(gen_mod). + +%% gen_mod callbacks. +-export([start/2, + stop/1, + reload/3, + depends/2, + mod_doc/0, + mod_opt_type/1, + mod_options/1]). + +%% gen_server callbacks. +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + +%% ejabberd_hooks callbacks. +-export([s2s_in_handle_info/2, + s2s_receive_packet/1, + sm_receive_packet/1, + reopen_log/0]). + +%% ejabberd_commands callbacks. +-export([add_blocked_domain/2, + add_to_spam_filter_cache/2, + drop_from_spam_filter_cache/2, + expire_spam_filter_cache/2, + get_blocked_domains/1, + get_commands_spec/0, + get_spam_filter_cache/1, + reload_spam_filter_files/1, + remove_blocked_domain/2]). + +-include("ejabberd_commands.hrl"). +-include("logger.hrl"). + +-include_lib("xmpp/include/xmpp.hrl"). + +-type url() :: binary(). +-type filename() :: binary() | none. +-type jid_set() :: sets:set(ljid()). +-type url_set() :: sets:set(url()). +-type s2s_in_state() :: ejabberd_s2s_in:state(). + +-record(state, + {host = <<>> :: binary(), + dump_fd = undefined :: file:io_device() | undefined, + url_set = sets:new() :: url_set(), + jid_set = sets:new() :: jid_set(), + jid_cache = #{} :: map(), + max_cache_size = 0 :: non_neg_integer() | unlimited, + rtbl_host = none :: binary() | none, + rtbl_subscribed = false :: boolean(), + rtbl_retry_timer = undefined :: reference() | undefined, + rtbl_domains_node :: binary(), + blocked_domains = #{} :: #{binary() => any()}, + whitelist_domains = #{} :: #{binary() => false} + }). + +-type state() :: #state{}. + +-define(COMMAND_TIMEOUT, timer:seconds(30)). +-define(HTTPC_TIMEOUT, timer:seconds(3)). +-define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). +-define(DEFAULT_CACHE_SIZE, 10000). + +%%-------------------------------------------------------------------- +%% gen_mod callbacks. +%%-------------------------------------------------------------------- +-spec start(binary(), gen_mod:opts()) -> ok | {error, any()}. +start(Host, Opts) -> + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_commands:register_commands(?MODULE, get_commands_spec()); + true -> + ok + end, + gen_mod:start_child(?MODULE, Host, Opts). + +-spec stop(binary()) -> ok | {error, any()}. +stop(Host) -> + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_commands:unregister_commands(get_commands_spec()); + true -> + ok + end, + gen_mod:stop_child(?MODULE, Host). + +-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. +reload(Host, NewOpts, OldOpts) -> + ?DEBUG("reloading", []), + Proc = get_proc_name(Host), + gen_server:cast(Proc, {reload, NewOpts, OldOpts}). + +-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. +depends(_Host, _Opts) -> + [{mod_pubsub, soft}]. + +-spec mod_opt_type(atom()) -> econf:validator(). +mod_opt_type(spam_domains_file) -> + econf:either( + econf:enum([none]), + econf:file()); +mod_opt_type(whitelist_domains_file) -> + econf:either( + none, + econf:binary()); +mod_opt_type(spam_dump_file) -> + econf:either( + econf:enum([none]), + econf:binary()); +mod_opt_type(spam_jids_file) -> + econf:either( + econf:enum([none]), + econf:file()); +mod_opt_type(spam_urls_file) -> + econf:either( + econf:enum([none]), + econf:file()); +mod_opt_type(access_spam) -> + econf:acl(); +mod_opt_type(cache_size) -> + econf:pos_int(unlimited); +mod_opt_type(rtbl_host) -> + econf:either( + econf:enum([none]), + econf:host()); +mod_opt_type(rtbl_domains_node) -> + econf:non_empty(econf:binary()). + +-spec mod_options(binary()) -> [{atom(), any()}]. +mod_options(_Host) -> + [{spam_domains_file, none}, + {spam_dump_file, none}, + {spam_jids_file, none}, + {spam_urls_file, none}, + {whitelist_domains_file, none}, + {access_spam, none}, + {cache_size, ?DEFAULT_CACHE_SIZE}, + {rtbl_host, none}, + {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}]. + +mod_doc() -> #{}. + +%%-------------------------------------------------------------------- +%% gen_server callbacks. +%%-------------------------------------------------------------------- +-spec init(list()) -> {ok, state()} | {stop, term()}. +init([Host, Opts]) -> + process_flag(trap_exit, true), + DumpFile = expand_host(gen_mod:get_opt(spam_dump_file, Opts), Host), + Files = #{domains => gen_mod:get_opt(spam_domains_file, Opts), + jid => gen_mod:get_opt(spam_jids_file, Opts), + url => gen_mod:get_opt(spam_urls_file, Opts), + whitelist_domains => gen_mod:get_opt(whitelist_domains_file, Opts)}, + try read_files(Files) of + #{jid := JIDsSet, url := URLsSet, domains := SpamDomainsSet, whitelist_domains := WhitelistDomains} -> + ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, + s2s_in_handle_info, 90), + ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, + s2s_receive_packet, 50), + ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, + sm_receive_packet, 50), + ejabberd_hooks:add(reopen_log_hook, ?MODULE, + reopen_log, 50), + ejabberd_hooks:add(local_send_to_resource_hook, Host, + mod_antispam_rtbl, pubsub_event_handler, 50), + RTBLHost = gen_mod:get_opt(rtbl_host, Opts), + RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), + InitState0 = #state{host = Host, + jid_set = JIDsSet, + url_set = URLsSet, + max_cache_size = gen_mod:get_opt(cache_size, Opts), + blocked_domains = set_to_map(SpamDomainsSet), + whitelist_domains = set_to_map(WhitelistDomains, false), + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode}, + mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), + InitState = init_open_dump_file(DumpFile, InitState0), + {ok, InitState} + catch {Op, File, Reason} when Op == open; + Op == read -> + ?CRITICAL_MSG("Cannot ~s ~s: ~s", [Op, File, format_error(Reason)]), + {stop, config_error} + end. + +init_open_dump_file(none, State) -> + State; +init_open_dump_file(DumpFile, State) -> + case filelib:ensure_dir(DumpFile) of + ok -> + ok; + {error, Reason} -> + Dirname = filename:dirname(DumpFile), + throw({open, Dirname, Reason}) + end, + open_dump_file(DumpFile, State). + +-spec handle_call(term(), {pid(), term()}, state()) + -> {reply, {spam_filter, term()}, state()} | {noreply, state()}. +handle_call({check_jid, From}, _From, #state{jid_set = JIDsSet} = State) -> + {Result, State1} = filter_jid(From, JIDsSet, State), + {reply, {spam_filter, Result}, State1}; +handle_call({check_body, URLs, JIDs, From}, _From, + #state{url_set = URLsSet, jid_set = JIDsSet} = State) -> + {Result1, State1} = filter_body(URLs, URLsSet, From, State), + {Result2, State2} = filter_body(JIDs, JIDsSet, From, State1), + Result = if Result1 == spam -> + Result1; + true -> + Result2 + end, + {reply, {spam_filter, Result}, State2}; +handle_call({resolve_redirects, URLs}, _From, State) -> + ResolvedURLs = do_resolve_redirects(URLs, []), + {reply, {spam_filter, ResolvedURLs}, State}; +handle_call({reload_files, Files}, _From, State) -> + {Result, State1} = reload_files(Files, State), + {reply, {spam_filter, Result}, State1}; +handle_call({expire_cache, Age}, _From, State) -> + {Result, State1} = expire_cache(Age, State), + {reply, {spam_filter, Result}, State1}; +handle_call({add_to_cache, JID}, _From, State) -> + {Result, State1} = add_to_cache(JID, State), + {reply, {spam_filter, Result}, State1}; +handle_call({drop_from_cache, JID}, _From, State) -> + {Result, State1} = drop_from_cache(JID, State), + {reply, {spam_filter, Result}, State1}; +handle_call(get_cache, _From, #state{jid_cache = Cache} = State) -> + {reply, {spam_filter, maps:to_list(Cache)}, State}; +handle_call({add_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains} = State) -> + BlockedDomains1 = maps:merge(BlockedDomains, #{Domain => true}), + Txt = format("~s added to blocked domains", [Domain]), + {reply, {spam_filter, {ok, Txt}}, State#state{blocked_domains = BlockedDomains1}}; +handle_call({remove_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains} = State) -> + BlockedDomains1 = maps:remove(Domain, BlockedDomains), + Txt = format("~s removed from blocked domains", [Domain]), + {reply, {spam_filter, {ok, Txt}}, State#state{blocked_domains = BlockedDomains1}}; +handle_call(get_blocked_domains, _From, #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = State) -> + {reply, {blocked_domains, maps:merge(BlockedDomains, WhitelistDomains)}, State}; +handle_call({is_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = State) -> + {reply, maps:get(Domain, maps:merge(BlockedDomains, WhitelistDomains), false) =/= false, State}; +handle_call(Request, From, State) -> + ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), + {noreply, State}. + +-spec handle_cast(term(), state()) -> {noreply, state()}. +handle_cast({dump, _XML}, #state{dump_fd = undefined} = State) -> + {noreply, State}; +handle_cast({dump, XML}, #state{dump_fd = Fd} = State) -> + case file:write(Fd, [XML, <<$\n>>]) of + ok -> + ok; + {error, Reason} -> + ?ERROR_MSG("Cannot write spam to dump file: ~s", + [file:format_error(Reason)]) + end, + {noreply, State}; +handle_cast({reload, NewOpts, OldOpts}, + #state{host = Host, + rtbl_host = OldRTBLHost, + rtbl_domains_node = OldRTBLDomainsNode, + rtbl_retry_timer = RTBLRetryTimer} = State) -> + misc:cancel_timer(RTBLRetryTimer), + State1 = case {gen_mod:get_opt(spam_dump_file, OldOpts), + gen_mod:get_opt(spam_dump_file, NewOpts)} of + {OldDumpFile, NewDumpFile} when NewDumpFile /= OldDumpFile -> + close_dump_file(expand_host(OldDumpFile, Host), State), + open_dump_file(expand_host(NewDumpFile, Host), State); + {_OldDumpFile, _NewDumpFile} -> + State + end, + State2 = case {gen_mod:get_opt(cache_size, OldOpts), + gen_mod:get_opt(cache_size, NewOpts)} of + {OldMax, NewMax} when NewMax < OldMax -> + shrink_cache(State1#state{max_cache_size = NewMax}); + {OldMax, NewMax} when NewMax > OldMax -> + State1#state{max_cache_size = NewMax}; + {_OldMax, _NewMax} -> + State1 + end, + ok = mod_antispam_rtbl:unsubscribe(OldRTBLHost, OldRTBLDomainsNode, Host), + Files = #{domains => gen_mod:get_opt(spam_domains_file, NewOpts), + jid => gen_mod:get_opt(spam_jids_file, NewOpts), + url => gen_mod:get_opt(spam_urls_file, NewOpts), + whitelist_domains => gen_mod:get_opt(whitelist_domains_file, NewOpts)}, + {_Result, State3} = reload_files(Files, State2#state{blocked_domains = #{}}), + RTBLHost = gen_mod:get_opt(rtbl_host, NewOpts), + RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, NewOpts), + ok = mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), + {noreply, State3#state{rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode}}; +handle_cast(reopen_log, State) -> + {noreply, reopen_dump_file(State)}; +handle_cast({update_blocked_domains, NewItems}, #state{blocked_domains = BlockedDomains} = State) -> + {noreply, State#state{blocked_domains = maps:merge(BlockedDomains, NewItems)}}; +handle_cast(Request, State) -> + ?ERROR_MSG("Got unexpected request from: ~p", [Request]), + {noreply, State}. + +-spec handle_info(term(), state()) -> {noreply, state()}. +handle_info({iq_reply, timeout, blocked_domains}, State) -> + ?WARNING_MSG("Fetching blocked domains failed: fetch timeout. Retrying in 60 seconds", []), + {noreply, State#state{rtbl_retry_timer = erlang:send_after(60000, self(), request_blocked_domains)}}; +handle_info({iq_reply, #iq{type = error} = IQ, blocked_domains}, State) -> + ?WARNING_MSG("Fetching blocked domains failed: ~p. Retrying in 60 seconds", + [xmpp:format_stanza_error(xmpp:get_error(IQ))]), + {noreply, State#state{rtbl_retry_timer = erlang:send_after(60000, self(), request_blocked_domains)}}; +handle_info({iq_reply, IQReply, blocked_domains}, + #state{blocked_domains = OldBlockedDomains, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode, + host = Host} = State) -> + case mod_antispam_rtbl:parse_blocked_domains(IQReply) of + undefined -> + ?WARNING_MSG("Fetching initial list failed: invalid result payload", []), + {noreply, State#state{rtbl_retry_timer = undefined}}; + NewBlockedDomains -> + ok = mod_antispam_rtbl:subscribe(RTBLHost, RTBLDomainsNode, Host), + {noreply, State#state{rtbl_retry_timer = undefined, + rtbl_subscribed = true, + blocked_domains = maps:merge(OldBlockedDomains, NewBlockedDomains)}} + end; +handle_info({iq_reply, timeout, subscribe_result}, State) -> + ?WARNING_MSG("Subscription error: request timeout", []), + {noreply, State#state{rtbl_subscribed = false}}; +handle_info({iq_reply, #iq{type = error} = IQ, subscribe_result}, State) -> + ?WARNING_MSG("Subscription error: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]), + {noreply, State#state{rtbl_subscribed = false}}; +handle_info({iq_reply, IQReply, subscribe_result}, State) -> + ?DEBUG("Got subscribe result: ~p", [IQReply]), + {noreply, State#state{rtbl_subscribed = true}}; +handle_info({iq_reply, _IQReply, unsubscribe_result}, State) -> + %% FIXME: we should check it's true (of type `result`, not `error`), but at that point, what + %% would we do? + {noreply, State#state{rtbl_subscribed = false}}; +handle_info(request_blocked_domains, #state{host = Host, rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode} = State) -> + mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), + {noreply, State}; +handle_info(Info, State) -> + ?ERROR_MSG("Got unexpected info: ~p", [Info]), + {noreply, State}. + +-spec terminate(normal | shutdown | {shutdown, term()} | term(), state()) -> ok. +terminate(Reason, + #state{host = Host, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode, + rtbl_retry_timer = RTBLRetryTimer} = State) -> + ?DEBUG("Stopping spam filter process for ~s: ~p", [Host, Reason]), + misc:cancel_timer(RTBLRetryTimer), + DumpFile = gen_mod:get_module_opt(Host, ?MODULE, spam_dump_file), + DumpFile1 = expand_host(DumpFile, Host), + close_dump_file(DumpFile1, State), + ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, + s2s_receive_packet, 50), + ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, + sm_receive_packet, 50), + ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, + s2s_in_handle_info, 90), + ejabberd_hooks:delete(local_send_to_resource_hook, Host, + mod_antispam_rtbl, pubsub_event_handler, 50), + mod_antispam_rtbl:unsubscribe(RTBLHost, RTBLDomainsNode,Host), + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_hooks:delete(reopen_log_hook, ?MODULE, + reopen_log, 50); + true -> + ok + end. + +-spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}. +code_change(_OldVsn, #state{host = Host} = State, _Extra) -> + ?DEBUG("Updating spam filter process for ~s", [Host]), + {ok, State}. + +%%-------------------------------------------------------------------- +%% Hook callbacks. +%%-------------------------------------------------------------------- + +-spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) + -> {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. +s2s_receive_packet({A, State}) -> + case sm_receive_packet(A) of + {stop, drop} -> + {stop, {drop, State}}; + Result -> + {Result, State} + end. + +-spec sm_receive_packet(stanza() | drop) -> stanza() | drop | {stop, drop}. +sm_receive_packet(drop = Acc) -> + Acc; +sm_receive_packet(#message{from = From, + to = #jid{lserver = LServer} = To, + type = Type} = Msg) + when Type /= groupchat, + Type /= error -> + do_check(From, To, LServer, Msg); +sm_receive_packet(#presence{from = From, + to = #jid{lserver = LServer} = To, + type = subscribe} = Presence) -> + do_check(From, To, LServer, Presence); +sm_receive_packet(Acc) -> + Acc. + +do_check(From, To, LServer, Stanza) -> + case needs_checking(From, To) of + true -> + case check_from(LServer, From) of + ham -> + case check_stanza(LServer, From, Stanza) of + ham -> + Stanza; + spam -> + reject(Stanza), + {stop, drop} + end; + spam -> + reject(Stanza), + {stop, drop} + end; + false -> + Stanza + end. + +check_stanza(LServer, From, #message{body = Body}) -> + check_body(LServer, From, xmpp:get_text(Body)); +check_stanza(_, _, _) -> + ham. + +-spec s2s_in_handle_info(s2s_in_state(), any()) + -> s2s_in_state() | {stop, s2s_in_state()}. +s2s_in_handle_info(State, {_Ref, {spam_filter, _}}) -> + ?DEBUG("Dropping expired spam filter result", []), + {stop, State}; +s2s_in_handle_info(State, _) -> + State. + +-spec reopen_log() -> ok. +reopen_log() -> + lists:foreach(fun(Host) -> + Proc = get_proc_name(Host), + gen_server:cast(Proc, reopen_log) + end, get_spam_filter_hosts()). + +%%-------------------------------------------------------------------- +%% Internal functions. +%%-------------------------------------------------------------------- +-spec needs_checking(jid(), jid()) -> boolean(). +needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> + case gen_mod:is_loaded(LServer, ?MODULE) of + true -> + Access = gen_mod:get_module_opt(LServer, ?MODULE, access_spam), + case acl:match_rule(LServer, Access, To) of + allow -> + ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), + false; + deny -> + ?DEBUG("Spam is filtered for ~s", [jid:encode(To)]), + not mod_roster:is_subscribed(From, To) andalso + not mod_roster:is_subscribed(jid:make(<<>>, FromHost), To) % likely a gateway + end; + false -> + ?DEBUG("~s not loaded for ~s", [?MODULE, LServer]), + false + end. + +-spec check_from(binary(), jid()) -> ham | spam. +check_from(Host, From) -> + Proc = get_proc_name(Host), + LFrom = {_, FromDomain, _} = jid:remove_resource(jid:tolower(From)), + try + case gen_server:call(Proc, {is_blocked_domain, FromDomain}) of + true -> + ?DEBUG("Spam JID found in blocked domains: ~p", [From]), + ejabberd_hooks:run(spam_found, Host, [{jid, From}]), + spam; + false -> + case gen_server:call(Proc, {check_jid, LFrom}) of + {spam_filter, Result} -> + Result + end + end + catch exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking ~s against list of blocked domains or spammers", + [jid:encode(From)]), + ham + end. + +-spec check_body(binary(), jid(), binary()) -> ham | spam. +check_body(Host, From, Body) -> + case {extract_urls(Host, Body), extract_jids(Body)} of + {none, none} -> + ?DEBUG("No JIDs/URLs found in message", []), + ham; + {URLs, JIDs} -> + Proc = get_proc_name(Host), + LFrom = jid:remove_resource(jid:tolower(From)), + try gen_server:call(Proc, {check_body, URLs, JIDs, LFrom}) of + {spam_filter, Result} -> + Result + catch exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking body", []), + ham + end + end. + +-spec extract_urls(binary(), binary()) -> {urls, [url()]} | none. +extract_urls(Host, Body) -> + RE = <<"https?://\\S+">>, + Options = [global, {capture, all, binary}], + case re:run(Body, RE, Options) of + {match, Captured} when is_list(Captured) -> + Urls = resolve_redirects(Host, lists:flatten(Captured)), + {urls, Urls}; + nomatch -> + none + end. + +-spec resolve_redirects(binary(), [url()]) -> [url()]. +resolve_redirects(Host, URLs) -> + Proc = get_proc_name(Host), + try gen_server:call(Proc, {resolve_redirects, URLs}) of + {spam_filter, ResolvedURLs} -> + ResolvedURLs + catch exit:{timeout, _} -> + ?WARNING_MSG("Timeout while resolving redirects: ~p", [URLs]), + URLs + end. + +-spec do_resolve_redirects([url()], [url()]) -> [url()]. +do_resolve_redirects([], Result) -> Result; +do_resolve_redirects([URL | Rest], Acc) -> + case + httpc:request(get, {URL, [{"user-agent", "curl/8.7.1"}]}, + [{autoredirect, false}, {timeout, ?HTTPC_TIMEOUT}], []) + of + {ok, {{_, StatusCode, _}, Headers, _Body}} when StatusCode >= 300, StatusCode < 400 -> + Location = proplists:get_value("location", Headers), + case Location == undefined orelse lists:member(Location, Acc) of + true -> + do_resolve_redirects(Rest, [URL | Acc]); + false -> + do_resolve_redirects([Location | Rest], [URL | Acc]) + end; + _Res -> + do_resolve_redirects(Rest, [URL | Acc]) + end. + +-spec extract_jids(binary()) -> {jids, [ljid()]} | none. +extract_jids(Body) -> + RE = <<"\\S+@\\S+">>, + Options = [global, {capture, all, binary}], + case re:run(Body, RE, Options) of + {match, Captured} when is_list(Captured) -> + {jids, lists:filtermap(fun try_decode_jid/1, + lists:flatten(Captured))}; + nomatch -> + none + end. + +-spec try_decode_jid(binary()) -> {true, ljid()} | false. +try_decode_jid(S) -> + try jid:decode(S) of + #jid{} = JID -> + {true, jid:remove_resource(jid:tolower(JID))} + catch _:{bad_jid, _} -> + false + end. + +-spec filter_jid(ljid(), jid_set(), state()) -> {ham | spam, state()}. +filter_jid(From, Set, #state{host = Host} = State) -> + case sets:is_element(From, Set) of + true -> + ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), + ejabberd_hooks:run(spam_found, Host, [{jid, From}]), + {spam, State}; + false -> + case cache_lookup(From, State) of + {true, State1} -> + ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), + ejabberd_hooks:run(spam_found, Host, [{jid, From}]), + {spam, State1}; + {false, State1} -> + ?DEBUG("JID not listed: ~s", [jid:encode(From)]), + {ham, State1} + end + end. + +-spec filter_body({urls, [url()]} | {jids, [ljid()]} | none, + url_set() | jid_set(), jid(), state()) + -> {ham | spam, state()}. +filter_body({_, Addrs}, Set, From, #state{host = Host} = State) -> + case lists:any(fun(Addr) -> sets:is_element(Addr, Set) end, Addrs) of + true -> + ?DEBUG("Spam addresses found: ~p", [Addrs]), + ejabberd_hooks:run(spam_found, Host, [{body, Addrs}]), + {spam, cache_insert(From, State)}; + false -> + ?DEBUG("Addresses not listed: ~p", [Addrs]), + {ham, State} + end; +filter_body(none, _Set, _From, State) -> + {ham, State}. + +-spec reload_files(#{Type :: atom() => filename()}, state()) + -> {ok | {error, binary()}, state()}. +reload_files(Files, #state{host = Host, blocked_domains = BlockedDomains} = State) -> + try read_files(Files) of + #{jid := JIDsSet, url := URLsSet, domains := SpamDomainsSet, whitelist_domains := WhitelistDomains} -> + case sets_equal(JIDsSet, State#state.jid_set) of + true -> + ?INFO_MSG("Reloaded spam JIDs for ~s (unchanged)", [Host]); + false -> + ?INFO_MSG("Reloaded spam JIDs for ~s (changed)", [Host]) + end, + case sets_equal(URLsSet, State#state.url_set) of + true -> + ?INFO_MSG("Reloaded spam URLs for ~s (unchanged)", [Host]); + false -> + ?INFO_MSG("Reloaded spam URLs for ~s (changed)", [Host]) + end, + {ok, State#state{jid_set = JIDsSet, + url_set = URLsSet, + blocked_domains = maps:merge(BlockedDomains, set_to_map(SpamDomainsSet)), + whitelist_domains = set_to_map(WhitelistDomains, false)} + } + catch {Op, File, Reason} when Op == open; + Op == read -> + Txt = format("Cannot ~s ~s for ~s: ~s", + [Op, File, Host, format_error(Reason)]), + ?ERROR_MSG("~s", [Txt]), + {{error, Txt}, State} + end. + +set_to_map(Set) -> + set_to_map(Set, true). + +set_to_map(Set, V) -> + sets:fold(fun(K, M) -> M#{K => V} end, #{}, Set). + +-spec read_files(#{Type => filename()}) -> #{jid => jid_set(), url => url_set(), Type => sets:set(binary())} + when Type :: atom(). +read_files(Files) -> + maps:map(fun(Type, Filename) -> + read_file(Filename, line_parser(Type)) + end, + Files). + +-spec line_parser(Type :: atom()) -> fun((binary()) -> binary()). +line_parser(jid) -> fun parse_jid/1; +line_parser(url) -> fun parse_url/1; +line_parser(_) -> fun trim/1. + +-spec read_file(filename(), fun((binary()) -> ljid() | url())) + -> jid_set() | url_set(). +read_file(none, _ParseLine) -> + sets:new(); +read_file(File, ParseLine) -> + case file:open(File, [read, binary, raw, {read_ahead, 65536}]) of + {ok, Fd} -> + try read_line(Fd, ParseLine, sets:new()) + catch throw:E -> throw({read, File, E}) + after ok = file:close(Fd) + end; + {error, Reason} -> + throw({open, File, Reason}) + end. + +-spec read_line(file:io_device(), fun((binary()) -> ljid() | url()), + jid_set() | url_set()) + -> jid_set() | url_set(). +read_line(Fd, ParseLine, Set) -> + case file:read_line(Fd) of + {ok, Line} -> + read_line(Fd, ParseLine, sets:add_element(ParseLine(Line), Set)); + {error, Reason} -> + throw(Reason); + eof -> + Set + end. + +-spec parse_jid(binary()) -> ljid(). +parse_jid(S) -> + try jid:decode(trim(S)) of + #jid{} = JID -> + jid:remove_resource(jid:tolower(JID)) + catch _:{bad_jid, _} -> + throw({bad_jid, S}) + end. + +-spec parse_url(binary()) -> url(). +parse_url(S) -> + URL = trim(S), + RE = <<"https?://\\S+$">>, + Options = [anchored, caseless, {capture, none}], + case re:run(URL, RE, Options) of + match -> + URL; + nomatch -> + throw({bad_url, S}) + end. + +-spec trim(binary()) -> binary(). +trim(S) -> + re:replace(S, <<"\\s+$">>, <<>>, [{return, binary}]). + +-spec reject(stanza()) -> ok. +reject(#message{from = From, to = To, type = Type, lang = Lang} = Msg) + when Type /= groupchat, + Type /= error -> + ?INFO_MSG("Rejecting unsolicited message from ~s to ~s", + [jid:encode(From), jid:encode(To)]), + Txt = <<"Your message is unsolicited">>, + Err = xmpp:err_policy_violation(Txt, Lang), + maybe_dump_spam(Msg), + ejabberd_router:route_error(Msg, Err); +reject(#presence{from = From, to = To, lang = Lang} = Presence) -> + ?INFO_MSG("Rejecting unsolicited presence from ~s to ~s", + [jid:encode(From), jid:encode(To)]), + Txt = <<"Your traffic is unsolicited">>, + Err = xmpp:err_policy_violation(Txt, Lang), + ejabberd_router:route_error(Presence, Err); +reject(_) -> + ok. + +-spec open_dump_file(filename(), state()) -> state(). +open_dump_file(none, State) -> + State#state{dump_fd = undefined}; +open_dump_file(Name, State) -> + Modes = [append, raw, binary, delayed_write], + case file:open(Name, Modes) of + {ok, Fd} -> + ?DEBUG("Opened ~s", [Name]), + State#state{dump_fd = Fd}; + {error, Reason} -> + ?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]), + State#state{dump_fd = undefined} + end. + +-spec close_dump_file(filename(), state()) -> ok. +close_dump_file(_Name, #state{dump_fd = undefined}) -> + ok; +close_dump_file(Name, #state{dump_fd = Fd}) -> + case file:close(Fd) of + ok -> + ?DEBUG("Closed ~s", [Name]); + {error, Reason} -> + ?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)]) + end. + +-spec reopen_dump_file(state()) -> state(). +reopen_dump_file(#state{host = Host} = State) -> + DumpFile = gen_mod:get_module_opt(Host, ?MODULE, spam_dump_file), + DumpFile1 = expand_host(DumpFile, Host), + close_dump_file(DumpFile1, State), + open_dump_file(DumpFile1, State). + +-spec maybe_dump_spam(message()) -> ok. +maybe_dump_spam(#message{to = #jid{lserver = LServer}} = Msg) -> + By = jid:make(<<>>, LServer), + Proc = get_proc_name(LServer), + Time = erlang:timestamp(), + Msg1 = misc:add_delay_info(Msg, By, Time), + XML = fxml:element_to_binary(xmpp:encode(Msg1)), + gen_server:cast(Proc, {dump, XML}). + +-spec get_proc_name(binary()) -> atom(). +get_proc_name(Host) -> + gen_mod:get_module_proc(Host, ?MODULE). + +-spec get_spam_filter_hosts() -> [binary()]. +get_spam_filter_hosts() -> + [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE)]. + +-spec expand_host(binary() | none, binary()) -> binary() | none. +expand_host(none, _Host) -> + none; +expand_host(Input, Host) -> + misc:expand_keyword(<<"@HOST@">>, Input, Host). + +-spec sets_equal(sets:set(), sets:set()) -> boolean(). +sets_equal(A, B) -> + sets:is_subset(A, B) andalso sets:is_subset(B, A). + +-spec format(io:format(), [term()]) -> binary(). +format(Format, Data) -> + iolist_to_binary(io_lib:format(Format, Data)). + +-spec format_error(atom() | tuple()) -> binary(). +format_error({bad_jid, JID}) -> + <<"Not a valid JID: ", JID/binary>>; +format_error({bad_url, URL}) -> + <<"Not an HTTP(S) URL: ", URL/binary>>; +format_error(Reason) -> + list_to_binary(file:format_error(Reason)). + +%%-------------------------------------------------------------------- +%% Caching. +%%-------------------------------------------------------------------- +-spec cache_insert(ljid(), state()) -> state(). +cache_insert(_LJID, #state{max_cache_size = 0} = State) -> + State; +cache_insert(LJID, #state{jid_cache = Cache, max_cache_size = MaxSize} = State) + when MaxSize /= unlimited, map_size(Cache) >= MaxSize -> + cache_insert(LJID, shrink_cache(State)); +cache_insert(LJID, #state{jid_cache = Cache} = State) -> + ?INFO_MSG("Caching spam JID: ~s", [jid:encode(LJID)]), + Cache1 = Cache#{LJID => erlang:monotonic_time(second)}, + State#state{jid_cache = Cache1}. + +-spec cache_lookup(ljid(), state()) -> {boolean(), state()}. +cache_lookup(LJID, #state{jid_cache = Cache} = State) -> + case Cache of + #{LJID := _Timestamp} -> + Cache1 = Cache#{LJID => erlang:monotonic_time(second)}, + State1 = State#state{jid_cache = Cache1}, + {true, State1}; + #{} -> + {false, State} + end. + +-spec shrink_cache(state()) -> state(). +shrink_cache(#state{jid_cache = Cache, max_cache_size = MaxSize} = State) -> + ShrinkedSize = round(MaxSize / 2), + N = map_size(Cache) - ShrinkedSize, + L = lists:keysort(2, maps:to_list(Cache)), + Cache1 = maps:from_list(lists:nthtail(N, L)), + State#state{jid_cache = Cache1}. + +-spec expire_cache(integer(), state()) -> {{ok, binary()}, state()}. +expire_cache(Age, #state{jid_cache = Cache} = State) -> + Threshold = erlang:monotonic_time(second) - Age, + Cache1 = maps:filter(fun(_, TS) -> TS >= Threshold end, Cache), + NumExp = map_size(Cache) - map_size(Cache1), + Txt = format("Expired ~B cache entries", [NumExp]), + {{ok, Txt}, State#state{jid_cache = Cache1}}. + +-spec add_to_cache(ljid(), state()) -> {{ok, binary()}, state()}. +add_to_cache(LJID, State) -> + State1 = cache_insert(LJID, State), + Txt = format("~s added to cache", [jid:encode(LJID)]), + {{ok, Txt}, State1}. + +-spec drop_from_cache(ljid(), state()) -> {{ok, binary()}, state()}. +drop_from_cache(LJID, #state{jid_cache = Cache} = State) -> + Cache1 = maps:remove(LJID, Cache), + if map_size(Cache1) < map_size(Cache) -> + Txt = format("~s removed from cache", [jid:encode(LJID)]), + {{ok, Txt}, State#state{jid_cache = Cache1}}; + true -> + Txt = format("~s wasn't cached", [jid:encode(LJID)]), + {{ok, Txt}, State} + end. + +%%-------------------------------------------------------------------- +%% ejabberd command callbacks. +%%-------------------------------------------------------------------- +-spec get_commands_spec() -> [ejabberd_commands()]. +get_commands_spec() -> + [#ejabberd_commands{name = reload_spam_filter_files, tags = [filter], + desc = "Reload spam JID/URL files", + module = ?MODULE, function = reload_spam_filter_files, + args = [{host, binary}], + result = {res, rescode}}, + #ejabberd_commands{name = get_spam_filter_cache, tags = [filter], + desc = "Show spam filter cache contents", + module = ?MODULE, function = get_spam_filter_cache, + args = [{host, binary}], + result = {spammers, {list, {spammer, {tuple, + [{jid, string}, {timestamp, integer}]}}}}}, + #ejabberd_commands{name = expire_spam_filter_cache, tags = [filter], + desc = "Remove old/unused spam JIDs from cache", + module = ?MODULE, function = expire_spam_filter_cache, + args = [{host, binary}, {seconds, integer}], + result = {res, restuple}}, + #ejabberd_commands{name = add_to_spam_filter_cache, tags = [filter], + desc = "Add JID to spam filter cache", + module = ?MODULE, + function = add_to_spam_filter_cache, + args = [{host, binary}, {jid, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = drop_from_spam_filter_cache, tags = [filter], + desc = "Drop JID from spam filter cache", + module = ?MODULE, + function = drop_from_spam_filter_cache, + args = [{host, binary}, {jid, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = get_blocked_domains, tags = [filter], + desc = "Get list of domains being blocked", + module = ?MODULE, + function = get_blocked_domains, + args = [{host, binary}], + result = {blocked_domains, {list, {jid, string}}}}, + #ejabberd_commands{name = add_blocked_domain, tags = [filter], + desc = "Add domain to list of blocked domains", + module = ?MODULE, + function = add_blocked_domain, + args = [{host, binary}, {domain, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = remove_blocked_domain, tags = [filter], + desc = "Remove domain from list of blocked domains", + module = ?MODULE, + function = remove_blocked_domain, + args = [{host, binary}, {domain, binary}], + result = {res, restuple}} + ]. + +for_all_hosts(F, A) -> + try lists:map( + fun(Host) -> + apply(F, [Host | A]) + end, get_spam_filter_hosts()) of + List -> + case lists:filter(fun({error, _}) -> true; (_) -> false end, List) of + [] -> hd(List); + Errors -> hd(Errors) + end + catch error:{badmatch, {error, _Reason} = Error} -> + Error + end. + +try_call_by_host(Host, Call) -> + LServer = jid:nameprep(Host), + Proc = get_proc_name(LServer), + try gen_server:call(Proc, Call, ?COMMAND_TIMEOUT) of + Result -> + Result + catch exit:{noproc, _} -> + {error, "Not configured for " ++ binary_to_list(Host)}; + exit:{timeout, _} -> + {error, "Timeout while querying ejabberd"} + end. + +-spec reload_spam_filter_files(binary()) -> ok | {error, string()}. +reload_spam_filter_files(<<"global">>) -> + for_all_hosts(fun reload_spam_filter_files/1, []); +reload_spam_filter_files(Host) -> + LServer = jid:nameprep(Host), + Files = #{domains => gen_mod:get_module_opt(LServer, ?MODULE, spam_domains_file), + jid => gen_mod:get_module_opt(LServer, ?MODULE, spam_jids_file), + url => gen_mod:get_module_opt(LServer, ?MODULE, spam_urls_file)}, + case try_call_by_host(Host, {reload_files, Files}) of + {spam_filter, ok} -> + ok; + {spam_filter, {error, Txt}} -> + {error, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end. + +-spec get_blocked_domains(binary()) -> [binary()]. +get_blocked_domains(Host) -> + case try_call_by_host(Host, get_blocked_domains) of + {blocked_domains, BlockedDomains} -> + maps:keys(maps:filter(fun(_, false) -> false; (_, _) -> true end, BlockedDomains)); + {error, _R} = Error -> + Error + end. + +-spec add_blocked_domain(binary(), binary()) -> {ok, string()}. +add_blocked_domain(<<"global">>, Domain) -> + for_all_hosts(fun add_blocked_domain/2, [Domain]); +add_blocked_domain(Host, Domain) -> + case try_call_by_host(Host, {add_blocked_domain, Domain}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end. + +-spec remove_blocked_domain(binary(), binary()) -> {ok, string()}. +remove_blocked_domain(<<"global">>, Domain) -> + for_all_hosts(fun remove_blocked_domain/2, [Domain]); +remove_blocked_domain(Host, Domain) -> + case try_call_by_host(Host, {remove_blocked_domain, Domain}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end. + +-spec get_spam_filter_cache(binary()) + -> [{binary(), integer()}] | {error, string()}. +get_spam_filter_cache(Host) -> + case try_call_by_host(Host, get_cache) of + {spam_filter, Cache} -> + [{jid:encode(JID), TS + erlang:time_offset(second)} || + {JID, TS} <- Cache]; + {error, _R} = Error -> + Error + end. + +-spec expire_spam_filter_cache(binary(), integer()) -> {ok | error, string()}. +expire_spam_filter_cache(<<"global">>, Age) -> + for_all_hosts(fun expire_spam_filter_cache/2, [Age]); +expire_spam_filter_cache(Host, Age) -> + case try_call_by_host(Host, {expire_cache, Age}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end. + +-spec add_to_spam_filter_cache(binary(), binary()) -> [{binary(), integer()}] | {error, string()}. +add_to_spam_filter_cache(<<"global">>, JID) -> + for_all_hosts(fun add_to_spam_filter_cache/2, [JID]); +add_to_spam_filter_cache(Host, EncJID) -> + try jid:decode(EncJID) of + #jid{} = JID -> + LJID = jid:remove_resource(jid:tolower(JID)), + case try_call_by_host(Host, {add_to_cache, LJID}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end + catch _:{bad_jid, _} -> + {error, "Not a valid JID: " ++ binary_to_list(EncJID)} + end. + +-spec drop_from_spam_filter_cache(binary(), binary()) -> {ok | error, string()}. +drop_from_spam_filter_cache(<<"global">>, JID) -> + for_all_hosts(fun drop_from_spam_filter_cache/2, [JID]); +drop_from_spam_filter_cache(Host, EncJID) -> + try jid:decode(EncJID) of + #jid{} = JID -> + LJID = jid:remove_resource(jid:tolower(JID)), + case try_call_by_host(Host, {drop_from_cache, LJID}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end + catch _:{bad_jid, _} -> + {error, "Not a valid JID: " ++ binary_to_list(EncJID)} + end. diff --git a/src/mod_antispam_rtbl.erl b/src/mod_antispam_rtbl.erl new file mode 100644 index 000000000..f9994fd08 --- /dev/null +++ b/src/mod_antispam_rtbl.erl @@ -0,0 +1,132 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_antispam_rtbl.erl +%%% Author : Stefan Strigler +%%% Purpose : Collection of RTBL specific functionality +%%% Created : 20 Mar 2025 by Stefan Strigler +%%% +%%% +%%% ejabberd, Copyright (C) 2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(mod_antispam_rtbl). +-author('stefan@strigler.de'). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). + +-define(SERVICE_MODULE, mod_antispam). +-define(SERVICE_JID_PREFIX, "rtbl-"). + +-export([parse_blocked_domains/1, + parse_pubsub_event/1, + pubsub_event_handler/1, + request_blocked_domains/3, + subscribe/3, + unsubscribe/3]). + +subscribe(RTBLHost, RTBLDomainsNode, From) -> + FromJID = service_jid(From), + SubIQ = #iq{type = set, to = jid:make(RTBLHost), from = FromJID, + sub_els = [ + #pubsub{subscribe = #ps_subscribe{jid = FromJID, node = RTBLDomainsNode}}]}, + ?DEBUG("Sending subscription request:~n~p", [xmpp:encode(SubIQ)]), + ejabberd_router:route_iq(SubIQ, subscribe_result, self()). + +-spec unsubscribe(binary() | none, binary(), binary()) -> ok. +unsubscribe(none, _PSNode, _From) -> + ok; +unsubscribe(RTBLHost, RTBLDomainsNode, From) -> + FromJID = jid:make(From), + SubIQ = #iq{type = set, to = jid:make(RTBLHost), from = FromJID, + sub_els = [ + #pubsub{unsubscribe = #ps_unsubscribe{jid = FromJID, node = RTBLDomainsNode}}]}, + ejabberd_router:route_iq(SubIQ, unsubscribe_result, self()). + +-spec request_blocked_domains(binary() | none, binary(), binary()) -> ok. +request_blocked_domains(none, _PSNode, _From) -> + ok; +request_blocked_domains(RTBLHost, RTBLDomainsNode, From) -> + IQ = #iq{type = get, from = jid:make(From), + to = jid:make(RTBLHost), + sub_els = [ + #pubsub{items = #ps_items{node = RTBLDomainsNode}}]}, + ?DEBUG("Requesting RTBL blocked domains from ~s:~n~p", [RTBLHost, xmpp:encode(IQ)]), + ejabberd_router:route_iq(IQ, blocked_domains, self()). + +-spec parse_blocked_domains(stanza()) -> #{binary() => any()} | undefined. +parse_blocked_domains(#iq{to = #jid{lserver = LServer}, type = result} = IQ) -> + ?DEBUG("parsing iq-result items: ~p", [IQ]), + RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), + case xmpp:get_subtag(IQ, #pubsub{}) of + #pubsub{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> + ?DEBUG("Got items:~n~p", [Items]), + parse_items(Items); + _ -> + undefined + end. + +-spec parse_pubsub_event(stanza()) -> #{binary() => any()}. +parse_pubsub_event(#message{to = #jid{lserver = LServer}} = Msg) -> + RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), + case xmpp:get_subtag(Msg, #ps_event{}) of + #ps_event{items = #ps_items{node = RTBLDomainsNode, retract = ID}} when ID /= undefined -> + ?DEBUG("Got item to retract:~n~p", [ID]), + #{ID => false}; + #ps_event{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> + ?DEBUG("Got items:~n~p", [Items]), + parse_items(Items); + Other -> + ?WARNING_MSG("Couldn't extract items: ~p", [Other]), + #{} + end. + +-spec parse_items([ps_item()]) -> #{binary() => any()}. +parse_items(Items) -> + lists:foldl( + fun(#ps_item{id = ID}, Acc) -> + %% TODO extract meta/extra instructions + maps:put(ID, true, Acc) + end, #{}, Items). + +-spec service_jid(binary()) -> jid(). +service_jid(Host) -> + jid:make(<<>>, Host, <>). + +%%-------------------------------------------------------------------- +%% Hook callbacks. +%%-------------------------------------------------------------------- + +-spec pubsub_event_handler(stanza()) -> drop | stanza(). +pubsub_event_handler(#message{from = FromJid, + to = #jid{lserver = LServer, + lresource = <>}} = Msg) -> + ?DEBUG("Got RTBL message:~n~p", [Msg]), + From = jid:encode(FromJid), + case gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_host) of + From -> + ParsedItems = parse_pubsub_event(Msg), + Proc = gen_mod:get_module_proc(LServer, ?SERVICE_MODULE), + gen_server:cast(Proc, {update_blocked_domains, ParsedItems}), + %% FIXME what's the difference between `{drop, ...}` and `{stop, {drop, ...}}`? + drop; + _Other -> + ?INFO_MSG("Got unexpected message from ~s to rtbl resource:~n~p", [From, Msg]), + Msg + end; +pubsub_event_handler(Acc) -> + ?DEBUG("unexpected something on pubsub_event_handler: ~p", [Acc]), + Acc. From 70bec7b714351cdb643811907f86a7fd1f76106a Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Thu, 22 May 2025 18:27:13 +0200 Subject: [PATCH 1153/1302] tests: update readme and compose to work with current sw versions --- test/docker/README.md | 8 ++++---- test/docker/docker-compose.yml | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test/docker/README.md b/test/docker/README.md index 93b1a3504..7caaa4bb7 100644 --- a/test/docker/README.md +++ b/test/docker/README.md @@ -10,7 +10,7 @@ attached to it. ``` mkdir test/docker/db/mysql/data mkdir test/docker/db/postgres/data -(cd test/docker; docker-compose up) +(cd test/docker; docker compose up) ``` You can stop all the databases with CTRL-C. @@ -20,8 +20,8 @@ You can stop all the databases with CTRL-C. The following commands will create the necessary login, user and database, will grant rights on the database in MSSQL and create the ejabberd schema: ``` -docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql -docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql +docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql -C +docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test -i /mssql.sql -C ``` ## Running tests @@ -44,7 +44,7 @@ make test You can fully clean up the environment with: ``` -(cd test/docker; docker-compose down) +(cd test/docker; docker compose down) ``` If you want to clean the data, you can remove the data volumes after the `docker-compose down` command: diff --git a/test/docker/docker-compose.yml b/test/docker/docker-compose.yml index bf99ae04e..bdc470a63 100644 --- a/test/docker/docker-compose.yml +++ b/test/docker/docker-compose.yml @@ -7,7 +7,6 @@ services: volumes: - mysqldata:/var/lib/mysql - ../../sql/mysql.sql:/docker-entrypoint-initdb.d/mysql.sql:ro - command: --default-authentication-plugin=mysql_native_password restart: always ports: - 3306:3306 From c48aa38c39e909eaaeb790e1fec50463aaf857d3 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Thu, 22 May 2025 18:26:12 +0200 Subject: [PATCH 1154/1302] mod_antispam: add test suite --- test/antispam_tests.erl | 152 +++++++++++++++++++ test/ejabberd_SUITE.erl | 5 + test/ejabberd_SUITE_data/ejabberd.mnesia.yml | 5 + test/ejabberd_SUITE_data/ejabberd.redis.yml | 5 + test/ejabberd_SUITE_data/spam_domains.txt | 1 + test/ejabberd_SUITE_data/spam_jids.txt | 1 + test/ejabberd_SUITE_data/spam_urls.txt | 1 + test/suite.erl | 23 +++ test/suite.hrl | 2 + 9 files changed, 195 insertions(+) create mode 100644 test/antispam_tests.erl create mode 100644 test/ejabberd_SUITE_data/spam_domains.txt create mode 100644 test/ejabberd_SUITE_data/spam_jids.txt create mode 100644 test/ejabberd_SUITE_data/spam_urls.txt diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl new file mode 100644 index 000000000..d7ce196c0 --- /dev/null +++ b/test/antispam_tests.erl @@ -0,0 +1,152 @@ +%%%------------------------------------------------------------------- +%%% Author : Stefan Strigler +%%% Created : 8 May 2025 by Stefan Strigler +%%% +%%% +%%% ejabberd, Copyright (C) 2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- +-module(antispam_tests). + +-compile(export_all). + +-import(suite, [recv_presence/1, send_recv/2, my_jid/1, muc_room_jid/1, + send/2, recv_message/1, recv_iq/1, muc_jid/1, + alt_room_jid/1, wait_for_slave/1, wait_for_master/1, + disconnect/1, put_event/2, get_event/1, peer_muc_jid/1, + my_muc_jid/1, get_features/2, set_opt/3]). +-include("suite.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +%%%=================================================================== +%%% Single tests +%%%=================================================================== +single_cases() -> + {antispam_single, [sequence], + [single_test(spam_files), + single_test(blocked_domains), + single_test(jid_cache), + single_test(rtbl_domains)]}. + +spam_files(Config) -> + Host = ?config(server, Config), + To = my_jid(Config), + + SpamJID = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), + SpamJIDMsg = #message{from = SpamJID, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + is_spam(SpamJIDMsg), + + Spammer = jid:make(<<"spammer">>, <<"localhost">>, <<"spam_client">>), + NoSpamMsg = #message{from = Spammer, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + is_not_spam(NoSpamMsg), + SpamMsg = #message{from = Spammer, to = To, type = chat, body = [#text{data = <<"hello world\nhttps://spam.domain.url">>}]}, + is_spam(SpamMsg), + %% now check this mischief is in jid_cache + is_spam(NoSpamMsg), + mod_antispam:drop_from_spam_filter_cache(Host, jid:to_string(Spammer)), + is_not_spam(NoSpamMsg), + + ?retry(100, 10, + ?match(true, (has_spam_domain(<<"spam_domain.org">>))(Host))), + + SpamDomain = jid:make(<<"spammer">>, <<"spam_domain.org">>, <<"spam_client">>), + SpamDomainMsg = #message{from = SpamDomain, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + is_spam(SpamDomainMsg), + ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam_domain.org">>)), + ?match([], mod_antispam:get_blocked_domains(Host)), + is_not_spam(SpamDomainMsg), + disconnect(Config). + +blocked_domains(Config) -> + Host = ?config(server, Config), + ?match([], mod_antispam:get_blocked_domains(Host)), + SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), + To = my_jid(Config), + Msg = #message{from = SpamFrom, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + is_not_spam(Msg), + ?match({ok, _}, mod_antispam:add_blocked_domain(<<"global">>, <<"spam.domain">>)), + is_spam(Msg), + Vhosts = [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, mod_antispam)], + NumVhosts = length(Vhosts), + ?match(NumVhosts, length(lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts))), + ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam.domain">>)), + ?match([], mod_antispam:get_blocked_domains(Host)), + is_not_spam(Msg), + ?match(NumVhosts, length(lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)) + 1), + ?match({ok, _}, mod_antispam:remove_blocked_domain(<<"global">>, <<"spam.domain">>)), + ?match([], lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)), + ?match({ok, _}, mod_antispam:add_blocked_domain(Host, <<"spam.domain">>)), + ?match([Host], lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)), + is_spam(Msg), + ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam.domain">>)), + is_not_spam(Msg), + disconnect(Config). + +jid_cache(Config) -> + Host = ?config(server, Config), + SpamFrom = jid:make(<<"spammer">>, Host, <<"spam_client">>), + To = my_jid(Config), + Msg = #message{from = SpamFrom, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + is_not_spam(Msg), + mod_antispam:add_to_spam_filter_cache(Host, jid:to_string(SpamFrom)), + is_spam(Msg), + mod_antispam:drop_from_spam_filter_cache(Host, jid:to_string(SpamFrom)), + is_not_spam(Msg), + disconnect(Config). + +rtbl_domains(Config) -> + Host = ?config(server, Config), + RTBLHost = jid:to_string(suite:pubsub_jid(Config)), + RTBLDomainsNode = <<"spam_source_domains">>, + OldOpts = gen_mod:get_module_opts(Host, mod_antispam), + NewOpts = maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), + Owner = jid:make(?config(user, Config), ?config(server, Config), <<>>), + {result, _} = mod_pubsub:create_node(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"flat">>), + {result, _} = mod_pubsub:publish_item(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"spam.source.domain">>, + [xmpp:encode(#ps_item{id = <<"spam.source.domain">>, sub_els = []})]), + mod_antispam:reload(Host, OldOpts, NewOpts), + ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam_domain.org">>)), + ?retry(100, 10, + ?match([<<"spam.source.domain">>], mod_antispam:get_blocked_domains(Host))), + {result, _} = mod_pubsub:publish_item(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"spam.source.another">>, + [xmpp:encode(#ps_item{id = <<"spam.source.another">>, sub_els = []})]), + ?retry(100, 10, + ?match(true, (has_spam_domain(<<"spam.source.another">>))(Host))), + {result, _} = mod_pubsub:delete_item(RTBLHost, RTBLDomainsNode, Owner, <<"spam.source.another">>, true), + ?retry(100, 10, + ?match(false, (has_spam_domain(<<"spam.source.another">>))(Host))), + {result, _} = mod_pubsub:delete_node(RTBLHost, RTBLDomainsNode, Owner), + disconnect(Config). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +single_test(T) -> + list_to_atom("antispam_" ++ atom_to_list(T)). + +has_spam_domain(Domain) -> + fun(Host) -> + lists:member(Domain, mod_antispam:get_blocked_domains(Host)) + end. + +is_not_spam(Msg) -> + ?match({Msg, undefined}, mod_antispam:s2s_receive_packet({Msg, undefined})). + +is_spam(Spam) -> + ?match({stop, {drop, undefined}}, mod_antispam:s2s_receive_packet({Spam, undefined})). diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index b0e47cde6..4b2a7fccc 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -339,6 +339,10 @@ init_per_testcase(TestCase, OrigConfig) -> bind(auth(connect(Config))); "replaced" ++ _ -> auth(connect(Config)); + "antispam" ++ _ -> + Password = ?config(password, Config), + ejabberd_auth:try_register(User, Server, Password), + open_session(bind(auth(connect(Config)))); _ when IsMaster or IsSlave -> Password = ?config(password, Config), ejabberd_auth:try_register(User, Server, Password), @@ -425,6 +429,7 @@ db_tests(DB) when DB == mnesia; DB == redis -> auth_md5, presence_broadcast, last, + antispam_tests:single_cases(), webadmin_tests:single_cases(), roster_tests:single_cases(), private_tests:single_cases(), diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml index 42ab23ab2..c8585e6e4 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml @@ -6,6 +6,11 @@ define_macro: mod_announce: db_type: internal access: local + mod_antispam: + rtbl_host: pubsub.mnesia.localhost + spam_jids_file: spam_jids.txt + spam_domains_file: spam_domains.txt + spam_urls_file: spam_urls.txt mod_blocking: [] mod_caps: db_type: internal diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml index 6ff7d7cdb..cdbc905bd 100644 --- a/test/ejabberd_SUITE_data/ejabberd.redis.yml +++ b/test/ejabberd_SUITE_data/ejabberd.redis.yml @@ -7,6 +7,11 @@ define_macro: mod_announce: db_type: internal access: local + mod_antispam: + rtbl_host: pubsub.redis.localhost + spam_jids_file: spam_jids.txt + spam_domains_file: spam_domains.txt + spam_urls_file: spam_urls.txt mod_blocking: [] mod_caps: db_type: internal diff --git a/test/ejabberd_SUITE_data/spam_domains.txt b/test/ejabberd_SUITE_data/spam_domains.txt new file mode 100644 index 000000000..c081998b7 --- /dev/null +++ b/test/ejabberd_SUITE_data/spam_domains.txt @@ -0,0 +1 @@ +spam_domain.org diff --git a/test/ejabberd_SUITE_data/spam_jids.txt b/test/ejabberd_SUITE_data/spam_jids.txt new file mode 100644 index 000000000..2b911fd82 --- /dev/null +++ b/test/ejabberd_SUITE_data/spam_jids.txt @@ -0,0 +1 @@ +spammer_jid@localhost diff --git a/test/ejabberd_SUITE_data/spam_urls.txt b/test/ejabberd_SUITE_data/spam_urls.txt new file mode 100644 index 000000000..857172d46 --- /dev/null +++ b/test/ejabberd_SUITE_data/spam_urls.txt @@ -0,0 +1 @@ +https://spam.domain.url diff --git a/test/suite.erl b/test/suite.erl index 28825b1d1..62b442580 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -51,6 +51,9 @@ init_config(Config) -> {ok, _} = file:copy(SelfSignedCertFile, filename:join([CWD, "self-signed-cert.pem"])), {ok, _} = file:copy(CAFile, filename:join([CWD, "ca.pem"])), + copy_file(Config, "spam_jids.txt"), + copy_file(Config, "spam_urls.txt"), + copy_file(Config, "spam_domains.txt"), {ok, MacrosContentTpl} = file:read_file(MacrosPathTpl), Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>, Backends = get_config_backends(), @@ -138,6 +141,11 @@ init_config(Config) -> {backends, Backends} |Config]. +copy_file(Config, File) -> + {ok, CWD} = file:get_cwd(), + DataDir = proplists:get_value(data_dir, Config), + {ok, _} = file:copy(filename:join([DataDir, File]), filename:join([CWD, File])). + copy_configtest_yml(DataDir, CWD) -> Files = filelib:wildcard(filename:join([DataDir, "configtest.yml"])), lists:foreach( @@ -906,6 +914,21 @@ receiver(NS, Owner, Socket, MRef) -> receiver(NS, Owner, Socket, MRef) end. +%% @doc Retry an action until success, at max N times with an interval +%% `Interval' +%% Shamlessly stolen (with slight adaptations) from snabbkaffee. +-spec retry(integer(), non_neg_integer(), fun(() -> Ret)) -> Ret. +retry(_, 0, Fun) -> + Fun(); +retry(Interval, N, Fun) -> + try Fun() + catch + EC:Err -> + timer:sleep(Interval), + ct:pal("retrying ~p more times, result was ~p:~p", [N, EC, Err]), + retry(Interval, N - 1, Fun) + end. + %%%=================================================================== %%% Clients puts and gets events via this relay. %%%=================================================================== diff --git a/test/suite.hrl b/test/suite.hrl index 08bb87df1..00b341cb1 100644 --- a/test/suite.hrl +++ b/test/suite.hrl @@ -89,6 +89,8 @@ -define(send_recv(Send, Recv), ?match(Recv, suite:send_recv(Config, Send))). +-define(retry(TIMEOUT, N, FUN), suite:retry(TIMEOUT, N, fun() -> FUN end)). + -define(COMMON_VHOST, <<"localhost">>). -define(MNESIA_VHOST, <<"mnesia.localhost">>). -define(REDIS_VHOST, <<"redis.localhost">>). From 639147be416ed5cf85043551155b4fa19ddec144 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Fri, 23 May 2025 17:16:37 +0200 Subject: [PATCH 1155/1302] fix pubsub retract items being a list of ids --- src/mod_antispam_rtbl.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_antispam_rtbl.erl b/src/mod_antispam_rtbl.erl index f9994fd08..d977aa0c6 100644 --- a/src/mod_antispam_rtbl.erl +++ b/src/mod_antispam_rtbl.erl @@ -83,12 +83,8 @@ parse_blocked_domains(#iq{to = #jid{lserver = LServer}, type = result} = IQ) -> parse_pubsub_event(#message{to = #jid{lserver = LServer}} = Msg) -> RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), case xmpp:get_subtag(Msg, #ps_event{}) of - #ps_event{items = #ps_items{node = RTBLDomainsNode, retract = ID}} when ID /= undefined -> - ?DEBUG("Got item to retract:~n~p", [ID]), - #{ID => false}; - #ps_event{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> - ?DEBUG("Got items:~n~p", [Items]), - parse_items(Items); + #ps_event{items = #ps_items{node = RTBLDomainsNode, items = Items, retract = RetractIds}} -> + maps:merge(retract_items(RetractIds), parse_items(Items)); Other -> ?WARNING_MSG("Couldn't extract items: ~p", [Other]), #{} @@ -102,6 +98,10 @@ parse_items(Items) -> maps:put(ID, true, Acc) end, #{}, Items). +-spec retract_items([binary()]) -> #{binary() => false}. +retract_items(Ids) -> + lists:foldl(fun(ID, Acc) -> Acc#{ID => false} end, #{}, Ids). + -spec service_jid(binary()) -> jid(). service_jid(Host) -> jid:make(<<>>, Host, <>). From 34b40aec663b4d08b5a80da7dd8fc84c4e691fdc Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Thu, 5 Jun 2025 14:31:02 +0200 Subject: [PATCH 1156/1302] mod_antispam: add format instructions --- src/mod_antispam.erl | 1007 ++++++++++++++++++++----------------- src/mod_antispam_rtbl.erl | 85 ++-- test/antispam_tests.erl | 102 ++-- 3 files changed, 675 insertions(+), 519 deletions(-) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 986f21066..c3489aefa 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -98,6 +98,8 @@ -define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). -define(DEFAULT_CACHE_SIZE, 10000). +%% @format-begin + %%-------------------------------------------------------------------- %% gen_mod callbacks. %%-------------------------------------------------------------------- @@ -114,10 +116,10 @@ start(Host, Opts) -> -spec stop(binary()) -> ok | {error, any()}. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_commands:unregister_commands(get_commands_spec()); - true -> - ok + false -> + ejabberd_commands:unregister_commands(get_commands_spec()); + true -> + ok end, gen_mod:stop_child(?MODULE, Host). @@ -134,34 +136,28 @@ depends(_Host, _Opts) -> -spec mod_opt_type(atom()) -> econf:validator(). mod_opt_type(spam_domains_file) -> econf:either( - econf:enum([none]), - econf:file()); + econf:enum([none]), econf:file()); mod_opt_type(whitelist_domains_file) -> - econf:either( - none, - econf:binary()); + econf:either(none, econf:binary()); mod_opt_type(spam_dump_file) -> econf:either( - econf:enum([none]), - econf:binary()); + econf:enum([none]), econf:binary()); mod_opt_type(spam_jids_file) -> econf:either( - econf:enum([none]), - econf:file()); + econf:enum([none]), econf:file()); mod_opt_type(spam_urls_file) -> econf:either( - econf:enum([none]), - econf:file()); + econf:enum([none]), econf:file()); mod_opt_type(access_spam) -> econf:acl(); mod_opt_type(cache_size) -> econf:pos_int(unlimited); mod_opt_type(rtbl_host) -> econf:either( - econf:enum([none]), - econf:host()); + econf:enum([none]), econf:host()); mod_opt_type(rtbl_domains_node) -> - econf:non_empty(econf:binary()). + econf:non_empty( + econf:binary()). -spec mod_options(binary()) -> [{atom(), any()}]. mod_options(_Host) -> @@ -175,7 +171,8 @@ mod_options(_Host) -> {rtbl_host, none}, {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}]. -mod_doc() -> #{}. +mod_doc() -> + #{}. %%-------------------------------------------------------------------- %% gen_server callbacks. @@ -184,67 +181,73 @@ mod_doc() -> #{}. init([Host, Opts]) -> process_flag(trap_exit, true), DumpFile = expand_host(gen_mod:get_opt(spam_dump_file, Opts), Host), - Files = #{domains => gen_mod:get_opt(spam_domains_file, Opts), - jid => gen_mod:get_opt(spam_jids_file, Opts), - url => gen_mod:get_opt(spam_urls_file, Opts), - whitelist_domains => gen_mod:get_opt(whitelist_domains_file, Opts)}, + Files = + #{domains => gen_mod:get_opt(spam_domains_file, Opts), + jid => gen_mod:get_opt(spam_jids_file, Opts), + url => gen_mod:get_opt(spam_urls_file, Opts), + whitelist_domains => gen_mod:get_opt(whitelist_domains_file, Opts)}, try read_files(Files) of - #{jid := JIDsSet, url := URLsSet, domains := SpamDomainsSet, whitelist_domains := WhitelistDomains} -> - ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, - s2s_in_handle_info, 90), - ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, - s2s_receive_packet, 50), - ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, - sm_receive_packet, 50), - ejabberd_hooks:add(reopen_log_hook, ?MODULE, - reopen_log, 50), - ejabberd_hooks:add(local_send_to_resource_hook, Host, - mod_antispam_rtbl, pubsub_event_handler, 50), - RTBLHost = gen_mod:get_opt(rtbl_host, Opts), - RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), - InitState0 = #state{host = Host, - jid_set = JIDsSet, - url_set = URLsSet, - max_cache_size = gen_mod:get_opt(cache_size, Opts), - blocked_domains = set_to_map(SpamDomainsSet), - whitelist_domains = set_to_map(WhitelistDomains, false), - rtbl_host = RTBLHost, - rtbl_domains_node = RTBLDomainsNode}, - mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), - InitState = init_open_dump_file(DumpFile, InitState0), - {ok, InitState} - catch {Op, File, Reason} when Op == open; - Op == read -> - ?CRITICAL_MSG("Cannot ~s ~s: ~s", [Op, File, format_error(Reason)]), - {stop, config_error} + #{jid := JIDsSet, + url := URLsSet, + domains := SpamDomainsSet, + whitelist_domains := WhitelistDomains} -> + ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), + ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), + ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), + ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 50), + ejabberd_hooks:add(local_send_to_resource_hook, + Host, + mod_antispam_rtbl, + pubsub_event_handler, + 50), + RTBLHost = gen_mod:get_opt(rtbl_host, Opts), + RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), + InitState0 = + #state{host = Host, + jid_set = JIDsSet, + url_set = URLsSet, + max_cache_size = gen_mod:get_opt(cache_size, Opts), + blocked_domains = set_to_map(SpamDomainsSet), + whitelist_domains = set_to_map(WhitelistDomains, false), + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode}, + mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), + InitState = init_open_dump_file(DumpFile, InitState0), + {ok, InitState} + catch + {Op, File, Reason} when Op == open; Op == read -> + ?CRITICAL_MSG("Cannot ~s ~s: ~s", [Op, File, format_error(Reason)]), + {stop, config_error} end. init_open_dump_file(none, State) -> State; init_open_dump_file(DumpFile, State) -> case filelib:ensure_dir(DumpFile) of - ok -> - ok; - {error, Reason} -> - Dirname = filename:dirname(DumpFile), - throw({open, Dirname, Reason}) + ok -> + ok; + {error, Reason} -> + Dirname = filename:dirname(DumpFile), + throw({open, Dirname, Reason}) end, open_dump_file(DumpFile, State). --spec handle_call(term(), {pid(), term()}, state()) - -> {reply, {spam_filter, term()}, state()} | {noreply, state()}. +-spec handle_call(term(), {pid(), term()}, state()) -> + {reply, {spam_filter, term()}, state()} | {noreply, state()}. handle_call({check_jid, From}, _From, #state{jid_set = JIDsSet} = State) -> {Result, State1} = filter_jid(From, JIDsSet, State), {reply, {spam_filter, Result}, State1}; -handle_call({check_body, URLs, JIDs, From}, _From, - #state{url_set = URLsSet, jid_set = JIDsSet} = State) -> +handle_call({check_body, URLs, JIDs, From}, + _From, + #state{url_set = URLsSet, jid_set = JIDsSet} = State) -> {Result1, State1} = filter_body(URLs, URLsSet, From, State), {Result2, State2} = filter_body(JIDs, JIDsSet, From, State1), - Result = if Result1 == spam -> - Result1; - true -> - Result2 - end, + Result = + if Result1 == spam -> + Result1; + true -> + Result2 + end, {reply, {spam_filter, Result}, State2}; handle_call({resolve_redirects, URLs}, _From, State) -> ResolvedURLs = do_resolve_redirects(URLs, []), @@ -263,18 +266,30 @@ handle_call({drop_from_cache, JID}, _From, State) -> {reply, {spam_filter, Result}, State1}; handle_call(get_cache, _From, #state{jid_cache = Cache} = State) -> {reply, {spam_filter, maps:to_list(Cache)}, State}; -handle_call({add_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains} = State) -> +handle_call({add_blocked_domain, Domain}, + _From, + #state{blocked_domains = BlockedDomains} = State) -> BlockedDomains1 = maps:merge(BlockedDomains, #{Domain => true}), Txt = format("~s added to blocked domains", [Domain]), {reply, {spam_filter, {ok, Txt}}, State#state{blocked_domains = BlockedDomains1}}; -handle_call({remove_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains} = State) -> +handle_call({remove_blocked_domain, Domain}, + _From, + #state{blocked_domains = BlockedDomains} = State) -> BlockedDomains1 = maps:remove(Domain, BlockedDomains), Txt = format("~s removed from blocked domains", [Domain]), {reply, {spam_filter, {ok, Txt}}, State#state{blocked_domains = BlockedDomains1}}; -handle_call(get_blocked_domains, _From, #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = State) -> +handle_call(get_blocked_domains, + _From, + #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = + State) -> {reply, {blocked_domains, maps:merge(BlockedDomains, WhitelistDomains)}, State}; -handle_call({is_blocked_domain, Domain}, _From, #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = State) -> - {reply, maps:get(Domain, maps:merge(BlockedDomains, WhitelistDomains), false) =/= false, State}; +handle_call({is_blocked_domain, Domain}, + _From, + #state{blocked_domains = BlockedDomains, whitelist_domains = WhitelistDomains} = + State) -> + {reply, + maps:get(Domain, maps:merge(BlockedDomains, WhitelistDomains), false) =/= false, + State}; handle_call(Request, From, State) -> ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), {noreply, State}. @@ -284,41 +299,43 @@ handle_cast({dump, _XML}, #state{dump_fd = undefined} = State) -> {noreply, State}; handle_cast({dump, XML}, #state{dump_fd = Fd} = State) -> case file:write(Fd, [XML, <<$\n>>]) of - ok -> - ok; - {error, Reason} -> - ?ERROR_MSG("Cannot write spam to dump file: ~s", - [file:format_error(Reason)]) + ok -> + ok; + {error, Reason} -> + ?ERROR_MSG("Cannot write spam to dump file: ~s", [file:format_error(Reason)]) end, {noreply, State}; handle_cast({reload, NewOpts, OldOpts}, - #state{host = Host, - rtbl_host = OldRTBLHost, - rtbl_domains_node = OldRTBLDomainsNode, - rtbl_retry_timer = RTBLRetryTimer} = State) -> + #state{host = Host, + rtbl_host = OldRTBLHost, + rtbl_domains_node = OldRTBLDomainsNode, + rtbl_retry_timer = RTBLRetryTimer} = + State) -> misc:cancel_timer(RTBLRetryTimer), - State1 = case {gen_mod:get_opt(spam_dump_file, OldOpts), - gen_mod:get_opt(spam_dump_file, NewOpts)} of - {OldDumpFile, NewDumpFile} when NewDumpFile /= OldDumpFile -> - close_dump_file(expand_host(OldDumpFile, Host), State), - open_dump_file(expand_host(NewDumpFile, Host), State); - {_OldDumpFile, _NewDumpFile} -> - State - end, - State2 = case {gen_mod:get_opt(cache_size, OldOpts), - gen_mod:get_opt(cache_size, NewOpts)} of - {OldMax, NewMax} when NewMax < OldMax -> - shrink_cache(State1#state{max_cache_size = NewMax}); - {OldMax, NewMax} when NewMax > OldMax -> - State1#state{max_cache_size = NewMax}; - {_OldMax, _NewMax} -> - State1 - end, + State1 = + case {gen_mod:get_opt(spam_dump_file, OldOpts), gen_mod:get_opt(spam_dump_file, NewOpts)} + of + {OldDumpFile, NewDumpFile} when NewDumpFile /= OldDumpFile -> + close_dump_file(expand_host(OldDumpFile, Host), State), + open_dump_file(expand_host(NewDumpFile, Host), State); + {_OldDumpFile, _NewDumpFile} -> + State + end, + State2 = + case {gen_mod:get_opt(cache_size, OldOpts), gen_mod:get_opt(cache_size, NewOpts)} of + {OldMax, NewMax} when NewMax < OldMax -> + shrink_cache(State1#state{max_cache_size = NewMax}); + {OldMax, NewMax} when NewMax > OldMax -> + State1#state{max_cache_size = NewMax}; + {_OldMax, _NewMax} -> + State1 + end, ok = mod_antispam_rtbl:unsubscribe(OldRTBLHost, OldRTBLDomainsNode, Host), - Files = #{domains => gen_mod:get_opt(spam_domains_file, NewOpts), - jid => gen_mod:get_opt(spam_jids_file, NewOpts), - url => gen_mod:get_opt(spam_urls_file, NewOpts), - whitelist_domains => gen_mod:get_opt(whitelist_domains_file, NewOpts)}, + Files = + #{domains => gen_mod:get_opt(spam_domains_file, NewOpts), + jid => gen_mod:get_opt(spam_jids_file, NewOpts), + url => gen_mod:get_opt(spam_urls_file, NewOpts), + whitelist_domains => gen_mod:get_opt(whitelist_domains_file, NewOpts)}, {_Result, State3} = reload_files(Files, State2#state{blocked_domains = #{}}), RTBLHost = gen_mod:get_opt(rtbl_host, NewOpts), RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, NewOpts), @@ -326,7 +343,8 @@ handle_cast({reload, NewOpts, OldOpts}, {noreply, State3#state{rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode}}; handle_cast(reopen_log, State) -> {noreply, reopen_dump_file(State)}; -handle_cast({update_blocked_domains, NewItems}, #state{blocked_domains = BlockedDomains} = State) -> +handle_cast({update_blocked_domains, NewItems}, + #state{blocked_domains = BlockedDomains} = State) -> {noreply, State#state{blocked_domains = maps:merge(BlockedDomains, NewItems)}}; handle_cast(Request, State) -> ?ERROR_MSG("Got unexpected request from: ~p", [Request]), @@ -334,32 +352,42 @@ handle_cast(Request, State) -> -spec handle_info(term(), state()) -> {noreply, state()}. handle_info({iq_reply, timeout, blocked_domains}, State) -> - ?WARNING_MSG("Fetching blocked domains failed: fetch timeout. Retrying in 60 seconds", []), - {noreply, State#state{rtbl_retry_timer = erlang:send_after(60000, self(), request_blocked_domains)}}; + ?WARNING_MSG("Fetching blocked domains failed: fetch timeout. Retrying in 60 seconds", + []), + {noreply, + State#state{rtbl_retry_timer = + erlang:send_after(60000, self(), request_blocked_domains)}}; handle_info({iq_reply, #iq{type = error} = IQ, blocked_domains}, State) -> ?WARNING_MSG("Fetching blocked domains failed: ~p. Retrying in 60 seconds", - [xmpp:format_stanza_error(xmpp:get_error(IQ))]), - {noreply, State#state{rtbl_retry_timer = erlang:send_after(60000, self(), request_blocked_domains)}}; + [xmpp:format_stanza_error( + xmpp:get_error(IQ))]), + {noreply, + State#state{rtbl_retry_timer = + erlang:send_after(60000, self(), request_blocked_domains)}}; handle_info({iq_reply, IQReply, blocked_domains}, - #state{blocked_domains = OldBlockedDomains, - rtbl_host = RTBLHost, - rtbl_domains_node = RTBLDomainsNode, - host = Host} = State) -> + #state{blocked_domains = OldBlockedDomains, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode, + host = Host} = + State) -> case mod_antispam_rtbl:parse_blocked_domains(IQReply) of - undefined -> - ?WARNING_MSG("Fetching initial list failed: invalid result payload", []), - {noreply, State#state{rtbl_retry_timer = undefined}}; - NewBlockedDomains -> - ok = mod_antispam_rtbl:subscribe(RTBLHost, RTBLDomainsNode, Host), - {noreply, State#state{rtbl_retry_timer = undefined, - rtbl_subscribed = true, - blocked_domains = maps:merge(OldBlockedDomains, NewBlockedDomains)}} + undefined -> + ?WARNING_MSG("Fetching initial list failed: invalid result payload", []), + {noreply, State#state{rtbl_retry_timer = undefined}}; + NewBlockedDomains -> + ok = mod_antispam_rtbl:subscribe(RTBLHost, RTBLDomainsNode, Host), + {noreply, + State#state{rtbl_retry_timer = undefined, + rtbl_subscribed = true, + blocked_domains = maps:merge(OldBlockedDomains, NewBlockedDomains)}} end; handle_info({iq_reply, timeout, subscribe_result}, State) -> ?WARNING_MSG("Subscription error: request timeout", []), {noreply, State#state{rtbl_subscribed = false}}; handle_info({iq_reply, #iq{type = error} = IQ, subscribe_result}, State) -> - ?WARNING_MSG("Subscription error: ~p", [xmpp:format_stanza_error(xmpp:get_error(IQ))]), + ?WARNING_MSG("Subscription error: ~p", + [xmpp:format_stanza_error( + xmpp:get_error(IQ))]), {noreply, State#state{rtbl_subscribed = false}}; handle_info({iq_reply, IQReply, subscribe_result}, State) -> ?DEBUG("Got subscribe result: ~p", [IQReply]), @@ -368,7 +396,11 @@ handle_info({iq_reply, _IQReply, unsubscribe_result}, State) -> %% FIXME: we should check it's true (of type `result`, not `error`), but at that point, what %% would we do? {noreply, State#state{rtbl_subscribed = false}}; -handle_info(request_blocked_domains, #state{host = Host, rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode} = State) -> +handle_info(request_blocked_domains, + #state{host = Host, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode} = + State) -> mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), {noreply, State}; handle_info(Info, State) -> @@ -377,30 +409,30 @@ handle_info(Info, State) -> -spec terminate(normal | shutdown | {shutdown, term()} | term(), state()) -> ok. terminate(Reason, - #state{host = Host, - rtbl_host = RTBLHost, - rtbl_domains_node = RTBLDomainsNode, - rtbl_retry_timer = RTBLRetryTimer} = State) -> + #state{host = Host, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode, + rtbl_retry_timer = RTBLRetryTimer} = + State) -> ?DEBUG("Stopping spam filter process for ~s: ~p", [Host, Reason]), misc:cancel_timer(RTBLRetryTimer), DumpFile = gen_mod:get_module_opt(Host, ?MODULE, spam_dump_file), DumpFile1 = expand_host(DumpFile, Host), close_dump_file(DumpFile1, State), - ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, - s2s_receive_packet, 50), - ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, - sm_receive_packet, 50), - ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, - s2s_in_handle_info, 90), - ejabberd_hooks:delete(local_send_to_resource_hook, Host, - mod_antispam_rtbl, pubsub_event_handler, 50), - mod_antispam_rtbl:unsubscribe(RTBLHost, RTBLDomainsNode,Host), + ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), + ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), + ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), + ejabberd_hooks:delete(local_send_to_resource_hook, + Host, + mod_antispam_rtbl, + pubsub_event_handler, + 50), + mod_antispam_rtbl:unsubscribe(RTBLHost, RTBLDomainsNode, Host), case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_hooks:delete(reopen_log_hook, ?MODULE, - reopen_log, 50); - true -> - ok + false -> + ejabberd_hooks:delete(reopen_log_hook, ?MODULE, reopen_log, 50); + true -> + ok end. -spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}. @@ -412,8 +444,8 @@ code_change(_OldVsn, #state{host = Host} = State, _Extra) -> %% Hook callbacks. %%-------------------------------------------------------------------- --spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) - -> {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. +-spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) -> + {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. s2s_receive_packet({A, State}) -> case sm_receive_packet(A) of {stop, drop} -> @@ -426,36 +458,37 @@ s2s_receive_packet({A, State}) -> sm_receive_packet(drop = Acc) -> Acc; sm_receive_packet(#message{from = From, - to = #jid{lserver = LServer} = To, - type = Type} = Msg) - when Type /= groupchat, - Type /= error -> + to = #jid{lserver = LServer} = To, + type = Type} = + Msg) + when Type /= groupchat, Type /= error -> do_check(From, To, LServer, Msg); sm_receive_packet(#presence{from = From, - to = #jid{lserver = LServer} = To, - type = subscribe} = Presence) -> + to = #jid{lserver = LServer} = To, + type = subscribe} = + Presence) -> do_check(From, To, LServer, Presence); sm_receive_packet(Acc) -> Acc. do_check(From, To, LServer, Stanza) -> case needs_checking(From, To) of - true -> - case check_from(LServer, From) of - ham -> - case check_stanza(LServer, From, Stanza) of - ham -> - Stanza; - spam -> - reject(Stanza), - {stop, drop} - end; - spam -> - reject(Stanza), - {stop, drop} - end; - false -> - Stanza + true -> + case check_from(LServer, From) of + ham -> + case check_stanza(LServer, From, Stanza) of + ham -> + Stanza; + spam -> + reject(Stanza), + {stop, drop} + end; + spam -> + reject(Stanza), + {stop, drop} + end; + false -> + Stanza end. check_stanza(LServer, From, #message{body = Body}) -> @@ -463,8 +496,8 @@ check_stanza(LServer, From, #message{body = Body}) -> check_stanza(_, _, _) -> ham. --spec s2s_in_handle_info(s2s_in_state(), any()) - -> s2s_in_state() | {stop, s2s_in_state()}. +-spec s2s_in_handle_info(s2s_in_state(), any()) -> + s2s_in_state() | {stop, s2s_in_state()}. s2s_in_handle_info(State, {_Ref, {spam_filter, _}}) -> ?DEBUG("Dropping expired spam filter result", []), {stop, State}; @@ -474,9 +507,10 @@ s2s_in_handle_info(State, _) -> -spec reopen_log() -> ok. reopen_log() -> lists:foreach(fun(Host) -> - Proc = get_proc_name(Host), - gen_server:cast(Proc, reopen_log) - end, get_spam_filter_hosts()). + Proc = get_proc_name(Host), + gen_server:cast(Proc, reopen_log) + end, + get_spam_filter_hosts()). %%-------------------------------------------------------------------- %% Internal functions. @@ -484,60 +518,70 @@ reopen_log() -> -spec needs_checking(jid(), jid()) -> boolean(). needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> case gen_mod:is_loaded(LServer, ?MODULE) of - true -> - Access = gen_mod:get_module_opt(LServer, ?MODULE, access_spam), - case acl:match_rule(LServer, Access, To) of - allow -> - ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), - false; - deny -> - ?DEBUG("Spam is filtered for ~s", [jid:encode(To)]), - not mod_roster:is_subscribed(From, To) andalso - not mod_roster:is_subscribed(jid:make(<<>>, FromHost), To) % likely a gateway - end; - false -> - ?DEBUG("~s not loaded for ~s", [?MODULE, LServer]), - false + true -> + Access = gen_mod:get_module_opt(LServer, ?MODULE, access_spam), + case acl:match_rule(LServer, Access, To) of + allow -> + ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), + false; + deny -> + ?DEBUG("Spam is filtered for ~s", [jid:encode(To)]), + not mod_roster:is_subscribed(From, To) + andalso not + mod_roster:is_subscribed( + jid:make(<<>>, FromHost), + To) % likely a gateway + end; + false -> + ?DEBUG("~s not loaded for ~s", [?MODULE, LServer]), + false end. -spec check_from(binary(), jid()) -> ham | spam. check_from(Host, From) -> Proc = get_proc_name(Host), - LFrom = {_, FromDomain, _} = jid:remove_resource(jid:tolower(From)), + LFrom = + {_, FromDomain, _} = + jid:remove_resource( + jid:tolower(From)), try - case gen_server:call(Proc, {is_blocked_domain, FromDomain}) of - true -> + case gen_server:call(Proc, {is_blocked_domain, FromDomain}) of + true -> ?DEBUG("Spam JID found in blocked domains: ~p", [From]), ejabberd_hooks:run(spam_found, Host, [{jid, From}]), spam; - false -> - case gen_server:call(Proc, {check_jid, LFrom}) of - {spam_filter, Result} -> - Result - end - end - catch exit:{timeout, _} -> - ?WARNING_MSG("Timeout while checking ~s against list of blocked domains or spammers", - [jid:encode(From)]), - ham + false -> + case gen_server:call(Proc, {check_jid, LFrom}) of + {spam_filter, Result} -> + Result + end + end + catch + exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking ~s against list of blocked domains or spammers", + [jid:encode(From)]), + ham end. -spec check_body(binary(), jid(), binary()) -> ham | spam. check_body(Host, From, Body) -> case {extract_urls(Host, Body), extract_jids(Body)} of - {none, none} -> - ?DEBUG("No JIDs/URLs found in message", []), - ham; - {URLs, JIDs} -> - Proc = get_proc_name(Host), - LFrom = jid:remove_resource(jid:tolower(From)), - try gen_server:call(Proc, {check_body, URLs, JIDs, LFrom}) of - {spam_filter, Result} -> - Result - catch exit:{timeout, _} -> - ?WARNING_MSG("Timeout while checking body", []), - ham - end + {none, none} -> + ?DEBUG("No JIDs/URLs found in message", []), + ham; + {URLs, JIDs} -> + Proc = get_proc_name(Host), + LFrom = + jid:remove_resource( + jid:tolower(From)), + try gen_server:call(Proc, {check_body, URLs, JIDs, LFrom}) of + {spam_filter, Result} -> + Result + catch + exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking body", []), + ham + end end. -spec extract_urls(binary(), binary()) -> {urls, [url()]} | none. @@ -545,11 +589,11 @@ extract_urls(Host, Body) -> RE = <<"https?://\\S+">>, Options = [global, {capture, all, binary}], case re:run(Body, RE, Options) of - {match, Captured} when is_list(Captured) -> - Urls = resolve_redirects(Host, lists:flatten(Captured)), - {urls, Urls}; - nomatch -> - none + {match, Captured} when is_list(Captured) -> + Urls = resolve_redirects(Host, lists:flatten(Captured)), + {urls, Urls}; + nomatch -> + none end. -spec resolve_redirects(binary(), [url()]) -> [url()]. @@ -558,17 +602,20 @@ resolve_redirects(Host, URLs) -> try gen_server:call(Proc, {resolve_redirects, URLs}) of {spam_filter, ResolvedURLs} -> ResolvedURLs - catch exit:{timeout, _} -> + catch + exit:{timeout, _} -> ?WARNING_MSG("Timeout while resolving redirects: ~p", [URLs]), URLs end. -spec do_resolve_redirects([url()], [url()]) -> [url()]. -do_resolve_redirects([], Result) -> Result; +do_resolve_redirects([], Result) -> + Result; do_resolve_redirects([URL | Rest], Acc) -> - case - httpc:request(get, {URL, [{"user-agent", "curl/8.7.1"}]}, - [{autoredirect, false}, {timeout, ?HTTPC_TIMEOUT}], []) + case httpc:request(get, + {URL, [{"user-agent", "curl/8.7.1"}]}, + [{autoredirect, false}, {timeout, ?HTTPC_TIMEOUT}], + []) of {ok, {{_, StatusCode, _}, Headers, _Body}} when StatusCode >= 300, StatusCode < 400 -> Location = proplists:get_value("location", Headers), @@ -577,7 +624,7 @@ do_resolve_redirects([URL | Rest], Acc) -> do_resolve_redirects(Rest, [URL | Acc]); false -> do_resolve_redirects([Location | Rest], [URL | Acc]) - end; + end; _Res -> do_resolve_redirects(Rest, [URL | Acc]) end. @@ -587,85 +634,91 @@ extract_jids(Body) -> RE = <<"\\S+@\\S+">>, Options = [global, {capture, all, binary}], case re:run(Body, RE, Options) of - {match, Captured} when is_list(Captured) -> - {jids, lists:filtermap(fun try_decode_jid/1, - lists:flatten(Captured))}; - nomatch -> - none + {match, Captured} when is_list(Captured) -> + {jids, lists:filtermap(fun try_decode_jid/1, lists:flatten(Captured))}; + nomatch -> + none end. -spec try_decode_jid(binary()) -> {true, ljid()} | false. try_decode_jid(S) -> try jid:decode(S) of - #jid{} = JID -> - {true, jid:remove_resource(jid:tolower(JID))} - catch _:{bad_jid, _} -> - false + #jid{} = JID -> + {true, + jid:remove_resource( + jid:tolower(JID))} + catch + _:{bad_jid, _} -> + false end. -spec filter_jid(ljid(), jid_set(), state()) -> {ham | spam, state()}. filter_jid(From, Set, #state{host = Host} = State) -> case sets:is_element(From, Set) of - true -> - ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), + true -> + ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), ejabberd_hooks:run(spam_found, Host, [{jid, From}]), - {spam, State}; - false -> - case cache_lookup(From, State) of - {true, State1} -> - ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), + {spam, State}; + false -> + case cache_lookup(From, State) of + {true, State1} -> + ?DEBUG("Spam JID found: ~s", [jid:encode(From)]), ejabberd_hooks:run(spam_found, Host, [{jid, From}]), - {spam, State1}; - {false, State1} -> - ?DEBUG("JID not listed: ~s", [jid:encode(From)]), - {ham, State1} - end + {spam, State1}; + {false, State1} -> + ?DEBUG("JID not listed: ~s", [jid:encode(From)]), + {ham, State1} + end end. -spec filter_body({urls, [url()]} | {jids, [ljid()]} | none, - url_set() | jid_set(), jid(), state()) - -> {ham | spam, state()}. + url_set() | jid_set(), + jid(), + state()) -> + {ham | spam, state()}. filter_body({_, Addrs}, Set, From, #state{host = Host} = State) -> case lists:any(fun(Addr) -> sets:is_element(Addr, Set) end, Addrs) of - true -> - ?DEBUG("Spam addresses found: ~p", [Addrs]), + true -> + ?DEBUG("Spam addresses found: ~p", [Addrs]), ejabberd_hooks:run(spam_found, Host, [{body, Addrs}]), - {spam, cache_insert(From, State)}; - false -> - ?DEBUG("Addresses not listed: ~p", [Addrs]), - {ham, State} + {spam, cache_insert(From, State)}; + false -> + ?DEBUG("Addresses not listed: ~p", [Addrs]), + {ham, State} end; filter_body(none, _Set, _From, State) -> {ham, State}. --spec reload_files(#{Type :: atom() => filename()}, state()) - -> {ok | {error, binary()}, state()}. +-spec reload_files(#{Type :: atom() => filename()}, state()) -> + {ok | {error, binary()}, state()}. reload_files(Files, #state{host = Host, blocked_domains = BlockedDomains} = State) -> try read_files(Files) of - #{jid := JIDsSet, url := URLsSet, domains := SpamDomainsSet, whitelist_domains := WhitelistDomains} -> - case sets_equal(JIDsSet, State#state.jid_set) of - true -> - ?INFO_MSG("Reloaded spam JIDs for ~s (unchanged)", [Host]); - false -> - ?INFO_MSG("Reloaded spam JIDs for ~s (changed)", [Host]) - end, - case sets_equal(URLsSet, State#state.url_set) of - true -> - ?INFO_MSG("Reloaded spam URLs for ~s (unchanged)", [Host]); - false -> - ?INFO_MSG("Reloaded spam URLs for ~s (changed)", [Host]) - end, - {ok, State#state{jid_set = JIDsSet, - url_set = URLsSet, - blocked_domains = maps:merge(BlockedDomains, set_to_map(SpamDomainsSet)), - whitelist_domains = set_to_map(WhitelistDomains, false)} - } - catch {Op, File, Reason} when Op == open; - Op == read -> - Txt = format("Cannot ~s ~s for ~s: ~s", - [Op, File, Host, format_error(Reason)]), - ?ERROR_MSG("~s", [Txt]), - {{error, Txt}, State} + #{jid := JIDsSet, + url := URLsSet, + domains := SpamDomainsSet, + whitelist_domains := WhitelistDomains} -> + case sets_equal(JIDsSet, State#state.jid_set) of + true -> + ?INFO_MSG("Reloaded spam JIDs for ~s (unchanged)", [Host]); + false -> + ?INFO_MSG("Reloaded spam JIDs for ~s (changed)", [Host]) + end, + case sets_equal(URLsSet, State#state.url_set) of + true -> + ?INFO_MSG("Reloaded spam URLs for ~s (unchanged)", [Host]); + false -> + ?INFO_MSG("Reloaded spam URLs for ~s (changed)", [Host]) + end, + {ok, + State#state{jid_set = JIDsSet, + url_set = URLsSet, + blocked_domains = maps:merge(BlockedDomains, set_to_map(SpamDomainsSet)), + whitelist_domains = set_to_map(WhitelistDomains, false)}} + catch + {Op, File, Reason} when Op == open; Op == read -> + Txt = format("Cannot ~s ~s for ~s: ~s", [Op, File, Host, format_error(Reason)]), + ?ERROR_MSG("~s", [Txt]), + {{error, Txt}, State} end. set_to_map(Set) -> @@ -674,54 +727,63 @@ set_to_map(Set) -> set_to_map(Set, V) -> sets:fold(fun(K, M) -> M#{K => V} end, #{}, Set). --spec read_files(#{Type => filename()}) -> #{jid => jid_set(), url => url_set(), Type => sets:set(binary())} - when Type :: atom(). +-spec read_files(#{Type => filename()}) -> + #{jid => jid_set(), + url => url_set(), + Type => sets:set(binary())} + when Type :: atom(). read_files(Files) -> - maps:map(fun(Type, Filename) -> - read_file(Filename, line_parser(Type)) - end, - Files). + maps:map(fun(Type, Filename) -> read_file(Filename, line_parser(Type)) end, Files). -spec line_parser(Type :: atom()) -> fun((binary()) -> binary()). -line_parser(jid) -> fun parse_jid/1; -line_parser(url) -> fun parse_url/1; -line_parser(_) -> fun trim/1. +line_parser(jid) -> + fun parse_jid/1; +line_parser(url) -> + fun parse_url/1; +line_parser(_) -> + fun trim/1. --spec read_file(filename(), fun((binary()) -> ljid() | url())) - -> jid_set() | url_set(). +-spec read_file(filename(), fun((binary()) -> ljid() | url())) -> jid_set() | url_set(). read_file(none, _ParseLine) -> sets:new(); read_file(File, ParseLine) -> case file:open(File, [read, binary, raw, {read_ahead, 65536}]) of - {ok, Fd} -> - try read_line(Fd, ParseLine, sets:new()) - catch throw:E -> throw({read, File, E}) - after ok = file:close(Fd) - end; - {error, Reason} -> - throw({open, File, Reason}) + {ok, Fd} -> + try + read_line(Fd, ParseLine, sets:new()) + catch + E -> + throw({read, File, E}) + after + ok = file:close(Fd) + end; + {error, Reason} -> + throw({open, File, Reason}) end. --spec read_line(file:io_device(), fun((binary()) -> ljid() | url()), - jid_set() | url_set()) - -> jid_set() | url_set(). +-spec read_line(file:io_device(), + fun((binary()) -> ljid() | url()), + jid_set() | url_set()) -> + jid_set() | url_set(). read_line(Fd, ParseLine, Set) -> case file:read_line(Fd) of - {ok, Line} -> - read_line(Fd, ParseLine, sets:add_element(ParseLine(Line), Set)); - {error, Reason} -> - throw(Reason); - eof -> - Set + {ok, Line} -> + read_line(Fd, ParseLine, sets:add_element(ParseLine(Line), Set)); + {error, Reason} -> + throw(Reason); + eof -> + Set end. -spec parse_jid(binary()) -> ljid(). parse_jid(S) -> try jid:decode(trim(S)) of - #jid{} = JID -> - jid:remove_resource(jid:tolower(JID)) - catch _:{bad_jid, _} -> - throw({bad_jid, S}) + #jid{} = JID -> + jid:remove_resource( + jid:tolower(JID)) + catch + _:{bad_jid, _} -> + throw({bad_jid, S}) end. -spec parse_url(binary()) -> url(). @@ -730,10 +792,10 @@ parse_url(S) -> RE = <<"https?://\\S+$">>, Options = [anchored, caseless, {capture, none}], case re:run(URL, RE, Options) of - match -> - URL; - nomatch -> - throw({bad_url, S}) + match -> + URL; + nomatch -> + throw({bad_url, S}) end. -spec trim(binary()) -> binary(). @@ -741,18 +803,24 @@ trim(S) -> re:replace(S, <<"\\s+$">>, <<>>, [{return, binary}]). -spec reject(stanza()) -> ok. -reject(#message{from = From, to = To, type = Type, lang = Lang} = Msg) - when Type /= groupchat, - Type /= error -> +reject(#message{from = From, + to = To, + type = Type, + lang = Lang} = + Msg) + when Type /= groupchat, Type /= error -> ?INFO_MSG("Rejecting unsolicited message from ~s to ~s", - [jid:encode(From), jid:encode(To)]), + [jid:encode(From), jid:encode(To)]), Txt = <<"Your message is unsolicited">>, Err = xmpp:err_policy_violation(Txt, Lang), maybe_dump_spam(Msg), ejabberd_router:route_error(Msg, Err); -reject(#presence{from = From, to = To, lang = Lang} = Presence) -> +reject(#presence{from = From, + to = To, + lang = Lang} = + Presence) -> ?INFO_MSG("Rejecting unsolicited presence from ~s to ~s", - [jid:encode(From), jid:encode(To)]), + [jid:encode(From), jid:encode(To)]), Txt = <<"Your traffic is unsolicited">>, Err = xmpp:err_policy_violation(Txt, Lang), ejabberd_router:route_error(Presence, Err); @@ -765,12 +833,12 @@ open_dump_file(none, State) -> open_dump_file(Name, State) -> Modes = [append, raw, binary, delayed_write], case file:open(Name, Modes) of - {ok, Fd} -> - ?DEBUG("Opened ~s", [Name]), - State#state{dump_fd = Fd}; - {error, Reason} -> - ?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]), - State#state{dump_fd = undefined} + {ok, Fd} -> + ?DEBUG("Opened ~s", [Name]), + State#state{dump_fd = Fd}; + {error, Reason} -> + ?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]), + State#state{dump_fd = undefined} end. -spec close_dump_file(filename(), state()) -> ok. @@ -778,10 +846,10 @@ close_dump_file(_Name, #state{dump_fd = undefined}) -> ok; close_dump_file(Name, #state{dump_fd = Fd}) -> case file:close(Fd) of - ok -> - ?DEBUG("Closed ~s", [Name]); - {error, Reason} -> - ?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)]) + ok -> + ?DEBUG("Closed ~s", [Name]); + {error, Reason} -> + ?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)]) end. -spec reopen_dump_file(state()) -> state(). @@ -797,7 +865,8 @@ maybe_dump_spam(#message{to = #jid{lserver = LServer}} = Msg) -> Proc = get_proc_name(LServer), Time = erlang:timestamp(), Msg1 = misc:add_delay_info(Msg, By, Time), - XML = fxml:element_to_binary(xmpp:encode(Msg1)), + XML = fxml:element_to_binary( + xmpp:encode(Msg1)), gen_server:cast(Proc, {dump, XML}). -spec get_proc_name(binary()) -> atom(). @@ -837,7 +906,7 @@ format_error(Reason) -> cache_insert(_LJID, #state{max_cache_size = 0} = State) -> State; cache_insert(LJID, #state{jid_cache = Cache, max_cache_size = MaxSize} = State) - when MaxSize /= unlimited, map_size(Cache) >= MaxSize -> + when MaxSize /= unlimited, map_size(Cache) >= MaxSize -> cache_insert(LJID, shrink_cache(State)); cache_insert(LJID, #state{jid_cache = Cache} = State) -> ?INFO_MSG("Caching spam JID: ~s", [jid:encode(LJID)]), @@ -847,12 +916,12 @@ cache_insert(LJID, #state{jid_cache = Cache} = State) -> -spec cache_lookup(ljid(), state()) -> {boolean(), state()}. cache_lookup(LJID, #state{jid_cache = Cache} = State) -> case Cache of - #{LJID := _Timestamp} -> - Cache1 = Cache#{LJID => erlang:monotonic_time(second)}, - State1 = State#state{jid_cache = Cache1}, - {true, State1}; - #{} -> - {false, State} + #{LJID := _Timestamp} -> + Cache1 = Cache#{LJID => erlang:monotonic_time(second)}, + State1 = State#state{jid_cache = Cache1}, + {true, State1}; + #{} -> + {false, State} end. -spec shrink_cache(state()) -> state(). @@ -860,7 +929,9 @@ shrink_cache(#state{jid_cache = Cache, max_cache_size = MaxSize} = State) -> ShrinkedSize = round(MaxSize / 2), N = map_size(Cache) - ShrinkedSize, L = lists:keysort(2, maps:to_list(Cache)), - Cache1 = maps:from_list(lists:nthtail(N, L)), + Cache1 = + maps:from_list( + lists:nthtail(N, L)), State#state{jid_cache = Cache1}. -spec expire_cache(integer(), state()) -> {{ok, binary()}, state()}. @@ -881,11 +952,11 @@ add_to_cache(LJID, State) -> drop_from_cache(LJID, #state{jid_cache = Cache} = State) -> Cache1 = maps:remove(LJID, Cache), if map_size(Cache1) < map_size(Cache) -> - Txt = format("~s removed from cache", [jid:encode(LJID)]), - {{ok, Txt}, State#state{jid_cache = Cache1}}; + Txt = format("~s removed from cache", [jid:encode(LJID)]), + {{ok, Txt}, State#state{jid_cache = Cache1}}; true -> - Txt = format("~s wasn't cached", [jid:encode(LJID)]), - {{ok, Txt}, State} + Txt = format("~s wasn't cached", [jid:encode(LJID)]), + {{ok, Txt}, State} end. %%-------------------------------------------------------------------- @@ -893,78 +964,96 @@ drop_from_cache(LJID, #state{jid_cache = Cache} = State) -> %%-------------------------------------------------------------------- -spec get_commands_spec() -> [ejabberd_commands()]. get_commands_spec() -> - [#ejabberd_commands{name = reload_spam_filter_files, tags = [filter], - desc = "Reload spam JID/URL files", - module = ?MODULE, function = reload_spam_filter_files, - args = [{host, binary}], - result = {res, rescode}}, - #ejabberd_commands{name = get_spam_filter_cache, tags = [filter], - desc = "Show spam filter cache contents", - module = ?MODULE, function = get_spam_filter_cache, - args = [{host, binary}], - result = {spammers, {list, {spammer, {tuple, - [{jid, string}, {timestamp, integer}]}}}}}, - #ejabberd_commands{name = expire_spam_filter_cache, tags = [filter], - desc = "Remove old/unused spam JIDs from cache", - module = ?MODULE, function = expire_spam_filter_cache, - args = [{host, binary}, {seconds, integer}], - result = {res, restuple}}, - #ejabberd_commands{name = add_to_spam_filter_cache, tags = [filter], - desc = "Add JID to spam filter cache", - module = ?MODULE, - function = add_to_spam_filter_cache, - args = [{host, binary}, {jid, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = drop_from_spam_filter_cache, tags = [filter], - desc = "Drop JID from spam filter cache", - module = ?MODULE, - function = drop_from_spam_filter_cache, - args = [{host, binary}, {jid, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = get_blocked_domains, tags = [filter], - desc = "Get list of domains being blocked", - module = ?MODULE, - function = get_blocked_domains, - args = [{host, binary}], - result = {blocked_domains, {list, {jid, string}}}}, - #ejabberd_commands{name = add_blocked_domain, tags = [filter], - desc = "Add domain to list of blocked domains", - module = ?MODULE, - function = add_blocked_domain, - args = [{host, binary}, {domain, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = remove_blocked_domain, tags = [filter], - desc = "Remove domain from list of blocked domains", - module = ?MODULE, - function = remove_blocked_domain, - args = [{host, binary}, {domain, binary}], - result = {res, restuple}} - ]. + [#ejabberd_commands{name = reload_spam_filter_files, + tags = [filter], + desc = "Reload spam JID/URL files", + module = ?MODULE, + function = reload_spam_filter_files, + args = [{host, binary}], + result = {res, rescode}}, + #ejabberd_commands{name = get_spam_filter_cache, + tags = [filter], + desc = "Show spam filter cache contents", + module = ?MODULE, + function = get_spam_filter_cache, + args = [{host, binary}], + result = + {spammers, + {list, {spammer, {tuple, [{jid, string}, {timestamp, integer}]}}}}}, + #ejabberd_commands{name = expire_spam_filter_cache, + tags = [filter], + desc = "Remove old/unused spam JIDs from cache", + module = ?MODULE, + function = expire_spam_filter_cache, + args = [{host, binary}, {seconds, integer}], + result = {res, restuple}}, + #ejabberd_commands{name = add_to_spam_filter_cache, + tags = [filter], + desc = "Add JID to spam filter cache", + module = ?MODULE, + function = add_to_spam_filter_cache, + args = [{host, binary}, {jid, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = drop_from_spam_filter_cache, + tags = [filter], + desc = "Drop JID from spam filter cache", + module = ?MODULE, + function = drop_from_spam_filter_cache, + args = [{host, binary}, {jid, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = get_blocked_domains, + tags = [filter], + desc = "Get list of domains being blocked", + module = ?MODULE, + function = get_blocked_domains, + args = [{host, binary}], + result = {blocked_domains, {list, {jid, string}}}}, + #ejabberd_commands{name = add_blocked_domain, + tags = [filter], + desc = "Add domain to list of blocked domains", + module = ?MODULE, + function = add_blocked_domain, + args = [{host, binary}, {domain, binary}], + result = {res, restuple}}, + #ejabberd_commands{name = remove_blocked_domain, + tags = [filter], + desc = "Remove domain from list of blocked domains", + module = ?MODULE, + function = remove_blocked_domain, + args = [{host, binary}, {domain, binary}], + result = {res, restuple}}]. for_all_hosts(F, A) -> - try lists:map( - fun(Host) -> - apply(F, [Host | A]) - end, get_spam_filter_hosts()) of - List -> - case lists:filter(fun({error, _}) -> true; (_) -> false end, List) of - [] -> hd(List); - Errors -> hd(Errors) - end - catch error:{badmatch, {error, _Reason} = Error} -> - Error + try lists:map(fun(Host) -> apply(F, [Host | A]) end, get_spam_filter_hosts()) of + List -> + case lists:filter(fun ({error, _}) -> + true; + (_) -> + false + end, + List) + of + [] -> + hd(List); + Errors -> + hd(Errors) + end + catch + error:{badmatch, {error, _Reason} = Error} -> + Error end. try_call_by_host(Host, Call) -> LServer = jid:nameprep(Host), Proc = get_proc_name(LServer), try gen_server:call(Proc, Call, ?COMMAND_TIMEOUT) of - Result -> - Result - catch exit:{noproc, _} -> - {error, "Not configured for " ++ binary_to_list(Host)}; - exit:{timeout, _} -> - {error, "Timeout while querying ejabberd"} + Result -> + Result + catch + exit:{noproc, _} -> + {error, "Not configured for " ++ binary_to_list(Host)}; + exit:{timeout, _} -> + {error, "Timeout while querying ejabberd"} end. -spec reload_spam_filter_files(binary()) -> ok | {error, string()}. @@ -972,25 +1061,32 @@ reload_spam_filter_files(<<"global">>) -> for_all_hosts(fun reload_spam_filter_files/1, []); reload_spam_filter_files(Host) -> LServer = jid:nameprep(Host), - Files = #{domains => gen_mod:get_module_opt(LServer, ?MODULE, spam_domains_file), - jid => gen_mod:get_module_opt(LServer, ?MODULE, spam_jids_file), - url => gen_mod:get_module_opt(LServer, ?MODULE, spam_urls_file)}, + Files = + #{domains => gen_mod:get_module_opt(LServer, ?MODULE, spam_domains_file), + jid => gen_mod:get_module_opt(LServer, ?MODULE, spam_jids_file), + url => gen_mod:get_module_opt(LServer, ?MODULE, spam_urls_file)}, case try_call_by_host(Host, {reload_files, Files}) of - {spam_filter, ok} -> - ok; - {spam_filter, {error, Txt}} -> - {error, binary_to_list(Txt)}; - {error, _R} = Error -> - Error + {spam_filter, ok} -> + ok; + {spam_filter, {error, Txt}} -> + {error, binary_to_list(Txt)}; + {error, _R} = Error -> + Error end. -spec get_blocked_domains(binary()) -> [binary()]. get_blocked_domains(Host) -> case try_call_by_host(Host, get_blocked_domains) of - {blocked_domains, BlockedDomains} -> - maps:keys(maps:filter(fun(_, false) -> false; (_, _) -> true end, BlockedDomains)); - {error, _R} = Error -> - Error + {blocked_domains, BlockedDomains} -> + maps:keys( + maps:filter(fun (_, false) -> + false; + (_, _) -> + true + end, + BlockedDomains)); + {error, _R} = Error -> + Error end. -spec add_blocked_domain(binary(), binary()) -> {ok, string()}. @@ -998,10 +1094,10 @@ add_blocked_domain(<<"global">>, Domain) -> for_all_hosts(fun add_blocked_domain/2, [Domain]); add_blocked_domain(Host, Domain) -> case try_call_by_host(Host, {add_blocked_domain, Domain}) of - {spam_filter, {Status, Txt}} -> - {Status, binary_to_list(Txt)}; - {error, _R} = Error -> - Error + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error end. -spec remove_blocked_domain(binary(), binary()) -> {ok, string()}. @@ -1009,21 +1105,19 @@ remove_blocked_domain(<<"global">>, Domain) -> for_all_hosts(fun remove_blocked_domain/2, [Domain]); remove_blocked_domain(Host, Domain) -> case try_call_by_host(Host, {remove_blocked_domain, Domain}) of - {spam_filter, {Status, Txt}} -> - {Status, binary_to_list(Txt)}; - {error, _R} = Error -> - Error + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error end. --spec get_spam_filter_cache(binary()) - -> [{binary(), integer()}] | {error, string()}. +-spec get_spam_filter_cache(binary()) -> [{binary(), integer()}] | {error, string()}. get_spam_filter_cache(Host) -> case try_call_by_host(Host, get_cache) of - {spam_filter, Cache} -> - [{jid:encode(JID), TS + erlang:time_offset(second)} || - {JID, TS} <- Cache]; - {error, _R} = Error -> - Error + {spam_filter, Cache} -> + [{jid:encode(JID), TS + erlang:time_offset(second)} || {JID, TS} <- Cache]; + {error, _R} = Error -> + Error end. -spec expire_spam_filter_cache(binary(), integer()) -> {ok | error, string()}. @@ -1031,27 +1125,31 @@ expire_spam_filter_cache(<<"global">>, Age) -> for_all_hosts(fun expire_spam_filter_cache/2, [Age]); expire_spam_filter_cache(Host, Age) -> case try_call_by_host(Host, {expire_cache, Age}) of - {spam_filter, {Status, Txt}} -> - {Status, binary_to_list(Txt)}; - {error, _R} = Error -> - Error + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error end. --spec add_to_spam_filter_cache(binary(), binary()) -> [{binary(), integer()}] | {error, string()}. +-spec add_to_spam_filter_cache(binary(), binary()) -> + [{binary(), integer()}] | {error, string()}. add_to_spam_filter_cache(<<"global">>, JID) -> for_all_hosts(fun add_to_spam_filter_cache/2, [JID]); add_to_spam_filter_cache(Host, EncJID) -> try jid:decode(EncJID) of - #jid{} = JID -> - LJID = jid:remove_resource(jid:tolower(JID)), - case try_call_by_host(Host, {add_to_cache, LJID}) of - {spam_filter, {Status, Txt}} -> - {Status, binary_to_list(Txt)}; - {error, _R} = Error -> - Error - end - catch _:{bad_jid, _} -> - {error, "Not a valid JID: " ++ binary_to_list(EncJID)} + #jid{} = JID -> + LJID = + jid:remove_resource( + jid:tolower(JID)), + case try_call_by_host(Host, {add_to_cache, LJID}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end + catch + _:{bad_jid, _} -> + {error, "Not a valid JID: " ++ binary_to_list(EncJID)} end. -spec drop_from_spam_filter_cache(binary(), binary()) -> {ok | error, string()}. @@ -1059,14 +1157,17 @@ drop_from_spam_filter_cache(<<"global">>, JID) -> for_all_hosts(fun drop_from_spam_filter_cache/2, [JID]); drop_from_spam_filter_cache(Host, EncJID) -> try jid:decode(EncJID) of - #jid{} = JID -> - LJID = jid:remove_resource(jid:tolower(JID)), - case try_call_by_host(Host, {drop_from_cache, LJID}) of - {spam_filter, {Status, Txt}} -> - {Status, binary_to_list(Txt)}; - {error, _R} = Error -> - Error - end - catch _:{bad_jid, _} -> - {error, "Not a valid JID: " ++ binary_to_list(EncJID)} + #jid{} = JID -> + LJID = + jid:remove_resource( + jid:tolower(JID)), + case try_call_by_host(Host, {drop_from_cache, LJID}) of + {spam_filter, {Status, Txt}} -> + {Status, binary_to_list(Txt)}; + {error, _R} = Error -> + Error + end + catch + _:{bad_jid, _} -> + {error, "Not a valid JID: " ++ binary_to_list(EncJID)} end. diff --git a/src/mod_antispam_rtbl.erl b/src/mod_antispam_rtbl.erl index d977aa0c6..93b346631 100644 --- a/src/mod_antispam_rtbl.erl +++ b/src/mod_antispam_rtbl.erl @@ -38,11 +38,15 @@ subscribe/3, unsubscribe/3]). +%% @format-begin + subscribe(RTBLHost, RTBLDomainsNode, From) -> FromJID = service_jid(From), - SubIQ = #iq{type = set, to = jid:make(RTBLHost), from = FromJID, - sub_els = [ - #pubsub{subscribe = #ps_subscribe{jid = FromJID, node = RTBLDomainsNode}}]}, + SubIQ = + #iq{type = set, + to = jid:make(RTBLHost), + from = FromJID, + sub_els = [#pubsub{subscribe = #ps_subscribe{jid = FromJID, node = RTBLDomainsNode}}]}, ?DEBUG("Sending subscription request:~n~p", [xmpp:encode(SubIQ)]), ejabberd_router:route_iq(SubIQ, subscribe_result, self()). @@ -51,19 +55,22 @@ unsubscribe(none, _PSNode, _From) -> ok; unsubscribe(RTBLHost, RTBLDomainsNode, From) -> FromJID = jid:make(From), - SubIQ = #iq{type = set, to = jid:make(RTBLHost), from = FromJID, - sub_els = [ - #pubsub{unsubscribe = #ps_unsubscribe{jid = FromJID, node = RTBLDomainsNode}}]}, + SubIQ = + #iq{type = set, + to = jid:make(RTBLHost), + from = FromJID, + sub_els = + [#pubsub{unsubscribe = #ps_unsubscribe{jid = FromJID, node = RTBLDomainsNode}}]}, ejabberd_router:route_iq(SubIQ, unsubscribe_result, self()). -spec request_blocked_domains(binary() | none, binary(), binary()) -> ok. request_blocked_domains(none, _PSNode, _From) -> ok; request_blocked_domains(RTBLHost, RTBLDomainsNode, From) -> - IQ = #iq{type = get, from = jid:make(From), - to = jid:make(RTBLHost), - sub_els = [ - #pubsub{items = #ps_items{node = RTBLDomainsNode}}]}, + IQ = #iq{type = get, + from = jid:make(From), + to = jid:make(RTBLHost), + sub_els = [#pubsub{items = #ps_items{node = RTBLDomainsNode}}]}, ?DEBUG("Requesting RTBL blocked domains from ~s:~n~p", [RTBLHost, xmpp:encode(IQ)]), ejabberd_router:route_iq(IQ, blocked_domains, self()). @@ -72,31 +79,35 @@ parse_blocked_domains(#iq{to = #jid{lserver = LServer}, type = result} = IQ) -> ?DEBUG("parsing iq-result items: ~p", [IQ]), RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), case xmpp:get_subtag(IQ, #pubsub{}) of - #pubsub{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> - ?DEBUG("Got items:~n~p", [Items]), - parse_items(Items); - _ -> - undefined + #pubsub{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> + ?DEBUG("Got items:~n~p", [Items]), + parse_items(Items); + _ -> + undefined end. -spec parse_pubsub_event(stanza()) -> #{binary() => any()}. parse_pubsub_event(#message{to = #jid{lserver = LServer}} = Msg) -> RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), case xmpp:get_subtag(Msg, #ps_event{}) of - #ps_event{items = #ps_items{node = RTBLDomainsNode, items = Items, retract = RetractIds}} -> - maps:merge(retract_items(RetractIds), parse_items(Items)); - Other -> - ?WARNING_MSG("Couldn't extract items: ~p", [Other]), - #{} + #ps_event{items = + #ps_items{node = RTBLDomainsNode, + items = Items, + retract = RetractIds}} -> + maps:merge(retract_items(RetractIds), parse_items(Items)); + Other -> + ?WARNING_MSG("Couldn't extract items: ~p", [Other]), + #{} end. -spec parse_items([ps_item()]) -> #{binary() => any()}. parse_items(Items) -> - lists:foldl( - fun(#ps_item{id = ID}, Acc) -> - %% TODO extract meta/extra instructions - maps:put(ID, true, Acc) - end, #{}, Items). + lists:foldl(fun(#ps_item{id = ID}, Acc) -> + %% TODO extract meta/extra instructions + maps:put(ID, true, Acc) + end, + #{}, + Items). -spec retract_items([binary()]) -> #{binary() => false}. retract_items(Ids) -> @@ -112,20 +123,22 @@ service_jid(Host) -> -spec pubsub_event_handler(stanza()) -> drop | stanza(). pubsub_event_handler(#message{from = FromJid, - to = #jid{lserver = LServer, - lresource = <>}} = Msg) -> + to = + #jid{lserver = LServer, + lresource = <>}} = + Msg) -> ?DEBUG("Got RTBL message:~n~p", [Msg]), From = jid:encode(FromJid), case gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_host) of - From -> - ParsedItems = parse_pubsub_event(Msg), - Proc = gen_mod:get_module_proc(LServer, ?SERVICE_MODULE), - gen_server:cast(Proc, {update_blocked_domains, ParsedItems}), - %% FIXME what's the difference between `{drop, ...}` and `{stop, {drop, ...}}`? - drop; - _Other -> - ?INFO_MSG("Got unexpected message from ~s to rtbl resource:~n~p", [From, Msg]), - Msg + From -> + ParsedItems = parse_pubsub_event(Msg), + Proc = gen_mod:get_module_proc(LServer, ?SERVICE_MODULE), + gen_server:cast(Proc, {update_blocked_domains, ParsedItems}), + %% FIXME what's the difference between `{drop, ...}` and `{stop, {drop, ...}}`? + drop; + _Other -> + ?INFO_MSG("Got unexpected message from ~s to rtbl resource:~n~p", [From, Msg]), + Msg end; pubsub_event_handler(Acc) -> ?DEBUG("unexpected something on pubsub_event_handler: ~p", [Acc]), diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index d7ce196c0..6d94b8750 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -31,6 +31,8 @@ my_muc_jid/1, get_features/2, set_opt/3]). -include("suite.hrl"). +%% @format-begin + %%%=================================================================== %%% API %%%=================================================================== @@ -38,35 +40,51 @@ %%% Single tests %%%=================================================================== single_cases() -> - {antispam_single, [sequence], - [single_test(spam_files), - single_test(blocked_domains), - single_test(jid_cache), - single_test(rtbl_domains)]}. + {antispam_single, + [sequence], + [single_test(spam_files), + single_test(blocked_domains), + single_test(jid_cache), + single_test(rtbl_domains)]}. spam_files(Config) -> Host = ?config(server, Config), To = my_jid(Config), SpamJID = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), - SpamJIDMsg = #message{from = SpamJID, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + SpamJIDMsg = + #message{from = SpamJID, + to = To, + type = chat, + body = [#text{data = <<"hello world">>}]}, is_spam(SpamJIDMsg), Spammer = jid:make(<<"spammer">>, <<"localhost">>, <<"spam_client">>), - NoSpamMsg = #message{from = Spammer, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + NoSpamMsg = + #message{from = Spammer, + to = To, + type = chat, + body = [#text{data = <<"hello world">>}]}, is_not_spam(NoSpamMsg), - SpamMsg = #message{from = Spammer, to = To, type = chat, body = [#text{data = <<"hello world\nhttps://spam.domain.url">>}]}, + SpamMsg = + #message{from = Spammer, + to = To, + type = chat, + body = [#text{data = <<"hello world\nhttps://spam.domain.url">>}]}, is_spam(SpamMsg), %% now check this mischief is in jid_cache is_spam(NoSpamMsg), mod_antispam:drop_from_spam_filter_cache(Host, jid:to_string(Spammer)), is_not_spam(NoSpamMsg), - ?retry(100, 10, - ?match(true, (has_spam_domain(<<"spam_domain.org">>))(Host))), + ?retry(100, 10, ?match(true, (has_spam_domain(<<"spam_domain.org">>))(Host))), SpamDomain = jid:make(<<"spammer">>, <<"spam_domain.org">>, <<"spam_client">>), - SpamDomainMsg = #message{from = SpamDomain, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + SpamDomainMsg = + #message{from = SpamDomain, + to = To, + type = chat, + body = [#text{data = <<"hello world">>}]}, is_spam(SpamDomainMsg), ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam_domain.org">>)), ?match([], mod_antispam:get_blocked_domains(Host)), @@ -78,9 +96,12 @@ blocked_domains(Config) -> ?match([], mod_antispam:get_blocked_domains(Host)), SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), To = my_jid(Config), - Msg = #message{from = SpamFrom, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + Msg = #message{from = SpamFrom, + to = To, + type = chat, + body = [#text{data = <<"hello world">>}]}, is_not_spam(Msg), - ?match({ok, _}, mod_antispam:add_blocked_domain(<<"global">>, <<"spam.domain">>)), + ?match({ok, _}, mod_antispam:add_blocked_domain(<<"global">>, <<"spam.domain">>)), is_spam(Msg), Vhosts = [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, mod_antispam)], NumVhosts = length(Vhosts), @@ -102,7 +123,10 @@ jid_cache(Config) -> Host = ?config(server, Config), SpamFrom = jid:make(<<"spammer">>, Host, <<"spam_client">>), To = my_jid(Config), - Msg = #message{from = SpamFrom, to = To, type = chat, body = [#text{data = <<"hello world">>}]}, + Msg = #message{from = SpamFrom, + to = To, + type = chat, + body = [#text{data = <<"hello world">>}]}, is_not_spam(Msg), mod_antispam:add_to_spam_filter_cache(Host, jid:to_string(SpamFrom)), is_spam(Msg), @@ -112,25 +136,45 @@ jid_cache(Config) -> rtbl_domains(Config) -> Host = ?config(server, Config), - RTBLHost = jid:to_string(suite:pubsub_jid(Config)), + RTBLHost = + jid:to_string( + suite:pubsub_jid(Config)), RTBLDomainsNode = <<"spam_source_domains">>, OldOpts = gen_mod:get_module_opts(Host, mod_antispam), - NewOpts = maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), + NewOpts = + maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), Owner = jid:make(?config(user, Config), ?config(server, Config), <<>>), - {result, _} = mod_pubsub:create_node(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"flat">>), - {result, _} = mod_pubsub:publish_item(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"spam.source.domain">>, - [xmpp:encode(#ps_item{id = <<"spam.source.domain">>, sub_els = []})]), + {result, _} = + mod_pubsub:create_node(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"flat">>), + {result, _} = + mod_pubsub:publish_item(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"spam.source.domain">>, + [xmpp:encode(#ps_item{id = <<"spam.source.domain">>, + sub_els = []})]), mod_antispam:reload(Host, OldOpts, NewOpts), ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam_domain.org">>)), - ?retry(100, 10, + ?retry(100, + 10, ?match([<<"spam.source.domain">>], mod_antispam:get_blocked_domains(Host))), - {result, _} = mod_pubsub:publish_item(RTBLHost, ?config(server, Config), RTBLDomainsNode, Owner, <<"spam.source.another">>, - [xmpp:encode(#ps_item{id = <<"spam.source.another">>, sub_els = []})]), - ?retry(100, 10, - ?match(true, (has_spam_domain(<<"spam.source.another">>))(Host))), - {result, _} = mod_pubsub:delete_item(RTBLHost, RTBLDomainsNode, Owner, <<"spam.source.another">>, true), - ?retry(100, 10, - ?match(false, (has_spam_domain(<<"spam.source.another">>))(Host))), + {result, _} = + mod_pubsub:publish_item(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"spam.source.another">>, + [xmpp:encode(#ps_item{id = <<"spam.source.another">>, + sub_els = []})]), + ?retry(100, 10, ?match(true, (has_spam_domain(<<"spam.source.another">>))(Host))), + {result, _} = + mod_pubsub:delete_item(RTBLHost, RTBLDomainsNode, Owner, <<"spam.source.another">>, true), + ?retry(100, 10, ?match(false, (has_spam_domain(<<"spam.source.another">>))(Host))), {result, _} = mod_pubsub:delete_node(RTBLHost, RTBLDomainsNode, Owner), disconnect(Config). @@ -141,9 +185,7 @@ single_test(T) -> list_to_atom("antispam_" ++ atom_to_list(T)). has_spam_domain(Domain) -> - fun(Host) -> - lists:member(Domain, mod_antispam:get_blocked_domains(Host)) - end. + fun(Host) -> lists:member(Domain, mod_antispam:get_blocked_domains(Host)) end. is_not_spam(Msg) -> ?match({Msg, undefined}, mod_antispam:s2s_receive_packet({Msg, undefined})). From ee46333def6bfcec25ae255d144e03f0d3c23030 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 9 Jun 2025 16:26:00 +0200 Subject: [PATCH 1157/1302] add make target test- Eg. invoke common test for specific test group only like $ CT_BACKEND=mnesia,redis make test-antispam_single --- Makefile.in | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Makefile.in b/Makefile.in index c10ff390d..cf7480702 100644 --- a/Makefile.in +++ b/Makefile.in @@ -661,6 +661,17 @@ test: @cd priv && ln -sf ../sql $(REBAR) $(SKIPDEPS) ct +.PHONY: test-% +define test-group-target +test-$1: + $(REBAR) $(SKIPDEPS) ct --suite=test/ejabberd_SUITE --group=$1 +endef + +ifneq ($(filter test-%,$(MAKECMDGOALS)),) +group_to_test := $(patsubst test-%,%,$(filter test-%,$(MAKECMDGOALS))) +$(eval $(call test-group-target,$(group_to_test))) +endif + test-eunit: $(REBAR) $(SKIPDEPS) eunit --verbose @@ -711,6 +722,7 @@ help: @echo " hooks Run hooks validator" @echo " test Run Common Tests suite [rebar3]" @echo " test-eunit Run EUnit suite [rebar3]" + @echo " test- Run Common Test suite for specific group only [rebar3]" @echo " xref Run cross reference analysis [rebar3]" #. From b607d95a93156420e866aef2c8ef6e6d8ebd4a80 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 6 Jun 2025 17:38:13 +0200 Subject: [PATCH 1158/1302] Refactorize each individual test case in individual functions --- test/antispam_tests.erl | 142 +++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index 6d94b8750..7c3b053b1 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -20,6 +20,7 @@ %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. %%% %%%---------------------------------------------------------------------- + -module(antispam_tests). -compile(export_all). @@ -39,59 +40,60 @@ %%%=================================================================== %%% Single tests %%%=================================================================== + single_cases() -> {antispam_single, [sequence], - [single_test(spam_files), - single_test(blocked_domains), + [single_test(block_by_jid), + single_test(block_by_url), + single_test(blocked_jid_is_cached), + single_test(uncache_blocked_jid), + single_test(check_blocked_domain), + single_test(unblock_domain), + single_test(empty_domain_list), + single_test(block_domain_globally), + single_test(check_domain_blocked_globally), + single_test(unblock_domain_in_vhost), + single_test(unblock_domain_globally), + single_test(block_domain_in_vhost), + single_test(unblock_domain_in_vhost2), single_test(jid_cache), single_test(rtbl_domains)]}. -spam_files(Config) -> - Host = ?config(server, Config), +%%%=================================================================== + +block_by_jid(Config) -> + is_spam(message_hello(<<"spammer_jid">>, <<"localhost">>, Config)). + +block_by_url(Config) -> + From = jid:make(<<"spammer">>, <<"localhost">>, <<"spam_client">>), To = my_jid(Config), + is_not_spam(message_hello(<<"spammer">>, <<"localhost">>, Config)), + is_spam(message(From, To, <<"hello world\nhttps://spam.domain.url">>)). - SpamJID = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), - SpamJIDMsg = - #message{from = SpamJID, - to = To, - type = chat, - body = [#text{data = <<"hello world">>}]}, - is_spam(SpamJIDMsg), +blocked_jid_is_cached(Config) -> + is_spam(message_hello(<<"spammer">>, <<"localhost">>, Config)). - Spammer = jid:make(<<"spammer">>, <<"localhost">>, <<"spam_client">>), - NoSpamMsg = - #message{from = Spammer, - to = To, - type = chat, - body = [#text{data = <<"hello world">>}]}, - is_not_spam(NoSpamMsg), - SpamMsg = - #message{from = Spammer, - to = To, - type = chat, - body = [#text{data = <<"hello world\nhttps://spam.domain.url">>}]}, - is_spam(SpamMsg), - %% now check this mischief is in jid_cache - is_spam(NoSpamMsg), +uncache_blocked_jid(Config) -> + Host = ?config(server, Config), + Spammer = jid:make(<<"spammer">>, <<"localhost">>, <<"">>), mod_antispam:drop_from_spam_filter_cache(Host, jid:to_string(Spammer)), - is_not_spam(NoSpamMsg), + is_not_spam(message_hello(<<"spammer">>, <<"localhost">>, Config)). +check_blocked_domain(Config) -> + Host = ?config(server, Config), ?retry(100, 10, ?match(true, (has_spam_domain(<<"spam_domain.org">>))(Host))), + is_spam(message_hello(<<"other_spammer">>, <<"spam_domain.org">>, Config)). - SpamDomain = jid:make(<<"spammer">>, <<"spam_domain.org">>, <<"spam_client">>), - SpamDomainMsg = - #message{from = SpamDomain, - to = To, - type = chat, - body = [#text{data = <<"hello world">>}]}, - is_spam(SpamDomainMsg), +unblock_domain(Config) -> + Host = ?config(server, Config), ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam_domain.org">>)), ?match([], mod_antispam:get_blocked_domains(Host)), - is_not_spam(SpamDomainMsg), - disconnect(Config). + is_not_spam(message_hello(<<"spammer">>, <<"spam_domain.org">>, Config)). -blocked_domains(Config) -> +%%%=================================================================== + +empty_domain_list(Config) -> Host = ?config(server, Config), ?match([], mod_antispam:get_blocked_domains(Host)), SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), @@ -100,40 +102,65 @@ blocked_domains(Config) -> to = To, type = chat, body = [#text{data = <<"hello world">>}]}, - is_not_spam(Msg), + is_not_spam(Msg). + +block_domain_globally(Config) -> ?match({ok, _}, mod_antispam:add_blocked_domain(<<"global">>, <<"spam.domain">>)), - is_spam(Msg), + SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), + To = my_jid(Config), + is_spam(message(SpamFrom, To, <<"hello world">>)). + +check_domain_blocked_globally(_Config) -> Vhosts = [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, mod_antispam)], NumVhosts = length(Vhosts), - ?match(NumVhosts, length(lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts))), + ?match(NumVhosts, length(lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts))). + +unblock_domain_in_vhost(Config) -> + Host = ?config(server, Config), ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam.domain">>)), ?match([], mod_antispam:get_blocked_domains(Host)), - is_not_spam(Msg), + SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), + To = my_jid(Config), + is_not_spam(message(SpamFrom, To, <<"hello world">>)). + +unblock_domain_globally(_Config) -> + Vhosts = [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, mod_antispam)], + NumVhosts = length(Vhosts), ?match(NumVhosts, length(lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)) + 1), ?match({ok, _}, mod_antispam:remove_blocked_domain(<<"global">>, <<"spam.domain">>)), - ?match([], lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)), + ?match([], lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)). + +block_domain_in_vhost(Config) -> + Host = ?config(server, Config), + Vhosts = [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, mod_antispam)], ?match({ok, _}, mod_antispam:add_blocked_domain(Host, <<"spam.domain">>)), ?match([Host], lists:filter(has_spam_domain(<<"spam.domain">>), Vhosts)), - is_spam(Msg), + SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), + To = my_jid(Config), + is_spam(message(SpamFrom, To, <<"hello world">>)). + +unblock_domain_in_vhost2(Config) -> + Host = ?config(server, Config), ?match({ok, _}, mod_antispam:remove_blocked_domain(Host, <<"spam.domain">>)), - is_not_spam(Msg), + SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), + To = my_jid(Config), + is_not_spam(message(SpamFrom, To, <<"hello world">>)), disconnect(Config). +%%%=================================================================== + jid_cache(Config) -> Host = ?config(server, Config), SpamFrom = jid:make(<<"spammer">>, Host, <<"spam_client">>), - To = my_jid(Config), - Msg = #message{from = SpamFrom, - to = To, - type = chat, - body = [#text{data = <<"hello world">>}]}, - is_not_spam(Msg), + is_not_spam(message_hello(<<"spammer">>, Host, Config)), mod_antispam:add_to_spam_filter_cache(Host, jid:to_string(SpamFrom)), - is_spam(Msg), + is_spam(message_hello(<<"spammer">>, Host, Config)), mod_antispam:drop_from_spam_filter_cache(Host, jid:to_string(SpamFrom)), - is_not_spam(Msg), + is_not_spam(message_hello(<<"spammer">>, Host, Config)), disconnect(Config). +%%%=================================================================== + rtbl_domains(Config) -> Host = ?config(server, Config), RTBLHost = @@ -192,3 +219,14 @@ is_not_spam(Msg) -> is_spam(Spam) -> ?match({stop, {drop, undefined}}, mod_antispam:s2s_receive_packet({Spam, undefined})). + +message_hello(Username, Host, Config) -> + SpamFrom = jid:make(Username, Host, <<"spam_client">>), + To = my_jid(Config), + message(SpamFrom, To, <<"hello world">>). + +message(From, To, BodyText) -> + #message{from = From, + to = To, + type = chat, + body = [#text{data = BodyText}]}. From 6122a525d24b379b68f3cff863f10bbbb52016a7 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 9 Jun 2025 16:31:26 +0200 Subject: [PATCH 1159/1302] mod_antispam: fix config types --- src/mod_antispam.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index c3489aefa..664e5d67f 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -138,10 +138,11 @@ mod_opt_type(spam_domains_file) -> econf:either( econf:enum([none]), econf:file()); mod_opt_type(whitelist_domains_file) -> - econf:either(none, econf:binary()); + econf:either( + econf:enum([none]), econf:file()); mod_opt_type(spam_dump_file) -> econf:either( - econf:enum([none]), econf:binary()); + econf:enum([none]), econf:file()); mod_opt_type(spam_jids_file) -> econf:either( econf:enum([none]), econf:file()); From ea19e4bc7fcacb261d4eb4b1e92533af055875f7 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 9 Jun 2025 16:35:10 +0200 Subject: [PATCH 1160/1302] mod_antispam: remove unnecessary check in test this was left over from debugging issues with fixtures --- test/antispam_tests.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index 7c3b053b1..da69bd630 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -81,8 +81,6 @@ uncache_blocked_jid(Config) -> is_not_spam(message_hello(<<"spammer">>, <<"localhost">>, Config)). check_blocked_domain(Config) -> - Host = ?config(server, Config), - ?retry(100, 10, ?match(true, (has_spam_domain(<<"spam_domain.org">>))(Host))), is_spam(message_hello(<<"other_spammer">>, <<"spam_domain.org">>, Config)). unblock_domain(Config) -> From 7a6e4098797b24fc6e512e68d5e32c07efdfb4ef Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 9 Jun 2025 16:38:52 +0200 Subject: [PATCH 1161/1302] mod_antispam: use message/3 in test --- test/antispam_tests.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index da69bd630..f6f589c0d 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -96,10 +96,7 @@ empty_domain_list(Config) -> ?match([], mod_antispam:get_blocked_domains(Host)), SpamFrom = jid:make(<<"spammer">>, <<"spam.domain">>, <<"spam_client">>), To = my_jid(Config), - Msg = #message{from = SpamFrom, - to = To, - type = chat, - body = [#text{data = <<"hello world">>}]}, + Msg = message(SpamFrom, To, <<"hello world">>), is_not_spam(Msg). block_domain_globally(Config) -> From 10ec128b945ad52183ba239d488c982e1684d8e2 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 9 Jun 2025 16:55:31 +0200 Subject: [PATCH 1162/1302] mod_antispam: test whitelisted domain --- test/antispam_tests.erl | 43 ++++++++++++++++++- test/ejabberd_SUITE_data/ejabberd.mnesia.yml | 1 + test/ejabberd_SUITE_data/ejabberd.redis.yml | 1 + .../ejabberd_SUITE_data/whitelist_domains.txt | 1 + test/suite.erl | 1 + 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 test/ejabberd_SUITE_data/whitelist_domains.txt diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index f6f589c0d..d56ac53cd 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -58,7 +58,8 @@ single_cases() -> single_test(block_domain_in_vhost), single_test(unblock_domain_in_vhost2), single_test(jid_cache), - single_test(rtbl_domains)]}. + single_test(rtbl_domains), + single_test(rtbl_domains_whitelisted)]}. %%%=================================================================== @@ -200,6 +201,46 @@ rtbl_domains(Config) -> {result, _} = mod_pubsub:delete_node(RTBLHost, RTBLDomainsNode, Owner), disconnect(Config). +rtbl_domains_whitelisted(Config) -> + Host = ?config(server, Config), + RTBLHost = + jid:to_string( + suite:pubsub_jid(Config)), + RTBLDomainsNode = <<"spam_source_domains">>, + OldOpts = gen_mod:get_module_opts(Host, mod_antispam), + NewOpts = + maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), + Owner = jid:make(?config(user, Config), ?config(server, Config), <<>>), + {result, _} = + mod_pubsub:create_node(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"flat">>), + {result, _} = + mod_pubsub:publish_item(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"whitelisted.domain">>, + [xmpp:encode(#ps_item{id = <<"whitelisted.domain">>, + sub_els = []})]), + mod_antispam:reload(Host, OldOpts, NewOpts), + {result, _} = + mod_pubsub:publish_item(RTBLHost, + ?config(server, Config), + RTBLDomainsNode, + Owner, + <<"yetanother.domain">>, + [xmpp:encode(#ps_item{id = <<"yetanother.domain">>, + sub_els = []})]), + ?retry(100, 10, ?match(true, (has_spam_domain(<<"yetanother.domain">>))(Host))), + %% we assume that the previous "whitelisted.domain" pubsub item has been consumed by now, so we + %% can check that it doesn't exist + ?match(false, (has_spam_domain(<<"whitelisted.domain">>))(Host)), + {result, _} = mod_pubsub:delete_node(RTBLHost, RTBLDomainsNode, Owner), + disconnect(Config). + %%%=================================================================== %%% Internal functions %%%=================================================================== diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml index c8585e6e4..80f4da81c 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml @@ -11,6 +11,7 @@ define_macro: spam_jids_file: spam_jids.txt spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt + whitelist_domains_file: whitelist_domains.txt mod_blocking: [] mod_caps: db_type: internal diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml index cdbc905bd..14737b17c 100644 --- a/test/ejabberd_SUITE_data/ejabberd.redis.yml +++ b/test/ejabberd_SUITE_data/ejabberd.redis.yml @@ -12,6 +12,7 @@ define_macro: spam_jids_file: spam_jids.txt spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt + whitelist_domains_file: whitelist_domains.txt mod_blocking: [] mod_caps: db_type: internal diff --git a/test/ejabberd_SUITE_data/whitelist_domains.txt b/test/ejabberd_SUITE_data/whitelist_domains.txt new file mode 100644 index 000000000..b953fb7f6 --- /dev/null +++ b/test/ejabberd_SUITE_data/whitelist_domains.txt @@ -0,0 +1 @@ +whitelisted.domain diff --git a/test/suite.erl b/test/suite.erl index 62b442580..7bc27e913 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -54,6 +54,7 @@ init_config(Config) -> copy_file(Config, "spam_jids.txt"), copy_file(Config, "spam_urls.txt"), copy_file(Config, "spam_domains.txt"), + copy_file(Config, "whitelist_domains.txt"), {ok, MacrosContentTpl} = file:read_file(MacrosPathTpl), Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>, Backends = get_config_backends(), From bae345b92b57a6f61ec1654b90ddc9a75b0184f1 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Tue, 17 Jun 2025 12:24:02 +0200 Subject: [PATCH 1163/1302] mod_antispam: test dump file --- test/antispam_tests.erl | 21 +++++++++++++++++++- test/ejabberd_SUITE_data/ejabberd.mnesia.yml | 1 + test/ejabberd_SUITE_data/ejabberd.redis.yml | 1 + test/suite.erl | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index d56ac53cd..0eb8d4041 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -59,7 +59,8 @@ single_cases() -> single_test(unblock_domain_in_vhost2), single_test(jid_cache), single_test(rtbl_domains), - single_test(rtbl_domains_whitelisted)]}. + single_test(rtbl_domains_whitelisted), + single_test(spam_dump_file)]}. %%%=================================================================== @@ -241,6 +242,20 @@ rtbl_domains_whitelisted(Config) -> {result, _} = mod_pubsub:delete_node(RTBLHost, RTBLDomainsNode, Owner), disconnect(Config). +%%%=================================================================== + +spam_dump_file(Config) -> + {ok, CWD} = file:get_cwd(), + Filename = filename:join([CWD, "spam.log"]), + ?retry(100, 10, + ?match(true, size(get_bytes(Filename)) > 0)), + From = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), + To = my_jid(Config), + is_spam(message(From, To, <<"A very specific spam message">>)), + ?retry(100, 100, + ?match({match, _}, + re:run(get_bytes(Filename), <<"A very specific spam message">>))). + %%%=================================================================== %%% Internal functions %%%=================================================================== @@ -266,3 +281,7 @@ message(From, To, BodyText) -> to = To, type = chat, body = [#text{data = BodyText}]}. + +get_bytes(Filename) -> + {ok, Bytes} = file:read_file(Filename), + Bytes. diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml index 80f4da81c..ae78645ae 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml @@ -12,6 +12,7 @@ define_macro: spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt whitelist_domains_file: whitelist_domains.txt + spam_dump_file: spam.log mod_blocking: [] mod_caps: db_type: internal diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml index 14737b17c..8ec927e86 100644 --- a/test/ejabberd_SUITE_data/ejabberd.redis.yml +++ b/test/ejabberd_SUITE_data/ejabberd.redis.yml @@ -13,6 +13,7 @@ define_macro: spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt whitelist_domains_file: whitelist_domains.txt + spam_dump_file: spam.log mod_blocking: [] mod_caps: db_type: internal diff --git a/test/suite.erl b/test/suite.erl index 7bc27e913..5fbd70463 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -55,6 +55,7 @@ init_config(Config) -> copy_file(Config, "spam_urls.txt"), copy_file(Config, "spam_domains.txt"), copy_file(Config, "whitelist_domains.txt"), + file:write_file(filename:join([CWD, "spam.log"]), []), {ok, MacrosContentTpl} = file:read_file(MacrosPathTpl), Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>, Backends = get_config_backends(), From 149b715b4f31ec9c22aba7445010e3b8c21ab88a Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 10 Jun 2025 13:52:59 +0200 Subject: [PATCH 1164/1302] New predefined keyword: LOG_PATH --- src/ejabberd_config.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 3a556d7f6..89f89504d 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -504,8 +504,12 @@ get_predefined_keywords(Host) -> [{<<"HOST">>, Host}] end, Home = misc:get_home(), + LogDirPath = + iolist_to_binary(filename:dirname( + ejabberd_logger:get_log_path())), HostList ++ [{<<"HOME">>, list_to_binary(Home)}, + {<<"LOG_PATH">>, LogDirPath}, {<<"SEMVER">>, ejabberd_option:version()}, {<<"VERSION">>, misc:semver_to_xxyy( From 85f05192c821782d5748bd0f334518a4e3614aa4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 10 Jun 2025 16:56:50 +0200 Subject: [PATCH 1165/1302] Move spam_dump_file implementation to a submodule --- src/mod_antispam.erl | 170 ++++++++++------------------------ src/mod_antispam_dump.erl | 189 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 120 deletions(-) create mode 100644 src/mod_antispam_dump.erl diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 664e5d67f..8b161b558 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -24,6 +24,8 @@ %%% %%%---------------------------------------------------------------------- +%%| definitions + -module(mod_antispam). -author('holger@zedat.fu-berlin.de'). -author('stefan@strigler.de'). @@ -51,8 +53,7 @@ %% ejabberd_hooks callbacks. -export([s2s_in_handle_info/2, s2s_receive_packet/1, - sm_receive_packet/1, - reopen_log/0]). + sm_receive_packet/1]). %% ejabberd_commands callbacks. -export([add_blocked_domain/2, @@ -67,11 +68,12 @@ -include("ejabberd_commands.hrl"). -include("logger.hrl"). +-include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -type url() :: binary(). --type filename() :: binary() | none. +-type filename() :: binary() | none | false. -type jid_set() :: sets:set(ljid()). -type url_set() :: sets:set(url()). -type s2s_in_state() :: ejabberd_s2s_in:state(). @@ -101,8 +103,8 @@ %% @format-begin %%-------------------------------------------------------------------- -%% gen_mod callbacks. -%%-------------------------------------------------------------------- +%%| gen_mod callbacks + -spec start(binary(), gen_mod:opts()) -> ok | {error, any()}. start(Host, Opts) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of @@ -142,7 +144,7 @@ mod_opt_type(whitelist_domains_file) -> econf:enum([none]), econf:file()); mod_opt_type(spam_dump_file) -> econf:either( - econf:enum([none]), econf:file()); + econf:bool(), econf:file(write)); mod_opt_type(spam_jids_file) -> econf:either( econf:enum([none]), econf:file()); @@ -163,7 +165,7 @@ mod_opt_type(rtbl_domains_node) -> -spec mod_options(binary()) -> [{atom(), any()}]. mod_options(_Host) -> [{spam_domains_file, none}, - {spam_dump_file, none}, + {spam_dump_file, false}, {spam_jids_file, none}, {spam_urls_file, none}, {whitelist_domains_file, none}, @@ -173,15 +175,28 @@ mod_options(_Host) -> {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}]. mod_doc() -> - #{}. + #{desc => ?T("Reads from text file and RTBL, filters stanzas and writes dump file."), + note => "added in 25.xx", + opts => + [{spam_dump_file, + #{value => ?T("false | true | Path"), + desc => + ?T("Path to the file to store blocked messages. " + "Use an absolute path, or the '@LOG_PATH@' macro to store logs " + "in the same place that the other ejabberd log files. " + "If set to 'false', does not dump stanzas, this is the default. " + "If set to 'true', it stores in '\"@LOG_PATH@/spam_dump_@HOST@.log\"'.")}}], + example => + ["modules:", + " mod_antispam:", + " spam_dump_file: \"@LOG_PATH@/spam/host-@HOST@.log\""]}. %%-------------------------------------------------------------------- -%% gen_server callbacks. -%%-------------------------------------------------------------------- +%%| gen_server callbacks + -spec init(list()) -> {ok, state()} | {stop, term()}. init([Host, Opts]) -> process_flag(trap_exit, true), - DumpFile = expand_host(gen_mod:get_opt(spam_dump_file, Opts), Host), Files = #{domains => gen_mod:get_opt(spam_domains_file, Opts), jid => gen_mod:get_opt(spam_jids_file, Opts), @@ -195,7 +210,6 @@ init([Host, Opts]) -> ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), - ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 50), ejabberd_hooks:add(local_send_to_resource_hook, Host, mod_antispam_rtbl, @@ -203,17 +217,17 @@ init([Host, Opts]) -> 50), RTBLHost = gen_mod:get_opt(rtbl_host, Opts), RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), - InitState0 = + InitState = #state{host = Host, jid_set = JIDsSet, url_set = URLsSet, + dump_fd = mod_antispam_dump:init_dumping(Host), max_cache_size = gen_mod:get_opt(cache_size, Opts), blocked_domains = set_to_map(SpamDomainsSet), whitelist_domains = set_to_map(WhitelistDomains, false), rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode}, mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), - InitState = init_open_dump_file(DumpFile, InitState0), {ok, InitState} catch {Op, File, Reason} when Op == open; Op == read -> @@ -221,18 +235,6 @@ init([Host, Opts]) -> {stop, config_error} end. -init_open_dump_file(none, State) -> - State; -init_open_dump_file(DumpFile, State) -> - case filelib:ensure_dir(DumpFile) of - ok -> - ok; - {error, Reason} -> - Dirname = filename:dirname(DumpFile), - throw({open, Dirname, Reason}) - end, - open_dump_file(DumpFile, State). - -spec handle_call(term(), {pid(), term()}, state()) -> {reply, {spam_filter, term()}, state()} | {noreply, state()}. handle_call({check_jid, From}, _From, #state{jid_set = JIDsSet} = State) -> @@ -296,32 +298,21 @@ handle_call(Request, From, State) -> {noreply, State}. -spec handle_cast(term(), state()) -> {noreply, state()}. -handle_cast({dump, _XML}, #state{dump_fd = undefined} = State) -> - {noreply, State}; -handle_cast({dump, XML}, #state{dump_fd = Fd} = State) -> - case file:write(Fd, [XML, <<$\n>>]) of - ok -> - ok; - {error, Reason} -> - ?ERROR_MSG("Cannot write spam to dump file: ~s", [file:format_error(Reason)]) - end, +handle_cast({dump_stanza, XML}, #state{dump_fd = Fd} = State) -> + mod_antispam_dump:write_stanza_dump(Fd, XML), {noreply, State}; +handle_cast(reopen_log, #state{host = Host, dump_fd = Fd} = State) -> + {noreply, State#state{dump_fd = mod_antispam_dump:reopen_dump_file(Host, Fd)}}; handle_cast({reload, NewOpts, OldOpts}, #state{host = Host, + dump_fd = Fd, rtbl_host = OldRTBLHost, rtbl_domains_node = OldRTBLDomainsNode, rtbl_retry_timer = RTBLRetryTimer} = State) -> misc:cancel_timer(RTBLRetryTimer), State1 = - case {gen_mod:get_opt(spam_dump_file, OldOpts), gen_mod:get_opt(spam_dump_file, NewOpts)} - of - {OldDumpFile, NewDumpFile} when NewDumpFile /= OldDumpFile -> - close_dump_file(expand_host(OldDumpFile, Host), State), - open_dump_file(expand_host(NewDumpFile, Host), State); - {_OldDumpFile, _NewDumpFile} -> - State - end, + State#state{dump_fd = mod_antispam_dump:reload_dumping(Host, Fd, OldOpts, NewOpts)}, State2 = case {gen_mod:get_opt(cache_size, OldOpts), gen_mod:get_opt(cache_size, NewOpts)} of {OldMax, NewMax} when NewMax < OldMax -> @@ -342,8 +333,6 @@ handle_cast({reload, NewOpts, OldOpts}, RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, NewOpts), ok = mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), {noreply, State3#state{rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode}}; -handle_cast(reopen_log, State) -> - {noreply, reopen_dump_file(State)}; handle_cast({update_blocked_domains, NewItems}, #state{blocked_domains = BlockedDomains} = State) -> {noreply, State#state{blocked_domains = maps:merge(BlockedDomains, NewItems)}}; @@ -411,15 +400,14 @@ handle_info(Info, State) -> -spec terminate(normal | shutdown | {shutdown, term()} | term(), state()) -> ok. terminate(Reason, #state{host = Host, + dump_fd = Fd, rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode, rtbl_retry_timer = RTBLRetryTimer} = - State) -> + _State) -> ?DEBUG("Stopping spam filter process for ~s: ~p", [Host, Reason]), misc:cancel_timer(RTBLRetryTimer), - DumpFile = gen_mod:get_module_opt(Host, ?MODULE, spam_dump_file), - DumpFile1 = expand_host(DumpFile, Host), - close_dump_file(DumpFile1, State), + mod_antispam_dump:terminate_dumping(Host, Fd), ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), @@ -429,12 +417,7 @@ terminate(Reason, pubsub_event_handler, 50), mod_antispam_rtbl:unsubscribe(RTBLHost, RTBLDomainsNode, Host), - case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of - false -> - ejabberd_hooks:delete(reopen_log_hook, ?MODULE, reopen_log, 50); - true -> - ok - end. + ok. -spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}. code_change(_OldVsn, #state{host = Host} = State, _Extra) -> @@ -442,8 +425,7 @@ code_change(_OldVsn, #state{host = Host} = State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- -%% Hook callbacks. -%%-------------------------------------------------------------------- +%%| Hook callbacks -spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) -> {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. @@ -505,17 +487,9 @@ s2s_in_handle_info(State, {_Ref, {spam_filter, _}}) -> s2s_in_handle_info(State, _) -> State. --spec reopen_log() -> ok. -reopen_log() -> - lists:foreach(fun(Host) -> - Proc = get_proc_name(Host), - gen_server:cast(Proc, reopen_log) - end, - get_spam_filter_hosts()). +%%-------------------------------------------------------------------- +%%| Internal functions -%%-------------------------------------------------------------------- -%% Internal functions. -%%-------------------------------------------------------------------- -spec needs_checking(jid(), jid()) -> boolean(). needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> case gen_mod:is_loaded(LServer, ?MODULE) of @@ -814,7 +788,7 @@ reject(#message{from = From, [jid:encode(From), jid:encode(To)]), Txt = <<"Your message is unsolicited">>, Err = xmpp:err_policy_violation(Txt, Lang), - maybe_dump_spam(Msg), + ejabberd_hooks:run(spam_stanza_rejected, To#jid.lserver, [Msg]), ejabberd_router:route_error(Msg, Err); reject(#presence{from = From, to = To, @@ -828,48 +802,6 @@ reject(#presence{from = From, reject(_) -> ok. --spec open_dump_file(filename(), state()) -> state(). -open_dump_file(none, State) -> - State#state{dump_fd = undefined}; -open_dump_file(Name, State) -> - Modes = [append, raw, binary, delayed_write], - case file:open(Name, Modes) of - {ok, Fd} -> - ?DEBUG("Opened ~s", [Name]), - State#state{dump_fd = Fd}; - {error, Reason} -> - ?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]), - State#state{dump_fd = undefined} - end. - --spec close_dump_file(filename(), state()) -> ok. -close_dump_file(_Name, #state{dump_fd = undefined}) -> - ok; -close_dump_file(Name, #state{dump_fd = Fd}) -> - case file:close(Fd) of - ok -> - ?DEBUG("Closed ~s", [Name]); - {error, Reason} -> - ?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)]) - end. - --spec reopen_dump_file(state()) -> state(). -reopen_dump_file(#state{host = Host} = State) -> - DumpFile = gen_mod:get_module_opt(Host, ?MODULE, spam_dump_file), - DumpFile1 = expand_host(DumpFile, Host), - close_dump_file(DumpFile1, State), - open_dump_file(DumpFile1, State). - --spec maybe_dump_spam(message()) -> ok. -maybe_dump_spam(#message{to = #jid{lserver = LServer}} = Msg) -> - By = jid:make(<<>>, LServer), - Proc = get_proc_name(LServer), - Time = erlang:timestamp(), - Msg1 = misc:add_delay_info(Msg, By, Time), - XML = fxml:element_to_binary( - xmpp:encode(Msg1)), - gen_server:cast(Proc, {dump, XML}). - -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?MODULE). @@ -878,12 +810,6 @@ get_proc_name(Host) -> get_spam_filter_hosts() -> [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE)]. --spec expand_host(binary() | none, binary()) -> binary() | none. -expand_host(none, _Host) -> - none; -expand_host(Input, Host) -> - misc:expand_keyword(<<"@HOST@">>, Input, Host). - -spec sets_equal(sets:set(), sets:set()) -> boolean(). sets_equal(A, B) -> sets:is_subset(A, B) andalso sets:is_subset(B, A). @@ -901,8 +827,8 @@ format_error(Reason) -> list_to_binary(file:format_error(Reason)). %%-------------------------------------------------------------------- -%% Caching. -%%-------------------------------------------------------------------- +%%| Caching + -spec cache_insert(ljid(), state()) -> state(). cache_insert(_LJID, #state{max_cache_size = 0} = State) -> State; @@ -961,8 +887,8 @@ drop_from_cache(LJID, #state{jid_cache = Cache} = State) -> end. %%-------------------------------------------------------------------- -%% ejabberd command callbacks. -%%-------------------------------------------------------------------- +%%| ejabberd command callbacks + -spec get_commands_spec() -> [ejabberd_commands()]. get_commands_spec() -> [#ejabberd_commands{name = reload_spam_filter_files, @@ -1172,3 +1098,7 @@ drop_from_spam_filter_cache(Host, EncJID) -> _:{bad_jid, _} -> {error, "Not a valid JID: " ++ binary_to_list(EncJID)} end. + +%%-------------------------------------------------------------------- + +%%| vim: set foldmethod=marker foldmarker=%%|,%%-: diff --git a/src/mod_antispam_dump.erl b/src/mod_antispam_dump.erl new file mode 100644 index 000000000..134da7c74 --- /dev/null +++ b/src/mod_antispam_dump.erl @@ -0,0 +1,189 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_antispam_dump.erl +%%% Author : Holger Weiss +%%% Author : Stefan Strigler +%%% Purpose : Filter spam messages based on sender JID and content +%%% Created : 31 Mar 2019 by Holger Weiss +%%% +%%% +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +%%| definitions +%% @format-begin + +-module(mod_antispam_dump). + +-author('holger@zedat.fu-berlin.de'). +-author('stefan@strigler.de'). + +-export([init_dumping/1, terminate_dumping/2, reload_dumping/4, reopen_dump_file/2, + write_stanza_dump/2]). +%% ejabberd_hooks callbacks. +-export([dump_spam_stanza/1, reopen_log/0]). + +-include("logger.hrl"). +-include("translate.hrl"). + +-include_lib("xmpp/include/xmpp.hrl"). + +-type filename() :: binary() | none | false. + +-define(MODULE_PARENT, mod_antispam). + +%%-------------------------------------------------------------------- +%%| Exported + +init_dumping(Host) -> + case get_path_option(Host) of + false -> + undefined; + DumpFile when is_binary(DumpFile) -> + case filelib:ensure_dir(DumpFile) of + ok -> + ejabberd_hooks:add(spam_stanza_rejected, Host, ?MODULE, dump_spam_stanza, 50), + ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 50), + open_dump_file(DumpFile); + {error, Reason} -> + Dirname = filename:dirname(DumpFile), + throw({open, Dirname, Reason}) + end + end. + +terminate_dumping(_Host, false) -> + ok; +terminate_dumping(Host, Fd) -> + DumpFile1 = get_path_option(Host), + close_dump_file(Fd, DumpFile1), + ejabberd_hooks:delete(spam_stanza_rejected, Host, ?MODULE, dump_spam_stanza, 50), + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_hooks:delete(reopen_log_hook, ?MODULE, reopen_log, 50); + true -> + ok + end. + +reload_dumping(Host, Fd, OldOpts, NewOpts) -> + case {get_path_option(Host, OldOpts), get_path_option(Host, NewOpts)} of + {Old, Old} -> + Fd; + {Old, New} -> + reopen_dump_file(Fd, Old, New) + end. + +-spec reopen_dump_file(binary(), file:io_device()) -> file:io_device(). +reopen_dump_file(Host, Fd) -> + DumpFile1 = get_path_option(Host), + reopen_dump_file(Fd, DumpFile1, DumpFile1). + +%%-------------------------------------------------------------------- +%%| Hook callbacks + +-spec dump_spam_stanza(message()) -> ok. +dump_spam_stanza(#message{to = #jid{lserver = LServer}} = Msg) -> + By = jid:make(<<>>, LServer), + Proc = get_proc_name(LServer), + Time = erlang:timestamp(), + Msg1 = misc:add_delay_info(Msg, By, Time), + XML = fxml:element_to_binary( + xmpp:encode(Msg1)), + gen_server:cast(Proc, {dump_stanza, XML}). + +-spec reopen_log() -> ok. +reopen_log() -> + lists:foreach(fun(Host) -> + Proc = get_proc_name(Host), + gen_server:cast(Proc, reopen_log) + end, + get_spam_filter_hosts()). + +%%-------------------------------------------------------------------- +%%| File management + +-spec open_dump_file(filename()) -> undefined | file:io_device(). +open_dump_file(false) -> + undefined; +open_dump_file(Name) -> + Modes = [append, raw, binary, delayed_write], + case file:open(Name, Modes) of + {ok, Fd} -> + ?DEBUG("Opened ~s", [Name]), + Fd; + {error, Reason} -> + ?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]), + undefined + end. + +-spec close_dump_file(undefined | file:io_device(), filename()) -> ok. +close_dump_file(undefined, false) -> + ok; +close_dump_file(Fd, Name) -> + case file:close(Fd) of + ok -> + ?DEBUG("Closed ~s", [Name]); + {error, Reason} -> + ?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)]) + end. + +-spec reopen_dump_file(file:io_device(), binary(), binary()) -> file:io_device(). +reopen_dump_file(Fd, OldDumpFile, NewDumpFile) -> + close_dump_file(Fd, OldDumpFile), + open_dump_file(NewDumpFile). + +write_stanza_dump(Fd, XML) -> + case file:write(Fd, [XML, <<$\n>>]) of + ok -> + ok; + {error, Reason} -> + ?ERROR_MSG("Cannot write spam to dump file: ~s", [file:format_error(Reason)]) + end. + +%%-------------------------------------------------------------------- +%%| Auxiliary + +get_path_option(Host) -> + Opts = gen_mod:get_module_opts(Host, ?MODULE_PARENT), + get_path_option(Host, Opts). + +get_path_option(Host, Opts) -> + case gen_mod:get_opt(spam_dump_file, Opts) of + false -> + false; + true -> + LogDirPath = + iolist_to_binary(filename:dirname( + ejabberd_logger:get_log_path())), + filename:join([LogDirPath, <<"spam_dump_", Host/binary, ".log">>]); + B when is_binary(B) -> + B + end. + +%%-------------------------------------------------------------------- +%%| Copied from mod_antispam.erl + +-spec get_proc_name(binary()) -> atom(). +get_proc_name(Host) -> + gen_mod:get_module_proc(Host, ?MODULE_PARENT). + +-spec get_spam_filter_hosts() -> [binary()]. +get_spam_filter_hosts() -> + [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE_PARENT)]. + +%%-------------------------------------------------------------------- + +%%| vim: set foldmethod=marker foldmarker=%%|,%%-: From d9a7b67f0e5755a19dc24d80d55fe74d373c63f3 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Tue, 17 Jun 2025 17:21:58 +0200 Subject: [PATCH 1166/1302] mod_antispam: increase timeout when waiting for dump file --- test/antispam_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index 0eb8d4041..280dacb97 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -247,7 +247,7 @@ rtbl_domains_whitelisted(Config) -> spam_dump_file(Config) -> {ok, CWD} = file:get_cwd(), Filename = filename:join([CWD, "spam.log"]), - ?retry(100, 10, + ?retry(100, 100, ?match(true, size(get_bytes(Filename)) > 0)), From = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), To = my_jid(Config), From f3b1b5d41935ee4c6e85d4f1dab5ed8ddca9d3b5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Jun 2025 11:35:06 +0200 Subject: [PATCH 1167/1302] Result of running "make format" --- test/antispam_tests.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index 280dacb97..debfe9981 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -247,14 +247,13 @@ rtbl_domains_whitelisted(Config) -> spam_dump_file(Config) -> {ok, CWD} = file:get_cwd(), Filename = filename:join([CWD, "spam.log"]), - ?retry(100, 100, - ?match(true, size(get_bytes(Filename)) > 0)), + ?retry(100, 100, ?match(true, size(get_bytes(Filename)) > 0)), From = jid:make(<<"spammer_jid">>, <<"localhost">>, <<"spam_client">>), To = my_jid(Config), is_spam(message(From, To, <<"A very specific spam message">>)), - ?retry(100, 100, - ?match({match, _}, - re:run(get_bytes(Filename), <<"A very specific spam message">>))). + ?retry(100, + 100, + ?match({match, _}, re:run(get_bytes(Filename), <<"A very specific spam message">>))). %%%=================================================================== %%% Internal functions From 432810db894dcc0f1160eea34cbf4eec1f611e6f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Jun 2025 11:34:34 +0200 Subject: [PATCH 1168/1302] Fix minor typos --- src/mod_antispam_dump.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mod_antispam_dump.erl b/src/mod_antispam_dump.erl index 134da7c74..04a671f05 100644 --- a/src/mod_antispam_dump.erl +++ b/src/mod_antispam_dump.erl @@ -2,7 +2,7 @@ %%% File : mod_antispam_dump.erl %%% Author : Holger Weiss %%% Author : Stefan Strigler -%%% Purpose : Filter spam messages based on sender JID and content +%%% Purpose : Manage dump file for filtered spam messages %%% Created : 31 Mar 2019 by Holger Weiss %%% %%% @@ -24,7 +24,7 @@ %%% %%%---------------------------------------------------------------------- -%%| definitions +%%| Definitions %% @format-begin -module(mod_antispam_dump). @@ -34,7 +34,7 @@ -export([init_dumping/1, terminate_dumping/2, reload_dumping/4, reopen_dump_file/2, write_stanza_dump/2]). -%% ejabberd_hooks callbacks. +%% ejabberd_hooks callbacks -export([dump_spam_stanza/1, reopen_log/0]). -include("logger.hrl"). From d00561b58c2d3f1ecf9d1ed1cda455f6ac08691c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Jun 2025 10:32:49 +0200 Subject: [PATCH 1169/1302] Move filtering implementation to a submodule --- src/mod_antispam.erl | 253 ++---------------------------- src/mod_antispam_filter.erl | 299 ++++++++++++++++++++++++++++++++++++ test/antispam_tests.erl | 5 +- 3 files changed, 313 insertions(+), 244 deletions(-) create mode 100644 src/mod_antispam_filter.erl diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 8b161b558..ef2106219 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -50,11 +50,6 @@ terminate/2, code_change/3]). -%% ejabberd_hooks callbacks. --export([s2s_in_handle_info/2, - s2s_receive_packet/1, - sm_receive_packet/1]). - %% ejabberd_commands callbacks. -export([add_blocked_domain/2, add_to_spam_filter_cache/2, @@ -76,7 +71,6 @@ -type filename() :: binary() | none | false. -type jid_set() :: sets:set(ljid()). -type url_set() :: sets:set(url()). --type s2s_in_state() :: ejabberd_s2s_in:state(). -record(state, {host = <<>> :: binary(), @@ -96,7 +90,6 @@ -type state() :: #state{}. -define(COMMAND_TIMEOUT, timer:seconds(30)). --define(HTTPC_TIMEOUT, timer:seconds(3)). -define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). -define(DEFAULT_CACHE_SIZE, 10000). @@ -178,7 +171,15 @@ mod_doc() -> #{desc => ?T("Reads from text file and RTBL, filters stanzas and writes dump file."), note => "added in 25.xx", opts => - [{spam_dump_file, + [{access_spam, + #{value => ?T("Access"), + desc => + ?T("Access rule that controls what accounts may receive spam messages. " + "If the rule returns `allow` for a given recipient, " + "spam messages aren't rejected for that recipient. " + "The default value is 'none', which means that all recipients " + "are subject to spam filtering verification.")}}, + {spam_dump_file, #{value => ?T("false | true | Path"), desc => ?T("Path to the file to store blocked messages. " @@ -207,9 +208,6 @@ init([Host, Opts]) -> url := URLsSet, domains := SpamDomainsSet, whitelist_domains := WhitelistDomains} -> - ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), - ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), - ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), ejabberd_hooks:add(local_send_to_resource_hook, Host, mod_antispam_rtbl, @@ -217,6 +215,7 @@ init([Host, Opts]) -> 50), RTBLHost = gen_mod:get_opt(rtbl_host, Opts), RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), + mod_antispam_filter:init_filtering(Host), InitState = #state{host = Host, jid_set = JIDsSet, @@ -252,9 +251,6 @@ handle_call({check_body, URLs, JIDs, From}, Result2 end, {reply, {spam_filter, Result}, State2}; -handle_call({resolve_redirects, URLs}, _From, State) -> - ResolvedURLs = do_resolve_redirects(URLs, []), - {reply, {spam_filter, ResolvedURLs}, State}; handle_call({reload_files, Files}, _From, State) -> {Result, State1} = reload_files(Files, State), {reply, {spam_filter, Result}, State1}; @@ -408,9 +404,7 @@ terminate(Reason, ?DEBUG("Stopping spam filter process for ~s: ~p", [Host, Reason]), misc:cancel_timer(RTBLRetryTimer), mod_antispam_dump:terminate_dumping(Host, Fd), - ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), - ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), - ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), + mod_antispam_filter:terminate_filtering(Host), ejabberd_hooks:delete(local_send_to_resource_hook, Host, mod_antispam_rtbl, @@ -424,209 +418,9 @@ code_change(_OldVsn, #state{host = Host} = State, _Extra) -> ?DEBUG("Updating spam filter process for ~s", [Host]), {ok, State}. -%%-------------------------------------------------------------------- -%%| Hook callbacks - --spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) -> - {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. -s2s_receive_packet({A, State}) -> - case sm_receive_packet(A) of - {stop, drop} -> - {stop, {drop, State}}; - Result -> - {Result, State} - end. - --spec sm_receive_packet(stanza() | drop) -> stanza() | drop | {stop, drop}. -sm_receive_packet(drop = Acc) -> - Acc; -sm_receive_packet(#message{from = From, - to = #jid{lserver = LServer} = To, - type = Type} = - Msg) - when Type /= groupchat, Type /= error -> - do_check(From, To, LServer, Msg); -sm_receive_packet(#presence{from = From, - to = #jid{lserver = LServer} = To, - type = subscribe} = - Presence) -> - do_check(From, To, LServer, Presence); -sm_receive_packet(Acc) -> - Acc. - -do_check(From, To, LServer, Stanza) -> - case needs_checking(From, To) of - true -> - case check_from(LServer, From) of - ham -> - case check_stanza(LServer, From, Stanza) of - ham -> - Stanza; - spam -> - reject(Stanza), - {stop, drop} - end; - spam -> - reject(Stanza), - {stop, drop} - end; - false -> - Stanza - end. - -check_stanza(LServer, From, #message{body = Body}) -> - check_body(LServer, From, xmpp:get_text(Body)); -check_stanza(_, _, _) -> - ham. - --spec s2s_in_handle_info(s2s_in_state(), any()) -> - s2s_in_state() | {stop, s2s_in_state()}. -s2s_in_handle_info(State, {_Ref, {spam_filter, _}}) -> - ?DEBUG("Dropping expired spam filter result", []), - {stop, State}; -s2s_in_handle_info(State, _) -> - State. - %%-------------------------------------------------------------------- %%| Internal functions --spec needs_checking(jid(), jid()) -> boolean(). -needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> - case gen_mod:is_loaded(LServer, ?MODULE) of - true -> - Access = gen_mod:get_module_opt(LServer, ?MODULE, access_spam), - case acl:match_rule(LServer, Access, To) of - allow -> - ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), - false; - deny -> - ?DEBUG("Spam is filtered for ~s", [jid:encode(To)]), - not mod_roster:is_subscribed(From, To) - andalso not - mod_roster:is_subscribed( - jid:make(<<>>, FromHost), - To) % likely a gateway - end; - false -> - ?DEBUG("~s not loaded for ~s", [?MODULE, LServer]), - false - end. - --spec check_from(binary(), jid()) -> ham | spam. -check_from(Host, From) -> - Proc = get_proc_name(Host), - LFrom = - {_, FromDomain, _} = - jid:remove_resource( - jid:tolower(From)), - try - case gen_server:call(Proc, {is_blocked_domain, FromDomain}) of - true -> - ?DEBUG("Spam JID found in blocked domains: ~p", [From]), - ejabberd_hooks:run(spam_found, Host, [{jid, From}]), - spam; - false -> - case gen_server:call(Proc, {check_jid, LFrom}) of - {spam_filter, Result} -> - Result - end - end - catch - exit:{timeout, _} -> - ?WARNING_MSG("Timeout while checking ~s against list of blocked domains or spammers", - [jid:encode(From)]), - ham - end. - --spec check_body(binary(), jid(), binary()) -> ham | spam. -check_body(Host, From, Body) -> - case {extract_urls(Host, Body), extract_jids(Body)} of - {none, none} -> - ?DEBUG("No JIDs/URLs found in message", []), - ham; - {URLs, JIDs} -> - Proc = get_proc_name(Host), - LFrom = - jid:remove_resource( - jid:tolower(From)), - try gen_server:call(Proc, {check_body, URLs, JIDs, LFrom}) of - {spam_filter, Result} -> - Result - catch - exit:{timeout, _} -> - ?WARNING_MSG("Timeout while checking body", []), - ham - end - end. - --spec extract_urls(binary(), binary()) -> {urls, [url()]} | none. -extract_urls(Host, Body) -> - RE = <<"https?://\\S+">>, - Options = [global, {capture, all, binary}], - case re:run(Body, RE, Options) of - {match, Captured} when is_list(Captured) -> - Urls = resolve_redirects(Host, lists:flatten(Captured)), - {urls, Urls}; - nomatch -> - none - end. - --spec resolve_redirects(binary(), [url()]) -> [url()]. -resolve_redirects(Host, URLs) -> - Proc = get_proc_name(Host), - try gen_server:call(Proc, {resolve_redirects, URLs}) of - {spam_filter, ResolvedURLs} -> - ResolvedURLs - catch - exit:{timeout, _} -> - ?WARNING_MSG("Timeout while resolving redirects: ~p", [URLs]), - URLs - end. - --spec do_resolve_redirects([url()], [url()]) -> [url()]. -do_resolve_redirects([], Result) -> - Result; -do_resolve_redirects([URL | Rest], Acc) -> - case httpc:request(get, - {URL, [{"user-agent", "curl/8.7.1"}]}, - [{autoredirect, false}, {timeout, ?HTTPC_TIMEOUT}], - []) - of - {ok, {{_, StatusCode, _}, Headers, _Body}} when StatusCode >= 300, StatusCode < 400 -> - Location = proplists:get_value("location", Headers), - case Location == undefined orelse lists:member(Location, Acc) of - true -> - do_resolve_redirects(Rest, [URL | Acc]); - false -> - do_resolve_redirects([Location | Rest], [URL | Acc]) - end; - _Res -> - do_resolve_redirects(Rest, [URL | Acc]) - end. - --spec extract_jids(binary()) -> {jids, [ljid()]} | none. -extract_jids(Body) -> - RE = <<"\\S+@\\S+">>, - Options = [global, {capture, all, binary}], - case re:run(Body, RE, Options) of - {match, Captured} when is_list(Captured) -> - {jids, lists:filtermap(fun try_decode_jid/1, lists:flatten(Captured))}; - nomatch -> - none - end. - --spec try_decode_jid(binary()) -> {true, ljid()} | false. -try_decode_jid(S) -> - try jid:decode(S) of - #jid{} = JID -> - {true, - jid:remove_resource( - jid:tolower(JID))} - catch - _:{bad_jid, _} -> - false - end. - -spec filter_jid(ljid(), jid_set(), state()) -> {ham | spam, state()}. filter_jid(From, Set, #state{host = Host} = State) -> case sets:is_element(From, Set) of @@ -777,31 +571,6 @@ parse_url(S) -> trim(S) -> re:replace(S, <<"\\s+$">>, <<>>, [{return, binary}]). --spec reject(stanza()) -> ok. -reject(#message{from = From, - to = To, - type = Type, - lang = Lang} = - Msg) - when Type /= groupchat, Type /= error -> - ?INFO_MSG("Rejecting unsolicited message from ~s to ~s", - [jid:encode(From), jid:encode(To)]), - Txt = <<"Your message is unsolicited">>, - Err = xmpp:err_policy_violation(Txt, Lang), - ejabberd_hooks:run(spam_stanza_rejected, To#jid.lserver, [Msg]), - ejabberd_router:route_error(Msg, Err); -reject(#presence{from = From, - to = To, - lang = Lang} = - Presence) -> - ?INFO_MSG("Rejecting unsolicited presence from ~s to ~s", - [jid:encode(From), jid:encode(To)]), - Txt = <<"Your traffic is unsolicited">>, - Err = xmpp:err_policy_violation(Txt, Lang), - ejabberd_router:route_error(Presence, Err); -reject(_) -> - ok. - -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?MODULE). diff --git a/src/mod_antispam_filter.erl b/src/mod_antispam_filter.erl new file mode 100644 index 000000000..9fd8abf36 --- /dev/null +++ b/src/mod_antispam_filter.erl @@ -0,0 +1,299 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_antispam_filter.erl +%%% Author : Holger Weiss +%%% Author : Stefan Strigler +%%% Purpose : Filter C2S and S2S stanzas +%%% Created : 31 Mar 2019 by Holger Weiss +%%% +%%% +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +%%| Definitions +%% @format-begin + +-module(mod_antispam_filter). + +-author('holger@zedat.fu-berlin.de'). +-author('stefan@strigler.de'). + +-export([init_filtering/1, terminate_filtering/1]). +%% ejabberd_hooks callbacks +-export([s2s_in_handle_info/2, s2s_receive_packet/1, sm_receive_packet/1]). + +-include("logger.hrl"). +-include("translate.hrl"). + +-include_lib("xmpp/include/xmpp.hrl"). + +-type url() :: binary(). +-type s2s_in_state() :: ejabberd_s2s_in:state(). + +-define(MODULE_PARENT, mod_antispam). +-define(HTTPC_TIMEOUT, timer:seconds(3)). + +%%-------------------------------------------------------------------- +%%| Exported + +init_filtering(Host) -> + ejabberd_hooks:add(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90), + ejabberd_hooks:add(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), + ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50). + +terminate_filtering(Host) -> + ejabberd_hooks:delete(s2s_receive_packet, Host, ?MODULE, s2s_receive_packet, 50), + ejabberd_hooks:delete(sm_receive_packet, Host, ?MODULE, sm_receive_packet, 50), + ejabberd_hooks:delete(s2s_in_handle_info, Host, ?MODULE, s2s_in_handle_info, 90). + +%%-------------------------------------------------------------------- +%%| Hook callbacks + +-spec s2s_receive_packet({stanza() | drop, s2s_in_state()}) -> + {stanza() | drop, s2s_in_state()} | {stop, {drop, s2s_in_state()}}. +s2s_receive_packet({A, State}) -> + case sm_receive_packet(A) of + {stop, drop} -> + {stop, {drop, State}}; + Result -> + {Result, State} + end. + +-spec sm_receive_packet(stanza() | drop) -> stanza() | drop | {stop, drop}. +sm_receive_packet(drop = Acc) -> + Acc; +sm_receive_packet(#message{from = From, + to = #jid{lserver = LServer} = To, + type = Type} = + Msg) + when Type /= groupchat, Type /= error -> + do_check(From, To, LServer, Msg); +sm_receive_packet(#presence{from = From, + to = #jid{lserver = LServer} = To, + type = subscribe} = + Presence) -> + do_check(From, To, LServer, Presence); +sm_receive_packet(Acc) -> + Acc. + +%%-------------------------------------------------------------------- +%%| Filtering deciding + +do_check(From, To, LServer, Stanza) -> + case needs_checking(From, To) of + true -> + case check_from(LServer, From) of + ham -> + case check_stanza(LServer, From, Stanza) of + ham -> + Stanza; + spam -> + reject(Stanza), + {stop, drop} + end; + spam -> + reject(Stanza), + {stop, drop} + end; + false -> + Stanza + end. + +check_stanza(LServer, From, #message{body = Body}) -> + check_body(LServer, From, xmpp:get_text(Body)); +check_stanza(_, _, _) -> + ham. + +-spec s2s_in_handle_info(s2s_in_state(), any()) -> + s2s_in_state() | {stop, s2s_in_state()}. +s2s_in_handle_info(State, {_Ref, {spam_filter, _}}) -> + ?DEBUG("Dropping expired spam filter result", []), + {stop, State}; +s2s_in_handle_info(State, _) -> + State. + +-spec needs_checking(jid(), jid()) -> boolean(). +needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> + case gen_mod:is_loaded(LServer, ?MODULE_PARENT) of + true -> + Access = gen_mod:get_module_opt(LServer, ?MODULE_PARENT, access_spam), + case acl:match_rule(LServer, Access, To) of + allow -> + ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), + false; + deny -> + ?DEBUG("Spam is filtered for ~s", [jid:encode(To)]), + not mod_roster:is_subscribed(From, To) + andalso not + mod_roster:is_subscribed( + jid:make(<<>>, FromHost), + To) % likely a gateway + end; + false -> + ?DEBUG("~s not loaded for ~s", [?MODULE_PARENT, LServer]), + false + end. + +-spec check_from(binary(), jid()) -> ham | spam. +check_from(Host, From) -> + Proc = get_proc_name(Host), + LFrom = + {_, FromDomain, _} = + jid:remove_resource( + jid:tolower(From)), + try + case gen_server:call(Proc, {is_blocked_domain, FromDomain}) of + true -> + ?DEBUG("Spam JID found in blocked domains: ~p", [From]), + ejabberd_hooks:run(spam_found, Host, [{jid, From}]), + spam; + false -> + case gen_server:call(Proc, {check_jid, LFrom}) of + {spam_filter, Result} -> + Result + end + end + catch + exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking ~s against list of blocked domains or spammers", + [jid:encode(From)]), + ham + end. + +-spec check_body(binary(), jid(), binary()) -> ham | spam. +check_body(Host, From, Body) -> + case {extract_urls(Host, Body), extract_jids(Body)} of + {none, none} -> + ?DEBUG("No JIDs/URLs found in message", []), + ham; + {URLs, JIDs} -> + Proc = get_proc_name(Host), + LFrom = + jid:remove_resource( + jid:tolower(From)), + try gen_server:call(Proc, {check_body, URLs, JIDs, LFrom}) of + {spam_filter, Result} -> + Result + catch + exit:{timeout, _} -> + ?WARNING_MSG("Timeout while checking body", []), + ham + end + end. + +%%-------------------------------------------------------------------- +%%| Auxiliary + +-spec extract_urls(binary(), binary()) -> {urls, [url()]} | none. +extract_urls(Host, Body) -> + RE = <<"https?://\\S+">>, + Options = [global, {capture, all, binary}], + case re:run(Body, RE, Options) of + {match, Captured} when is_list(Captured) -> + Urls = resolve_redirects(Host, lists:flatten(Captured)), + {urls, Urls}; + nomatch -> + none + end. + +-spec resolve_redirects(binary(), [url()]) -> [url()]. +resolve_redirects(_Host, URLs) -> + try do_resolve_redirects(URLs, []) of + ResolvedURLs -> + ResolvedURLs + catch + exit:{timeout, _} -> + ?WARNING_MSG("Timeout while resolving redirects: ~p", [URLs]), + URLs + end. + +-spec do_resolve_redirects([url()], [url()]) -> [url()]. +do_resolve_redirects([], Result) -> + Result; +do_resolve_redirects([URL | Rest], Acc) -> + case httpc:request(get, + {URL, [{"user-agent", "curl/8.7.1"}]}, + [{autoredirect, false}, {timeout, ?HTTPC_TIMEOUT}], + []) + of + {ok, {{_, StatusCode, _}, Headers, _Body}} when StatusCode >= 300, StatusCode < 400 -> + Location = proplists:get_value("location", Headers), + case Location == undefined orelse lists:member(Location, Acc) of + true -> + do_resolve_redirects(Rest, [URL | Acc]); + false -> + do_resolve_redirects([Location | Rest], [URL | Acc]) + end; + _Res -> + do_resolve_redirects(Rest, [URL | Acc]) + end. + +-spec extract_jids(binary()) -> {jids, [ljid()]} | none. +extract_jids(Body) -> + RE = <<"\\S+@\\S+">>, + Options = [global, {capture, all, binary}], + case re:run(Body, RE, Options) of + {match, Captured} when is_list(Captured) -> + {jids, lists:filtermap(fun try_decode_jid/1, lists:flatten(Captured))}; + nomatch -> + none + end. + +-spec try_decode_jid(binary()) -> {true, ljid()} | false. +try_decode_jid(S) -> + try jid:decode(S) of + #jid{} = JID -> + {true, + jid:remove_resource( + jid:tolower(JID))} + catch + _:{bad_jid, _} -> + false + end. + +-spec reject(stanza()) -> ok. +reject(#message{from = From, + to = To, + type = Type, + lang = Lang} = + Msg) + when Type /= groupchat, Type /= error -> + ?INFO_MSG("Rejecting unsolicited message from ~s to ~s", + [jid:encode(From), jid:encode(To)]), + Txt = <<"Your message is unsolicited">>, + Err = xmpp:err_policy_violation(Txt, Lang), + ejabberd_hooks:run(spam_stanza_rejected, To#jid.lserver, [Msg]), + ejabberd_router:route_error(Msg, Err); +reject(#presence{from = From, + to = To, + lang = Lang} = + Presence) -> + ?INFO_MSG("Rejecting unsolicited presence from ~s to ~s", + [jid:encode(From), jid:encode(To)]), + Txt = <<"Your traffic is unsolicited">>, + Err = xmpp:err_policy_violation(Txt, Lang), + ejabberd_router:route_error(Presence, Err); +reject(_) -> + ok. + +-spec get_proc_name(binary()) -> atom(). +get_proc_name(Host) -> + gen_mod:get_module_proc(Host, ?MODULE_PARENT). + +%%-------------------------------------------------------------------- + +%%| vim: set foldmethod=marker foldmarker=%%|,%%-: diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index debfe9981..f60872913 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -265,10 +265,11 @@ has_spam_domain(Domain) -> fun(Host) -> lists:member(Domain, mod_antispam:get_blocked_domains(Host)) end. is_not_spam(Msg) -> - ?match({Msg, undefined}, mod_antispam:s2s_receive_packet({Msg, undefined})). + ?match({Msg, undefined}, mod_antispam_filter:s2s_receive_packet({Msg, undefined})). is_spam(Spam) -> - ?match({stop, {drop, undefined}}, mod_antispam:s2s_receive_packet({Spam, undefined})). + ?match({stop, {drop, undefined}}, + mod_antispam_filter:s2s_receive_packet({Spam, undefined})). message_hello(Username, Host, Config) -> SpamFrom = jid:make(Username, Host, <<"spam_client">>), From 288eecc23d1e483b6eadba40460559a66841533a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Jun 2025 10:30:52 +0200 Subject: [PATCH 1170/1302] Use misc:encode_pid/1 in ejabberd_sm_sql --- src/ejabberd_sm_sql.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl index bef3774e1..e3f35f505 100644 --- a/src/ejabberd_sm_sql.erl +++ b/src/ejabberd_sm_sql.erl @@ -109,7 +109,7 @@ set_session(#session{sid = {Now, Pid}, usr = {U, LServer, R}, delete_session(#session{usr = {_, LServer, _}, sid = {Now, Pid}}) -> TS = now_to_timestamp(Now), - PidS = list_to_binary(erlang:pid_to_list(Pid)), + PidS = misc:encode_pid(Pid), case ejabberd_sql:sql_query( LServer, ?SQL("delete from sm where usec=%(TS)d and pid=%(PidS)s")) of From 653413e912a335538b3eb1306feef6d56297971b Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Apr 2025 17:58:58 +0200 Subject: [PATCH 1171/1302] Run new webadmin hooks to add items to system menu --- priv/css/admin.css | 5 ++++- src/ejabberd_web_admin.erl | 22 +++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/priv/css/admin.css b/priv/css/admin.css index 9e76cccda..12fa97e22 100644 --- a/priv/css/admin.css +++ b/priv/css/admin.css @@ -131,9 +131,12 @@ ul li #navhead a, ul li #navheadsub a, ul li #navheadsubsub a { background: #424a55; color: #fff; } + +#navitemlogin-start { + border-top: 0.2em solid #cae7e4; +} #navitemlogin { padding: 0.5em; - border-top: 0.2em solid #cae7e4; border-bottom: 0.2em solid #cae7e4; padding-left: 0.5em; } diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 0e122d451..ea63d817a 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1660,9 +1660,19 @@ make_login_items(#request{us = {Username, Host}} = R, Level) -> _ -> UserEl end, + MenuPost = + case ejabberd_hooks:run_fold(webadmin_menu_system_post, [], [R]) of + [] -> + []; + PostElements -> + [{xmlel, + <<"div">>, + [{<<"id">>, <<"navitemlogin">>}], + [?XE(<<"ul">>, PostElements)]}] + end, [{xmlel, <<"li">>, - [], + [{<<"id">>, <<"navitemlogin-start">>}], [{xmlel, <<"div">>, [{<<"id">>, <<"navitemlogin">>}], @@ -1673,10 +1683,12 @@ make_login_items(#request{us = {Username, Host}} = R, Level) -> R, [{<<"sentence">>, misc:atom_to_binary(node())}], [{only, value}, - {result_links, [{sentence, node, Level, <<"">>}]}])]), - ?LI([?C(unicode:characters_to_binary("📤")), - ?AC(<<(binary:copy(<<"../">>, Level))/binary, "logout/">>, - <<"Logout">>)])])]}]}]. + {result_links, [{sentence, node, Level, <<"">>}]}])])] + ++ ejabberd_hooks:run_fold(webadmin_menu_system_inside, [], [R]) + ++ [?LI([?C(unicode:characters_to_binary("📤")), + ?AC(<<(binary:copy(<<"../">>, Level))/binary, "logout/">>, + <<"Logout">>)])])]}] + ++ MenuPost}]. %%%================================== From d6a00f5151385c1968e18f2904680cc7fbb51423 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 18 Jun 2025 17:26:10 +0200 Subject: [PATCH 1172/1302] mod_conversejs: Add link in WebAdmin to local Converse if configured --- src/mod_conversejs.erl | 72 ++++++++++++++++++++++++++++++++++++++++-- src/mod_host_meta.erl | 8 ++--- 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 3673f345f..8f2bcc5e7 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -31,6 +31,7 @@ -export([start/2, stop/1, reload/3, process/2, depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]). +-export([web_menu_system/2]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). @@ -39,7 +40,7 @@ -include("ejabberd_web_admin.hrl"). start(_Host, _Opts) -> - ok. + {ok, [{hook, webadmin_menu_system_post, web_menu_system, 50, global}]}. stop(_Host) -> ok. @@ -50,8 +51,10 @@ reload(_Host, _NewOpts, _OldOpts) -> depends(_Host, _Opts) -> []. -process([], #request{method = 'GET', host = Host, raw_path = RawPath}) -> +process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1}) -> + [RawPath | _] = string:split(RawPath1, "?"), ExtraOptions = get_auth_options(Host) + ++ get_autologin_options(Query) ++ get_register_options(Host) ++ get_extra_options(Host), Domain = mod_conversejs_opt:default_domain(Host), @@ -221,6 +224,71 @@ get_auto_file_url(Host, Filename, Default) -> _ -> Filename end. +%%---------------------------------------------------------------------- +%% WebAdmin link and autologin +%%---------------------------------------------------------------------- + +%% @format-begin + +web_menu_system(Result, + #request{host = Host, + auth = Auth, + tp = Protocol}) -> + AutoUrl = mod_host_meta:get_auto_url(any, ?MODULE), + ConverseUrl = misc:expand_keyword(<<"@HOST@">>, AutoUrl, Host), + AutologinQuery = + case {Protocol, Auth} of + {http, {Jid, _Password}} -> + <<"/?autologinjid=", Jid/binary>>; + {https, {Jid, Password}} -> + AuthToken = build_token(Jid, Password), + <<"/?autologinjid=", Jid/binary, "&autologintoken=", AuthToken/binary>>; + _ -> + <<"">> + end, + ConverseEl = + ?LI([?C(unicode:characters_to_binary("☯️")), + ?XAE(<<"a">>, + [{<<"href">>, <>}, + {<<"target">>, <<"_blank">>}], + [?C(unicode:characters_to_binary("Converse"))])]), + [ConverseEl | Result]. + +get_autologin_options(Query) -> + case {proplists:get_value(<<"autologinjid">>, Query), + proplists:get_value(<<"autologintoken">>, Query)} + of + {undefined, _} -> + []; + {Jid, Token} -> + [{<<"auto_login">>, <<"true">>}, + {<<"jid">>, <<"admin@localhost">>}, + {<<"password">>, check_token_get_password(Jid, Token)}] + end. + +build_token(Jid, Password) -> + Minutes = + integer_to_binary(calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) + div 60), + Cookie = + misc:atom_to_binary( + erlang:get_cookie()), + str:sha(<>). + +check_token_get_password(_, undefined) -> + <<"">>; +check_token_get_password(JidString, TokenProvided) -> + Jid = jid:decode(JidString), + Password = ejabberd_auth:get_password_s(Jid#jid.luser, Jid#jid.lserver), + case build_token(JidString, Password) of + TokenProvided -> + Password; + _ -> + <<"">> + end. +%% @format-end + %%---------------------------------------------------------------------- %% %%---------------------------------------------------------------------- diff --git a/src/mod_host_meta.erl b/src/mod_host_meta.erl index c99da63a2..ae7d7d697 100644 --- a/src/mod_host_meta.erl +++ b/src/mod_host_meta.erl @@ -34,7 +34,7 @@ -export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, mod_options/1, depends/2]). -export([mod_doc/0]). --export([get_url/4]). +-export([get_url/4, get_auto_url/2]). -include("logger.hrl"). @@ -151,10 +151,10 @@ get_auto_url(Tls, Module) -> [] -> undefined; [{ThisTls, Port, Path} | _] -> Protocol = case {ThisTls, Module} of - {false, mod_bosh} -> <<"http">>; - {true, mod_bosh} -> <<"https">>; {false, ejabberd_http_ws} -> <<"ws">>; - {true, ejabberd_http_ws} -> <<"wss">> + {true, ejabberd_http_ws} -> <<"wss">>; + {false, _} -> <<"http">>; + {true, _} -> <<"https">> end, < Date: Thu, 19 Jun 2025 12:37:17 +0200 Subject: [PATCH 1173/1302] ejabberd_http: Remove unused default_host option and state element The option 'default_host' for ejabberd_http was added years ago in commit 7d623d5. However it was problematic and the usage was removed in commit 83c291c. It's time to remove its last forgotten bits. --- src/ejabberd_http.erl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index aad7f7897..0e09c26c9 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -66,7 +66,6 @@ request_headers = [], end_of_request = false, options = [], - default_host, custom_headers, trail = <<>>, allow_unencrypted_sasl2, @@ -300,7 +299,6 @@ process_header(State, Data) -> #state{sockmod = SockMod, socket = Socket, trail = State3#state.trail, options = State#state.options, - default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re}; @@ -308,7 +306,6 @@ process_header(State, Data) -> #state{end_of_request = true, trail = State3#state.trail, options = State#state.options, - default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re} @@ -316,7 +313,6 @@ process_header(State, Data) -> _ -> #state{end_of_request = true, options = State#state.options, - default_host = State#state.default_host, custom_headers = State#state.custom_headers, request_handlers = State#state.request_handlers, addr_re = State#state.addr_re} @@ -926,8 +922,6 @@ listen_opt_type(request_handlers) -> econf:binary(), fun(Path) -> str:tokens(Path, <<"/">>) end), econf:beam([[{socket_handoff, 3}, {process, 2}]])); -listen_opt_type(default_host) -> - econf:domain(); listen_opt_type(custom_headers) -> econf:map( econf:binary(), @@ -943,5 +937,4 @@ listen_options() -> {allow_unencrypted_sasl2, false}, {request_handlers, []}, {tag, <<>>}, - {default_host, undefined}, {custom_headers, []}]. From c20d745028dcf2c772c581ae2cae2f88df565679 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 17 Jun 2025 17:31:49 +0200 Subject: [PATCH 1174/1302] New option hosts_alias and function resolve_host_alias/1 (4400) --- src/ejabberd_config.erl | 40 ++++++++++++++++++++++++++++++++++++ src/ejabberd_http.erl | 2 +- src/ejabberd_option.erl | 5 +++++ src/ejabberd_options.erl | 9 ++++++++ src/ejabberd_options_doc.erl | 17 +++++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 3a556d7f6..3344a3f15 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -39,6 +39,7 @@ -export([callback_modules/1]). -export([set_option/2]). -export([get_defined_keywords/1, get_predefined_keywords/1, replace_keywords/2, replace_keywords/3]). +-export([resolve_host_alias/1]). %% Deprecated functions -export([get_option/2]). @@ -510,6 +511,45 @@ get_predefined_keywords(Host) -> {<<"VERSION">>, misc:semver_to_xxyy( ejabberd_option:version())}]. + +resolve_host_alias(Host) -> + case lists:member(Host, ejabberd_option:hosts()) of + true -> + Host; + false -> + resolve_host_alias2(Host) + end. + +resolve_host_alias2(Host) -> + Result = + lists:filter(fun({Alias1, _Vhost}) -> is_glob_match(Host, Alias1) end, + ejabberd_option:hosts_alias()), + case Result of + [{_, Vhost} | _] when is_binary(Vhost) -> + ?DEBUG("(~p) Alias host '~s' resolved into vhost '~s'", [self(), Host, Vhost]), + Vhost; + [] -> + ?DEBUG("(~p) Request sent to host '~s', which isn't a vhost or an alias", + [self(), Host]), + Host + end. + +%% Copied from ejabberd-2.0.0/src/acl.erl +is_regexp_match(String, RegExp) -> + case ejabberd_regexp:run(String, RegExp) of + nomatch -> + false; + match -> + true; + {error, ErrDesc} -> + io:format("Wrong regexp ~p in ACL: ~p", [RegExp, ErrDesc]), + false + end. + +is_glob_match(String, <<"!", Glob/binary>>) -> + not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)); +is_glob_match(String, Glob) -> + is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)). %% @format-end %%%=================================================================== diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 0e09c26c9..0ca7d3ff0 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -273,7 +273,7 @@ process_header(State, Data) -> request_headers = add_header(Name, Langs, State)}; {ok, {http_header, _, 'Host' = Name, _, Value}} -> {Host, Port, TP} = get_transfer_protocol(State#state.addr_re, SockMod, Value), - State#state{request_host = Host, + State#state{request_host = ejabberd_config:resolve_host_alias(Host), request_port = Port, request_tp = TP, request_headers = add_header(Name, Value, State)}; diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index d9ded9bdf..3a69da867 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -55,6 +55,7 @@ -export([hide_sensitive_log_data/0, hide_sensitive_log_data/1]). -export([host_config/0]). -export([hosts/0]). +-export([hosts_alias/0]). -export([include_config_file/0, include_config_file/1]). -export([install_contrib_modules/0]). -export([jwt_auth_only_rule/0, jwt_auth_only_rule/1]). @@ -479,6 +480,10 @@ host_config() -> hosts() -> ejabberd_config:get_option({hosts, global}). +-spec hosts_alias() -> [{binary(),binary()}]. +hosts_alias() -> + ejabberd_config:get_option({hosts_alias, global}). + -spec include_config_file() -> any(). include_config_file() -> include_config_file(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 489320840..521687aa2 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -182,6 +182,13 @@ opt_type(host_config) -> [unique])); opt_type(hosts) -> econf:non_empty(econf:list(econf:domain(), [unique])); +opt_type(hosts_alias) -> + econf:and_then( + econf:map(econf:domain(), econf:domain(), [unique]), + econf:map( + econf:domain(), + econf:enum(ejabberd_config:get_option(hosts)), + [unique])); opt_type(include_config_file) -> econf:any(); opt_type(install_contrib_modules) -> @@ -587,6 +594,7 @@ options() -> {extauth_program, undefined}, {fqdn, fun fqdn/1}, {hide_sensitive_log_data, false}, + {hosts_alias, []}, {host_config, []}, {include_config_file, []}, {language, <<"en">>}, @@ -766,6 +774,7 @@ globals() -> ext_api_path_oauth, fqdn, hosts, + hosts_alias, host_config, install_contrib_modules, listen, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index cbbc4be9f..375de0ca2 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -712,6 +712,23 @@ doc() -> " domain.tld:", " auth_method:", " - ldap"]}}, + {hosts_alias, + #{value => "{Alias: Host}", + desc => + ?T("Define aliases for existing vhosts managed by ejabberd. " + "An alias may be a regexp expression. " + "This option is only consulted by the 'ejabberd_http' listener."), + note => "added in 25.xx", + example => + ["hosts:", + " - domain.tld", + " - example.org", + "", + "hosts_alias:", + " xmpp.domain.tld: domain.tld", + " jabber.domain.tld: domain.tld", + " mytest.net: example.org", + " \"exa*\": example.org"]}}, {include_config_file, #{value => "[Filename, ...\\] | {Filename: Options}", desc => From 5def9cef9f2e1a933ee88e6f778707a87693f177 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Jun 2025 23:47:53 +0200 Subject: [PATCH 1175/1302] ext_mod: When upgrading module, clean also the compiled directories --- src/ext_mod.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index 8af0300aa..d735b3a55 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -148,7 +148,8 @@ get_commands_spec() -> #ejabberd_commands{name = module_upgrade, tags = [modules], desc = "Upgrade the running code of an installed module", - longdesc = "In practice, this uninstalls and installs the module", + longdesc = "In practice, this uninstalls, cleans the compiled files, and installs the module", + note = "improved in 25.xx", module = ?MODULE, function = upgrade, args_desc = ["Module name"], args_example = [<<"mod_rest">>], @@ -289,8 +290,19 @@ upgrade(Module) when is_atom(Module) -> upgrade(misc:atom_to_binary(Module)); upgrade(Package) when is_binary(Package) -> uninstall(Package), + clean(Package), install(Package). +clean(Package) -> + Spec = [S || {Mod, S} <- available(), misc:atom_to_binary(Mod)==Package], + case Spec of + [] -> + {error, not_available}; + [Attrs] -> + Path = proplists:get_value(path, Attrs), + [delete_path(SubPath) || SubPath <- filelib:wildcard(Path++"/{deps,ebin}")] + end. + add_sources(Path) when is_list(Path) -> add_sources(iolist_to_binary(module_name(Path)), Path). add_sources(_, "") -> From a0c97b33e0ee4ee8dcbf9a949d6dcc67af8393bd Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 20 Jun 2025 12:28:33 +0200 Subject: [PATCH 1176/1302] CONTAINER.md: Move ejabberd-contrib content from Docs website --- CONTAINER.md | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) diff --git a/CONTAINER.md b/CONTAINER.md index de28e9fb3..904362db6 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -310,6 +310,152 @@ now you can define the admin account JID using an environment variable: Check the [full example](#customized-example) for other example. +### ejabberd-contrib + +This section addresses those topics related to +[ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib): + +- [Download source code](#download-source-code) +- [Install a module](#install-a-module) +- [Install git for dependencies](#install-git-for-dependencies) +- [Install your module](#install-your-module) + +--- + +#### Download source code + +The `ejabberd` container image includes the ejabberd-contrib git repository source code, +but `ecs` does not, so first download it: +```bash +$ docker exec ejabberd ejabberdctl modules_update_specs +``` + +#### Install a module + +Compile and install any of the contributed modules, for example: +```bash +docker exec ejabberd ejabberdctl module_install mod_statsdx + +Module mod_statsdx has been installed and started. +It's configured in the file: + /opt/ejabberd/.ejabberd-modules/mod_statsdx/conf/mod_statsdx.yml +Configure the module in that file, or remove it +and configure in your main ejabberd.yml +``` + +#### Install git for dependencies + +Some modules depend on erlang libraries, +but the container images do not include `git` or `mix` to download them. +Consequently, when you attempt to install such a module, +there will be error messages like: + +```bash +docker exec ejabberd ejabberdctl module_install ejabberd_observer_cli + +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: + /bin/sh: mix: not found +Fetching dependency observer_cli: + /bin/sh: git: not found +... +``` + +the solution is to install `git` in the container image: + +```bash +docker exec --user root ejabberd apk add git + +fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz +fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz +(1/3) Installing pcre2 (10.43-r0) +(2/3) Installing git (2.47.2-r0) +(3/3) Installing git-init-template (2.47.2-r0) +Executing busybox-1.37.0-r12.trigger +OK: 27 MiB in 42 packages +``` + +and now you can upgrade the module: + +```bash +docker exec ejabberd ejabberdctl module_upgrade ejabberd_observer_cli + +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: +/bin/sh: mix: not found +Fetching dependency observer_cli: Cloning into 'observer_cli'... +Fetching dependency os_stats: Cloning into 'os_stats'... +Fetching dependency recon: Cloning into 'recon'... +Inlining: inline_size=24 inline_effort=150 +Old inliner: threshold=0 functions=[{insert,2},{merge,2}] +Module ejabberd_observer_cli has been installed. +Now you can configure it in your ejabberd.yml +I'll download "recon" using git because I can't use Mix to fetch from hex.pm: +/bin/sh: mix: not found +``` + +#### Install your module + +If you [developed an ejabberd module](https://docs.ejabberd.im/developer/extending-ejabberd/modules/), +you can install it in your container image: + +1. Create a local directory for `ejabberd-modules`: + + ``` sh + mkdir docker-modules + ``` + +2. Then create the directory structure for your custom module: + + ``` sh + cd docker-modules + + mkdir -p sources/mod_hello_world/ + touch sources/mod_hello_world/mod_hello_world.spec + + mkdir sources/mod_hello_world/src/ + mv mod_hello_world.erl sources/mod_hello_world/src/ + + mkdir sources/mod_hello_world/conf/ + echo -e "modules:\n mod_hello_world: {}" > sources/mod_hello_world/conf/mod_hello_world.yml + + cd .. + ``` + +3. Grant ownership of that directory to the UID that ejabberd will use inside the Docker image: + + ``` sh + sudo chown 9000 -R docker-modules/ + ``` + +4. Start ejabberd in the container: + + ``` sh + sudo docker run \ + --name hellotest \ + -d \ + --volume "$(pwd)/docker-modules:/home/ejabberd/.ejabberd-modules/" \ + -p 5222:5222 \ + -p 5280:5280 \ + ejabberd/ecs + ``` + +5. Check the module is available for installing, and then install it: + + ``` sh + sudo docker exec -it hellotest ejabberdctl modules_available + mod_hello_world [] + + sudo docker exec -it hellotest ejabberdctl module_install mod_hello_world + ``` + +6. If the module works correctly, you will see `Hello` in the ejabberd logs when it starts: + + ``` sh + sudo docker exec -it hellotest grep Hello logs/ejabberd.log + 2020-10-06 13:40:13.154335+00:00 [info] + <0.492.0>@mod_hello_world:start/2:15 Hello, ejabberd world! + ``` + + ### ejabberdapi When the container is running (and thus ejabberd), you can exec commands inside the container @@ -418,12 +564,12 @@ it is necessary to change the old hostname stored in Mnesia. This section is equivalent to the ejabberd Documentation [Change Computer Hostname](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname), but particularized to containers that use this -ecs container image from ejabberd 23.01 or older. +`ecs` container image from ejabberd 23.01 or older. #### Setup Old Container Let's assume a container running ejabberd 23.01 (or older) from -this ecs container image, with the database directory binded +this `ecs` container image, with the database directory binded and one registered account. This can be produced with: ```bash @@ -929,7 +1075,7 @@ Let's summarize the differences between both container images. Legend: | Software | Erlang/OTP 27.3.3-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | -| [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included | +| [ejabberd-contrib](#ejabberd-contrib) | included | not included | | [ejabberdapi](#ejabberdapi) | included :orange_circle: | included | | :black_square_button: **Ports** | | [1880](#ports) for WebAdmin | yes :orange_circle: | yes :orange_circle: | From 6b47d3eb0daa4a0e8e6d0a8f2f69be74e2b53637 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 20 Jun 2025 16:00:16 +0200 Subject: [PATCH 1177/1302] mod_auth_fast: Clear tokens on kick, change pass and unregister (#4397)(#4398)(#4399) --- src/ejabberd_auth.erl | 2 +- src/ejabberd_sm.erl | 4 +++- src/mod_auth_fast.erl | 14 ++++++++++++-- src/mod_auth_fast_mnesia.erl | 12 ++++++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index f29ccbb96..ecb102bb8 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -755,7 +755,7 @@ db_set_password(User, Server, PlainPassword, Passwords, Mod) -> end end, case Ret of - {ok, _} -> ok; + {ok, _} -> ejabberd_hooks:run(set_password, Server, [User, Server]); {error, _} = Err -> Err end. diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index f12cd39e0..296452736 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -481,8 +481,10 @@ c2s_handle_info(#{lang := Lang, bind2_session_id := {Tag, _}} = State, {stop, ejabberd_c2s:send(State1, Err)}; c2s_handle_info(State, {replaced_with_bind_tag, _}) -> State; -c2s_handle_info(#{lang := Lang} = State, kick) -> +c2s_handle_info(#{lang := Lang, jid := JID} = State, kick) -> Err = xmpp:serr_policy_violation(?T("has been kicked"), Lang), + ejabberd_hooks:run(sm_kick_user, JID#jid.lserver, + [JID#jid.luser, JID#jid.lserver]), {stop, ejabberd_c2s:send(State, Err)}; c2s_handle_info(#{lang := Lang} = State, {exit, Reason}) -> Err = xmpp:serr_conflict(Reason, Lang), diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index 0cad39b85..c26c21072 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -30,7 +30,7 @@ -export([mod_doc/0]). %% Hooks -export([c2s_inline_features/2, c2s_handle_sasl2_inline/1, - get_tokens/3, get_mechanisms/1]). + get_tokens/3, get_mechanisms/1, remove_user_tokens/2]). -include_lib("xmpp/include/xmpp.hrl"). -include_lib("xmpp/include/scram.hrl"). @@ -54,7 +54,10 @@ start(Host, Opts) -> Mod = gen_mod:db_mod(Opts, ?MODULE), Mod:init(Host, Opts), {ok, [{hook, c2s_inline_features, c2s_inline_features, 50}, - {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 10}]}. + {hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 10}, + {hook, set_password, remove_user_tokens, 50}, + {hook, sm_kick_user, remove_user_tokens, 50}, + {hook, remove_user, remove_user_tokens, 50}]}. -spec stop(binary()) -> ok. stop(_Host) -> @@ -165,3 +168,10 @@ c2s_handle_sasl2_inline({#{server := Server, user := User, sasl2_ua_id := UA, _ -> Acc end. + +-spec remove_user_tokens(binary(), binary()) -> ok. +remove_user_tokens(User, Server) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:del_tokens(LServer, LUser). diff --git a/src/mod_auth_fast_mnesia.erl b/src/mod_auth_fast_mnesia.erl index 53337b390..081449b6e 100644 --- a/src/mod_auth_fast_mnesia.erl +++ b/src/mod_auth_fast_mnesia.erl @@ -28,12 +28,12 @@ %% API -export([init/2]). --export([get_tokens/3, del_token/4, set_token/6, rotate_token/3]). +-export([get_tokens/3, del_token/4, del_tokens/2, set_token/6, rotate_token/3]). -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). --record(mod_auth_fast, {key = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary()} | '$1', +-record(mod_auth_fast, {key = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() | '_'} | '$1', token = <<>> :: binary() | '_', created_at = 0 :: non_neg_integer() | '_', expires_at = 0 :: non_neg_integer() | '_'}). @@ -94,6 +94,14 @@ del_token(LServer, LUser, UA, Type) -> end, transaction(F). +-spec del_tokens(binary(), binary()) -> ok | {error, atom()}. +del_tokens(LServer, LUser) -> + F = fun() -> + Elements = mnesia:match_object(#mod_auth_fast{key = {LServer, LUser, '_'}, _ = '_'}), + [mnesia:delete_object(E) || E <- Elements] + end, + transaction(F). + -spec set_token(binary(), binary(), binary(), current | next, binary(), non_neg_integer()) -> ok | {error, atom()}. set_token(LServer, LUser, UA, Type, Token, Expires) -> From bddcf0624ee856efe1679c1ac154a539e4b8a3c2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Jun 2025 18:27:38 +0200 Subject: [PATCH 1178/1302] mod_antispam: Move some definitions to a header file --- include/mod_antispam.hrl | 26 ++++++++++++++++++++++++++ src/mod_antispam.erl | 6 +----- src/mod_antispam_dump.erl | 11 ++++------- src/mod_antispam_filter.erl | 11 +++++------ src/mod_antispam_rtbl.erl | 1 + 5 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 include/mod_antispam.hrl diff --git a/include/mod_antispam.hrl b/include/mod_antispam.hrl new file mode 100644 index 000000000..7f681046e --- /dev/null +++ b/include/mod_antispam.hrl @@ -0,0 +1,26 @@ +%%%---------------------------------------------------------------------- +%%% +%%% ejabberd, Copyright (C) 2002-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-define(MODULE_ANTISPAM, mod_antispam). + +-type url() :: binary(). +-type filename() :: binary() | none | false. +-type jid_set() :: sets:set(ljid()). +-type url_set() :: sets:set(url()). diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index ef2106219..a48f7235c 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -63,15 +63,11 @@ -include("ejabberd_commands.hrl"). -include("logger.hrl"). +-include("mod_antispam.hrl"). -include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --type url() :: binary(). --type filename() :: binary() | none | false. --type jid_set() :: sets:set(ljid()). --type url_set() :: sets:set(url()). - -record(state, {host = <<>> :: binary(), dump_fd = undefined :: file:io_device() | undefined, diff --git a/src/mod_antispam_dump.erl b/src/mod_antispam_dump.erl index 04a671f05..f92ac0bf1 100644 --- a/src/mod_antispam_dump.erl +++ b/src/mod_antispam_dump.erl @@ -38,14 +38,11 @@ -export([dump_spam_stanza/1, reopen_log/0]). -include("logger.hrl"). +-include("mod_antispam.hrl"). -include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --type filename() :: binary() | none | false. - --define(MODULE_PARENT, mod_antispam). - %%-------------------------------------------------------------------- %%| Exported @@ -157,7 +154,7 @@ write_stanza_dump(Fd, XML) -> %%| Auxiliary get_path_option(Host) -> - Opts = gen_mod:get_module_opts(Host, ?MODULE_PARENT), + Opts = gen_mod:get_module_opts(Host, ?MODULE_ANTISPAM), get_path_option(Host, Opts). get_path_option(Host, Opts) -> @@ -178,11 +175,11 @@ get_path_option(Host, Opts) -> -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> - gen_mod:get_module_proc(Host, ?MODULE_PARENT). + gen_mod:get_module_proc(Host, ?MODULE_ANTISPAM). -spec get_spam_filter_hosts() -> [binary()]. get_spam_filter_hosts() -> - [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE_PARENT)]. + [H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE_ANTISPAM)]. %%-------------------------------------------------------------------- diff --git a/src/mod_antispam_filter.erl b/src/mod_antispam_filter.erl index 9fd8abf36..7367c2ba8 100644 --- a/src/mod_antispam_filter.erl +++ b/src/mod_antispam_filter.erl @@ -38,13 +38,12 @@ -include("logger.hrl"). -include("translate.hrl"). +-include("mod_antispam.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --type url() :: binary(). -type s2s_in_state() :: ejabberd_s2s_in:state(). --define(MODULE_PARENT, mod_antispam). -define(HTTPC_TIMEOUT, timer:seconds(3)). %%-------------------------------------------------------------------- @@ -128,9 +127,9 @@ s2s_in_handle_info(State, _) -> -spec needs_checking(jid(), jid()) -> boolean(). needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> - case gen_mod:is_loaded(LServer, ?MODULE_PARENT) of + case gen_mod:is_loaded(LServer, ?MODULE_ANTISPAM) of true -> - Access = gen_mod:get_module_opt(LServer, ?MODULE_PARENT, access_spam), + Access = gen_mod:get_module_opt(LServer, ?MODULE_ANTISPAM, access_spam), case acl:match_rule(LServer, Access, To) of allow -> ?DEBUG("Spam not filtered for ~s", [jid:encode(To)]), @@ -144,7 +143,7 @@ needs_checking(#jid{lserver = FromHost} = From, #jid{lserver = LServer} = To) -> To) % likely a gateway end; false -> - ?DEBUG("~s not loaded for ~s", [?MODULE_PARENT, LServer]), + ?DEBUG("~s not loaded for ~s", [?MODULE_ANTISPAM, LServer]), false end. @@ -292,7 +291,7 @@ reject(_) -> -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> - gen_mod:get_module_proc(Host, ?MODULE_PARENT). + gen_mod:get_module_proc(Host, ?MODULE_ANTISPAM). %%-------------------------------------------------------------------- diff --git a/src/mod_antispam_rtbl.erl b/src/mod_antispam_rtbl.erl index 93b346631..246b59dfa 100644 --- a/src/mod_antispam_rtbl.erl +++ b/src/mod_antispam_rtbl.erl @@ -27,6 +27,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). +-include("mod_antispam.hrl"). -define(SERVICE_MODULE, mod_antispam). -define(SERVICE_JID_PREFIX, "rtbl-"). From 88ae3fddf3bceb86cd8d3e0ac2e5f5a7bb756b7d Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Jun 2025 23:33:09 +0200 Subject: [PATCH 1179/1302] mod_antispam: Sort and document files options --- src/mod_antispam.erl | 80 +++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index a48f7235c..fda470953 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -125,10 +125,17 @@ depends(_Host, _Opts) -> [{mod_pubsub, soft}]. -spec mod_opt_type(atom()) -> econf:validator(). -mod_opt_type(spam_domains_file) -> +mod_opt_type(access_spam) -> + econf:acl(); +mod_opt_type(cache_size) -> + econf:pos_int(unlimited); +mod_opt_type(rtbl_host) -> econf:either( - econf:enum([none]), econf:file()); -mod_opt_type(whitelist_domains_file) -> + econf:enum([none]), econf:host()); +mod_opt_type(rtbl_domains_node) -> + econf:non_empty( + econf:binary()); +mod_opt_type(spam_domains_file) -> econf:either( econf:enum([none]), econf:file()); mod_opt_type(spam_dump_file) -> @@ -140,28 +147,21 @@ mod_opt_type(spam_jids_file) -> mod_opt_type(spam_urls_file) -> econf:either( econf:enum([none]), econf:file()); -mod_opt_type(access_spam) -> - econf:acl(); -mod_opt_type(cache_size) -> - econf:pos_int(unlimited); -mod_opt_type(rtbl_host) -> +mod_opt_type(whitelist_domains_file) -> econf:either( - econf:enum([none]), econf:host()); -mod_opt_type(rtbl_domains_node) -> - econf:non_empty( - econf:binary()). + econf:enum([none]), econf:file()). -spec mod_options(binary()) -> [{atom(), any()}]. mod_options(_Host) -> - [{spam_domains_file, none}, + [{access_spam, none}, + {cache_size, ?DEFAULT_CACHE_SIZE}, + {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}, + {rtbl_host, none}, + {spam_domains_file, none}, {spam_dump_file, false}, {spam_jids_file, none}, {spam_urls_file, none}, - {whitelist_domains_file, none}, - {access_spam, none}, - {cache_size, ?DEFAULT_CACHE_SIZE}, - {rtbl_host, none}, - {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}]. + {whitelist_domains_file, none}]. mod_doc() -> #{desc => ?T("Reads from text file and RTBL, filters stanzas and writes dump file."), @@ -171,10 +171,20 @@ mod_doc() -> #{value => ?T("Access"), desc => ?T("Access rule that controls what accounts may receive spam messages. " - "If the rule returns `allow` for a given recipient, " + "If the rule returns 'allow' for a given recipient, " "spam messages aren't rejected for that recipient. " "The default value is 'none', which means that all recipients " "are subject to spam filtering verification.")}}, + {spam_domains_file, + #{value => ?T("none | Path"), + desc => + ?T("Path to a plain text file containing a list of " + "known spam domains, one domain per line. " + "Messages and subscription requests sent from one of the listed domains " + "are classified as spam if sender is not in recipient's roster. " + "This list of domains gets merged with the one retrieved " + "by an RTBL host if any given. " + "The default value is 'none'.")}}, {spam_dump_file, #{value => ?T("false | true | Path"), desc => @@ -182,7 +192,37 @@ mod_doc() -> "Use an absolute path, or the '@LOG_PATH@' macro to store logs " "in the same place that the other ejabberd log files. " "If set to 'false', does not dump stanzas, this is the default. " - "If set to 'true', it stores in '\"@LOG_PATH@/spam_dump_@HOST@.log\"'.")}}], + "If set to 'true', it stores in '\"@LOG_PATH@/spam_dump_@HOST@.log\"'.")}}, + {spam_jids_file, + #{value => ?T("none | Path"), + desc => + ?T("Path to a plain text file containing a list of " + "known spammer JIDs, one JID per line. " + "Messages and subscription requests sent from one of " + "the listed JIDs are classified as spam. " + "Messages containing at least one of the listed JIDs" + "are classified as spam as well. " + "Furthermore, the sender's JID will be cached, " + "so that future traffic originating from that JID will also be classified as spam. " + "The default value is 'none'.")}}, + {spam_urls_file, + #{value => ?T("none | Path"), + desc => + ?T("Path to a plain text file containing a list of " + "URLs known to be mentioned in spam message bodies. " + "Messages containing at least one of the listed URLs are classified as spam. " + "Furthermore, the sender's JID will be cached, " + "so that future traffic originating from that JID will be classified as spam as well. " + "The default value is 'none'.")}}, + {whitelist_domains_file, + #{value => ?T("none | Path"), + desc => + ?T("Path to a file containing a list of " + "domains to whitelist from being blocked, one per line. " + "If either it is in 'spam_domains_file' or more realistically " + "in a domain sent by a RTBL host (see option 'rtbl_host') " + "then this domain will be ignored and stanzas from there won't be blocked. " + "The default value is 'none'.")}}], example => ["modules:", " mod_antispam:", From a77c7e36b005fa9492102cdd4c94bc47f02bfd7e Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 21 Jun 2025 23:33:35 +0200 Subject: [PATCH 1180/1302] Move spam files parsing to a submodule --- src/mod_antispam.erl | 193 ++++++++++--------------------------- src/mod_antispam_files.erl | 182 ++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 142 deletions(-) create mode 100644 src/mod_antispam_files.erl diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index fda470953..500cb0244 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -234,41 +234,33 @@ mod_doc() -> -spec init(list()) -> {ok, state()} | {stop, term()}. init([Host, Opts]) -> process_flag(trap_exit, true), - Files = - #{domains => gen_mod:get_opt(spam_domains_file, Opts), - jid => gen_mod:get_opt(spam_jids_file, Opts), - url => gen_mod:get_opt(spam_urls_file, Opts), - whitelist_domains => gen_mod:get_opt(whitelist_domains_file, Opts)}, - try read_files(Files) of - #{jid := JIDsSet, - url := URLsSet, - domains := SpamDomainsSet, - whitelist_domains := WhitelistDomains} -> - ejabberd_hooks:add(local_send_to_resource_hook, - Host, - mod_antispam_rtbl, - pubsub_event_handler, - 50), - RTBLHost = gen_mod:get_opt(rtbl_host, Opts), - RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), - mod_antispam_filter:init_filtering(Host), - InitState = - #state{host = Host, - jid_set = JIDsSet, - url_set = URLsSet, - dump_fd = mod_antispam_dump:init_dumping(Host), - max_cache_size = gen_mod:get_opt(cache_size, Opts), - blocked_domains = set_to_map(SpamDomainsSet), - whitelist_domains = set_to_map(WhitelistDomains, false), - rtbl_host = RTBLHost, - rtbl_domains_node = RTBLDomainsNode}, - mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), - {ok, InitState} - catch - {Op, File, Reason} when Op == open; Op == read -> - ?CRITICAL_MSG("Cannot ~s ~s: ~s", [Op, File, format_error(Reason)]), - {stop, config_error} - end. + mod_antispam_files:init_files(Host), + FilesResults = read_files(Host), + #{jid := JIDsSet, + url := URLsSet, + domains := SpamDomainsSet, + whitelist_domains := WhitelistDomains} = + FilesResults, + ejabberd_hooks:add(local_send_to_resource_hook, + Host, + mod_antispam_rtbl, + pubsub_event_handler, + 50), + RTBLHost = gen_mod:get_opt(rtbl_host, Opts), + RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), + mod_antispam_filter:init_filtering(Host), + InitState = + #state{host = Host, + jid_set = JIDsSet, + url_set = URLsSet, + dump_fd = mod_antispam_dump:init_dumping(Host), + max_cache_size = gen_mod:get_opt(cache_size, Opts), + blocked_domains = set_to_map(SpamDomainsSet), + whitelist_domains = set_to_map(WhitelistDomains, false), + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode}, + mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), + {ok, InitState}. -spec handle_call(term(), {pid(), term()}, state()) -> {reply, {spam_filter, term()}, state()} | {noreply, state()}. @@ -287,8 +279,8 @@ handle_call({check_body, URLs, JIDs, From}, Result2 end, {reply, {spam_filter, Result}, State2}; -handle_call({reload_files, Files}, _From, State) -> - {Result, State1} = reload_files(Files, State), +handle_call(reload_spam_files, _From, State) -> + {Result, State1} = reload_files(State), {reply, {spam_filter, Result}, State1}; handle_call({expire_cache, Age}, _From, State) -> {Result, State1} = expire_cache(Age, State), @@ -355,12 +347,7 @@ handle_cast({reload, NewOpts, OldOpts}, State1 end, ok = mod_antispam_rtbl:unsubscribe(OldRTBLHost, OldRTBLDomainsNode, Host), - Files = - #{domains => gen_mod:get_opt(spam_domains_file, NewOpts), - jid => gen_mod:get_opt(spam_jids_file, NewOpts), - url => gen_mod:get_opt(spam_urls_file, NewOpts), - whitelist_domains => gen_mod:get_opt(whitelist_domains_file, NewOpts)}, - {_Result, State3} = reload_files(Files, State2#state{blocked_domains = #{}}), + {_Result, State3} = reload_files(State2#state{blocked_domains = #{}}), RTBLHost = gen_mod:get_opt(rtbl_host, NewOpts), RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, NewOpts), ok = mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), @@ -440,6 +427,7 @@ terminate(Reason, ?DEBUG("Stopping spam filter process for ~s: ~p", [Host, Reason]), misc:cancel_timer(RTBLRetryTimer), mod_antispam_dump:terminate_dumping(Host, Fd), + mod_antispam_files:terminate_files(Host), mod_antispam_filter:terminate_filtering(Host), ejabberd_hooks:delete(local_send_to_resource_hook, Host, @@ -494,10 +482,9 @@ filter_body({_, Addrs}, Set, From, #state{host = Host} = State) -> filter_body(none, _Set, _From, State) -> {ham, State}. --spec reload_files(#{Type :: atom() => filename()}, state()) -> - {ok | {error, binary()}, state()}. -reload_files(Files, #state{host = Host, blocked_domains = BlockedDomains} = State) -> - try read_files(Files) of +-spec reload_files(state()) -> {ok | {error, binary()}, state()}. +reload_files(#state{host = Host, blocked_domains = BlockedDomains} = State) -> + case read_files(Host) of #{jid := JIDsSet, url := URLsSet, domains := SpamDomainsSet, @@ -518,12 +505,9 @@ reload_files(Files, #state{host = Host, blocked_domains = BlockedDomains} = Stat State#state{jid_set = JIDsSet, url_set = URLsSet, blocked_domains = maps:merge(BlockedDomains, set_to_map(SpamDomainsSet)), - whitelist_domains = set_to_map(WhitelistDomains, false)}} - catch - {Op, File, Reason} when Op == open; Op == read -> - Txt = format("Cannot ~s ~s for ~s: ~s", [Op, File, Host, format_error(Reason)]), - ?ERROR_MSG("~s", [Txt]), - {{error, Txt}, State} + whitelist_domains = set_to_map(WhitelistDomains, false)}}; + {config_error, ErrorText} -> + {{error, ErrorText}, State} end. set_to_map(Set) -> @@ -532,80 +516,18 @@ set_to_map(Set) -> set_to_map(Set, V) -> sets:fold(fun(K, M) -> M#{K => V} end, #{}, Set). --spec read_files(#{Type => filename()}) -> - #{jid => jid_set(), - url => url_set(), - Type => sets:set(binary())} - when Type :: atom(). -read_files(Files) -> - maps:map(fun(Type, Filename) -> read_file(Filename, line_parser(Type)) end, Files). - --spec line_parser(Type :: atom()) -> fun((binary()) -> binary()). -line_parser(jid) -> - fun parse_jid/1; -line_parser(url) -> - fun parse_url/1; -line_parser(_) -> - fun trim/1. - --spec read_file(filename(), fun((binary()) -> ljid() | url())) -> jid_set() | url_set(). -read_file(none, _ParseLine) -> - sets:new(); -read_file(File, ParseLine) -> - case file:open(File, [read, binary, raw, {read_ahead, 65536}]) of - {ok, Fd} -> - try - read_line(Fd, ParseLine, sets:new()) - catch - E -> - throw({read, File, E}) - after - ok = file:close(Fd) - end; - {error, Reason} -> - throw({open, File, Reason}) - end. - --spec read_line(file:io_device(), - fun((binary()) -> ljid() | url()), - jid_set() | url_set()) -> - jid_set() | url_set(). -read_line(Fd, ParseLine, Set) -> - case file:read_line(Fd) of - {ok, Line} -> - read_line(Fd, ParseLine, sets:add_element(ParseLine(Line), Set)); - {error, Reason} -> - throw(Reason); - eof -> - Set - end. - --spec parse_jid(binary()) -> ljid(). -parse_jid(S) -> - try jid:decode(trim(S)) of - #jid{} = JID -> - jid:remove_resource( - jid:tolower(JID)) - catch - _:{bad_jid, _} -> - throw({bad_jid, S}) - end. - --spec parse_url(binary()) -> url(). -parse_url(S) -> - URL = trim(S), - RE = <<"https?://\\S+$">>, - Options = [anchored, caseless, {capture, none}], - case re:run(URL, RE, Options) of - match -> - URL; - nomatch -> - throw({bad_url, S}) - end. - --spec trim(binary()) -> binary(). -trim(S) -> - re:replace(S, <<"\\s+$">>, <<>>, [{return, binary}]). +read_files(Host) -> + AccInitial = + #{jid => sets:new(), + url => sets:new(), + domains => sets:new(), + whitelist_domains => sets:new()}, + Files = + #{jid => gen_mod:get_module_opt(Host, ?MODULE, spam_jids_file), + url => gen_mod:get_module_opt(Host, ?MODULE, spam_urls_file), + domains => gen_mod:get_module_opt(Host, ?MODULE, spam_domains_file), + whitelist_domains => gen_mod:get_module_opt(Host, ?MODULE, whitelist_domains_file)}, + ejabberd_hooks:run_fold(antispam_get_lists, Host, AccInitial, [Files]). -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> @@ -623,14 +545,6 @@ sets_equal(A, B) -> format(Format, Data) -> iolist_to_binary(io_lib:format(Format, Data)). --spec format_error(atom() | tuple()) -> binary(). -format_error({bad_jid, JID}) -> - <<"Not a valid JID: ", JID/binary>>; -format_error({bad_url, URL}) -> - <<"Not an HTTP(S) URL: ", URL/binary>>; -format_error(Reason) -> - list_to_binary(file:format_error(Reason)). - %%-------------------------------------------------------------------- %%| Caching @@ -792,16 +706,11 @@ try_call_by_host(Host, Call) -> reload_spam_filter_files(<<"global">>) -> for_all_hosts(fun reload_spam_filter_files/1, []); reload_spam_filter_files(Host) -> - LServer = jid:nameprep(Host), - Files = - #{domains => gen_mod:get_module_opt(LServer, ?MODULE, spam_domains_file), - jid => gen_mod:get_module_opt(LServer, ?MODULE, spam_jids_file), - url => gen_mod:get_module_opt(LServer, ?MODULE, spam_urls_file)}, - case try_call_by_host(Host, {reload_files, Files}) of + case try_call_by_host(Host, reload_spam_files) of {spam_filter, ok} -> ok; {spam_filter, {error, Txt}} -> - {error, binary_to_list(Txt)}; + {error, Txt}; {error, _R} = Error -> Error end. diff --git a/src/mod_antispam_files.erl b/src/mod_antispam_files.erl new file mode 100644 index 000000000..0981edbb2 --- /dev/null +++ b/src/mod_antispam_files.erl @@ -0,0 +1,182 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_antispam_files.erl +%%% Author : Holger Weiss +%%% Author : Stefan Strigler +%%% Purpose : Filter spam messages based on sender JID and content +%%% Created : 31 Mar 2019 by Holger Weiss +%%% +%%% +%%% ejabberd, Copyright (C) 2019-2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +%%| definitions +%% @format-begin + +-module(mod_antispam_files). + +-author('holger@zedat.fu-berlin.de'). +-author('stefan@strigler.de'). + +%% Exported +-export([init_files/1, terminate_files/1]). +% Hooks +-export([get_files_lists/2]). + +-include("ejabberd_commands.hrl"). +-include("logger.hrl"). +-include("mod_antispam.hrl"). +-include("translate.hrl"). + +-include_lib("xmpp/include/xmpp.hrl"). + +-type files_map() :: #{atom() => filename()}. +-type lists_map() :: + #{jid => jid_set(), + url => url_set(), + atom() => sets:set(binary())}. + +-define(COMMAND_TIMEOUT, timer:seconds(30)). +-define(DEFAULT_CACHE_SIZE, 10000). +-define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). +-define(HTTPC_TIMEOUT, timer:seconds(3)). + +%%-------------------------------------------------------------------- +%%| Exported + +init_files(Host) -> + ejabberd_hooks:add(antispam_get_lists, Host, ?MODULE, get_files_lists, 50). + +terminate_files(Host) -> + ejabberd_hooks:delete(antispam_get_lists, Host, ?MODULE, get_files_lists, 50). + +%%-------------------------------------------------------------------- +%%| Hooks + +-spec get_files_lists(lists_map(), files_map()) -> lists_map(). +get_files_lists(#{jid := AccJids, + url := AccUrls, + domains := AccDomains, + whitelist_domains := AccWhitelist} = + Acc, + Files) -> + try read_files(Files) of + #{jid := JIDsSet, + url := URLsSet, + domains := SpamDomainsSet, + whitelist_domains := WhitelistDomains} -> + Acc#{jid => sets:union(AccJids, JIDsSet), + url => sets:union(AccUrls, URLsSet), + domains => sets:union(AccDomains, SpamDomainsSet), + whitelist_domains => sets:union(AccWhitelist, WhitelistDomains)} + catch + {Op, File, Reason} when Op == open; Op == read -> + ErrorText = format("Error trying to ~s file ~s: ~s", [Op, File, format_error(Reason)]), + ?CRITICAL_MSG(ErrorText, []), + {stop, {config_error, ErrorText}} + end. + +%%-------------------------------------------------------------------- +%%| read_files + +-spec read_files(files_map()) -> lists_map(). +read_files(Files) -> + maps:map(fun(Type, Filename) -> read_file(Filename, line_parser(Type)) end, Files). + +-spec line_parser(Type :: atom()) -> fun((binary()) -> binary()). +line_parser(jid) -> + fun parse_jid/1; +line_parser(url) -> + fun parse_url/1; +line_parser(_) -> + fun trim/1. + +-spec read_file(filename(), fun((binary()) -> ljid() | url())) -> jid_set() | url_set(). +read_file(none, _ParseLine) -> + sets:new(); +read_file(File, ParseLine) -> + case file:open(File, [read, binary, raw, {read_ahead, 65536}]) of + {ok, Fd} -> + try + read_line(Fd, ParseLine, sets:new()) + catch + E -> + throw({read, File, E}) + after + ok = file:close(Fd) + end; + {error, Reason} -> + throw({open, File, Reason}) + end. + +-spec read_line(file:io_device(), + fun((binary()) -> ljid() | url()), + jid_set() | url_set()) -> + jid_set() | url_set(). +read_line(Fd, ParseLine, Set) -> + case file:read_line(Fd) of + {ok, Line} -> + read_line(Fd, ParseLine, sets:add_element(ParseLine(Line), Set)); + {error, Reason} -> + throw(Reason); + eof -> + Set + end. + +-spec parse_jid(binary()) -> ljid(). +parse_jid(S) -> + try jid:decode(trim(S)) of + #jid{} = JID -> + jid:remove_resource( + jid:tolower(JID)) + catch + _:{bad_jid, _} -> + throw({bad_jid, S}) + end. + +-spec parse_url(binary()) -> url(). +parse_url(S) -> + URL = trim(S), + RE = <<"https?://\\S+$">>, + Options = [anchored, caseless, {capture, none}], + case re:run(URL, RE, Options) of + match -> + URL; + nomatch -> + throw({bad_url, S}) + end. + +-spec trim(binary()) -> binary(). +trim(S) -> + re:replace(S, <<"\\s+$">>, <<>>, [{return, binary}]). + +%% Function copied from mod_antispam.erl +-spec format(io:format(), [term()]) -> binary(). +format(Format, Data) -> + iolist_to_binary(io_lib:format(Format, Data)). + +-spec format_error(atom() | tuple()) -> binary(). +format_error({bad_jid, JID}) -> + <<"Not a valid JID: ", JID/binary>>; +format_error({bad_url, URL}) -> + <<"Not an HTTP(S) URL: ", URL/binary>>; +format_error(Reason) -> + list_to_binary(file:format_error(Reason)). + +%%-------------------------------------------------------------------- + +%%| vim: set foldmethod=marker foldmarker=%%|,%%-: From 3d89c9199ccf775dea7ad8d3a66a7d56ccb5d5f8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jun 2025 12:33:48 +0200 Subject: [PATCH 1181/1302] gen_mod: Add support to prepare module stopping before actually stopping any module Follows the reasoning of application:prep_stop, but applied to gen_mod: https://www.erlang.org/docs/28/apps/kernel/application.html#c:prep_stop/1 --- src/ejabberd_app.erl | 1 + src/gen_mod.erl | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 5d15d98fd..a31d7b1a6 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -109,6 +109,7 @@ prep_stop(State) -> ejabberd_service:stop(), ejabberd_s2s:stop(), ejabberd_system_monitor:stop(), + gen_mod:prep_stop(), gen_mod:stop(), State. diff --git a/src/gen_mod.erl b/src/gen_mod.erl index c07e1d691..dc3ba3c56 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). -export([init/1, start_link/0, start_child/3, start_child/4, - stop_child/1, stop_child/2, stop/0, config_reloaded/0]). + stop_child/1, stop_child/2, prep_stop/0, stop/0, config_reloaded/0]). -export([start_module/2, stop_module/2, stop_module_keep_config/2, get_opt/2, set_opt/3, get_opt_hosts/1, is_equal_opt/3, get_module_opt/3, get_module_opts/2, get_module_opt_hosts/2, @@ -76,6 +76,7 @@ -callback start(binary(), opts()) -> ok | {ok, pid()} | {ok, [registration()]} | {error, term()}. +-callback prep_stop(binary()) -> any(). -callback stop(binary()) -> any(). -callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}. -callback mod_opt_type(atom()) -> econf:validator(). @@ -86,7 +87,7 @@ example => [string()] | [{binary(), [string()]}]}. -callback depends(binary(), opts()) -> [{module(), hard | soft}]. --optional_callbacks([mod_opt_type/1, reload/3]). +-optional_callbacks([mod_opt_type/1, reload/3, prep_stop/1]). -export_type([opts/0]). -export_type([db_type/0]). @@ -114,6 +115,10 @@ init([]) -> {read_concurrency, true}]), {ok, {{one_for_one, 10, 1}, []}}. +-spec prep_stop() -> ok. +prep_stop() -> + prep_stop_modules(). + -spec stop() -> ok. stop() -> ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 60), @@ -301,6 +306,21 @@ is_app_running(AppName) -> lists:keymember(AppName, 1, application:which_applications(Timeout)). +-spec prep_stop_modules() -> ok. +prep_stop_modules() -> + lists:foreach( + fun(Host) -> + prep_stop_modules(Host) + end, ejabberd_option:hosts()). + +-spec prep_stop_modules(binary()) -> ok. +prep_stop_modules(Host) -> + Modules = lists:reverse(loaded_modules_with_opts(Host)), + lists:foreach( + fun({Module, _Args}) -> + prep_stop_module_keep_config(Host, Module) + end, Modules). + -spec stop_modules() -> ok. stop_modules() -> lists:foreach( @@ -320,6 +340,22 @@ stop_modules(Host) -> stop_module(Host, Module) -> stop_module_keep_config(Host, Module). +-spec prep_stop_module_keep_config(binary(), atom()) -> error | ok. +prep_stop_module_keep_config(Host, Module) -> + ?DEBUG("Preparing to stop ~ts at ~ts", [Module, Host]), + try Module:prep_stop(Host) of + _ -> + ok + catch ?EX_RULE(error, undef, _St) -> + ok; + ?EX_RULE(Class, Reason, St) -> + StackTrace = ?EX_STACK(St), + ?ERROR_MSG("Failed to prepare stop module ~ts at ~ts:~n** ~ts", + [Module, Host, + misc:format_exception(2, Class, Reason, StackTrace)]), + error + end. + -spec stop_module_keep_config(binary(), atom()) -> error | ok. stop_module_keep_config(Host, Module) -> ?DEBUG("Stopping ~ts at ~ts", [Module, Host]), From 263e1f59f718d62b5dc56d3e0fc14f7ab059710c Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jun 2025 16:38:46 +0200 Subject: [PATCH 1182/1302] Fix problem calling get_log_path when ejabberd is stopping When ejabberd is being stopped and some module calls ejabberd_logger:get_log_path(), application:load/1 crashes with error: ** Reason for termination == ** {terminating, [{application_controller,call,2, [{file,"application_controller.erl"},{line,511}]}, {application,load1,2,[{file,"application.erl"},{line,274}]}, {ejabberd_config,env_binary_to_list,2, [{file,"/home/git/ejabberd/src/ejabberd_config.erl"}, {line,343}]}, {ejabberd_logger,get_log_path,0, [{file,"/home/git/ejabberd/src/ejabberd_logger.erl"}, {line,55}]}, --- src/ejabberd_config.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 89f89504d..a1bf043a6 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -340,7 +340,12 @@ may_hide_data(Data) -> -spec env_binary_to_list(atom(), atom()) -> {ok, any()} | undefined. env_binary_to_list(Application, Parameter) -> %% Application need to be loaded to allow setting parameters - application:load(Application), + case proplists:is_defined(Application, application:loaded_applications()) of + true -> + ok; + false -> + application:load(Application) + end, case application:get_env(Application, Parameter) of {ok, Val} when is_binary(Val) -> BVal = binary_to_list(Val), From b65c11daf67e9d6e8b8fd8d587dff35f3ee91ff3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 26 Jun 2025 00:14:11 +0200 Subject: [PATCH 1183/1302] New predefined keyword: CONFIG_PATH --- src/ejabberd_config.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index a1bf043a6..29e491c0e 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -509,11 +509,15 @@ get_predefined_keywords(Host) -> [{<<"HOST">>, Host}] end, Home = misc:get_home(), + ConfigDirPath = + iolist_to_binary(filename:dirname( + ejabberd_config:path())), LogDirPath = iolist_to_binary(filename:dirname( ejabberd_logger:get_log_path())), HostList ++ [{<<"HOME">>, list_to_binary(Home)}, + {<<"CONFIG_PATH">>, ConfigDirPath}, {<<"LOG_PATH">>, LogDirPath}, {<<"SEMVER">>, ejabberd_option:version()}, {<<"VERSION">>, From c3f5083f15a9ad2df4c04cfa22cf03c3be5506ee Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jun 2025 17:56:37 +0200 Subject: [PATCH 1184/1302] Use the new gen_mod:prep_stop/1 feature This fixes the problem when stopping the module with multiple vhosts: unsubscribing from a local pubsub requires mod_pubsub in that vhost running, but ejabberd stops mod_pubsub from a vhost before stopping mod_antispam in other vhost. --- src/mod_antispam.erl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 500cb0244..1706f4377 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -35,6 +35,7 @@ %% gen_mod callbacks. -export([start/2, + prep_stop/1, stop/1, reload/3, depends/2, @@ -104,6 +105,13 @@ start(Host, Opts) -> end, gen_mod:start_child(?MODULE, Host, Opts). +-spec prep_stop(binary()) -> ok | {error, any()}. +prep_stop(Host) -> + case try_call_by_host(Host, prepare_stop) of + ready_to_stop -> + ok + end. + -spec stop(binary()) -> ok | {error, any()}. stop(Host) -> case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of @@ -317,6 +325,14 @@ handle_call({is_blocked_domain, Domain}, {reply, maps:get(Domain, maps:merge(BlockedDomains, WhitelistDomains), false) =/= false, State}; +handle_call(prepare_stop, + _From, + #state{host = Host, + rtbl_host = RTBLHost, + rtbl_domains_node = RTBLDomainsNode} = + State) -> + mod_antispam_rtbl:unsubscribe(RTBLHost, RTBLDomainsNode, Host), + {reply, ready_to_stop, State}; handle_call(Request, From, State) -> ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]), {noreply, State}. From 740b0c7dd7a50524a927e34bfc3cd4d877d08163 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Sat, 5 Jul 2025 14:19:20 +0200 Subject: [PATCH 1185/1302] mod_pubsub_serverinfo: initial import as found on ejabberd-contrib --- include/pubsub_serverinfo_codec.hrl | 17 + specs/pubsub_serverinfo_codec.spec | 52 ++ src/mod_pubsub_serverinfo.erl | 312 ++++++++++ src/pubsub_serverinfo_codec.erl | 734 +++++++++++++++++++++++ src/pubsub_serverinfo_codec_external.erl | 12 + 5 files changed, 1127 insertions(+) create mode 100644 include/pubsub_serverinfo_codec.hrl create mode 100644 specs/pubsub_serverinfo_codec.spec create mode 100644 src/mod_pubsub_serverinfo.erl create mode 100644 src/pubsub_serverinfo_codec.erl create mode 100644 src/pubsub_serverinfo_codec_external.erl diff --git a/include/pubsub_serverinfo_codec.hrl b/include/pubsub_serverinfo_codec.hrl new file mode 100644 index 000000000..81b975798 --- /dev/null +++ b/include/pubsub_serverinfo_codec.hrl @@ -0,0 +1,17 @@ +%% Created automatically by XML generator (fxml_gen.erl) +%% Source: pubsub_serverinfo_codec.spec + +-record(pubsub_serverinfo_remote_domain, {name = <<>> :: binary(), + type = [] :: ['bidi' | 'incoming' | 'outgoing']}). +-type pubsub_serverinfo_remote_domain() :: #pubsub_serverinfo_remote_domain{}. + +-record(pubsub_serverinfo_domain, {name = <<>> :: binary(), + remote_domain :: 'undefined' | [#pubsub_serverinfo_remote_domain{}]}). +-type pubsub_serverinfo_domain() :: #pubsub_serverinfo_domain{}. + +-record(pubsub_serverinfo, {domain = [] :: [#pubsub_serverinfo_domain{}]}). +-type pubsub_serverinfo() :: #pubsub_serverinfo{}. + +-type pubsub_serverinfo_codec() :: pubsub_serverinfo() | + pubsub_serverinfo_domain() | + pubsub_serverinfo_remote_domain(). diff --git a/specs/pubsub_serverinfo_codec.spec b/specs/pubsub_serverinfo_codec.spec new file mode 100644 index 000000000..44966e9f1 --- /dev/null +++ b/specs/pubsub_serverinfo_codec.spec @@ -0,0 +1,52 @@ +-xml(pubsub_serverinfo, + #elem{name = <<"serverinfo">>, + xmlns = <<"urn:xmpp:serverinfo:0">>, + module = pubsub_serverinfo_codec, + result = {pubsub_serverinfo, '$domain'}, + refs = [#ref{name = pubsub_serverinfo_domain, + label = '$domain', + min = 0}]}). + +-xml(pubsub_serverinfo_domain, + #elem{name = <<"domain">>, + xmlns = <<"urn:xmpp:serverinfo:0">>, + module = pubsub_serverinfo_codec, + result = {pubsub_serverinfo_domain, '$name', '$remote_domain'}, + attrs = [#attr{name = <<"name">>, + label = '$name', + required = true}], + refs = [#ref{name = pubsub_serverinfo_federation, + label = '$remote_domain', + min = 0, max = 1}]}). + +-xml(pubsub_serverinfo_federation, + #elem{name = <<"federation">>, + xmlns = <<"urn:xmpp:serverinfo:0">>, + module = pubsub_serverinfo_codec, + result = '$remote_domain', + refs = [#ref{name = pubsub_serverinfo_remote_domain, + label = '$remote_domain', + min = 0}]}). + +-xml(pubsub_serverinfo_remote_domain, + #elem{name = <<"remote-domain">>, + xmlns = <<"urn:xmpp:serverinfo:0">>, + module = pubsub_serverinfo_codec, + result = {pubsub_serverinfo_remote_domain, '$name', '$type'}, + attrs = [#attr{name = <<"name">>, + label = '$name', + required = true}], + refs = [#ref{name = pubsub_serverinfo_connection, + label = '$type', + min = 0}]}). + +-xml(pubsub_serverinfo_connection, + #elem{name = <<"connection">>, + xmlns = <<"urn:xmpp:serverinfo:0">>, + module = pubsub_serverinfo_codec, + result = '$type', + attrs = [#attr{name = <<"type">>, + label = '$type', + required = true, + enc = {enc_enum, []}, + dec = {dec_enum, [[incoming, outgoing, bidi]]}}]}). diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl new file mode 100644 index 000000000..4ebc93732 --- /dev/null +++ b/src/mod_pubsub_serverinfo.erl @@ -0,0 +1,312 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_pubsub_serverinfo.erl +%%% Author : Stefan Strigler +%%% Purpose : Exposes server information over Pub/Sub +%%% Created : 26 Dec 2023 by Guus der Kinderen +%%% +%%% +%%% ejabberd, Copyright (C) 2023 - 2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_pubsub_serverinfo). +-author('stefan@strigler.de'). +-behaviour(gen_mod). +-behaviour(gen_server). + +-include("pubsub_serverinfo_codec.hrl"). +-include("logger.hrl"). +-include_lib("xmpp/include/xmpp.hrl"). + +%% gen_mod callbacks. +-export([start/2, stop/1, depends/2, mod_options/1, mod_opt_type/1, get_local_features/5, mod_doc/0]). +-export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2]). +-export([in_auth_result/3, out_auth_result/2, get_info/5]). + +-define(NS_URN_SERVERINFO, <<"urn:xmpp:serverinfo:0">>). +-define(PUBLIC_HOSTS_URL, <<"https://data.xmpp.net/providers/v2/providers-Ds.json">>). + +-record(state, {host, pubsub_host, node, monitors = #{}, timer = undefined, public_hosts = []}). + +start(Host, Opts) -> + case pubsub_host(Host, Opts) of + {error, _Reason} = Error -> + Error; + PubsubHost -> + xmpp:register_codec(pubsub_serverinfo_codec), + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), + ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), + ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50), + ejabberd_hooks:add(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50), + gen_mod:start_child(?MODULE, Host, PubsubHost) + end. + +stop(Host) -> + ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50), + ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 50), + ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50), + ejabberd_hooks:delete(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50), + gen_mod:stop_child(?MODULE, Host). + +init([Host, PubsubHost]) -> + TRef = timer:send_interval(timer:minutes(5), self(), update_pubsub), + Monitors = init_monitors(Host), + PublicHosts = fetch_public_hosts(), + State = #state{host = Host, + pubsub_host = PubsubHost, + node = <<"serverinfo">>, + timer = TRef, + monitors = Monitors, + public_hosts = PublicHosts}, + self() ! update_pubsub, + {ok, State}. + +-spec init_monitors(binary()) -> map(). +init_monitors(Host) -> + lists:foldl( + fun(Domain, Monitors) -> + RefIn = make_ref(), % just dummies + RefOut = make_ref(), + maps:merge(#{RefIn => {incoming, {Host, Domain, true}}, + RefOut => {outgoing, {Host, Domain, true}}}, + Monitors) + end, + #{}, + ejabberd_option:hosts() -- [Host]). + +-spec fetch_public_hosts() -> list(). +fetch_public_hosts() -> + try + {ok, {{_, 200, _}, _Headers, Body}} = httpc:request(get, {?PUBLIC_HOSTS_URL, []}, [{timeout, 1000}], [{body_format, binary}]), + case misc:json_decode(Body) of + PublicHosts when is_list(PublicHosts) -> PublicHosts; + Other -> + ?WARNING_MSG("Parsed JSON for public hosts was not a list: ~p", [Other]), + [] + end + catch E:R -> + ?WARNING_MSG("Failed fetching public hosts (~p): ~p", [E, R]), + [] + end. + +handle_cast({Event, Domain, Pid}, #state{host = Host, monitors = Mons} = State) + when Event == register_in; Event == register_out -> + Ref = monitor(process, Pid), + IsPublic = check_if_public(Domain, State), + NewMons = maps:put(Ref, {event_to_dir(Event), {Host, Domain, IsPublic}}, Mons), + {noreply, State#state{monitors = NewMons}}; +handle_cast(_, State) -> + {noreply, State}. + +event_to_dir(register_in) -> incoming; +event_to_dir(register_out) -> outgoing. + +handle_call(_Request, _From, State) -> + {noreply, State}. + +handle_info({iq_reply, IQReply, {LServer, RServer}}, + #state{monitors = Mons} = State) -> + case IQReply of + #iq{type = result, sub_els = [El]} -> + case xmpp:decode(El) of + #disco_info{features = Features} -> + case lists:member(?NS_URN_SERVERINFO, Features) of + true -> + NewMons = maps:fold(fun(Ref, {Dir, {LServer0, RServer0, _}}, Acc) + when LServer == LServer0, RServer == RServer0 -> + maps:put(Ref, {Dir, {LServer, RServer, true}}, Acc); + (Ref, Other, Acc) -> + maps:put(Ref, Other, Acc) + end, + #{}, + Mons), + {noreply, State#state{monitors = NewMons}}; + _ -> + {noreply, State} + end; + _ -> + {noreply, State} + end; + _ -> + {noreply, State} + end; +handle_info(update_pubsub, State) -> + update_pubsub(State), + {noreply, State}; +handle_info({'DOWN', Mon, process, _Pid, _Info}, #state{monitors = Mons} = State) -> + {noreply, State#state{monitors = maps:remove(Mon, Mons)}}; +handle_info(_Request, State) -> + {noreply, State}. + +terminate(_Reason, #state{monitors = Mons, timer = Timer}) -> + case is_reference(Timer) of + true -> + case erlang:cancel_timer(Timer) of + false -> + receive {timeout, Timer, _} -> ok + after 0 -> ok + end; + _ -> + ok + end; + _ -> + ok + end, + maps:fold( + fun(Mon, _, _) -> + demonitor(Mon) + end, ok, Mons). + +depends(_Host, _Opts) -> + [{mod_pubsub, hard}]. + +mod_options(_Host) -> + [{pubsub_host, undefined}]. + +mod_opt_type(pubsub_host) -> + econf:either(undefined, econf:host()). + +mod_doc() -> #{}. + +in_auth_result(#{server_host := Host, remote_server := RServer} = State, true, _Server) -> + gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_in, RServer, self()}), + State; +in_auth_result(State, _, _) -> + State. + +out_auth_result(#{server_host := Host, remote_server := RServer} = State, true) -> + gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_out, RServer, self()}), + State; +out_auth_result(State, _) -> + State. + +check_if_public(Domain, State) -> + maybe_send_disco_info(is_public(Domain, State) orelse is_monitored(Domain, State), Domain, State). + +is_public(Domain, #state{public_hosts = PublicHosts}) -> + lists:member(Domain, PublicHosts). + +is_monitored(Domain, #state{host = Host, monitors = Mons}) -> + maps:size( + maps:filter( + fun(_Ref, {_Dir, {LServer, RServer, IsPublic}}) + when LServer == Host, RServer == Domain -> IsPublic; + (_Ref, _Other) -> false + end, + Mons)) =/= 0. + +maybe_send_disco_info(true, _Domain, _State) -> true; +maybe_send_disco_info(false, Domain, #state{host = Host}) -> + Proc = gen_mod:get_module_proc(Host, ?MODULE), + IQ = #iq{type = get, from = jid:make(Host), + to = jid:make(Domain), sub_els = [#disco_info{}]}, + ejabberd_router:route_iq(IQ, {Host, Domain}, Proc), + false. + +update_pubsub(#state{host = Host, pubsub_host = PubsubHost, node = Node, monitors = Mons}) -> + Map = maps:fold( + fun(_, {Dir, {MyDomain, Target, IsPublic}}, Acc) -> + maps:update_with(MyDomain, + fun(Acc2) -> + maps:update_with(Target, + fun({Types, _}) -> {Types#{Dir => true}, IsPublic} end, + {#{Dir => true}, IsPublic}, Acc2) + end, #{Target => {#{Dir => true}, IsPublic}}, Acc) + end, #{}, Mons), + Domains = maps:fold( + fun(MyDomain, Targets, Acc) -> + Remote = maps:fold( + fun(Remote, {Types, true}, Acc2) -> + [#pubsub_serverinfo_remote_domain{name = Remote, type = maps:keys(Types)} | Acc2]; + (_HiddenRemote, {Types, false}, Acc2) -> + [#pubsub_serverinfo_remote_domain{type = maps:keys(Types)} | Acc2] + end, [], Targets), + [#pubsub_serverinfo_domain{name = MyDomain, remote_domain = Remote} | Acc] + end, [], Map), + + PubOpts = [{persist_items, true}, {max_items, 1}, {access_model, open}], + ?DEBUG("Publishing serverinfo pubsub item on ~s: ~p", [PubsubHost, Domains]), + mod_pubsub:publish_item( + PubsubHost, Host, Node, jid:make(Host), + <<"current">>, [xmpp:encode(#pubsub_serverinfo{domain = Domains})], PubOpts, all). + +get_local_features({error, _} = Acc, _From, _To, _Node, _Lang) -> + Acc; +get_local_features(Acc, _From, _To, Node, _Lang) when Node == <<>> -> + case Acc of + {result, Features} -> + {result, [?NS_URN_SERVERINFO | Features]}; + empty -> {result, [?NS_URN_SERVERINFO]} + end; +get_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + +get_info(Acc, Host, Mod, Node, Lang) when (Mod == undefined orelse Mod == mod_disco), Node == <<"">> -> + case mod_disco:get_info(Acc, Host, Mod, Node, Lang) of + [#xdata{fields = Fields} = XD | Rest] -> + PubsubHost = pubsub_host(Host), + NodeField = #xdata_field{var = <<"serverinfo-pubsub-node">>, + values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}, + {stop, [XD#xdata{fields = Fields ++ [NodeField]} | Rest]}; + _ -> + Acc + end; +get_info(Acc, Host, Mod, Node, _Lang) when Node == <<"">>, is_atom(Mod) -> + PubsubHost = pubsub_host(Host), + [#xdata{type = result, + fields = [ + #xdata_field{type = hidden, + var = <<"FORM_TYPE">>, + values = [?NS_SERVERINFO]}, + #xdata_field{var = <<"serverinfo-pubsub-node">>, + values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}]} | Acc]; +get_info(Acc, _Host, _Mod, _Node, _Lang) -> + Acc. + +pubsub_host(Host) -> + case pubsub_host(Host, gen_mod:get_module_opts(Host, ?MODULE)) of + {error, _Reason} = Error -> + throw(Error); + PubsubHost -> + PubsubHost + end. + +pubsub_host(Host, Opts) -> + case gen_mod:get_opt(pubsub_host, Opts) of + undefined -> + PubsubHost = hd(get_mod_pubsub_hosts(Host)), + ?INFO_MSG("No pubsub_host in configuration for ~p, choosing ~s", [?MODULE, PubsubHost]), + PubsubHost; + PubsubHost -> + case check_pubsub_host_exists(Host, PubsubHost) of + true -> + PubsubHost; + false -> + {error, {pubsub_host_does_not_exist, PubsubHost}} + end + end. + +check_pubsub_host_exists(Host, PubsubHost) -> + lists:member(PubsubHost, get_mod_pubsub_hosts(Host)). + +get_mod_pubsub_hosts(Host) -> + case gen_mod:get_module_opt(Host, mod_pubsub, hosts) of + [] -> + [gen_mod:get_module_opt(Host, mod_pubsub, host)]; + PubsubHosts -> + PubsubHosts + end. diff --git a/src/pubsub_serverinfo_codec.erl b/src/pubsub_serverinfo_codec.erl new file mode 100644 index 000000000..9b16edeaf --- /dev/null +++ b/src/pubsub_serverinfo_codec.erl @@ -0,0 +1,734 @@ +%% Created automatically by XML generator (fxml_gen.erl) +%% Source: pubsub_serverinfo_codec.spec + +-module(pubsub_serverinfo_codec). + +-compile(export_all). + +decode(El) -> decode(El, <<>>, []). + +decode(El, Opts) -> decode(El, <<>>, Opts). + +decode({xmlel, Name, Attrs, _} = El, TopXMLNS, Opts) -> + XMLNS = get_attr(<<"xmlns">>, Attrs, TopXMLNS), + case get_mod(Name, XMLNS) of + undefined when XMLNS == <<>> -> + erlang:error({pubsub_serverinfo_codec, + {missing_tag_xmlns, Name}}); + undefined -> + erlang:error({pubsub_serverinfo_codec, + {unknown_tag, Name, XMLNS}}); + Mod -> Mod:do_decode(Name, XMLNS, El, Opts) + end. + +encode(El) -> encode(El, <<>>). + +encode({xmlel, _, _, _} = El, _) -> El; +encode({xmlcdata, _} = CData, _) -> CData; +encode(El, TopXMLNS) -> + Mod = get_mod(El), + Mod:do_encode(El, TopXMLNS). + + +get_name(El) -> + Mod = get_mod(El), + Mod:do_get_name(El). + +get_ns(El) -> + Mod = get_mod(El), + Mod:do_get_ns(El). + +is_known_tag({xmlel, Name, Attrs, _}, TopXMLNS) -> + XMLNS = get_attr(<<"xmlns">>, Attrs, TopXMLNS), + get_mod(Name, XMLNS) /= undefined. + +get_els(Term) -> + Mod = get_mod(Term), + Mod:get_els(Term). + +set_els(Term, Els) -> + Mod = get_mod(Term), + Mod:set_els(Term, Els). + +do_decode(<<"connection">>, <<"urn:xmpp:serverinfo:0">>, + El, Opts) -> + decode_pubsub_serverinfo_connection(<<"urn:xmpp:serverinfo:0">>, + Opts, + El); +do_decode(<<"remote-domain">>, + <<"urn:xmpp:serverinfo:0">>, El, Opts) -> + decode_pubsub_serverinfo_remote_domain(<<"urn:xmpp:serverinfo:0">>, + Opts, + El); +do_decode(<<"federation">>, <<"urn:xmpp:serverinfo:0">>, + El, Opts) -> + decode_pubsub_serverinfo_federation(<<"urn:xmpp:serverinfo:0">>, + Opts, + El); +do_decode(<<"domain">>, <<"urn:xmpp:serverinfo:0">>, El, + Opts) -> + decode_pubsub_serverinfo_domain(<<"urn:xmpp:serverinfo:0">>, + Opts, + El); +do_decode(<<"serverinfo">>, <<"urn:xmpp:serverinfo:0">>, + El, Opts) -> + decode_pubsub_serverinfo(<<"urn:xmpp:serverinfo:0">>, + Opts, + El); +do_decode(Name, <<>>, _, _) -> + erlang:error({pubsub_serverinfo_codec, + {missing_tag_xmlns, Name}}); +do_decode(Name, XMLNS, _, _) -> + erlang:error({pubsub_serverinfo_codec, + {unknown_tag, Name, XMLNS}}). + +tags() -> + [{<<"connection">>, <<"urn:xmpp:serverinfo:0">>}, + {<<"remote-domain">>, <<"urn:xmpp:serverinfo:0">>}, + {<<"federation">>, <<"urn:xmpp:serverinfo:0">>}, + {<<"domain">>, <<"urn:xmpp:serverinfo:0">>}, + {<<"serverinfo">>, <<"urn:xmpp:serverinfo:0">>}]. + +do_encode({pubsub_serverinfo, _} = Serverinfo, + TopXMLNS) -> + encode_pubsub_serverinfo(Serverinfo, TopXMLNS); +do_encode({pubsub_serverinfo_domain, _, _} = Domain, + TopXMLNS) -> + encode_pubsub_serverinfo_domain(Domain, TopXMLNS); +do_encode({pubsub_serverinfo_remote_domain, _, _} = + Remote_domain, + TopXMLNS) -> + encode_pubsub_serverinfo_remote_domain(Remote_domain, + TopXMLNS). + +do_get_name({pubsub_serverinfo, _}) -> <<"serverinfo">>; +do_get_name({pubsub_serverinfo_domain, _, _}) -> + <<"domain">>; +do_get_name({pubsub_serverinfo_remote_domain, _, _}) -> + <<"remote-domain">>. + +do_get_ns({pubsub_serverinfo, _}) -> + <<"urn:xmpp:serverinfo:0">>; +do_get_ns({pubsub_serverinfo_domain, _, _}) -> + <<"urn:xmpp:serverinfo:0">>; +do_get_ns({pubsub_serverinfo_remote_domain, _, _}) -> + <<"urn:xmpp:serverinfo:0">>. + +register_module(Mod) -> + register_module(Mod, pubsub_serverinfo_codec_external). + +unregister_module(Mod) -> + unregister_module(Mod, + pubsub_serverinfo_codec_external). + +format_error({bad_attr_value, Attr, Tag, XMLNS}) -> + <<"Bad value of attribute '", Attr/binary, "' in tag <", + Tag/binary, "/> qualified by namespace '", XMLNS/binary, + "'">>; +format_error({bad_cdata_value, <<>>, Tag, XMLNS}) -> + <<"Bad value of cdata in tag <", Tag/binary, + "/> qualified by namespace '", XMLNS/binary, "'">>; +format_error({missing_tag, Tag, XMLNS}) -> + <<"Missing tag <", Tag/binary, + "/> qualified by namespace '", XMLNS/binary, "'">>; +format_error({missing_attr, Attr, Tag, XMLNS}) -> + <<"Missing attribute '", Attr/binary, "' in tag <", + Tag/binary, "/> qualified by namespace '", XMLNS/binary, + "'">>; +format_error({missing_cdata, <<>>, Tag, XMLNS}) -> + <<"Missing cdata in tag <", Tag/binary, + "/> qualified by namespace '", XMLNS/binary, "'">>; +format_error({unknown_tag, Tag, XMLNS}) -> + <<"Unknown tag <", Tag/binary, + "/> qualified by namespace '", XMLNS/binary, "'">>; +format_error({missing_tag_xmlns, Tag}) -> + <<"Missing namespace for tag <", Tag/binary, "/>">>. + +io_format_error({bad_attr_value, Attr, Tag, XMLNS}) -> + {<<"Bad value of attribute '~s' in tag <~s/> " + "qualified by namespace '~s'">>, + [Attr, Tag, XMLNS]}; +io_format_error({bad_cdata_value, <<>>, Tag, XMLNS}) -> + {<<"Bad value of cdata in tag <~s/> qualified " + "by namespace '~s'">>, + [Tag, XMLNS]}; +io_format_error({missing_tag, Tag, XMLNS}) -> + {<<"Missing tag <~s/> qualified by namespace " + "'~s'">>, + [Tag, XMLNS]}; +io_format_error({missing_attr, Attr, Tag, XMLNS}) -> + {<<"Missing attribute '~s' in tag <~s/> " + "qualified by namespace '~s'">>, + [Attr, Tag, XMLNS]}; +io_format_error({missing_cdata, <<>>, Tag, XMLNS}) -> + {<<"Missing cdata in tag <~s/> qualified " + "by namespace '~s'">>, + [Tag, XMLNS]}; +io_format_error({unknown_tag, Tag, XMLNS}) -> + {<<"Unknown tag <~s/> qualified by namespace " + "'~s'">>, + [Tag, XMLNS]}; +io_format_error({missing_tag_xmlns, Tag}) -> + {<<"Missing namespace for tag <~s/>">>, [Tag]}. + +get_attr(Attr, Attrs, Default) -> + case lists:keyfind(Attr, 1, Attrs) of + {_, Val} -> Val; + false -> Default + end. + +enc_xmlns_attrs(XMLNS, XMLNS) -> []; +enc_xmlns_attrs(XMLNS, _) -> [{<<"xmlns">>, XMLNS}]. + +choose_top_xmlns(<<>>, NSList, TopXMLNS) -> + case lists:member(TopXMLNS, NSList) of + true -> TopXMLNS; + false -> hd(NSList) + end; +choose_top_xmlns(XMLNS, _, _) -> XMLNS. + +register_module(Mod, ResolverMod) -> + MD5Sum = try Mod:module_info(md5) of + Val -> Val + catch + error:badarg -> + {ok, {Mod, Val}} = beam_lib:md5(code:which(Mod)), + Val + end, + case orddict:find(Mod, ResolverMod:modules()) of + {ok, MD5Sum} -> ok; + _ -> + Mods = orddict:store(Mod, + MD5Sum, + ResolverMod:modules()), + recompile_resolver(Mods, ResolverMod) + end. + +unregister_module(Mod, ResolverMod) -> + case orddict:find(Mod, ResolverMod:modules()) of + {ok, _} -> + Mods = orddict:erase(Mod, ResolverMod:modules()), + recompile_resolver(Mods, ResolverMod); + error -> ok + end. + +recompile_resolver(Mods, ResolverMod) -> + Tags = lists:flatmap(fun (M) -> + [{Name, XMLNS, M} || {Name, XMLNS} <- M:tags()] + end, + orddict:fetch_keys(Mods)), + Records = lists:flatmap(fun (M) -> + [{RecName, RecSize, M} + || {RecName, RecSize} <- M:records()] + end, + orddict:fetch_keys(Mods)), + Lookup1 = string:join(lists:map(fun ({RecName, + RecSize, + M}) -> + io_lib:format("lookup({~s}) -> '~s'", + [string:join([io_lib:format("'~s'", + [RecName]) + | ["_" + || _ + <- lists:seq(1, + RecSize)]], + ","), + M]) + end, + Records) + ++ + ["lookup(Term) -> erlang:error(badarg, " + "[Term])."], + ";" ++ io_lib:nl()), + Lookup2 = string:join(lists:map(fun ({Name, + XMLNS, + M}) -> + io_lib:format("lookup(~w, ~w) -> '~s'", + [Name, XMLNS, M]) + end, + Tags) + ++ ["lookup(_, _) -> undefined."], + ";" ++ io_lib:nl()), + Modules = io_lib:format("modules() -> [~s].", + [string:join([io_lib:format("{'~s', ~w}", [M, S]) + || {M, S} <- Mods], + ",")]), + Module = io_lib:format("-module(~s).", [ResolverMod]), + Compile = "-compile(export_all).", + Forms = lists:map(fun (Expr) -> + {ok, Tokens, _} = + erl_scan:string(lists:flatten(Expr)), + {ok, Form} = erl_parse:parse_form(Tokens), + Form + end, + [Module, Compile, Modules, Lookup1, Lookup2]), + {ok, Code} = case compile:forms(Forms, []) of + {ok, ResolverMod, Bin} -> {ok, Bin}; + {ok, ResolverMod, Bin, _Warnings} -> {ok, Bin}; + Error -> Error + end, + {module, ResolverMod} = code:load_binary(ResolverMod, + "nofile", + Code), + ok. + +dec_enum(Val, Enums) -> + AtomVal = erlang:binary_to_existing_atom(Val, utf8), + case lists:member(AtomVal, Enums) of + true -> AtomVal + end. + +enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8). + +pp(pubsub_serverinfo, 1) -> [domain]; +pp(pubsub_serverinfo_domain, 2) -> + [name, remote_domain]; +pp(pubsub_serverinfo_remote_domain, 2) -> [name, type]; +pp(xmlel, 3) -> [name, attrs, children]; +pp(Name, Arity) -> + try get_mod(erlang:make_tuple(Arity + 1, + undefined, + [{1, Name}])) + of + Mod -> Mod:pp(Name, Arity) + catch + error:badarg -> no + end. + +records() -> + [{pubsub_serverinfo, 1}, + {pubsub_serverinfo_domain, 2}, + {pubsub_serverinfo_remote_domain, 2}]. + +get_mod(<<"serverinfo">>, + <<"urn:xmpp:serverinfo:0">>) -> + pubsub_serverinfo_codec; +get_mod(<<"remote-domain">>, + <<"urn:xmpp:serverinfo:0">>) -> + pubsub_serverinfo_codec; +get_mod(<<"federation">>, + <<"urn:xmpp:serverinfo:0">>) -> + pubsub_serverinfo_codec; +get_mod(<<"domain">>, <<"urn:xmpp:serverinfo:0">>) -> + pubsub_serverinfo_codec; +get_mod(<<"connection">>, + <<"urn:xmpp:serverinfo:0">>) -> + pubsub_serverinfo_codec; +get_mod(Name, XMLNS) -> + pubsub_serverinfo_codec_external:lookup(Name, XMLNS). + +get_mod({pubsub_serverinfo, _}) -> + pubsub_serverinfo_codec; +get_mod({pubsub_serverinfo_domain, _, _}) -> + pubsub_serverinfo_codec; +get_mod({pubsub_serverinfo_remote_domain, _, _}) -> + pubsub_serverinfo_codec; +get_mod(Record) -> + pubsub_serverinfo_codec_external:lookup(Record). + +decode_pubsub_serverinfo_connection(__TopXMLNS, __Opts, + {xmlel, <<"connection">>, _attrs, _els}) -> + Type = + decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + _attrs, + undefined), + Type. + +decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + [{<<"type">>, _val} | _attrs], + _Type) -> + decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + _attrs, + _val); +decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + [_ | _attrs], Type) -> + decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + _attrs, + Type); +decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, + [], Type) -> + decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, + Type). + +encode_pubsub_serverinfo_connection(Type, __TopXMLNS) -> + __NewTopXMLNS = + pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, + [], + __TopXMLNS), + _els = [], + _attrs = + encode_pubsub_serverinfo_connection_attr_type(Type, + pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, + __TopXMLNS)), + {xmlel, <<"connection">>, _attrs, _els}. + +decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, + undefined) -> + erlang:error({pubsub_serverinfo_codec, + {missing_attr, + <<"type">>, + <<"connection">>, + __TopXMLNS}}); +decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, + _val) -> + case catch dec_enum(_val, [incoming, outgoing, bidi]) of + {'EXIT', _} -> + erlang:error({pubsub_serverinfo_codec, + {bad_attr_value, + <<"type">>, + <<"connection">>, + __TopXMLNS}}); + _res -> _res + end. + +encode_pubsub_serverinfo_connection_attr_type(_val, + _acc) -> + [{<<"type">>, enc_enum(_val)} | _acc]. + +decode_pubsub_serverinfo_remote_domain(__TopXMLNS, + __Opts, + {xmlel, + <<"remote-domain">>, + _attrs, + _els}) -> + Type = + decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, + _els, + []), + Name = + decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + _attrs, + undefined), + {pubsub_serverinfo_remote_domain, Name, Type}. + +decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, [], Type) -> + lists:reverse(Type); +decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, + [{xmlel, + <<"connection">>, + _attrs, + _} = + _el + | _els], + Type) -> + case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, + _attrs, + __TopXMLNS) + of + <<"urn:xmpp:serverinfo:0">> -> + decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, + _els, + [decode_pubsub_serverinfo_connection(<<"urn:xmpp:serverinfo:0">>, + __Opts, + _el) + | Type]); + _ -> + decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, + _els, + Type) + end; +decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, [_ | _els], Type) -> + decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, + __Opts, + _els, + Type). + +decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + [{<<"name">>, _val} | _attrs], + _Name) -> + decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + _attrs, + _val); +decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + [_ | _attrs], Name) -> + decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + _attrs, + Name); +decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, + [], Name) -> + decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, + Name). + +encode_pubsub_serverinfo_remote_domain({pubsub_serverinfo_remote_domain, + Name, + Type}, + __TopXMLNS) -> + __NewTopXMLNS = + pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, + [], + __TopXMLNS), + _els = + lists:reverse('encode_pubsub_serverinfo_remote_domain_$type'(Type, + __NewTopXMLNS, + [])), + _attrs = + encode_pubsub_serverinfo_remote_domain_attr_name(Name, + pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, + __TopXMLNS)), + {xmlel, <<"remote-domain">>, _attrs, _els}. + +'encode_pubsub_serverinfo_remote_domain_$type'([], + __TopXMLNS, _acc) -> + _acc; +'encode_pubsub_serverinfo_remote_domain_$type'([Type + | _els], + __TopXMLNS, _acc) -> + 'encode_pubsub_serverinfo_remote_domain_$type'(_els, + __TopXMLNS, + [encode_pubsub_serverinfo_connection(Type, + __TopXMLNS) + | _acc]). + +decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, + undefined) -> + erlang:error({pubsub_serverinfo_codec, + {missing_attr, + <<"name">>, + <<"remote-domain">>, + __TopXMLNS}}); +decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, + _val) -> + _val. + +encode_pubsub_serverinfo_remote_domain_attr_name(_val, + _acc) -> + [{<<"name">>, _val} | _acc]. + +decode_pubsub_serverinfo_federation(__TopXMLNS, __Opts, + {xmlel, <<"federation">>, _attrs, _els}) -> + Remote_domain = + decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, + _els, + []), + Remote_domain. + +decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, [], Remote_domain) -> + lists:reverse(Remote_domain); +decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, + [{xmlel, + <<"remote-domain">>, + _attrs, + _} = + _el + | _els], + Remote_domain) -> + case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, + _attrs, + __TopXMLNS) + of + <<"urn:xmpp:serverinfo:0">> -> + decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, + _els, + [decode_pubsub_serverinfo_remote_domain(<<"urn:xmpp:serverinfo:0">>, + __Opts, + _el) + | Remote_domain]); + _ -> + decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, + _els, + Remote_domain) + end; +decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, [_ | _els], Remote_domain) -> + decode_pubsub_serverinfo_federation_els(__TopXMLNS, + __Opts, + _els, + Remote_domain). + +encode_pubsub_serverinfo_federation(Remote_domain, + __TopXMLNS) -> + __NewTopXMLNS = + pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, + [], + __TopXMLNS), + _els = + lists:reverse('encode_pubsub_serverinfo_federation_$remote_domain'(Remote_domain, + __NewTopXMLNS, + [])), + _attrs = + pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, + __TopXMLNS), + {xmlel, <<"federation">>, _attrs, _els}. + +'encode_pubsub_serverinfo_federation_$remote_domain'([], + __TopXMLNS, _acc) -> + _acc; +'encode_pubsub_serverinfo_federation_$remote_domain'([Remote_domain + | _els], + __TopXMLNS, _acc) -> + 'encode_pubsub_serverinfo_federation_$remote_domain'(_els, + __TopXMLNS, + [encode_pubsub_serverinfo_remote_domain(Remote_domain, + __TopXMLNS) + | _acc]). + +decode_pubsub_serverinfo_domain(__TopXMLNS, __Opts, + {xmlel, <<"domain">>, _attrs, _els}) -> + Remote_domain = + decode_pubsub_serverinfo_domain_els(__TopXMLNS, + __Opts, + _els, + undefined), + Name = decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, + _attrs, + undefined), + {pubsub_serverinfo_domain, Name, Remote_domain}. + +decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, + [], Remote_domain) -> + Remote_domain; +decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, + [{xmlel, <<"federation">>, _attrs, _} = _el + | _els], + Remote_domain) -> + case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, + _attrs, + __TopXMLNS) + of + <<"urn:xmpp:serverinfo:0">> -> + decode_pubsub_serverinfo_domain_els(__TopXMLNS, + __Opts, + _els, + decode_pubsub_serverinfo_federation(<<"urn:xmpp:serverinfo:0">>, + __Opts, + _el)); + _ -> + decode_pubsub_serverinfo_domain_els(__TopXMLNS, + __Opts, + _els, + Remote_domain) + end; +decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, + [_ | _els], Remote_domain) -> + decode_pubsub_serverinfo_domain_els(__TopXMLNS, + __Opts, + _els, + Remote_domain). + +decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, + [{<<"name">>, _val} | _attrs], _Name) -> + decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, + _attrs, + _val); +decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, + [_ | _attrs], Name) -> + decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, + _attrs, + Name); +decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, [], + Name) -> + decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, + Name). + +encode_pubsub_serverinfo_domain({pubsub_serverinfo_domain, + Name, + Remote_domain}, + __TopXMLNS) -> + __NewTopXMLNS = + pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, + [], + __TopXMLNS), + _els = + lists:reverse('encode_pubsub_serverinfo_domain_$remote_domain'(Remote_domain, + __NewTopXMLNS, + [])), + _attrs = encode_pubsub_serverinfo_domain_attr_name(Name, + pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, + __TopXMLNS)), + {xmlel, <<"domain">>, _attrs, _els}. + +'encode_pubsub_serverinfo_domain_$remote_domain'(undefined, + __TopXMLNS, _acc) -> + _acc; +'encode_pubsub_serverinfo_domain_$remote_domain'(Remote_domain, + __TopXMLNS, _acc) -> + [encode_pubsub_serverinfo_federation(Remote_domain, + __TopXMLNS) + | _acc]. + +decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, + undefined) -> + erlang:error({pubsub_serverinfo_codec, + {missing_attr, <<"name">>, <<"domain">>, __TopXMLNS}}); +decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, + _val) -> + _val. + +encode_pubsub_serverinfo_domain_attr_name(_val, _acc) -> + [{<<"name">>, _val} | _acc]. + +decode_pubsub_serverinfo(__TopXMLNS, __Opts, + {xmlel, <<"serverinfo">>, _attrs, _els}) -> + Domain = decode_pubsub_serverinfo_els(__TopXMLNS, + __Opts, + _els, + []), + {pubsub_serverinfo, Domain}. + +decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, [], + Domain) -> + lists:reverse(Domain); +decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, + [{xmlel, <<"domain">>, _attrs, _} = _el | _els], + Domain) -> + case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, + _attrs, + __TopXMLNS) + of + <<"urn:xmpp:serverinfo:0">> -> + decode_pubsub_serverinfo_els(__TopXMLNS, + __Opts, + _els, + [decode_pubsub_serverinfo_domain(<<"urn:xmpp:serverinfo:0">>, + __Opts, + _el) + | Domain]); + _ -> + decode_pubsub_serverinfo_els(__TopXMLNS, + __Opts, + _els, + Domain) + end; +decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, + [_ | _els], Domain) -> + decode_pubsub_serverinfo_els(__TopXMLNS, + __Opts, + _els, + Domain). + +encode_pubsub_serverinfo({pubsub_serverinfo, Domain}, + __TopXMLNS) -> + __NewTopXMLNS = + pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, + [], + __TopXMLNS), + _els = + lists:reverse('encode_pubsub_serverinfo_$domain'(Domain, + __NewTopXMLNS, + [])), + _attrs = + pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, + __TopXMLNS), + {xmlel, <<"serverinfo">>, _attrs, _els}. + +'encode_pubsub_serverinfo_$domain'([], __TopXMLNS, + _acc) -> + _acc; +'encode_pubsub_serverinfo_$domain'([Domain | _els], + __TopXMLNS, _acc) -> + 'encode_pubsub_serverinfo_$domain'(_els, + __TopXMLNS, + [encode_pubsub_serverinfo_domain(Domain, + __TopXMLNS) + | _acc]). diff --git a/src/pubsub_serverinfo_codec_external.erl b/src/pubsub_serverinfo_codec_external.erl new file mode 100644 index 000000000..2f2df47e3 --- /dev/null +++ b/src/pubsub_serverinfo_codec_external.erl @@ -0,0 +1,12 @@ +%% Created automatically by XML generator (fxml_gen.erl) +%% Source: pubsub_serverinfo_codec.spec + +-module(pubsub_serverinfo_codec_external). + +-compile(export_all). + +modules() -> []. + +lookup(_, _) -> undefined. + +lookup(Term) -> erlang:error(badarg, [Term]). From bf54cc59e12ef4c678066d06d621f792a8b5e639 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Sat, 5 Jul 2025 14:23:03 +0200 Subject: [PATCH 1186/1302] mod_pubsub_serverinfo: apply `make format` --- src/mod_pubsub_serverinfo.erl | 334 +++++++++++++++++++--------------- 1 file changed, 191 insertions(+), 143 deletions(-) diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index 4ebc93732..d8b47fa91 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -42,17 +42,19 @@ -record(state, {host, pubsub_host, node, monitors = #{}, timer = undefined, public_hosts = []}). +%% @format-begin + start(Host, Opts) -> case pubsub_host(Host, Opts) of - {error, _Reason} = Error -> - Error; - PubsubHost -> - xmpp:register_codec(pubsub_serverinfo_codec), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), - ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), - ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50), - ejabberd_hooks:add(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50), - gen_mod:start_child(?MODULE, Host, PubsubHost) + {error, _Reason} = Error -> + Error; + PubsubHost -> + xmpp:register_codec(pubsub_serverinfo_codec), + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), + ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), + ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50), + ejabberd_hooks:add(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50), + gen_mod:start_child(?MODULE, Host, PubsubHost) end. stop(Host) -> @@ -63,48 +65,53 @@ stop(Host) -> gen_mod:stop_child(?MODULE, Host). init([Host, PubsubHost]) -> - TRef = timer:send_interval(timer:minutes(5), self(), update_pubsub), + TRef = + timer:send_interval( + timer:minutes(5), self(), update_pubsub), Monitors = init_monitors(Host), PublicHosts = fetch_public_hosts(), - State = #state{host = Host, - pubsub_host = PubsubHost, - node = <<"serverinfo">>, - timer = TRef, - monitors = Monitors, - public_hosts = PublicHosts}, + State = + #state{host = Host, + pubsub_host = PubsubHost, + node = <<"serverinfo">>, + timer = TRef, + monitors = Monitors, + public_hosts = PublicHosts}, self() ! update_pubsub, {ok, State}. -spec init_monitors(binary()) -> map(). init_monitors(Host) -> - lists:foldl( - fun(Domain, Monitors) -> - RefIn = make_ref(), % just dummies - RefOut = make_ref(), - maps:merge(#{RefIn => {incoming, {Host, Domain, true}}, - RefOut => {outgoing, {Host, Domain, true}}}, - Monitors) - end, - #{}, - ejabberd_option:hosts() -- [Host]). + lists:foldl(fun(Domain, Monitors) -> + RefIn = make_ref(), % just dummies + RefOut = make_ref(), + maps:merge(#{RefIn => {incoming, {Host, Domain, true}}, + RefOut => {outgoing, {Host, Domain, true}}}, + Monitors) + end, + #{}, + ejabberd_option:hosts() -- [Host]). -spec fetch_public_hosts() -> list(). fetch_public_hosts() -> try - {ok, {{_, 200, _}, _Headers, Body}} = httpc:request(get, {?PUBLIC_HOSTS_URL, []}, [{timeout, 1000}], [{body_format, binary}]), + {ok, {{_, 200, _}, _Headers, Body}} = + httpc:request(get, {?PUBLIC_HOSTS_URL, []}, [{timeout, 1000}], [{body_format, binary}]), case misc:json_decode(Body) of - PublicHosts when is_list(PublicHosts) -> PublicHosts; + PublicHosts when is_list(PublicHosts) -> + PublicHosts; Other -> ?WARNING_MSG("Parsed JSON for public hosts was not a list: ~p", [Other]), [] end - catch E:R -> + catch + E:R -> ?WARNING_MSG("Failed fetching public hosts (~p): ~p", [E, R]), [] end. handle_cast({Event, Domain, Pid}, #state{host = Host, monitors = Mons} = State) - when Event == register_in; Event == register_out -> + when Event == register_in; Event == register_out -> Ref = monitor(process, Pid), IsPublic = check_if_public(Domain, State), NewMons = maps:put(Ref, {event_to_dir(Event), {Host, Domain, IsPublic}}, Mons), @@ -112,37 +119,41 @@ handle_cast({Event, Domain, Pid}, #state{host = Host, monitors = Mons} = State) handle_cast(_, State) -> {noreply, State}. -event_to_dir(register_in) -> incoming; -event_to_dir(register_out) -> outgoing. +event_to_dir(register_in) -> + incoming; +event_to_dir(register_out) -> + outgoing. handle_call(_Request, _From, State) -> {noreply, State}. -handle_info({iq_reply, IQReply, {LServer, RServer}}, - #state{monitors = Mons} = State) -> +handle_info({iq_reply, IQReply, {LServer, RServer}}, #state{monitors = Mons} = State) -> case IQReply of - #iq{type = result, sub_els = [El]} -> - case xmpp:decode(El) of - #disco_info{features = Features} -> - case lists:member(?NS_URN_SERVERINFO, Features) of - true -> - NewMons = maps:fold(fun(Ref, {Dir, {LServer0, RServer0, _}}, Acc) - when LServer == LServer0, RServer == RServer0 -> - maps:put(Ref, {Dir, {LServer, RServer, true}}, Acc); - (Ref, Other, Acc) -> - maps:put(Ref, Other, Acc) - end, - #{}, - Mons), - {noreply, State#state{monitors = NewMons}}; - _ -> - {noreply, State} - end; - _ -> - {noreply, State} - end; - _ -> - {noreply, State} + #iq{type = result, sub_els = [El]} -> + case xmpp:decode(El) of + #disco_info{features = Features} -> + case lists:member(?NS_URN_SERVERINFO, Features) of + true -> + NewMons = + maps:fold(fun (Ref, {Dir, {LServer0, RServer0, _}}, Acc) + when LServer == LServer0, RServer == RServer0 -> + maps:put(Ref, + {Dir, {LServer, RServer, true}}, + Acc); + (Ref, Other, Acc) -> + maps:put(Ref, Other, Acc) + end, + #{}, + Mons), + {noreply, State#state{monitors = NewMons}}; + _ -> + {noreply, State} + end; + _ -> + {noreply, State} + end; + _ -> + {noreply, State} end; handle_info(update_pubsub, State) -> update_pubsub(State), @@ -154,22 +165,22 @@ handle_info(_Request, State) -> terminate(_Reason, #state{monitors = Mons, timer = Timer}) -> case is_reference(Timer) of - true -> - case erlang:cancel_timer(Timer) of - false -> - receive {timeout, Timer, _} -> ok - after 0 -> ok - end; - _ -> - ok - end; - _ -> - ok + true -> + case erlang:cancel_timer(Timer) of + false -> + receive + {timeout, Timer, _} -> + ok + after 0 -> + ok + end; + _ -> + ok + end; + _ -> + ok end, - maps:fold( - fun(Mon, _, _) -> - demonitor(Mon) - end, ok, Mons). + maps:fold(fun(Mon, _, _) -> demonitor(Mon) end, ok, Mons). depends(_Host, _Opts) -> [{mod_pubsub, hard}]. @@ -180,124 +191,161 @@ mod_options(_Host) -> mod_opt_type(pubsub_host) -> econf:either(undefined, econf:host()). -mod_doc() -> #{}. +mod_doc() -> + #{}. in_auth_result(#{server_host := Host, remote_server := RServer} = State, true, _Server) -> - gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_in, RServer, self()}), + gen_server:cast( + gen_mod:get_module_proc(Host, ?MODULE), {register_in, RServer, self()}), State; in_auth_result(State, _, _) -> State. out_auth_result(#{server_host := Host, remote_server := RServer} = State, true) -> - gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_out, RServer, self()}), + gen_server:cast( + gen_mod:get_module_proc(Host, ?MODULE), {register_out, RServer, self()}), State; out_auth_result(State, _) -> State. check_if_public(Domain, State) -> - maybe_send_disco_info(is_public(Domain, State) orelse is_monitored(Domain, State), Domain, State). + maybe_send_disco_info(is_public(Domain, State) orelse is_monitored(Domain, State), + Domain, + State). is_public(Domain, #state{public_hosts = PublicHosts}) -> lists:member(Domain, PublicHosts). is_monitored(Domain, #state{host = Host, monitors = Mons}) -> maps:size( - maps:filter( - fun(_Ref, {_Dir, {LServer, RServer, IsPublic}}) - when LServer == Host, RServer == Domain -> IsPublic; - (_Ref, _Other) -> false - end, - Mons)) =/= 0. + maps:filter(fun (_Ref, {_Dir, {LServer, RServer, IsPublic}}) + when LServer == Host, RServer == Domain -> + IsPublic; + (_Ref, _Other) -> + false + end, + Mons)) + =/= 0. -maybe_send_disco_info(true, _Domain, _State) -> true; +maybe_send_disco_info(true, _Domain, _State) -> + true; maybe_send_disco_info(false, Domain, #state{host = Host}) -> Proc = gen_mod:get_module_proc(Host, ?MODULE), - IQ = #iq{type = get, from = jid:make(Host), - to = jid:make(Domain), sub_els = [#disco_info{}]}, + IQ = #iq{type = get, + from = jid:make(Host), + to = jid:make(Domain), + sub_els = [#disco_info{}]}, ejabberd_router:route_iq(IQ, {Host, Domain}, Proc), false. -update_pubsub(#state{host = Host, pubsub_host = PubsubHost, node = Node, monitors = Mons}) -> - Map = maps:fold( - fun(_, {Dir, {MyDomain, Target, IsPublic}}, Acc) -> - maps:update_with(MyDomain, - fun(Acc2) -> - maps:update_with(Target, - fun({Types, _}) -> {Types#{Dir => true}, IsPublic} end, - {#{Dir => true}, IsPublic}, Acc2) - end, #{Target => {#{Dir => true}, IsPublic}}, Acc) - end, #{}, Mons), - Domains = maps:fold( - fun(MyDomain, Targets, Acc) -> - Remote = maps:fold( - fun(Remote, {Types, true}, Acc2) -> - [#pubsub_serverinfo_remote_domain{name = Remote, type = maps:keys(Types)} | Acc2]; - (_HiddenRemote, {Types, false}, Acc2) -> - [#pubsub_serverinfo_remote_domain{type = maps:keys(Types)} | Acc2] - end, [], Targets), - [#pubsub_serverinfo_domain{name = MyDomain, remote_domain = Remote} | Acc] - end, [], Map), +update_pubsub(#state{host = Host, + pubsub_host = PubsubHost, + node = Node, + monitors = Mons}) -> + Map = maps:fold(fun(_, {Dir, {MyDomain, Target, IsPublic}}, Acc) -> + maps:update_with(MyDomain, + fun(Acc2) -> + maps:update_with(Target, + fun({Types, _}) -> + {Types#{Dir => true}, IsPublic} + end, + {#{Dir => true}, IsPublic}, + Acc2) + end, + #{Target => {#{Dir => true}, IsPublic}}, + Acc) + end, + #{}, + Mons), + Domains = + maps:fold(fun(MyDomain, Targets, Acc) -> + Remote = + maps:fold(fun (Remote, {Types, true}, Acc2) -> + [#pubsub_serverinfo_remote_domain{name = Remote, + type = + maps:keys(Types)} + | Acc2]; + (_HiddenRemote, {Types, false}, Acc2) -> + [#pubsub_serverinfo_remote_domain{type = + maps:keys(Types)} + | Acc2] + end, + [], + Targets), + [#pubsub_serverinfo_domain{name = MyDomain, remote_domain = Remote} | Acc] + end, + [], + Map), PubOpts = [{persist_items, true}, {max_items, 1}, {access_model, open}], ?DEBUG("Publishing serverinfo pubsub item on ~s: ~p", [PubsubHost, Domains]), - mod_pubsub:publish_item( - PubsubHost, Host, Node, jid:make(Host), - <<"current">>, [xmpp:encode(#pubsub_serverinfo{domain = Domains})], PubOpts, all). + mod_pubsub:publish_item(PubsubHost, + Host, + Node, + jid:make(Host), + <<"current">>, + [xmpp:encode(#pubsub_serverinfo{domain = Domains})], + PubOpts, + all). get_local_features({error, _} = Acc, _From, _To, _Node, _Lang) -> Acc; get_local_features(Acc, _From, _To, Node, _Lang) when Node == <<>> -> case Acc of - {result, Features} -> - {result, [?NS_URN_SERVERINFO | Features]}; - empty -> {result, [?NS_URN_SERVERINFO]} + {result, Features} -> + {result, [?NS_URN_SERVERINFO | Features]}; + empty -> + {result, [?NS_URN_SERVERINFO]} end; get_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. -get_info(Acc, Host, Mod, Node, Lang) when (Mod == undefined orelse Mod == mod_disco), Node == <<"">> -> +get_info(Acc, Host, Mod, Node, Lang) + when Mod == undefined orelse Mod == mod_disco, Node == <<"">> -> case mod_disco:get_info(Acc, Host, Mod, Node, Lang) of - [#xdata{fields = Fields} = XD | Rest] -> - PubsubHost = pubsub_host(Host), - NodeField = #xdata_field{var = <<"serverinfo-pubsub-node">>, - values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}, - {stop, [XD#xdata{fields = Fields ++ [NodeField]} | Rest]}; - _ -> - Acc + [#xdata{fields = Fields} = XD | Rest] -> + PubsubHost = pubsub_host(Host), + NodeField = + #xdata_field{var = <<"serverinfo-pubsub-node">>, + values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}, + {stop, [XD#xdata{fields = Fields ++ [NodeField]} | Rest]}; + _ -> + Acc end; get_info(Acc, Host, Mod, Node, _Lang) when Node == <<"">>, is_atom(Mod) -> PubsubHost = pubsub_host(Host), [#xdata{type = result, - fields = [ - #xdata_field{type = hidden, - var = <<"FORM_TYPE">>, - values = [?NS_SERVERINFO]}, - #xdata_field{var = <<"serverinfo-pubsub-node">>, - values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}]} | Acc]; + fields = + [#xdata_field{type = hidden, + var = <<"FORM_TYPE">>, + values = [?NS_SERVERINFO]}, + #xdata_field{var = <<"serverinfo-pubsub-node">>, + values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}]} + | Acc]; get_info(Acc, _Host, _Mod, _Node, _Lang) -> Acc. pubsub_host(Host) -> case pubsub_host(Host, gen_mod:get_module_opts(Host, ?MODULE)) of - {error, _Reason} = Error -> - throw(Error); - PubsubHost -> - PubsubHost + {error, _Reason} = Error -> + throw(Error); + PubsubHost -> + PubsubHost end. pubsub_host(Host, Opts) -> case gen_mod:get_opt(pubsub_host, Opts) of - undefined -> - PubsubHost = hd(get_mod_pubsub_hosts(Host)), - ?INFO_MSG("No pubsub_host in configuration for ~p, choosing ~s", [?MODULE, PubsubHost]), - PubsubHost; - PubsubHost -> - case check_pubsub_host_exists(Host, PubsubHost) of - true -> - PubsubHost; - false -> - {error, {pubsub_host_does_not_exist, PubsubHost}} - end + undefined -> + PubsubHost = hd(get_mod_pubsub_hosts(Host)), + ?INFO_MSG("No pubsub_host in configuration for ~p, choosing ~s", [?MODULE, PubsubHost]), + PubsubHost; + PubsubHost -> + case check_pubsub_host_exists(Host, PubsubHost) of + true -> + PubsubHost; + false -> + {error, {pubsub_host_does_not_exist, PubsubHost}} + end end. check_pubsub_host_exists(Host, PubsubHost) -> @@ -305,8 +353,8 @@ check_pubsub_host_exists(Host, PubsubHost) -> get_mod_pubsub_hosts(Host) -> case gen_mod:get_module_opt(Host, mod_pubsub, hosts) of - [] -> - [gen_mod:get_module_opt(Host, mod_pubsub, host)]; - PubsubHosts -> - PubsubHosts + [] -> + [gen_mod:get_module_opt(Host, mod_pubsub, host)]; + PubsubHosts -> + PubsubHosts end. From a6823d157c95fba829017a9d249f779e58b6d163 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Sat, 5 Jul 2025 14:38:55 +0200 Subject: [PATCH 1187/1302] mod_pubsub_serverinfo: add documentation --- src/mod_pubsub_serverinfo.erl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index d8b47fa91..f002808bc 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -30,6 +30,8 @@ -include("pubsub_serverinfo_codec.hrl"). -include("logger.hrl"). +-include("translate.hrl"). + -include_lib("xmpp/include/xmpp.hrl"). %% gen_mod callbacks. @@ -192,7 +194,22 @@ mod_opt_type(pubsub_host) -> econf:either(undefined, econf:host()). mod_doc() -> - #{}. + #{desc => [?T("Exposes s2s information over Pub/Sub"), "", + ?T("Announces support for the ProtoXEP PubSub Server Information, by adding its Service Discovery feature." + "Active S2S connections are published to a local pubsub node as advertised by Service Discovery. Only those connections that support this feature as well are exposed with their domain names, otherwise they are shown as anonymous nodes. At startup a list of well known public servers is being fetched. Those are not shown as anonymous even if they don't support this feature." + "Currently the name of the node is hardcoded as \"serverinfo\". The local service to be used can be configured as `pubsub_host`. Otherwise a good guess is taken." + "This module has a hard dependency on `mod_pubsub` for this reason. Also `mod_disco` must be configured for this feature to work."), "", + ?T("NOTE: The module only shows S2S connections established while the module is running: after installing the module, please run `ejabberdctl stop_s2s_connections`, or restart ejabberd.")], + note => "added in 25.xx", + opts => [{pubsub_host, + #{value => "undefined | string()", + desc => ?T("This option specifies which pubsub host to use to advertise S2S connections. This must be a vhost local to this service and handled by `mod_pubsub`. This is only needed if your configuration has more than one vhost in mod_pubsub's `hosts` option. If there's more than one and this option is not given, we just pick the first one.")} + }], + example => + ["modules:", + " mod_pubsub_serverinfo:", + " pubsub_host: custom.pubsub.domain.local"] + }. in_auth_result(#{server_host := Host, remote_server := RServer} = State, true, _Server) -> gen_server:cast( From c567005241bda343d96d551c4e6cab82378f9236 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Sat, 5 Jul 2025 14:54:11 +0200 Subject: [PATCH 1188/1302] mod_pubsub_serverinfo: get pubsub host from server state --- src/mod_pubsub_serverinfo.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index f002808bc..8141e243f 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -126,6 +126,8 @@ event_to_dir(register_in) -> event_to_dir(register_out) -> outgoing. +handle_call(pubsub_host, _From, #state{pubsub_host = PubsubHost} = State) -> + {reply, {ok, PubsubHost}, State}; handle_call(_Request, _From, State) -> {noreply, State}. @@ -343,12 +345,8 @@ get_info(Acc, _Host, _Mod, _Node, _Lang) -> Acc. pubsub_host(Host) -> - case pubsub_host(Host, gen_mod:get_module_opts(Host, ?MODULE)) of - {error, _Reason} = Error -> - throw(Error); - PubsubHost -> - PubsubHost - end. + {ok, PubsubHost} = gen_server:call(gen_mod:get_module_proc(Host, ?MODULE), pubsub_host), + PubsubHost. pubsub_host(Host, Opts) -> case gen_mod:get_opt(pubsub_host, Opts) of From c93ea2c22f00d4992f4292e9490e04ba7ba35e7e Mon Sep 17 00:00:00 2001 From: Bruno MATEU Date: Mon, 7 Jul 2025 08:28:23 +0200 Subject: [PATCH 1189/1302] missing comma in postgres schema --- sql/pg.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/pg.sql b/sql/pg.sql index 75674e3a8..dd83e087e 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -18,7 +18,7 @@ CREATE TABLE users ( username text NOT NULL, - "type" smallint NOT NULL + "type" smallint NOT NULL, "password" text NOT NULL, serverkey text NOT NULL DEFAULT '', salt text NOT NULL DEFAULT '', From 5f293cb1e0f163117e7a8c3ab1fe300ea82806dc Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 23 Jun 2025 09:58:52 +0200 Subject: [PATCH 1190/1302] Document more options --- src/mod_antispam.erl | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 1706f4377..9baaeffc1 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -172,7 +172,14 @@ mod_options(_Host) -> {whitelist_domains_file, none}]. mod_doc() -> - #{desc => ?T("Reads from text file and RTBL, filters stanzas and writes dump file."), + #{desc => + ?T("Filter spam messages and subscription requests received from " + "remote servers based on " + "https://xmppbl.org/[Real-Time Block Lists (RTBL)], " + "lists of known spammer JIDs and/or URLs mentioned in spam messages. " + "Traffic classified as spam is rejected with an error " + "(and an '[info]' message is logged) unless the sender " + "is subscribed to the recipient's presence."), note => "added in 25.xx", opts => [{access_spam, @@ -183,6 +190,16 @@ mod_doc() -> "spam messages aren't rejected for that recipient. " "The default value is 'none', which means that all recipients " "are subject to spam filtering verification.")}}, + {cache_size, + #{value => "pos_integer()", + desc => + ?T("Maximum number of JIDs that will be cached due to sending spam URLs. " + "If that limit is exceeded, the least recently used " + "entries are removed from the cache. " + "Setting this option to '0' disables the caching feature. " + "Note that separate caches are used for each virtual host, " + " and that the caches aren't distributed across cluster nodes. " + "The default value is '10000'.")}}, {spam_domains_file, #{value => ?T("none | Path"), desc => @@ -197,9 +214,11 @@ mod_doc() -> #{value => ?T("false | true | Path"), desc => ?T("Path to the file to store blocked messages. " - "Use an absolute path, or the '@LOG_PATH@' macro to store logs " + "Use an absolute path, or the '@LOG_PATH@' " + "https://docs.ejabberd.im/admin/configuration/file-format/#predefined-keywords[predefined keyword] " + "to store logs " "in the same place that the other ejabberd log files. " - "If set to 'false', does not dump stanzas, this is the default. " + "If set to 'false', it doesn't dump stanzas, which is the default. " "If set to 'true', it stores in '\"@LOG_PATH@/spam_dump_@HOST@.log\"'.")}}, {spam_jids_file, #{value => ?T("none | Path"), @@ -228,12 +247,13 @@ mod_doc() -> ?T("Path to a file containing a list of " "domains to whitelist from being blocked, one per line. " "If either it is in 'spam_domains_file' or more realistically " - "in a domain sent by a RTBL host (see option 'rtbl_host') " + "in a domain sent by a RTBL host (see option 'rtbl_services') " "then this domain will be ignored and stanzas from there won't be blocked. " "The default value is 'none'.")}}], example => ["modules:", " mod_antispam:", + " spam_jids_file: \"@CONFIG_PATH@/spam_jids.txt\"", " spam_dump_file: \"@LOG_PATH@/spam/host-@HOST@.log\""]}. %%-------------------------------------------------------------------- From 500af47b7948a2173562276fecd4c787bbf33a80 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 7 Jul 2025 17:21:27 +0200 Subject: [PATCH 1191/1302] mod_pubsub_serverinfo: codec spec moved to p1/xmpp --- include/pubsub_serverinfo_codec.hrl | 17 - mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 32 +- specs/pubsub_serverinfo_codec.spec | 52 -- src/mod_pubsub_serverinfo.erl | 2 - src/pubsub_serverinfo_codec.erl | 734 ----------------------- src/pubsub_serverinfo_codec_external.erl | 12 - 8 files changed, 18 insertions(+), 835 deletions(-) delete mode 100644 include/pubsub_serverinfo_codec.hrl delete mode 100644 specs/pubsub_serverinfo_codec.spec delete mode 100644 src/pubsub_serverinfo_codec.erl delete mode 100644 src/pubsub_serverinfo_codec_external.erl diff --git a/include/pubsub_serverinfo_codec.hrl b/include/pubsub_serverinfo_codec.hrl deleted file mode 100644 index 81b975798..000000000 --- a/include/pubsub_serverinfo_codec.hrl +++ /dev/null @@ -1,17 +0,0 @@ -%% Created automatically by XML generator (fxml_gen.erl) -%% Source: pubsub_serverinfo_codec.spec - --record(pubsub_serverinfo_remote_domain, {name = <<>> :: binary(), - type = [] :: ['bidi' | 'incoming' | 'outgoing']}). --type pubsub_serverinfo_remote_domain() :: #pubsub_serverinfo_remote_domain{}. - --record(pubsub_serverinfo_domain, {name = <<>> :: binary(), - remote_domain :: 'undefined' | [#pubsub_serverinfo_remote_domain{}]}). --type pubsub_serverinfo_domain() :: #pubsub_serverinfo_domain{}. - --record(pubsub_serverinfo, {domain = [] :: [#pubsub_serverinfo_domain{}]}). --type pubsub_serverinfo() :: #pubsub_serverinfo{}. - --type pubsub_serverinfo_codec() :: pubsub_serverinfo() | - pubsub_serverinfo_domain() | - pubsub_serverinfo_remote_domain(). diff --git a/mix.exs b/mix.exs index c8d6d82bc..308d95c51 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "74ed2d87222d3bd5e86f7c41daaa28fae59d4995", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "fbaea1bfb4244e4287fe8449144e8e72d9a842d1", override: true}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 63c51b8fb..ad3b004c7 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "fbaea1bfb4244e4287fe8449144e8e72d9a842d1"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 973df5a07..365d3fd3e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -16,23 +16,23 @@ {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, - {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, + {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.18">>},0}, {<<"p1_acme">>, {git,"https://github.com/processone/p1_acme", {ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}}, 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.32">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.33">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, - {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, - {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.32">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0}, + {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}}, + {ref,"fbaea1bfb4244e4287fe8449144e8e72d9a842d1"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ @@ -50,16 +50,16 @@ {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, - {<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, + {<<"mqtree">>, <<"B004E80BBEE5BC49E774B839F88162BDFF5CB654ABBDB79C8381AE4B13510A1B">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"3F95D7E3413FC8F0BE80ABB4BE1A0D7F67066A36905085CD5A423145598B0CB0">>}, + {<<"p1_pgsql">>, <<"585F720C76B9BD27C5313DBB2C3CAD0CA19AB4493DE0B34A7496B51B65F5613A">>}, {<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, - {<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, - {<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, - {<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, + {<<"stringprep">>, <<"63FD7FF5417A4A48DB6BB529C83D678361D34188367C1B13B3EAF39ACDEDB8E5">>}, + {<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>}, + {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, @@ -75,15 +75,15 @@ {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, - {<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, + {<<"mqtree">>, <<"F73827CECF9A310670F7D7909FC88EAB40B45290FE48E5C7E45AB7235B29B919">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"268B01E8F4EB75C211A31495A25C2815C549AECCE2F0DF1A161C6E0A2CDE061E">>}, + {<<"p1_pgsql">>, <<"3FB6A9617DB146419D420FFE7E94B9179A7CB5063D9C9450EF8B13FDAD2A709F">>}, {<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, - {<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, - {<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, - {<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, + {<<"stringprep">>, <<"6069CB059F5D18A312C42E5F374E9DE415DF68F7E3090C9C1A5505E2A8532710">>}, + {<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>}, + {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} ]. diff --git a/specs/pubsub_serverinfo_codec.spec b/specs/pubsub_serverinfo_codec.spec deleted file mode 100644 index 44966e9f1..000000000 --- a/specs/pubsub_serverinfo_codec.spec +++ /dev/null @@ -1,52 +0,0 @@ --xml(pubsub_serverinfo, - #elem{name = <<"serverinfo">>, - xmlns = <<"urn:xmpp:serverinfo:0">>, - module = pubsub_serverinfo_codec, - result = {pubsub_serverinfo, '$domain'}, - refs = [#ref{name = pubsub_serverinfo_domain, - label = '$domain', - min = 0}]}). - --xml(pubsub_serverinfo_domain, - #elem{name = <<"domain">>, - xmlns = <<"urn:xmpp:serverinfo:0">>, - module = pubsub_serverinfo_codec, - result = {pubsub_serverinfo_domain, '$name', '$remote_domain'}, - attrs = [#attr{name = <<"name">>, - label = '$name', - required = true}], - refs = [#ref{name = pubsub_serverinfo_federation, - label = '$remote_domain', - min = 0, max = 1}]}). - --xml(pubsub_serverinfo_federation, - #elem{name = <<"federation">>, - xmlns = <<"urn:xmpp:serverinfo:0">>, - module = pubsub_serverinfo_codec, - result = '$remote_domain', - refs = [#ref{name = pubsub_serverinfo_remote_domain, - label = '$remote_domain', - min = 0}]}). - --xml(pubsub_serverinfo_remote_domain, - #elem{name = <<"remote-domain">>, - xmlns = <<"urn:xmpp:serverinfo:0">>, - module = pubsub_serverinfo_codec, - result = {pubsub_serverinfo_remote_domain, '$name', '$type'}, - attrs = [#attr{name = <<"name">>, - label = '$name', - required = true}], - refs = [#ref{name = pubsub_serverinfo_connection, - label = '$type', - min = 0}]}). - --xml(pubsub_serverinfo_connection, - #elem{name = <<"connection">>, - xmlns = <<"urn:xmpp:serverinfo:0">>, - module = pubsub_serverinfo_codec, - result = '$type', - attrs = [#attr{name = <<"type">>, - label = '$type', - required = true, - enc = {enc_enum, []}, - dec = {dec_enum, [[incoming, outgoing, bidi]]}}]}). diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index 8141e243f..cc7672dfe 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -28,7 +28,6 @@ -behaviour(gen_mod). -behaviour(gen_server). --include("pubsub_serverinfo_codec.hrl"). -include("logger.hrl"). -include("translate.hrl"). @@ -51,7 +50,6 @@ start(Host, Opts) -> {error, _Reason} = Error -> Error; PubsubHost -> - xmpp:register_codec(pubsub_serverinfo_codec), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50), ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50), ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50), diff --git a/src/pubsub_serverinfo_codec.erl b/src/pubsub_serverinfo_codec.erl deleted file mode 100644 index 9b16edeaf..000000000 --- a/src/pubsub_serverinfo_codec.erl +++ /dev/null @@ -1,734 +0,0 @@ -%% Created automatically by XML generator (fxml_gen.erl) -%% Source: pubsub_serverinfo_codec.spec - --module(pubsub_serverinfo_codec). - --compile(export_all). - -decode(El) -> decode(El, <<>>, []). - -decode(El, Opts) -> decode(El, <<>>, Opts). - -decode({xmlel, Name, Attrs, _} = El, TopXMLNS, Opts) -> - XMLNS = get_attr(<<"xmlns">>, Attrs, TopXMLNS), - case get_mod(Name, XMLNS) of - undefined when XMLNS == <<>> -> - erlang:error({pubsub_serverinfo_codec, - {missing_tag_xmlns, Name}}); - undefined -> - erlang:error({pubsub_serverinfo_codec, - {unknown_tag, Name, XMLNS}}); - Mod -> Mod:do_decode(Name, XMLNS, El, Opts) - end. - -encode(El) -> encode(El, <<>>). - -encode({xmlel, _, _, _} = El, _) -> El; -encode({xmlcdata, _} = CData, _) -> CData; -encode(El, TopXMLNS) -> - Mod = get_mod(El), - Mod:do_encode(El, TopXMLNS). - - -get_name(El) -> - Mod = get_mod(El), - Mod:do_get_name(El). - -get_ns(El) -> - Mod = get_mod(El), - Mod:do_get_ns(El). - -is_known_tag({xmlel, Name, Attrs, _}, TopXMLNS) -> - XMLNS = get_attr(<<"xmlns">>, Attrs, TopXMLNS), - get_mod(Name, XMLNS) /= undefined. - -get_els(Term) -> - Mod = get_mod(Term), - Mod:get_els(Term). - -set_els(Term, Els) -> - Mod = get_mod(Term), - Mod:set_els(Term, Els). - -do_decode(<<"connection">>, <<"urn:xmpp:serverinfo:0">>, - El, Opts) -> - decode_pubsub_serverinfo_connection(<<"urn:xmpp:serverinfo:0">>, - Opts, - El); -do_decode(<<"remote-domain">>, - <<"urn:xmpp:serverinfo:0">>, El, Opts) -> - decode_pubsub_serverinfo_remote_domain(<<"urn:xmpp:serverinfo:0">>, - Opts, - El); -do_decode(<<"federation">>, <<"urn:xmpp:serverinfo:0">>, - El, Opts) -> - decode_pubsub_serverinfo_federation(<<"urn:xmpp:serverinfo:0">>, - Opts, - El); -do_decode(<<"domain">>, <<"urn:xmpp:serverinfo:0">>, El, - Opts) -> - decode_pubsub_serverinfo_domain(<<"urn:xmpp:serverinfo:0">>, - Opts, - El); -do_decode(<<"serverinfo">>, <<"urn:xmpp:serverinfo:0">>, - El, Opts) -> - decode_pubsub_serverinfo(<<"urn:xmpp:serverinfo:0">>, - Opts, - El); -do_decode(Name, <<>>, _, _) -> - erlang:error({pubsub_serverinfo_codec, - {missing_tag_xmlns, Name}}); -do_decode(Name, XMLNS, _, _) -> - erlang:error({pubsub_serverinfo_codec, - {unknown_tag, Name, XMLNS}}). - -tags() -> - [{<<"connection">>, <<"urn:xmpp:serverinfo:0">>}, - {<<"remote-domain">>, <<"urn:xmpp:serverinfo:0">>}, - {<<"federation">>, <<"urn:xmpp:serverinfo:0">>}, - {<<"domain">>, <<"urn:xmpp:serverinfo:0">>}, - {<<"serverinfo">>, <<"urn:xmpp:serverinfo:0">>}]. - -do_encode({pubsub_serverinfo, _} = Serverinfo, - TopXMLNS) -> - encode_pubsub_serverinfo(Serverinfo, TopXMLNS); -do_encode({pubsub_serverinfo_domain, _, _} = Domain, - TopXMLNS) -> - encode_pubsub_serverinfo_domain(Domain, TopXMLNS); -do_encode({pubsub_serverinfo_remote_domain, _, _} = - Remote_domain, - TopXMLNS) -> - encode_pubsub_serverinfo_remote_domain(Remote_domain, - TopXMLNS). - -do_get_name({pubsub_serverinfo, _}) -> <<"serverinfo">>; -do_get_name({pubsub_serverinfo_domain, _, _}) -> - <<"domain">>; -do_get_name({pubsub_serverinfo_remote_domain, _, _}) -> - <<"remote-domain">>. - -do_get_ns({pubsub_serverinfo, _}) -> - <<"urn:xmpp:serverinfo:0">>; -do_get_ns({pubsub_serverinfo_domain, _, _}) -> - <<"urn:xmpp:serverinfo:0">>; -do_get_ns({pubsub_serverinfo_remote_domain, _, _}) -> - <<"urn:xmpp:serverinfo:0">>. - -register_module(Mod) -> - register_module(Mod, pubsub_serverinfo_codec_external). - -unregister_module(Mod) -> - unregister_module(Mod, - pubsub_serverinfo_codec_external). - -format_error({bad_attr_value, Attr, Tag, XMLNS}) -> - <<"Bad value of attribute '", Attr/binary, "' in tag <", - Tag/binary, "/> qualified by namespace '", XMLNS/binary, - "'">>; -format_error({bad_cdata_value, <<>>, Tag, XMLNS}) -> - <<"Bad value of cdata in tag <", Tag/binary, - "/> qualified by namespace '", XMLNS/binary, "'">>; -format_error({missing_tag, Tag, XMLNS}) -> - <<"Missing tag <", Tag/binary, - "/> qualified by namespace '", XMLNS/binary, "'">>; -format_error({missing_attr, Attr, Tag, XMLNS}) -> - <<"Missing attribute '", Attr/binary, "' in tag <", - Tag/binary, "/> qualified by namespace '", XMLNS/binary, - "'">>; -format_error({missing_cdata, <<>>, Tag, XMLNS}) -> - <<"Missing cdata in tag <", Tag/binary, - "/> qualified by namespace '", XMLNS/binary, "'">>; -format_error({unknown_tag, Tag, XMLNS}) -> - <<"Unknown tag <", Tag/binary, - "/> qualified by namespace '", XMLNS/binary, "'">>; -format_error({missing_tag_xmlns, Tag}) -> - <<"Missing namespace for tag <", Tag/binary, "/>">>. - -io_format_error({bad_attr_value, Attr, Tag, XMLNS}) -> - {<<"Bad value of attribute '~s' in tag <~s/> " - "qualified by namespace '~s'">>, - [Attr, Tag, XMLNS]}; -io_format_error({bad_cdata_value, <<>>, Tag, XMLNS}) -> - {<<"Bad value of cdata in tag <~s/> qualified " - "by namespace '~s'">>, - [Tag, XMLNS]}; -io_format_error({missing_tag, Tag, XMLNS}) -> - {<<"Missing tag <~s/> qualified by namespace " - "'~s'">>, - [Tag, XMLNS]}; -io_format_error({missing_attr, Attr, Tag, XMLNS}) -> - {<<"Missing attribute '~s' in tag <~s/> " - "qualified by namespace '~s'">>, - [Attr, Tag, XMLNS]}; -io_format_error({missing_cdata, <<>>, Tag, XMLNS}) -> - {<<"Missing cdata in tag <~s/> qualified " - "by namespace '~s'">>, - [Tag, XMLNS]}; -io_format_error({unknown_tag, Tag, XMLNS}) -> - {<<"Unknown tag <~s/> qualified by namespace " - "'~s'">>, - [Tag, XMLNS]}; -io_format_error({missing_tag_xmlns, Tag}) -> - {<<"Missing namespace for tag <~s/>">>, [Tag]}. - -get_attr(Attr, Attrs, Default) -> - case lists:keyfind(Attr, 1, Attrs) of - {_, Val} -> Val; - false -> Default - end. - -enc_xmlns_attrs(XMLNS, XMLNS) -> []; -enc_xmlns_attrs(XMLNS, _) -> [{<<"xmlns">>, XMLNS}]. - -choose_top_xmlns(<<>>, NSList, TopXMLNS) -> - case lists:member(TopXMLNS, NSList) of - true -> TopXMLNS; - false -> hd(NSList) - end; -choose_top_xmlns(XMLNS, _, _) -> XMLNS. - -register_module(Mod, ResolverMod) -> - MD5Sum = try Mod:module_info(md5) of - Val -> Val - catch - error:badarg -> - {ok, {Mod, Val}} = beam_lib:md5(code:which(Mod)), - Val - end, - case orddict:find(Mod, ResolverMod:modules()) of - {ok, MD5Sum} -> ok; - _ -> - Mods = orddict:store(Mod, - MD5Sum, - ResolverMod:modules()), - recompile_resolver(Mods, ResolverMod) - end. - -unregister_module(Mod, ResolverMod) -> - case orddict:find(Mod, ResolverMod:modules()) of - {ok, _} -> - Mods = orddict:erase(Mod, ResolverMod:modules()), - recompile_resolver(Mods, ResolverMod); - error -> ok - end. - -recompile_resolver(Mods, ResolverMod) -> - Tags = lists:flatmap(fun (M) -> - [{Name, XMLNS, M} || {Name, XMLNS} <- M:tags()] - end, - orddict:fetch_keys(Mods)), - Records = lists:flatmap(fun (M) -> - [{RecName, RecSize, M} - || {RecName, RecSize} <- M:records()] - end, - orddict:fetch_keys(Mods)), - Lookup1 = string:join(lists:map(fun ({RecName, - RecSize, - M}) -> - io_lib:format("lookup({~s}) -> '~s'", - [string:join([io_lib:format("'~s'", - [RecName]) - | ["_" - || _ - <- lists:seq(1, - RecSize)]], - ","), - M]) - end, - Records) - ++ - ["lookup(Term) -> erlang:error(badarg, " - "[Term])."], - ";" ++ io_lib:nl()), - Lookup2 = string:join(lists:map(fun ({Name, - XMLNS, - M}) -> - io_lib:format("lookup(~w, ~w) -> '~s'", - [Name, XMLNS, M]) - end, - Tags) - ++ ["lookup(_, _) -> undefined."], - ";" ++ io_lib:nl()), - Modules = io_lib:format("modules() -> [~s].", - [string:join([io_lib:format("{'~s', ~w}", [M, S]) - || {M, S} <- Mods], - ",")]), - Module = io_lib:format("-module(~s).", [ResolverMod]), - Compile = "-compile(export_all).", - Forms = lists:map(fun (Expr) -> - {ok, Tokens, _} = - erl_scan:string(lists:flatten(Expr)), - {ok, Form} = erl_parse:parse_form(Tokens), - Form - end, - [Module, Compile, Modules, Lookup1, Lookup2]), - {ok, Code} = case compile:forms(Forms, []) of - {ok, ResolverMod, Bin} -> {ok, Bin}; - {ok, ResolverMod, Bin, _Warnings} -> {ok, Bin}; - Error -> Error - end, - {module, ResolverMod} = code:load_binary(ResolverMod, - "nofile", - Code), - ok. - -dec_enum(Val, Enums) -> - AtomVal = erlang:binary_to_existing_atom(Val, utf8), - case lists:member(AtomVal, Enums) of - true -> AtomVal - end. - -enc_enum(Atom) -> erlang:atom_to_binary(Atom, utf8). - -pp(pubsub_serverinfo, 1) -> [domain]; -pp(pubsub_serverinfo_domain, 2) -> - [name, remote_domain]; -pp(pubsub_serverinfo_remote_domain, 2) -> [name, type]; -pp(xmlel, 3) -> [name, attrs, children]; -pp(Name, Arity) -> - try get_mod(erlang:make_tuple(Arity + 1, - undefined, - [{1, Name}])) - of - Mod -> Mod:pp(Name, Arity) - catch - error:badarg -> no - end. - -records() -> - [{pubsub_serverinfo, 1}, - {pubsub_serverinfo_domain, 2}, - {pubsub_serverinfo_remote_domain, 2}]. - -get_mod(<<"serverinfo">>, - <<"urn:xmpp:serverinfo:0">>) -> - pubsub_serverinfo_codec; -get_mod(<<"remote-domain">>, - <<"urn:xmpp:serverinfo:0">>) -> - pubsub_serverinfo_codec; -get_mod(<<"federation">>, - <<"urn:xmpp:serverinfo:0">>) -> - pubsub_serverinfo_codec; -get_mod(<<"domain">>, <<"urn:xmpp:serverinfo:0">>) -> - pubsub_serverinfo_codec; -get_mod(<<"connection">>, - <<"urn:xmpp:serverinfo:0">>) -> - pubsub_serverinfo_codec; -get_mod(Name, XMLNS) -> - pubsub_serverinfo_codec_external:lookup(Name, XMLNS). - -get_mod({pubsub_serverinfo, _}) -> - pubsub_serverinfo_codec; -get_mod({pubsub_serverinfo_domain, _, _}) -> - pubsub_serverinfo_codec; -get_mod({pubsub_serverinfo_remote_domain, _, _}) -> - pubsub_serverinfo_codec; -get_mod(Record) -> - pubsub_serverinfo_codec_external:lookup(Record). - -decode_pubsub_serverinfo_connection(__TopXMLNS, __Opts, - {xmlel, <<"connection">>, _attrs, _els}) -> - Type = - decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - _attrs, - undefined), - Type. - -decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - [{<<"type">>, _val} | _attrs], - _Type) -> - decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - _attrs, - _val); -decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - [_ | _attrs], Type) -> - decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - _attrs, - Type); -decode_pubsub_serverinfo_connection_attrs(__TopXMLNS, - [], Type) -> - decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, - Type). - -encode_pubsub_serverinfo_connection(Type, __TopXMLNS) -> - __NewTopXMLNS = - pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, - [], - __TopXMLNS), - _els = [], - _attrs = - encode_pubsub_serverinfo_connection_attr_type(Type, - pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, - __TopXMLNS)), - {xmlel, <<"connection">>, _attrs, _els}. - -decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, - undefined) -> - erlang:error({pubsub_serverinfo_codec, - {missing_attr, - <<"type">>, - <<"connection">>, - __TopXMLNS}}); -decode_pubsub_serverinfo_connection_attr_type(__TopXMLNS, - _val) -> - case catch dec_enum(_val, [incoming, outgoing, bidi]) of - {'EXIT', _} -> - erlang:error({pubsub_serverinfo_codec, - {bad_attr_value, - <<"type">>, - <<"connection">>, - __TopXMLNS}}); - _res -> _res - end. - -encode_pubsub_serverinfo_connection_attr_type(_val, - _acc) -> - [{<<"type">>, enc_enum(_val)} | _acc]. - -decode_pubsub_serverinfo_remote_domain(__TopXMLNS, - __Opts, - {xmlel, - <<"remote-domain">>, - _attrs, - _els}) -> - Type = - decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, - _els, - []), - Name = - decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - _attrs, - undefined), - {pubsub_serverinfo_remote_domain, Name, Type}. - -decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, [], Type) -> - lists:reverse(Type); -decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, - [{xmlel, - <<"connection">>, - _attrs, - _} = - _el - | _els], - Type) -> - case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, - _attrs, - __TopXMLNS) - of - <<"urn:xmpp:serverinfo:0">> -> - decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, - _els, - [decode_pubsub_serverinfo_connection(<<"urn:xmpp:serverinfo:0">>, - __Opts, - _el) - | Type]); - _ -> - decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, - _els, - Type) - end; -decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, [_ | _els], Type) -> - decode_pubsub_serverinfo_remote_domain_els(__TopXMLNS, - __Opts, - _els, - Type). - -decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - [{<<"name">>, _val} | _attrs], - _Name) -> - decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - _attrs, - _val); -decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - [_ | _attrs], Name) -> - decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - _attrs, - Name); -decode_pubsub_serverinfo_remote_domain_attrs(__TopXMLNS, - [], Name) -> - decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, - Name). - -encode_pubsub_serverinfo_remote_domain({pubsub_serverinfo_remote_domain, - Name, - Type}, - __TopXMLNS) -> - __NewTopXMLNS = - pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, - [], - __TopXMLNS), - _els = - lists:reverse('encode_pubsub_serverinfo_remote_domain_$type'(Type, - __NewTopXMLNS, - [])), - _attrs = - encode_pubsub_serverinfo_remote_domain_attr_name(Name, - pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, - __TopXMLNS)), - {xmlel, <<"remote-domain">>, _attrs, _els}. - -'encode_pubsub_serverinfo_remote_domain_$type'([], - __TopXMLNS, _acc) -> - _acc; -'encode_pubsub_serverinfo_remote_domain_$type'([Type - | _els], - __TopXMLNS, _acc) -> - 'encode_pubsub_serverinfo_remote_domain_$type'(_els, - __TopXMLNS, - [encode_pubsub_serverinfo_connection(Type, - __TopXMLNS) - | _acc]). - -decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, - undefined) -> - erlang:error({pubsub_serverinfo_codec, - {missing_attr, - <<"name">>, - <<"remote-domain">>, - __TopXMLNS}}); -decode_pubsub_serverinfo_remote_domain_attr_name(__TopXMLNS, - _val) -> - _val. - -encode_pubsub_serverinfo_remote_domain_attr_name(_val, - _acc) -> - [{<<"name">>, _val} | _acc]. - -decode_pubsub_serverinfo_federation(__TopXMLNS, __Opts, - {xmlel, <<"federation">>, _attrs, _els}) -> - Remote_domain = - decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, - _els, - []), - Remote_domain. - -decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, [], Remote_domain) -> - lists:reverse(Remote_domain); -decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, - [{xmlel, - <<"remote-domain">>, - _attrs, - _} = - _el - | _els], - Remote_domain) -> - case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, - _attrs, - __TopXMLNS) - of - <<"urn:xmpp:serverinfo:0">> -> - decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, - _els, - [decode_pubsub_serverinfo_remote_domain(<<"urn:xmpp:serverinfo:0">>, - __Opts, - _el) - | Remote_domain]); - _ -> - decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, - _els, - Remote_domain) - end; -decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, [_ | _els], Remote_domain) -> - decode_pubsub_serverinfo_federation_els(__TopXMLNS, - __Opts, - _els, - Remote_domain). - -encode_pubsub_serverinfo_federation(Remote_domain, - __TopXMLNS) -> - __NewTopXMLNS = - pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, - [], - __TopXMLNS), - _els = - lists:reverse('encode_pubsub_serverinfo_federation_$remote_domain'(Remote_domain, - __NewTopXMLNS, - [])), - _attrs = - pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, - __TopXMLNS), - {xmlel, <<"federation">>, _attrs, _els}. - -'encode_pubsub_serverinfo_federation_$remote_domain'([], - __TopXMLNS, _acc) -> - _acc; -'encode_pubsub_serverinfo_federation_$remote_domain'([Remote_domain - | _els], - __TopXMLNS, _acc) -> - 'encode_pubsub_serverinfo_federation_$remote_domain'(_els, - __TopXMLNS, - [encode_pubsub_serverinfo_remote_domain(Remote_domain, - __TopXMLNS) - | _acc]). - -decode_pubsub_serverinfo_domain(__TopXMLNS, __Opts, - {xmlel, <<"domain">>, _attrs, _els}) -> - Remote_domain = - decode_pubsub_serverinfo_domain_els(__TopXMLNS, - __Opts, - _els, - undefined), - Name = decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, - _attrs, - undefined), - {pubsub_serverinfo_domain, Name, Remote_domain}. - -decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, - [], Remote_domain) -> - Remote_domain; -decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, - [{xmlel, <<"federation">>, _attrs, _} = _el - | _els], - Remote_domain) -> - case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, - _attrs, - __TopXMLNS) - of - <<"urn:xmpp:serverinfo:0">> -> - decode_pubsub_serverinfo_domain_els(__TopXMLNS, - __Opts, - _els, - decode_pubsub_serverinfo_federation(<<"urn:xmpp:serverinfo:0">>, - __Opts, - _el)); - _ -> - decode_pubsub_serverinfo_domain_els(__TopXMLNS, - __Opts, - _els, - Remote_domain) - end; -decode_pubsub_serverinfo_domain_els(__TopXMLNS, __Opts, - [_ | _els], Remote_domain) -> - decode_pubsub_serverinfo_domain_els(__TopXMLNS, - __Opts, - _els, - Remote_domain). - -decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, - [{<<"name">>, _val} | _attrs], _Name) -> - decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, - _attrs, - _val); -decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, - [_ | _attrs], Name) -> - decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, - _attrs, - Name); -decode_pubsub_serverinfo_domain_attrs(__TopXMLNS, [], - Name) -> - decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, - Name). - -encode_pubsub_serverinfo_domain({pubsub_serverinfo_domain, - Name, - Remote_domain}, - __TopXMLNS) -> - __NewTopXMLNS = - pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, - [], - __TopXMLNS), - _els = - lists:reverse('encode_pubsub_serverinfo_domain_$remote_domain'(Remote_domain, - __NewTopXMLNS, - [])), - _attrs = encode_pubsub_serverinfo_domain_attr_name(Name, - pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, - __TopXMLNS)), - {xmlel, <<"domain">>, _attrs, _els}. - -'encode_pubsub_serverinfo_domain_$remote_domain'(undefined, - __TopXMLNS, _acc) -> - _acc; -'encode_pubsub_serverinfo_domain_$remote_domain'(Remote_domain, - __TopXMLNS, _acc) -> - [encode_pubsub_serverinfo_federation(Remote_domain, - __TopXMLNS) - | _acc]. - -decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, - undefined) -> - erlang:error({pubsub_serverinfo_codec, - {missing_attr, <<"name">>, <<"domain">>, __TopXMLNS}}); -decode_pubsub_serverinfo_domain_attr_name(__TopXMLNS, - _val) -> - _val. - -encode_pubsub_serverinfo_domain_attr_name(_val, _acc) -> - [{<<"name">>, _val} | _acc]. - -decode_pubsub_serverinfo(__TopXMLNS, __Opts, - {xmlel, <<"serverinfo">>, _attrs, _els}) -> - Domain = decode_pubsub_serverinfo_els(__TopXMLNS, - __Opts, - _els, - []), - {pubsub_serverinfo, Domain}. - -decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, [], - Domain) -> - lists:reverse(Domain); -decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, - [{xmlel, <<"domain">>, _attrs, _} = _el | _els], - Domain) -> - case pubsub_serverinfo_codec:get_attr(<<"xmlns">>, - _attrs, - __TopXMLNS) - of - <<"urn:xmpp:serverinfo:0">> -> - decode_pubsub_serverinfo_els(__TopXMLNS, - __Opts, - _els, - [decode_pubsub_serverinfo_domain(<<"urn:xmpp:serverinfo:0">>, - __Opts, - _el) - | Domain]); - _ -> - decode_pubsub_serverinfo_els(__TopXMLNS, - __Opts, - _els, - Domain) - end; -decode_pubsub_serverinfo_els(__TopXMLNS, __Opts, - [_ | _els], Domain) -> - decode_pubsub_serverinfo_els(__TopXMLNS, - __Opts, - _els, - Domain). - -encode_pubsub_serverinfo({pubsub_serverinfo, Domain}, - __TopXMLNS) -> - __NewTopXMLNS = - pubsub_serverinfo_codec:choose_top_xmlns(<<"urn:xmpp:serverinfo:0">>, - [], - __TopXMLNS), - _els = - lists:reverse('encode_pubsub_serverinfo_$domain'(Domain, - __NewTopXMLNS, - [])), - _attrs = - pubsub_serverinfo_codec:enc_xmlns_attrs(__NewTopXMLNS, - __TopXMLNS), - {xmlel, <<"serverinfo">>, _attrs, _els}. - -'encode_pubsub_serverinfo_$domain'([], __TopXMLNS, - _acc) -> - _acc; -'encode_pubsub_serverinfo_$domain'([Domain | _els], - __TopXMLNS, _acc) -> - 'encode_pubsub_serverinfo_$domain'(_els, - __TopXMLNS, - [encode_pubsub_serverinfo_domain(Domain, - __TopXMLNS) - | _acc]). diff --git a/src/pubsub_serverinfo_codec_external.erl b/src/pubsub_serverinfo_codec_external.erl deleted file mode 100644 index 2f2df47e3..000000000 --- a/src/pubsub_serverinfo_codec_external.erl +++ /dev/null @@ -1,12 +0,0 @@ -%% Created automatically by XML generator (fxml_gen.erl) -%% Source: pubsub_serverinfo_codec.spec - --module(pubsub_serverinfo_codec_external). - --compile(export_all). - -modules() -> []. - -lookup(_, _) -> undefined. - -lookup(Term) -> erlang:error(badarg, [Term]). From 5e93725044c54e4f440276a6f7e7daf9902ca36a Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 27 Jun 2025 17:56:58 +0200 Subject: [PATCH 1192/1302] Replace options rtbl_host and rtbl_domains_node with rtbl_services --- include/mod_antispam.hrl | 10 +++ src/mod_antispam.erl | 67 ++++++++++++++++---- src/mod_antispam_files.erl | 1 - src/mod_antispam_rtbl.erl | 7 +- test/antispam_tests.erl | 7 +- test/ejabberd_SUITE_data/ejabberd.mnesia.yml | 3 +- test/ejabberd_SUITE_data/ejabberd.redis.yml | 3 +- 7 files changed, 76 insertions(+), 22 deletions(-) diff --git a/include/mod_antispam.hrl b/include/mod_antispam.hrl index 7f681046e..c30f24620 100644 --- a/include/mod_antispam.hrl +++ b/include/mod_antispam.hrl @@ -24,3 +24,13 @@ -type filename() :: binary() | none | false. -type jid_set() :: sets:set(ljid()). -type url_set() :: sets:set(url()). + +-define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). + +-record(rtbl_service, + {host = none :: binary() | none, + node = ?DEFAULT_RTBL_DOMAINS_NODE :: binary(), + subscribed = false :: boolean(), + retry_timer = undefined :: reference() | undefined}). + +-type rtbl_service() :: #rtbl_service{}. diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 9baaeffc1..0e3a1b8a7 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -51,6 +51,8 @@ terminate/2, code_change/3]). +-export([get_rtbl_services_option/1]). + %% ejabberd_commands callbacks. -export([add_blocked_domain/2, add_to_spam_filter_cache/2, @@ -87,7 +89,6 @@ -type state() :: #state{}. -define(COMMAND_TIMEOUT, timer:seconds(30)). --define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). -define(DEFAULT_CACHE_SIZE, 10000). %% @format-begin @@ -137,12 +138,14 @@ mod_opt_type(access_spam) -> econf:acl(); mod_opt_type(cache_size) -> econf:pos_int(unlimited); -mod_opt_type(rtbl_host) -> - econf:either( - econf:enum([none]), econf:host()); -mod_opt_type(rtbl_domains_node) -> - econf:non_empty( - econf:binary()); +mod_opt_type(rtbl_services) -> + econf:list( + econf:either( + econf:binary(), + econf:map( + econf:binary(), + econf:map( + econf:enum([spam_source_domains_node]), econf:binary())))); mod_opt_type(spam_domains_file) -> econf:either( econf:enum([none]), econf:file()); @@ -159,12 +162,11 @@ mod_opt_type(whitelist_domains_file) -> econf:either( econf:enum([none]), econf:file()). --spec mod_options(binary()) -> [{atom(), any()}]. +-spec mod_options(binary()) -> [{rtbl_services, [tuple()]} | {atom(), any()}]. mod_options(_Host) -> [{access_spam, none}, {cache_size, ?DEFAULT_CACHE_SIZE}, - {rtbl_domains_node, ?DEFAULT_RTBL_DOMAINS_NODE}, - {rtbl_host, none}, + {rtbl_services, []}, {spam_domains_file, none}, {spam_dump_file, false}, {spam_jids_file, none}, @@ -200,6 +202,21 @@ mod_doc() -> "Note that separate caches are used for each virtual host, " " and that the caches aren't distributed across cluster nodes. " "The default value is '10000'.")}}, + {rtbl_services, + #{value => ?T("[Service]"), + example => + ["rtbl_services:", + " - pubsub.server1.localhost:", + " spam_source_domains_node: actual_custom_pubsub_node"], + desc => + ?T("Query a RTBL service to get domains to block, as provided by " + "https://xmppbl.org/[xmppbl.org]. " + "Please note right now this option only supports one service in that list. " + "For blocking spam and abuse on MUC channels, please use _`mod_muc_rtbl`_ for now. " + "If only the host is provided, the default node names will be assumed. " + "If the node name is different than 'spam_source_domains', " + "you can setup the custom node name with the option 'spam_source_domains_node'. " + "The default value is an empty list of services.")}}, {spam_domains_file, #{value => ?T("none | Path"), desc => @@ -253,6 +270,8 @@ mod_doc() -> example => ["modules:", " mod_antispam:", + " rtbl_services:", + " - xmppbl.org", " spam_jids_file: \"@CONFIG_PATH@/spam_jids.txt\"", " spam_dump_file: \"@LOG_PATH@/spam/host-@HOST@.log\""]}. @@ -274,8 +293,7 @@ init([Host, Opts]) -> mod_antispam_rtbl, pubsub_event_handler, 50), - RTBLHost = gen_mod:get_opt(rtbl_host, Opts), - RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, Opts), + [#rtbl_service{host = RTBLHost, node = RTBLDomainsNode}] = get_rtbl_services_option(Opts), mod_antispam_filter:init_filtering(Host), InitState = #state{host = Host, @@ -384,8 +402,8 @@ handle_cast({reload, NewOpts, OldOpts}, end, ok = mod_antispam_rtbl:unsubscribe(OldRTBLHost, OldRTBLDomainsNode, Host), {_Result, State3} = reload_files(State2#state{blocked_domains = #{}}), - RTBLHost = gen_mod:get_opt(rtbl_host, NewOpts), - RTBLDomainsNode = gen_mod:get_opt(rtbl_domains_node, NewOpts), + [#rtbl_service{host = RTBLHost, node = RTBLDomainsNode}] = + get_rtbl_services_option(NewOpts), ok = mod_antispam_rtbl:request_blocked_domains(RTBLHost, RTBLDomainsNode, Host), {noreply, State3#state{rtbl_host = RTBLHost, rtbl_domains_node = RTBLDomainsNode}}; handle_cast({update_blocked_domains, NewItems}, @@ -565,6 +583,27 @@ read_files(Host) -> whitelist_domains => gen_mod:get_module_opt(Host, ?MODULE, whitelist_domains_file)}, ejabberd_hooks:run_fold(antispam_get_lists, Host, AccInitial, [Files]). +get_rtbl_services_option(Host) when is_binary(Host) -> + get_rtbl_services_option(gen_mod:get_module_opts(Host, ?MODULE)); +get_rtbl_services_option(Opts) when is_map(Opts) -> + Services = gen_mod:get_opt(rtbl_services, Opts), + case length(Services) =< 1 of + true -> + ok; + false -> + ?WARNING_MSG("Option rtbl_services only supports one service, but several " + "were configured. Will use only first one", + []) + end, + case Services of + [] -> + [#rtbl_service{}]; + [Host | _] when is_binary(Host) -> + [#rtbl_service{host = Host, node = ?DEFAULT_RTBL_DOMAINS_NODE}]; + [[{Host, [{spam_source_domains_node, Node}]}] | _] -> + [#rtbl_service{host = Host, node = Node}] + end. + -spec get_proc_name(binary()) -> atom(). get_proc_name(Host) -> gen_mod:get_module_proc(Host, ?MODULE). diff --git a/src/mod_antispam_files.erl b/src/mod_antispam_files.erl index 0981edbb2..0362a1b72 100644 --- a/src/mod_antispam_files.erl +++ b/src/mod_antispam_files.erl @@ -52,7 +52,6 @@ -define(COMMAND_TIMEOUT, timer:seconds(30)). -define(DEFAULT_CACHE_SIZE, 10000). --define(DEFAULT_RTBL_DOMAINS_NODE, <<"spam_source_domains">>). -define(HTTPC_TIMEOUT, timer:seconds(3)). %%-------------------------------------------------------------------- diff --git a/src/mod_antispam_rtbl.erl b/src/mod_antispam_rtbl.erl index 246b59dfa..df5bac591 100644 --- a/src/mod_antispam_rtbl.erl +++ b/src/mod_antispam_rtbl.erl @@ -78,7 +78,7 @@ request_blocked_domains(RTBLHost, RTBLDomainsNode, From) -> -spec parse_blocked_domains(stanza()) -> #{binary() => any()} | undefined. parse_blocked_domains(#iq{to = #jid{lserver = LServer}, type = result} = IQ) -> ?DEBUG("parsing iq-result items: ~p", [IQ]), - RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), + [#rtbl_service{node = RTBLDomainsNode}] = mod_antispam:get_rtbl_services_option(LServer), case xmpp:get_subtag(IQ, #pubsub{}) of #pubsub{items = #ps_items{node = RTBLDomainsNode, items = Items}} -> ?DEBUG("Got items:~n~p", [Items]), @@ -89,7 +89,7 @@ parse_blocked_domains(#iq{to = #jid{lserver = LServer}, type = result} = IQ) -> -spec parse_pubsub_event(stanza()) -> #{binary() => any()}. parse_pubsub_event(#message{to = #jid{lserver = LServer}} = Msg) -> - RTBLDomainsNode = gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_domains_node), + [#rtbl_service{node = RTBLDomainsNode}] = mod_antispam:get_rtbl_services_option(LServer), case xmpp:get_subtag(Msg, #ps_event{}) of #ps_event{items = #ps_items{node = RTBLDomainsNode, @@ -130,7 +130,8 @@ pubsub_event_handler(#message{from = FromJid, Msg) -> ?DEBUG("Got RTBL message:~n~p", [Msg]), From = jid:encode(FromJid), - case gen_mod:get_module_opt(LServer, ?SERVICE_MODULE, rtbl_host) of + [#rtbl_service{host = RTBLHost}] = mod_antispam:get_rtbl_services_option(LServer), + case RTBLHost of From -> ParsedItems = parse_pubsub_event(Msg), Proc = gen_mod:get_module_proc(LServer, ?SERVICE_MODULE), diff --git a/test/antispam_tests.erl b/test/antispam_tests.erl index f60872913..8d7bd2472 100644 --- a/test/antispam_tests.erl +++ b/test/antispam_tests.erl @@ -31,6 +31,7 @@ disconnect/1, put_event/2, get_event/1, peer_muc_jid/1, my_muc_jid/1, get_features/2, set_opt/3]). -include("suite.hrl"). +-include("mod_antispam.hrl"). %% @format-begin @@ -166,7 +167,8 @@ rtbl_domains(Config) -> RTBLDomainsNode = <<"spam_source_domains">>, OldOpts = gen_mod:get_module_opts(Host, mod_antispam), NewOpts = - maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), + maps:merge(OldOpts, + #{rtbl_services => [#rtbl_service{host = RTBLHost, node = RTBLDomainsNode}]}), Owner = jid:make(?config(user, Config), ?config(server, Config), <<>>), {result, _} = mod_pubsub:create_node(RTBLHost, @@ -210,7 +212,8 @@ rtbl_domains_whitelisted(Config) -> RTBLDomainsNode = <<"spam_source_domains">>, OldOpts = gen_mod:get_module_opts(Host, mod_antispam), NewOpts = - maps:merge(OldOpts, #{rtbl_host => RTBLHost, rtbl_domains_node => RTBLDomainsNode}), + maps:merge(OldOpts, + #{rtbl_services => [#rtbl_service{host = RTBLHost, node = RTBLDomainsNode}]}), Owner = jid:make(?config(user, Config), ?config(server, Config), <<>>), {result, _} = mod_pubsub:create_node(RTBLHost, diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml index ae78645ae..56fdf5e6e 100644 --- a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml +++ b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml @@ -7,7 +7,8 @@ define_macro: db_type: internal access: local mod_antispam: - rtbl_host: pubsub.mnesia.localhost + rtbl_services: + - "pubsub.mnesia.localhost" spam_jids_file: spam_jids.txt spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml index 8ec927e86..fb1ba435f 100644 --- a/test/ejabberd_SUITE_data/ejabberd.redis.yml +++ b/test/ejabberd_SUITE_data/ejabberd.redis.yml @@ -8,7 +8,8 @@ define_macro: db_type: internal access: local mod_antispam: - rtbl_host: pubsub.redis.localhost + rtbl_services: + - "pubsub.redis.localhost" spam_jids_file: spam_jids.txt spam_domains_file: spam_domains.txt spam_urls_file: spam_urls.txt From 3b972fe4a3acc90b514d97c4e34bc358d18ac9f5 Mon Sep 17 00:00:00 2001 From: Stefan Strigler Date: Mon, 7 Jul 2025 17:58:56 +0200 Subject: [PATCH 1193/1302] update p1/xmpp to latest --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 308d95c51..7a12a248a 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "fbaea1bfb4244e4287fe8449144e8e72d9a842d1", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "9b028c110083e4d979d88c286873d0abf08fa532", override: true}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index ad3b004c7..eb160ed8b 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "fbaea1bfb4244e4287fe8449144e8e72d9a842d1"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "9b028c110083e4d979d88c286873d0abf08fa532"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index 365d3fd3e..d4452766d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -32,7 +32,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"fbaea1bfb4244e4287fe8449144e8e72d9a842d1"}}, + {ref,"9b028c110083e4d979d88c286873d0abf08fa532"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ From 427a29c74e6ed3e4023fd2193cb64f75c27bbe9a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 17 Mar 2025 15:11:24 +0100 Subject: [PATCH 1194/1302] Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4, libexpat 2.7.1, OpenSSL 3.5.1 Notice: - installers use OTP 27.3.4.1, the latest available right now - containers use OTP 27.3.4, because container for 27.3.4.1 was not published, see https://hub.docker.com/_/erlang --- .github/container/Dockerfile | 4 ++-- CONTAINER.md | 2 +- tools/make-binaries | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index ccf1af5a6..4e4e7c0d2 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,6 +1,6 @@ #' Define default build variables -ARG OTP_VSN='27.3.3' -ARG ELIXIR_VSN='1.18.3' +ARG OTP_VSN='27.3.4' +ARG ELIXIR_VSN='1.18.4' ARG UID='9000' ARG USER='ejabberd' ARG HOME="opt/$USER" diff --git a/CONTAINER.md b/CONTAINER.md index 904362db6..3ae82cae8 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.3-alpine
Elixir 1.18.3 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.4-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | diff --git a/tools/make-binaries b/tools/make-binaries index 2d3e9adb1..21d7b605a 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,12 +67,12 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.27.0' termcap_vsn='1.3.1' -expat_vsn='2.6.4' +expat_vsn='2.7.1' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.4.1' -otp_vsn='27.3.3' -elixir_vsn='1.18.3' +ssl_vsn='3.5.1' +otp_vsn='27.3.4.1' +elixir_vsn='1.18.4' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' jpeg_vsn='9f' From e94ccabcf04a683aa1694b42503d996617f3bf68 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Jul 2025 10:42:24 +0200 Subject: [PATCH 1195/1302] mod_antispam: Move commands to a new "spam" API tag --- src/mod_antispam.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 0e3a1b8a7..4a3ffb177 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -686,14 +686,14 @@ drop_from_cache(LJID, #state{jid_cache = Cache} = State) -> -spec get_commands_spec() -> [ejabberd_commands()]. get_commands_spec() -> [#ejabberd_commands{name = reload_spam_filter_files, - tags = [filter], + tags = [spam], desc = "Reload spam JID/URL files", module = ?MODULE, function = reload_spam_filter_files, args = [{host, binary}], result = {res, rescode}}, #ejabberd_commands{name = get_spam_filter_cache, - tags = [filter], + tags = [spam], desc = "Show spam filter cache contents", module = ?MODULE, function = get_spam_filter_cache, @@ -702,42 +702,42 @@ get_commands_spec() -> {spammers, {list, {spammer, {tuple, [{jid, string}, {timestamp, integer}]}}}}}, #ejabberd_commands{name = expire_spam_filter_cache, - tags = [filter], + tags = [spam], desc = "Remove old/unused spam JIDs from cache", module = ?MODULE, function = expire_spam_filter_cache, args = [{host, binary}, {seconds, integer}], result = {res, restuple}}, #ejabberd_commands{name = add_to_spam_filter_cache, - tags = [filter], + tags = [spam], desc = "Add JID to spam filter cache", module = ?MODULE, function = add_to_spam_filter_cache, args = [{host, binary}, {jid, binary}], result = {res, restuple}}, #ejabberd_commands{name = drop_from_spam_filter_cache, - tags = [filter], + tags = [spam], desc = "Drop JID from spam filter cache", module = ?MODULE, function = drop_from_spam_filter_cache, args = [{host, binary}, {jid, binary}], result = {res, restuple}}, #ejabberd_commands{name = get_blocked_domains, - tags = [filter], + tags = [spam], desc = "Get list of domains being blocked", module = ?MODULE, function = get_blocked_domains, args = [{host, binary}], result = {blocked_domains, {list, {jid, string}}}}, #ejabberd_commands{name = add_blocked_domain, - tags = [filter], + tags = [spam], desc = "Add domain to list of blocked domains", module = ?MODULE, function = add_blocked_domain, args = [{host, binary}, {domain, binary}], result = {res, restuple}}, #ejabberd_commands{name = remove_blocked_domain, - tags = [filter], + tags = [spam], desc = "Remove domain from list of blocked domains", module = ?MODULE, function = remove_blocked_domain, From ad3eee059e7b5a25d1723be2c6de2ca68c5f7a9f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Jul 2025 10:46:17 +0200 Subject: [PATCH 1196/1302] mod_antispam: Annotate ejabberd version of the new commands --- src/mod_antispam.erl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 4a3ffb177..c2472c601 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -690,6 +690,7 @@ get_commands_spec() -> desc = "Reload spam JID/URL files", module = ?MODULE, function = reload_spam_filter_files, + note = "added in 25.07", args = [{host, binary}], result = {res, rescode}}, #ejabberd_commands{name = get_spam_filter_cache, @@ -697,6 +698,7 @@ get_commands_spec() -> desc = "Show spam filter cache contents", module = ?MODULE, function = get_spam_filter_cache, + note = "added in 25.07", args = [{host, binary}], result = {spammers, @@ -706,6 +708,7 @@ get_commands_spec() -> desc = "Remove old/unused spam JIDs from cache", module = ?MODULE, function = expire_spam_filter_cache, + note = "added in 25.07", args = [{host, binary}, {seconds, integer}], result = {res, restuple}}, #ejabberd_commands{name = add_to_spam_filter_cache, @@ -713,6 +716,7 @@ get_commands_spec() -> desc = "Add JID to spam filter cache", module = ?MODULE, function = add_to_spam_filter_cache, + note = "added in 25.07", args = [{host, binary}, {jid, binary}], result = {res, restuple}}, #ejabberd_commands{name = drop_from_spam_filter_cache, @@ -720,6 +724,7 @@ get_commands_spec() -> desc = "Drop JID from spam filter cache", module = ?MODULE, function = drop_from_spam_filter_cache, + note = "added in 25.07", args = [{host, binary}, {jid, binary}], result = {res, restuple}}, #ejabberd_commands{name = get_blocked_domains, @@ -727,6 +732,7 @@ get_commands_spec() -> desc = "Get list of domains being blocked", module = ?MODULE, function = get_blocked_domains, + note = "added in 25.07", args = [{host, binary}], result = {blocked_domains, {list, {jid, string}}}}, #ejabberd_commands{name = add_blocked_domain, @@ -734,6 +740,7 @@ get_commands_spec() -> desc = "Add domain to list of blocked domains", module = ?MODULE, function = add_blocked_domain, + note = "added in 25.07", args = [{host, binary}, {domain, binary}], result = {res, restuple}}, #ejabberd_commands{name = remove_blocked_domain, @@ -741,6 +748,7 @@ get_commands_spec() -> desc = "Remove domain from list of blocked domains", module = ?MODULE, function = remove_blocked_domain, + note = "added in 25.07", args = [{host, binary}, {domain, binary}], result = {res, restuple}}]. From bf39da7b8b211456d3f356f2b73d756d8117e9b8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Jul 2025 12:34:19 +0200 Subject: [PATCH 1197/1302] mod_pubsub_serverinfo: Rephrase documentation and improve markdown --- src/mod_pubsub_serverinfo.erl | 38 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index cc7672dfe..7b4f92fbc 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -194,17 +194,33 @@ mod_opt_type(pubsub_host) -> econf:either(undefined, econf:host()). mod_doc() -> - #{desc => [?T("Exposes s2s information over Pub/Sub"), "", - ?T("Announces support for the ProtoXEP PubSub Server Information, by adding its Service Discovery feature." - "Active S2S connections are published to a local pubsub node as advertised by Service Discovery. Only those connections that support this feature as well are exposed with their domain names, otherwise they are shown as anonymous nodes. At startup a list of well known public servers is being fetched. Those are not shown as anonymous even if they don't support this feature." - "Currently the name of the node is hardcoded as \"serverinfo\". The local service to be used can be configured as `pubsub_host`. Otherwise a good guess is taken." - "This module has a hard dependency on `mod_pubsub` for this reason. Also `mod_disco` must be configured for this feature to work."), "", - ?T("NOTE: The module only shows S2S connections established while the module is running: after installing the module, please run `ejabberdctl stop_s2s_connections`, or restart ejabberd.")], - note => "added in 25.xx", - opts => [{pubsub_host, - #{value => "undefined | string()", - desc => ?T("This option specifies which pubsub host to use to advertise S2S connections. This must be a vhost local to this service and handled by `mod_pubsub`. This is only needed if your configuration has more than one vhost in mod_pubsub's `hosts` option. If there's more than one and this option is not given, we just pick the first one.")} - }], + #{desc => + [?T("This module adds support for " + "https://xmpp.org/extensions/xep-0485.html[XEP-0485: PubSub Server Information] " + "to expose S2S information over the Pub/Sub service."), + "", + ?T("Active S2S connections are published to a local PubSub node. " + "Currently the node name is hardcoded as '\"serverinfo\"'."), + "", + ?T("Connections that support this feature are exposed with their domain names, " + "otherwise they are shown as anonymous nodes. " + "At startup a list of well known public servers is fetched. " + "Those are not shown as anonymous even if they don't support this feature."), + "", + ?T("Please note that the module only shows S2S connections established while the module is running. " + "If you install the module at runtime, run _`stop_s2s_connections`_ API or restart ejabberd " + "to force S2S reconnections that the module will detect and publish."), + "", + ?T("This module depends on _`mod_pubsub`_ and _`mod_disco`_.")], + note => "added in 25.07", + opts => + [{pubsub_host, + #{value => "undefined | string()", + desc => + ?T("Use this local PubSub host to advertise S2S connections. " + "This must be a host local to this service handled by _`mod_pubsub`_. " + "This option is only needed if your configuration has more than one host in mod_pubsub's 'hosts' option. " + "The default value is the first host defined in mod_pubsub 'hosts' option.")}}], example => ["modules:", " mod_pubsub_serverinfo:", From b118dd8fc6da2fda64f965a30a8e209a18be45e5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Jul 2025 11:34:04 +0200 Subject: [PATCH 1198/1302] Update reference to XEP-0485 support --- src/ejabberd.erl | 1 - src/mod_pubsub_serverinfo.erl | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd.erl b/src/ejabberd.erl index e410fb016..844ef7ea2 100644 --- a/src/ejabberd.erl +++ b/src/ejabberd.erl @@ -42,7 +42,6 @@ -protocol({xep, 388, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 440, '0.4.0', '24.02', "complete", ""}). -protocol({xep, 474, '0.4.0', '24.02', "complete", "0.4.0 since 25.03"}). --protocol({xep, 485, '0.1.0', '24.02', "complete", "mod_pubsub_serverinfo in ejabberd-contrib.git"}). -export([start/0, stop/0, halt/0, start_app/1, start_app/2, get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]). diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index 7b4f92fbc..80da0d26a 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -25,6 +25,9 @@ -module(mod_pubsub_serverinfo). -author('stefan@strigler.de'). + +-protocol({xep, 485, '0.1.1', '25.07', "complete", ""}). + -behaviour(gen_mod). -behaviour(gen_server). From 443f39bfdb4fedfeee81b48924eef1c62254b25d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 8 Jul 2025 12:34:34 +0200 Subject: [PATCH 1199/1302] Result of running "make format doap options" --- ejabberd.doap | 15 ++++++-- src/mod_antispam_opt.erl | 62 +++++++++++++++++++++++++++++++ src/mod_pubsub_serverinfo.erl | 9 ++--- src/mod_pubsub_serverinfo_opt.erl | 13 +++++++ 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 src/mod_antispam_opt.erl create mode 100644 src/mod_pubsub_serverinfo_opt.erl diff --git a/ejabberd.doap b/ejabberd.doap index 1fc7df52e..0e72bf3ad 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -773,6 +773,15 @@ mod_mam + + + + 0.2.0 + 24.12 + complete + mod_mam + + @@ -821,10 +830,10 @@ - 0.2.0 - 24.02 + 0.1.1 + 25.07 complete - , mod_pubsub_serverinfo in ejabberd-contrib.git + mod_pubsub_serverinfo diff --git a/src/mod_antispam_opt.erl b/src/mod_antispam_opt.erl new file mode 100644 index 000000000..008a2aac1 --- /dev/null +++ b/src/mod_antispam_opt.erl @@ -0,0 +1,62 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_antispam_opt). + +-export([access_spam/1]). +-export([cache_size/1]). +-export([rtbl_services/1]). +-export([spam_domains_file/1]). +-export([spam_dump_file/1]). +-export([spam_jids_file/1]). +-export([spam_urls_file/1]). +-export([whitelist_domains_file/1]). + +-spec access_spam(gen_mod:opts() | global | binary()) -> 'none' | acl:acl(). +access_spam(Opts) when is_map(Opts) -> + gen_mod:get_opt(access_spam, Opts); +access_spam(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, access_spam). + +-spec cache_size(gen_mod:opts() | global | binary()) -> 'unlimited' | pos_integer(). +cache_size(Opts) when is_map(Opts) -> + gen_mod:get_opt(cache_size, Opts); +cache_size(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, cache_size). + +-spec rtbl_services(gen_mod:opts() | global | binary()) -> [binary() | [{binary(),[{'spam_source_domains_node',binary()}]}]]. +rtbl_services(Opts) when is_map(Opts) -> + gen_mod:get_opt(rtbl_services, Opts); +rtbl_services(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, rtbl_services). + +-spec spam_domains_file(gen_mod:opts() | global | binary()) -> 'none' | binary(). +spam_domains_file(Opts) when is_map(Opts) -> + gen_mod:get_opt(spam_domains_file, Opts); +spam_domains_file(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, spam_domains_file). + +-spec spam_dump_file(gen_mod:opts() | global | binary()) -> boolean() | binary(). +spam_dump_file(Opts) when is_map(Opts) -> + gen_mod:get_opt(spam_dump_file, Opts); +spam_dump_file(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, spam_dump_file). + +-spec spam_jids_file(gen_mod:opts() | global | binary()) -> 'none' | binary(). +spam_jids_file(Opts) when is_map(Opts) -> + gen_mod:get_opt(spam_jids_file, Opts); +spam_jids_file(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, spam_jids_file). + +-spec spam_urls_file(gen_mod:opts() | global | binary()) -> 'none' | binary(). +spam_urls_file(Opts) when is_map(Opts) -> + gen_mod:get_opt(spam_urls_file, Opts); +spam_urls_file(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, spam_urls_file). + +-spec whitelist_domains_file(gen_mod:opts() | global | binary()) -> 'none' | binary(). +whitelist_domains_file(Opts) when is_map(Opts) -> + gen_mod:get_opt(whitelist_domains_file, Opts); +whitelist_domains_file(Host) -> + gen_mod:get_module_opt(Host, mod_antispam, whitelist_domains_file). + diff --git a/src/mod_pubsub_serverinfo.erl b/src/mod_pubsub_serverinfo.erl index 80da0d26a..45a24a31e 100644 --- a/src/mod_pubsub_serverinfo.erl +++ b/src/mod_pubsub_serverinfo.erl @@ -225,10 +225,7 @@ mod_doc() -> "This option is only needed if your configuration has more than one host in mod_pubsub's 'hosts' option. " "The default value is the first host defined in mod_pubsub 'hosts' option.")}}], example => - ["modules:", - " mod_pubsub_serverinfo:", - " pubsub_host: custom.pubsub.domain.local"] - }. + ["modules:", " mod_pubsub_serverinfo:", " pubsub_host: custom.pubsub.domain.local"]}. in_auth_result(#{server_host := Host, remote_server := RServer} = State, true, _Server) -> gen_server:cast( @@ -362,7 +359,9 @@ get_info(Acc, _Host, _Mod, _Node, _Lang) -> Acc. pubsub_host(Host) -> - {ok, PubsubHost} = gen_server:call(gen_mod:get_module_proc(Host, ?MODULE), pubsub_host), + {ok, PubsubHost} = + gen_server:call( + gen_mod:get_module_proc(Host, ?MODULE), pubsub_host), PubsubHost. pubsub_host(Host, Opts) -> diff --git a/src/mod_pubsub_serverinfo_opt.erl b/src/mod_pubsub_serverinfo_opt.erl new file mode 100644 index 000000000..731715f3c --- /dev/null +++ b/src/mod_pubsub_serverinfo_opt.erl @@ -0,0 +1,13 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_pubsub_serverinfo_opt). + +-export([pubsub_host/1]). + +-spec pubsub_host(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). +pubsub_host(Opts) when is_map(Opts) -> + gen_mod:get_opt(pubsub_host, Opts); +pubsub_host(Host) -> + gen_mod:get_module_opt(Host, mod_pubsub_serverinfo, pubsub_host). + From 4694a482f4f38fbff8253db4a0e4e558c18634b3 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Tue, 8 Jul 2025 20:03:23 +0200 Subject: [PATCH 1200/1302] Update doap with info about xep-0486 --- ejabberd.doap | 9 +++++++++ src/mod_muc.erl | 1 + 2 files changed, 10 insertions(+) diff --git a/ejabberd.doap b/ejabberd.doap index 0e72bf3ad..b48ad5892 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -836,5 +836,14 @@ mod_pubsub_serverinfo + + + + 0.1.0 + 24.07 + complete + mod_muc + + diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ac87fec68..7a8a9f9ea 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -26,6 +26,7 @@ -author('alexey@process-one.net'). -protocol({xep, 45, '1.25', '0.5.0', "complete", ""}). -protocol({xep, 249, '1.2', '0.5.0', "complete", ""}). +-protocol({xep, 486, '0.1.0', '24.07', "complete", ""}). -ifndef(GEN_SERVER). -define(GEN_SERVER, gen_server). -endif. From 72bc9b6c7f6afce7aa671a47eee343cc25b0abcb Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 9 Jul 2025 13:21:03 +0200 Subject: [PATCH 1201/1302] Allow s2s connections to accept client certificates that have only server purpose Due to Google Chrome certification requirements we can expect that in near future there will be no certificate authority that will issue certifcates that have both server and client auth purposes. This change makes s2s listeners ignore cert purposes, and should allow servers that have those new certificate to use it, to authenticate new s2s connections. This fixes issue #4392 --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 7 ++++--- src/ejabberd_s2s_in.erl | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 7a12a248a..3fead1d4b 100644 --- a/mix.exs +++ b/mix.exs @@ -120,7 +120,7 @@ defmodule Ejabberd.MixProject do {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, - {:fast_tls, "~> 1.1.22"}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "f1e55d6d6bdf109ebc48dda880d028c95f349c3b", override: true}, {:fast_xml, git: "https://github.com/processone/fast_xml", ref: "72e1c1b2eef84804399095704f2d729d5df8f02e", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, diff --git a/rebar.config b/rebar.config index eb160ed8b..9f1652b50 100644 --- a/rebar.config +++ b/rebar.config @@ -44,7 +44,7 @@ {esip, "~> 1.0.57", {git, "https://github.com/processone/esip", {tag, "1.0.57"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.13", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", {tag, "1.1.22"}}}, + {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", "f1e55d6d6bdf109ebc48dda880d028c95f349c3b"}}, {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e"}}, {fast_yaml, "~> 1.0.37", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, diff --git a/rebar.lock b/rebar.lock index d4452766d..50ef76ab4 100644 --- a/rebar.lock +++ b/rebar.lock @@ -6,7 +6,10 @@ {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, {<<"esip">>,{pkg,<<"esip">>,<<"1.0.57">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, - {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.22">>},0}, + {<<"fast_tls">>, + {git,"https://github.com/processone/fast_tls", + {ref,"f1e55d6d6bdf109ebc48dda880d028c95f349c3b"}}, + 0}, {<<"fast_xml">>, {git,"https://github.com/processone/fast_xml", {ref,"72e1c1b2eef84804399095704f2d729d5df8f02e"}}, @@ -44,7 +47,6 @@ {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, {<<"esip">>, <<"4B14E4832D08B9FFC10D855B5D10B3083232B1D53DEB4C046679496CE85569C4">>}, {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, - {<<"fast_tls">>, <<"44356B256AFAD4399C2FC5059A3066669DAFD8BD4E4E796C9C1CF8910DDD265E">>}, {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, @@ -69,7 +71,6 @@ {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, {<<"esip">>, <<"19C357E1817B1E04792EF359BF900400F3E6D0E5ADE929FD72F88EA9B44AF2ED">>}, {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, - {<<"fast_tls">>, <<"E65779AEFB7AB15C4755230FEF8077E687D20CC5A3984A5974F9F657E8E2485B">>}, {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index e065f8418..c985e3afc 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -138,7 +138,7 @@ process_closed(#{server := LServer} = State, Reason) -> %%% xmpp_stream_in callbacks %%%=================================================================== tls_options(#{tls_options := TLSOpts, lserver := LServer, server_host := ServerHost}) -> - ejabberd_s2s:tls_options(LServer, ServerHost, TLSOpts). + [override_cert_purpose | ejabberd_s2s:tls_options(LServer, ServerHost, TLSOpts)]. tls_required(#{server_host := ServerHost}) -> ejabberd_s2s:tls_required(ServerHost). From 99d323b1dd1c27b9349c9a8de085b4cb34f20980 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 9 Jul 2025 17:09:21 +0200 Subject: [PATCH 1202/1302] Update fast_tls with updated deps --- mix.exs | 2 +- rebar.config | 2 +- rebar.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 3fead1d4b..f667a834a 100644 --- a/mix.exs +++ b/mix.exs @@ -120,7 +120,7 @@ defmodule Ejabberd.MixProject do {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "f1e55d6d6bdf109ebc48dda880d028c95f349c3b", override: true}, + {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "d6ed64546e4f0d92d6a334827ea36b0c38307878", override: true}, {:fast_xml, git: "https://github.com/processone/fast_xml", ref: "72e1c1b2eef84804399095704f2d729d5df8f02e", override: true}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, diff --git a/rebar.config b/rebar.config index 9f1652b50..192c7b18c 100644 --- a/rebar.config +++ b/rebar.config @@ -44,7 +44,7 @@ {esip, "~> 1.0.57", {git, "https://github.com/processone/esip", {tag, "1.0.57"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.13", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", "f1e55d6d6bdf109ebc48dda880d028c95f349c3b"}}, + {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", "d6ed64546e4f0d92d6a334827ea36b0c38307878"}}, {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e"}}, {fast_yaml, "~> 1.0.37", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, diff --git a/rebar.lock b/rebar.lock index 50ef76ab4..caac96087 100644 --- a/rebar.lock +++ b/rebar.lock @@ -8,7 +8,7 @@ {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, {<<"fast_tls">>, {git,"https://github.com/processone/fast_tls", - {ref,"f1e55d6d6bdf109ebc48dda880d028c95f349c3b"}}, + {ref,"d6ed64546e4f0d92d6a334827ea36b0c38307878"}}, 0}, {<<"fast_xml">>, {git,"https://github.com/processone/fast_xml", From 1e0b8cb5470e25b0f7928fb360aa74192a35bf7c Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 10 Jul 2025 09:05:58 +0200 Subject: [PATCH 1203/1302] Bump Erlang/OTP 27.3.4.1 for container image too --- .github/container/Dockerfile | 2 +- CONTAINER.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 4e4e7c0d2..6cc9f885e 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,5 @@ #' Define default build variables -ARG OTP_VSN='27.3.4' +ARG OTP_VSN='27.3.4.1' ARG ELIXIR_VSN='1.18.4' ARG UID='9000' ARG USER='ejabberd' diff --git a/CONTAINER.md b/CONTAINER.md index 3ae82cae8..ed6596aa6 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.4-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.4.1-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | From 611ebce0d25b672c7446def4fdab59dbc5e58185 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 10 Jul 2025 09:18:34 +0200 Subject: [PATCH 1204/1302] Update Greek translation (thanks to GiannosOB) --- priv/msgs/el.msg | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/priv/msgs/el.msg b/priv/msgs/el.msg index 8f905164a..0d18b30c4 100644 --- a/priv/msgs/el.msg +++ b/priv/msgs/el.msg @@ -12,9 +12,10 @@ {"A Web Page","Μία ιστοσελίδα"}. {"Accept","Αποδοχή"}. {"Access denied by service policy","Άρνηση πρόσβασης, λόγω τακτικής παροχής υπηρεσιών"}. -{"Access model","Καθορίστε το μοντέλο πρόσβασης"}. +{"Access model","Μοντέλο πρόσβασης"}. {"Account doesn't exist","Ο λογαριασμός δεν υπάρχει"}. {"Action on user","Eνέργεια για το χρήστη"}. +{"Add a hat to a user","Προσθέστε ένα καπέλο σε έναν χρήστη"}. {"Add User","Προσθήκη Χρήστη"}. {"Administration of ","Διαχείριση του "}. {"Administration","Διαχείριση"}. @@ -45,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","Όποιος έχει συνδρομή παρουσίας και των δύο ή από μπορεί να εγγραφεί και να ανακτήσει στοιχεία"}. {"Anyone with Voice","Οποιοσδήποτε με Φωνή"}. {"Anyone","Οποιοσδήποτε"}. +{"API Commands","Εντολές του API"}. {"April","Απρίλιος"}. +{"Arguments","Επιχειρήματα"}. {"Attribute 'channel' is required for this request","Το δηλωτικό 'channel' απαιτείται για αυτό το Ερώτημα"}. {"Attribute 'id' is mandatory for MIX messages","Το δηλωτικό 'id' επιτακτικό για μηνύματα MIX"}. {"Attribute 'jid' is not allowed here","Το δηλωτικό 'jid' δεν επιτρέπεται εδώ"}. @@ -71,6 +74,7 @@ {"Changing role/affiliation is not allowed","Η αλλαγή ρόλου/ομάδας δεν επιτρέπεται"}. {"Channel already exists","Το κανάλι υπάρχει ήδη"}. {"Channel does not exist","Το κανάλι δεν υπάρχει"}. +{"Channel JID","JID καναλιού"}. {"Channels","Κανάλια"}. {"Characters not allowed:","Χαρακτήρες που δεν επιτρέπονται:"}. {"Chatroom configuration modified","Η ρύθμιση παραμέτρων της αίθουσας σύνεδριασης τροποποιηθηκε"}. @@ -84,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","Επιλέξτε αν θα εγκρίθεί η εγγραφή αυτής της οντότητας."}. {"City","Πόλη"}. {"Client acknowledged more stanzas than sent by server","Ο πελάτης γνωρίζει περισσότερα δωμάτια από αυτά που στάλθηκαν από τον εξυπηρετητή"}. +{"Clustering","Συσταδοποίηση"}. {"Commands","Εντολές"}. {"Conference room does not exist","Η αίθουσα σύνεδριασης δεν υπάρχει"}. {"Configuration of room ~s","Διαμόρφωση δωματίου ~ s"}. @@ -119,6 +124,7 @@ {"ejabberd","ejabberd"}. {"Email Address","Ηλεκτρονική Διεύθυνση"}. {"Email","Ηλεκτρονικό ταχυδρομείο"}. +{"Enable hats","Ενεργοποίηση καπέλων"}. {"Enable logging","Ενεργοποίηση καταγραφής"}. {"Enable message archiving","Ενεργοποιήστε την αρχειοθέτηση μηνυμάτων"}. {"Enabling push without 'node' attribute is not supported","Η ενεργοποίηση της ώθησης χωρίς το χαρακτηριστικό 'κόμβος' δεν υποστηρίζεται"}. @@ -151,6 +157,8 @@ {"Full List of Room Admins","Πλήρης Κατάλογος Διαχειριστών αιθουσών"}. {"Full List of Room Owners","Πλήρης Κατάλογος Ιδιοκτητών αιθουσών"}. {"Full Name","Ονοματεπώνυμο"}. +{"Get List of Online Users","Λίστα online χρηστών"}. +{"Get List of Registered Users","Λίστα εγγεγραμμένων χρηστών"}. {"Get Number of Online Users","Έκθεση αριθμού συνδεδεμένων χρηστών"}. {"Get Number of Registered Users","Έκθεση αριθμού εγγεγραμμένων χρηστών"}. {"Get Pending","Λήψη των εκκρεμοτήτων"}. @@ -163,6 +171,10 @@ {"has been kicked because of an affiliation change","έχει αποβληθεί λόγω αλλαγής υπαγωγής"}. {"has been kicked because the room has been changed to members-only","αποβλήθηκε επειδή η αίθουσα αλλάξε γιά μέλη μόνο"}. {"has been kicked","αποβλήθηκε"}. +{"Hash of the vCard-temp avatar of this room","Hash του vCard-temp avatar αυτού του δωματίου"}. +{"Hat title","Τίτλος καπέλου"}. +{"Hat URI","Καπέλο URI"}. +{"Hats limit exceeded","Υπέρβαση του ορίου καπέλων"}. {"Host unknown","Άγνωστος εξυπηρετητής"}. {"HTTP File Upload","Ανέβασμα αρχείου"}. {"Idle connection","Αδρανής σύνδεση"}. @@ -183,6 +195,8 @@ {"Incorrect value of 'action' attribute","Λανθασμένη τιμή του χαρακτηριστικού 'action'"}. {"Incorrect value of 'action' in data form","Λανθασμένη τιμή 'action' στη φόρμα δεδομένων"}. {"Incorrect value of 'path' in data form","Λανθασμένη τιμή 'path' στη φόρμα δεδομένων"}. +{"Installed Modules:","Εγκατεστημένες ενότητες:"}. +{"Install","Εγκατάσταση"}. {"Insufficient privilege","Ανεπαρκή προνόμια"}. {"Internal server error","Εσωτερικό σφάλμα"}. {"Invalid 'from' attribute in forwarded message","Μη έγκυρο χαρακτηριστικό 'από' στο προωθούμενο μήνυμα"}. @@ -198,6 +212,8 @@ {"January","Ιανουάριος"}. {"JID normalization denied by service policy","Απετράπη η κανονικοποίηση του JID, λόγω της τακτικής Παροχής Υπηρεσιών"}. {"JID normalization failed","Απετράπη η κανονικοποίηση του JID"}. +{"Joined MIX channels of ~ts","Ενσωματωμένα κανάλια MIX του ~ts"}. +{"Joined MIX channels:","Ενσωματωμένα κανάλια MIX:"}. {"joins the room","συνδέεται στην αίθουσα"}. {"July","Ιούλιος"}. {"June","Ιούνιος"}. @@ -209,6 +225,9 @@ {"Last year","Πέρυσι"}. {"Least significant bits of SHA-256 hash of text should equal hexadecimal label","Τα ψηφία μικρότερης αξίας του αθροίσματος SHA-256 του κειμένου θα έπρεπε να ισούνται με την δεκαεξαδική ετικέτα"}. {"leaves the room","εγκαταλείπει την αίθουσα"}. +{"List of users with hats","Λίστα των χρηστών με καπέλα"}. +{"List users with hats","Λίστα χρηστών με καπέλα"}. +{"Logged Out","Αποσυνδεδεμένος"}. {"Logging","Καταγραφή"}. {"Make participants list public","Κάντε δημόσιο τον κατάλογο συμμετεχόντων"}. {"Make room CAPTCHA protected","Κάντε την αίθουσα προστατεύομενη με CAPTCHA"}. @@ -220,6 +239,7 @@ {"Malformed username","Λανθασμένη μορφή ονόματος χρήστη"}. {"MAM preference modification denied by service policy","Άρνηση αλλαγής προτιμήσεων MAM, λόγω της τακτικής Παροχής Υπηρεσιών"}. {"March","Μάρτιος"}. +{"Max # of items to persist, or `max` for no specific limit other than a server imposed maximum","Μέγιστος αριθμός στοιχείων που πρέπει να παραμείνουν, ή « Μέγιστο » για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. {"Max payload size in bytes","Μέγιστο μέγεθος φορτίου σε bytes"}. {"Maximum file size","Μέγιστο μέγεθος αρχείου"}. {"Maximum Number of History Messages Returned by Room","Μέγιστος αριθμός μηνυμάτων Ιστορικού που επιστρέφονται από την Αίθουσα"}. @@ -290,6 +310,7 @@ {"Node ~p","Κόμβος ~p"}. {"Nodeprep has failed","Το Nodeprep απέτυχε"}. {"Nodes","Κόμβοι"}. +{"Node","Κόμβος"}. {"None","Κανένα"}. {"Not allowed","Δεν επιτρέπεται"}. {"Not Found","Δε βρέθηκε"}. @@ -303,7 +324,9 @@ {"Number of Offline Messages","Πλήθος μηνυμάτων Χωρίς Σύνδεση"}. {"Number of online users","Αριθμός συνδεδεμένων χρηστών"}. {"Number of registered users","Αριθμός εγγεγραμμένων χρηστών"}. +{"Number of seconds after which to automatically purge items, or `max` for no specific limit other than a server imposed maximum","Αριθμός δευτερολέπτων μετά από τα οποία θα καθαρίζονται αυτόματα τα στοιχεία, ή `max` για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. {"Occupants are allowed to invite others","Οι συμμετέχοντες μπορούν να προσκαλέσουν και άλλους"}. +{"Occupants are allowed to query others","Οι κάτοικοι επιτρέπεται να ρωτούν άλλους"}. {"Occupants May Change the Subject","Επιτρέψτε στους χρήστες να αλλάζουν το Θέμα"}. {"October","Οκτώβριος"}. {"OK","Εντάξει"}. @@ -317,6 +340,7 @@ {"Only members may query archives of this room","Μόνο μέλη μπορούν να δούνε τα αρχεία αυτής της αίθουσας"}. {"Only moderators and participants are allowed to change the subject in this room","Μόνο οι συντονιστές και οι συμμετέχοντες μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. {"Only moderators are allowed to change the subject in this room","Μόνο οι συντονιστές μπορούν να αλλάξουν το θέμα αυτής της αίθουσας"}. +{"Only moderators are allowed to retract messages","Μόνο οι συντονιστές επιτρέπεται να αποσύρουν μηνύματα"}. {"Only moderators can approve voice requests","Μόνο οι συντονιστές μπορούν να εγκρίνουν τις αιτήσεις φωνής"}. {"Only occupants are allowed to send messages to the conference","Μόνο οι συμμετέχοντες επιτρέπεται να στέλνουν μηνύματα στο συνέδριο"}. {"Only occupants are allowed to send queries to the conference","Μόνο οι συμμετέχοντες επιτρέπεται να στείλουν ερωτήματα στη διάσκεψη"}. @@ -326,9 +350,11 @@ {"Only those on a whitelist may subscribe and retrieve items","Μόνο όσοι βρίσκονται στη λίστα επιτρεπόμενων μπορούν να εγγραφούν και να ανακτήσουν αντικείμενα"}. {"Organization Name","Όνομα Οργανισμού"}. {"Organization Unit","Μονάδα Οργανισμού"}. +{"Other Modules Available:","Διαθέσιμες άλλες ενότητες:"}. {"Outgoing s2s Connections","Εξερχόμενες S2S Συνδέσεις"}. {"Owner privileges required","Aπαιτούνται προνόμια ιδιοκτήτη"}. {"Packet relay is denied by service policy","Απαγορεύεται η αναμετάδοση πακέτων, λόγω της τακτικής Παροχής Υπηρεσιών"}. +{"Participant ID","ID συμμετέχοντος"}. {"Participant","Συμμετέχων"}. {"Password Verification","Επαλήθευση κωδικού πρόσβασης"}. {"Password Verification:","Επαλήθευση κωδικού πρόσβασης:"}. @@ -336,6 +362,7 @@ {"Password:","Κωδικός πρόσβασης:"}. {"Path to Dir","Τοποθεσία κατάλογου αρχείων"}. {"Path to File","Τοποθεσία Αρχείου"}. +{"Payload semantic type information","Πληροφορίες σημασιολογικού τύπου ωφέλιμου φορτίου"}. {"Period: ","Περίοδος: "}. {"Persist items to storage","Μόνιμη αποθήκευση στοιχείων"}. {"Persistent","Μόνιμη"}. @@ -371,6 +398,7 @@ {"Register an XMPP account","Καταχωρείστε έναν XMPP λογαριασμό χρήστη"}. {"Register","Καταχωρήστε"}. {"Remote copy","Εξ αποστάσεως αντίγραφο"}. +{"Remove a hat from a user","Αφαίρεση ενός καπέλου από έναν χρήστη"}. {"Remove User","Αφαίρεση χρήστη"}. {"Replaced by new connection","Αντικαταστάθηκε από μια νέα σύνδεση"}. {"Request has timed out","Το αίτημα έληξε"}. @@ -383,6 +411,7 @@ {"Restore binary backup immediately:","Επαναφορά δυαδικού αντιγράφου ασφαλείας αμέσως:"}. {"Restore plain text backup immediately:","Επαναφορά αντιγράφου ασφαλείας από αρχείο κειμένου αμέσως:"}. {"Restore","Επαναφορά Αντιγράφου Ασφαλείας"}. +{"Result","Αποτέλεσμα"}. {"Roles and Affiliations that May Retrieve Member List","Ρόλοι και δεσμοί που μπορούν να λάβουν την λίστα μελών"}. {"Roles for which Presence is Broadcasted","Ρόλοι των οποίων η παρουσία δηλώνεται δημόσια"}. {"Roles that May Send Private Messages","Ρόλοι που επιτρέπεται να αποστέλλουν ιδιωτικά μηνύματα"}. @@ -414,13 +443,16 @@ {"Set message of the day on all hosts and send to online users","Ορίστε μήνυμα ημέρας και άμεση αποστολή στους συνδεδεμένους χρήστες σε όλους τους κεντρικούς υπολογιστές"}. {"Shared Roster Groups","Κοινές Ομάδες Καταλόγων Επαφών"}. {"Show Integral Table","Δείτε Ολοκληρωτικό Πίνακα"}. +{"Show Occupants Join/Leave","Εμφάνιση ενοίκων Join/Leave"}. {"Show Ordinary Table","Δείτε Κοινό Πίνακα"}. {"Shut Down Service","Τερματισμός Υπηρεσίας"}. {"SOCKS5 Bytestreams","Bytestreams του SOCKS5"}. {"Some XMPP clients can store your password in the computer, but you should do this only in your personal computer for safety reasons.","Ορισμένοι πελάτες XMPP μπορούν να αποθηκεύσουν τον κωδικό πρόσβασής σας στον υπολογιστή, αλλά θα πρέπει να το κάνετε μόνο στον προσωπικό σας υπολογιστή για λόγους ασφαλείας."}. +{"Sources Specs:","Πηγές Προδιαγραφές:"}. {"Specify the access model","Καθορίστε το μοντέλο πρόσβασης"}. {"Specify the event message type","Καθορίστε τον τύπο μηνύματος συμβάντος"}. {"Specify the publisher model","Καθορίστε το μοντέλο εκδότη"}. +{"Stanza id is not valid","Το Stanza id δεν είναι έγκυρο"}. {"Stanza ID","Ταυτότητα Δωματίου"}. {"Statically specify a replyto of the node owner(s)","Προσδιορίστε (στατικά) το Απάντηση Προς του ιδιοκτήτη-ων του κόμβου"}. {"Stopped Nodes","Σταματημένοι Κόμβοι"}. @@ -457,7 +489,10 @@ {"The JIDs of those to contact with questions","Το JID αυτών με τους οποίους θα επικοινωνήσετε με ερωτήσεις"}. {"The JIDs of those with an affiliation of owner","Το JID αυτών που σχετίζονται με τον ιδιοκτήτη"}. {"The JIDs of those with an affiliation of publisher","Το JID αυτών που σχετίζονται με τον εκδότη"}. +{"The list of all online users","Ο κατάλογος όλων των online χρηστών"}. +{"The list of all users","Ο κατάλογος όλων των χρηστών"}. {"The list of JIDs that may associate leaf nodes with a collection","Λίστα των JIDs που μπορούν να σχετίζουν leaf κόμβους με μια Συλλογή"}. +{"The maximum number of child nodes that can be associated with a collection, or `max` for no specific limit other than a server imposed maximum","Ο μέγιστος αριθμός των παιδικών κόμβων που μπορούν να συσχετιστούν με μια συλλογή, ή `max` για κανένα συγκεκριμένο όριο εκτός από το μέγιστο που επιβάλλει ο διακομιστής"}. {"The minimum number of milliseconds between sending any two notification digests","Το ελάχιστο πλήθος χιλιοστών του δευτερολέπτου μεταξύ της αποστολής δύο συγχωνεύσεων ειδοποιήσεων"}. {"The name of the node","Το όνομα του κόμβου"}. {"The node is a collection node","Ο κόμβος είναι κόμβος Συλλογής"}. @@ -476,6 +511,7 @@ {"The query is only allowed from local users","Το ερώτημα επιτρέπεται μόνο από τοπικούς χρήστες"}. {"The query must not contain elements","Το ερώτημα δεν πρέπει να περιέχει στοιχείο "}. {"The room subject can be modified by participants","Το θέμα μπορεί να τροποποιηθεί από τους συμμετέχοντες"}. +{"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","Οι πληροφορίες σημασιολογικού τύπου των δεδομένων στον κόμβο, που συνήθως καθορίζονται από το χώρο ονομάτων του ωφέλιμου φορτίου (εάν υπάρχει)"}. {"The sender of the last received message","Ο αποστολέας του τελευταίου εισερχομένου μηνύματος"}. {"The stanza MUST contain only one element, one element, or one element","Η stanza ΠΡΕΠΕΙ να περιέχει μόνο ένα στοιχείο , ένα στοιχείο ή ένα στοιχείο "}. {"The subscription identifier associated with the subscription request","Το αναγνωριστικό συνδρομής συσχετίστηκε με το αίτημα συνδρομής"}. @@ -505,6 +541,7 @@ {"Too many unacked stanzas","Πάρα πολλές μη αναγνωρισμένες stanzas"}. {"Too many users in this conference","Πάρα πολλοί χρήστες σε αυτή τη διάσκεψη"}. {"Traffic rate limit is exceeded","Υπέρφορτωση"}. +{"~ts's MAM Archive","Αρχείο MAM του ~ts"}. {"~ts's Offline Messages Queue","~ts's Χωρίς Σύνδεση Μηνύματα"}. {"Tuesday","Τρίτη"}. {"Unable to generate a CAPTCHA","Αδύνατη η δημιουργία CAPTCHA"}. @@ -512,17 +549,23 @@ {"Unauthorized","Χωρίς Εξουσιοδότηση"}. {"Unexpected action","Απροσδόκητη ενέργεια"}. {"Unexpected error condition: ~p","Απροσδόκητες συνθήκες σφάλματος: ~p"}. +{"Uninstall","Απεγκατάσταση"}. {"Unregister an XMPP account","Καταργήση λογαριασμού XMPP"}. {"Unregister","Καταργήση εγγραφής"}. {"Unsupported element","Μη υποστηριζόμενο στοιχείο "}. {"Unsupported version","Μη υποστηριζόμενη έκδοση"}. {"Update message of the day (don't send)","Ενημέρωση μηνύματος ημέρας (χωρίς άμεση αποστολή)"}. {"Update message of the day on all hosts (don't send)","Ενημέρωση μηνύματος ημέρας σε όλους τους κεντρικούς υπολογιστές (χωρίς άμεση αποστολή)"}. +{"Update specs to get modules source, then install desired ones.","Ενημερώστε τις προδιαγραφές για να λάβετε την πηγή των ενοτήτων και, στη συνέχεια, εγκαταστήστε τις επιθυμητές."}. +{"Update Specs","Προδιαγραφές ενημέρωσης"}. +{"Updating the vCard is not supported by the vCard storage backend","Η ενημέρωση της vCard δεν υποστηρίζεται από το backend αποθήκευσης vCard"}. +{"Upgrade","Αναβάθμιση"}. {"URL for Archived Discussion Logs","URL αρχειοθετημένων καταγραφών συζητήσεων"}. {"User already exists","Ο χρήστης υπάρχει ήδη"}. {"User JID","JID Χρήστη"}. {"User (jid)","Χρήστης (jid)"}. {"User Management","Διαχείριση χρηστών"}. +{"User not allowed to perform an IQ set on another user's vCard.","Ο χρήστης δεν επιτρέπεται να εκτελέσει ένα σετ IQ στην vCard ενός άλλου χρήστη."}. {"User removed","Ο Χρήστης αφαιρέθηκε"}. {"User session not found","Η περίοδος σύνδεσης χρήστη δεν βρέθηκε"}. {"User session terminated","Η περίοδος σύνδεσης χρήστη τερματίστηκε"}. @@ -538,12 +581,14 @@ {"Value of '~s' should be integer","Η τιμή του '~s' θα πρέπει να είναι ακέραιος"}. {"Value 'set' of 'type' attribute is not allowed","Δεν επιτρέπεται η παράμετρος 'set' του 'type'"}. {"vCard User Search","vCard Αναζήτηση χρηστών"}. +{"View joined MIX channels","Προβολή ενταγμένων καναλιών MIX"}. {"Virtual Hosts","Eικονικοί κεντρικοί υπολογιστές"}. {"Visitors are not allowed to change their nicknames in this room","Οι επισκέπτες δεν επιτρέπεται να αλλάξουν τα ψευδώνυμα τους σε αυτή την αίθουσα"}. {"Visitors are not allowed to send messages to all occupants","Οι επισκέπτες δεν επιτρέπεται να στείλουν μηνύματα σε όλους τους συμμετέχοντες"}. {"Visitor","Επισκέπτης"}. {"Voice requests are disabled in this conference","Τα αιτήματα φωνής είναι απενεργοποιημένα, σε αυτό το συνέδριο"}. {"Voice request","Αίτημα φωνής"}. +{"Web client which allows to join the room anonymously","Web client που επιτρέπει την ανώνυμη είσοδο στην αίθουσα"}. {"Wednesday","Τετάρτη"}. {"When a new subscription is processed and whenever a subscriber comes online","Όταν μία νέα συνδρομή βρίσκεται εν επεξεργασία και όποτε ένας συνδρομητής συνδεθεί"}. {"When a new subscription is processed","Όταν μία νέα συνδρομή βρίσκεται εν επεξεργασία"}. @@ -556,6 +601,7 @@ {"Whether to allow subscriptions","Εάν επιτρέπονται συνδρομές"}. {"Whether to make all subscriptions temporary, based on subscriber presence","Αν επιτρέπεται να γίνουν όλες οι συνδρομές προσωρινές, βασιζόμενοι στην παρουσία του συνδρομητή"}. {"Whether to notify owners about new subscribers and unsubscribes","Αν πρέπει να ειδοποιούνται οι ιδιοκτήτες για νέους συνδρομητές και αποχωρήσεις"}. +{"Who can send private messages","Ποιος μπορεί να στείλει ιδιωτικά μηνύματα"}. {"Who may associate leaf nodes with a collection","Ποιός μπορεί να συσχετίζει leaf nodes με μία συλλογή"}. {"Wrong parameters in the web formulary","Εσφαλμένες παράμετροι στην διαμόρφωση τυπικότητας του δυκτίου"}. {"Wrong xmlns","Εσφαλμένο xmlns"}. @@ -567,6 +613,7 @@ {"XMPP Show Value of XA (Extended Away)","Δείξε τιμή XMPP Αξία του Λίαν Απομακρυσμένος"}. {"XMPP URI of Associated Publish-Subscribe Node","XMPP URI του συσχετισμένου κόμβου Δημοσίευσης-Εγγραφής"}. {"You are being removed from the room because of a system shutdown","Απαιτείται η απομάκρυνσή σας από την αίθουσα, λόγω τερματισμού συστήματος"}. +{"You are not allowed to send private messages","Δεν επιτρέπεται η αποστολή ιδιωτικών μηνυμάτων"}. {"You are not joined to the channel","Δεν λαμβάνετε μέρος στο κανάλι"}. {"You can later change your password using an XMPP client.","Μπορείτε αργότερα να αλλάξετε τον κωδικό πρόσβασής σας χρησιμοποιώντας ένα πρόγραμμα-πελάτη XMPP."}. {"You have been banned from this room","Σας έχει απαγορευθεί η είσοδος σε αυτή την αίθουσα"}. From cfa787c4b67a85350d113b965b459085b003c4dd Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 10 Jul 2025 09:19:13 +0200 Subject: [PATCH 1205/1302] Update Tamil translation (thanks to TamilNeram) --- priv/msgs/ta.msg | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/priv/msgs/ta.msg b/priv/msgs/ta.msg index fceafe5c0..d76425610 100644 --- a/priv/msgs/ta.msg +++ b/priv/msgs/ta.msg @@ -46,7 +46,9 @@ {"Anyone with a presence subscription of both or from may subscribe and retrieve items","அல்லது இரண்டின் இருப்பு சந்தா உள்ள எவரும் உருப்படிகளை குழுசேர் மற்றும் மீட்டெடுக்கலாம்"}. {"Anyone with Voice","குரல் உள்ள எவரும்"}. {"Anyone","யாரும்"}. +{"API Commands","பநிஇ கட்டளைகள்"}. {"April","ப-சித்திரை"}. +{"Arguments","வாதங்கள்"}. {"Attribute 'channel' is required for this request","இந்த கோரிக்கைக்கு 'சேனல்' என்ற பண்புக்கூறு தேவை"}. {"Attribute 'id' is mandatory for MIX messages","கலவை செய்திகளுக்கு 'ஐடி' கட்டாயமாகும்"}. {"Attribute 'jid' is not allowed here","'சிட்' என்ற பண்புக்கூறு இங்கே அனுமதிக்கப்படவில்லை"}. @@ -86,6 +88,7 @@ {"Choose whether to approve this entity's subscription.","இந்த நிறுவனத்தின் சந்தாவை அங்கீகரிக்க வேண்டுமா என்பதைத் தேர்வுசெய்க."}. {"City","நகரம்"}. {"Client acknowledged more stanzas than sent by server","சேவையகத்தால் அனுப்பப்பட்டதை விட கிளையன்ட் அதிக சரணத்தை ஒப்புக் கொண்டார்"}. +{"Clustering","கிளச்டரிங்"}. {"Commands","கட்டளைகள்"}. {"Conference room does not exist","மாநாட்டு அறை இல்லை"}. {"Configuration of room ~s","அறையின் உள்ளமைவு ~s"}. @@ -259,7 +262,7 @@ {"Module failed to handle the query","தொகுதி வினவலைக் கையாளத் தவறிவிட்டது"}. {"Monday","திங்கள்"}. {"Multicast","மல்டிகாச்ட்"}. -{"Multiple elements are not allowed by RFC6121","பல <உருப்படி/> கூறுகள் RFC6121 ஆல் அனுமதிக்கப்படாது"}. +{"Multiple elements are not allowed by RFC6121","பல கூறுகள் RFC6121 ஆல் அனுமதிக்கப்படாது"}. {"Multi-User Chat","பல பயனர் அரட்டை"}. {"Name","பெயர்"}. {"Natural Language for Room Discussions","அறை விவாதங்களுக்கு இயற்கை மொழி"}. @@ -281,7 +284,7 @@ {"No data form found","தரவு படிவம் எதுவும் கிடைக்கவில்லை"}. {"No Data","தரவு இல்லை"}. {"No features available","நற்பொருத்தங்கள் எதுவும் கிடைக்கவில்லை"}. -{"No element found","இல்லை <முன்னோக்கி/> உறுப்பு காணப்பட்டது"}. +{"No element found","இல்லை உறுப்பு காணப்பட்டது"}. {"No hook has processed this command","இந்த கட்டளையை எந்த ஊக்கும் செயலாக்கவில்லை"}. {"No info about last activity found","கடைசி செயல்பாட்டைப் பற்றிய எந்த தகவலும் கிடைக்கவில்லை"}. {"No 'item' element found","'உருப்படி' உறுப்பு இல்லை"}. @@ -332,8 +335,8 @@ {"Online","ஆன்லைனில்"}. {"Only collection node owners may associate leaf nodes with the collection","சேகரிப்பு முனை உரிமையாளர்கள் மட்டுமே இலை முனைகளை சேகரிப்புடன் தொடர்புபடுத்தலாம்"}. {"Only deliver notifications to available users","கிடைக்கக்கூடிய பயனர்களுக்கு மட்டுமே அறிவிப்புகளை வழங்கவும்"}. -{"Only or tags are allowed"," அல்லது <முடக்கு/> குறிச்சொற்கள் மட்டுமே அனுமதிக்கப்படுகின்றன"}. -{"Only element is allowed in this query","இந்த வினவலில் <பட்டியல்/> உறுப்பு மட்டுமே அனுமதிக்கப்படுகிறது"}. +{"Only or tags are allowed","அல்லது குறிச்சொற்கள் மட்டுமே அனுமதிக்கப்படுகின்றன"}. +{"Only element is allowed in this query","இந்த வினவலில் உறுப்பு மட்டுமே அனுமதிக்கப்படுகிறது"}. {"Only members may query archives of this room","உறுப்பினர்கள் மட்டுமே இந்த அறையின் காப்பகங்களை வினவலாம்"}. {"Only moderators and participants are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மற்றும் பங்கேற்பாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. {"Only moderators are allowed to change the subject in this room","இந்த அறையில் உள்ள விசயத்தை மாற்ற மதிப்பீட்டாளர்கள் மட்டுமே அனுமதிக்கப்படுகிறார்கள்"}. @@ -408,6 +411,7 @@ {"Restore binary backup immediately:","பைனரி காப்புப்பிரதியை உடனடியாக மீட்டமைக்கவும்:"}. {"Restore plain text backup immediately:","எளிய உரை காப்புப்பிரதியை உடனடியாக மீட்டெடுக்கவும்:"}. {"Restore","மீட்டமை"}. +{"Result","விளைவு"}. {"Roles and Affiliations that May Retrieve Member List","உறுப்பினர் பட்டியலை மீட்டெடுக்கக்கூடிய பாத்திரங்கள் மற்றும் இணைப்புகள்"}. {"Roles for which Presence is Broadcasted","இருப்பு ஒளிபரப்பப்படும் பாத்திரங்கள்"}. {"Roles that May Send Private Messages","தனிப்பட்ட செய்திகளை அனுப்பக்கூடிய பாத்திரங்கள்"}. @@ -505,11 +509,11 @@ {"The passwords are different","கடவுச்சொற்கள் வேறுபட்டவை"}. {"The presence states for which an entity wants to receive notifications","ஒரு நிறுவனம் அறிவிப்புகளைப் பெற விரும்பும் இருப்பு நிலைகள்"}. {"The query is only allowed from local users","வினவல் உள்ளக பயனர்களிடமிருந்து மட்டுமே அனுமதிக்கப்படுகிறது"}. -{"The query must not contain elements","வினவலில் <பொருள்/> கூறுகள் இருக்கக்கூடாது"}. +{"The query must not contain elements","வினவலில் கூறுகள் இருக்கக் கூடாது"}. {"The room subject can be modified by participants","அறை பொருள் பங்கேற்பாளர்களால் மாற்றப்படலாம்"}. {"The semantic type information of data in the node, usually specified by the namespace of the payload (if any)","முனையில் உள்ள தரவின் சொற்பொருள் வகை செய்தி, பொதுவாக பேலோடின் பெயர்வெளியால் குறிப்பிடப்படுகிறது (ஏதேனும் இருந்தால்)"}. {"The sender of the last received message","கடைசியாக பெறப்பட்ட செய்தியை அனுப்பு"}. -{"The stanza MUST contain only one element, one element, or one element","ச்டான்சாவில் ஒரே <செயலில்/> உறுப்பு, ஒரு <இயல்புநிலை/> உறுப்பு அல்லது ஒரு <பட்டியல்/> உறுப்பு மட்டுமே இருக்க வேண்டும்"}. +{"The stanza MUST contain only one element, one element, or one element","ச்டான்சாவில் ஒரே உறுப்பு, ஒரு உறுப்பு அல்லது ஒரு உறுப்பு மட்டுமே இருக்க வேண்டும்"}. {"The subscription identifier associated with the subscription request","சந்தா கோரிக்கையுடன் தொடர்புடைய சந்தா அடையாளங்காட்டி"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","பொருத்தமான செய்தி உடல் உறுப்பை உருவாக்குவதற்காக பேலோடுகளுக்கு பயன்படுத்தக்கூடிய எக்ச்எச்எல் மாற்றத்தின் முகவரி."}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","செல்லுபடியாகும் தரவு படிவங்களை உருவாக்குவதற்காக பேலோட் வடிவத்தில் பயன்படுத்தக்கூடிய ஒரு எக்ச்எச்எல் உருமாற்றத்தின் முகவரி, கிளையன்ட் பொதுவான தரவு படிவங்கள் வழங்குதல் எஞ்சின் பயன்படுத்தி காண்பிக்க முடியும்"}. @@ -530,8 +534,8 @@ {"Too many active bytestreams","பல செயலில் உள்ள பைட்டிரீம்கள்"}. {"Too many CAPTCHA requests","பல கேப்ட்சா கோரிக்கைகள்"}. {"Too many child elements","பல குழந்தை கூறுகள்"}. -{"Too many elements","பல <உருப்படி/> கூறுகள்"}. -{"Too many elements","பல <பட்டியல்/> கூறுகள்"}. +{"Too many elements","பல கூறுகள்"}. +{"Too many elements","பல கூறுகள்"}. {"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","இந்த ஐபி முகவரியிலிருந்து (~p) பல (~s) தோல்வியுற்ற அங்கீகாரங்கள் ~s. முகவரி UTC இல் தடைசெய்யப்படும்"}. {"Too many receiver fields were specified","அதிகமான ரிசீவர் புலங்கள் குறிப்பிடப்பட்டன"}. {"Too many unacked stanzas","பல அறியப்படாத சரணங்கள்"}. @@ -548,7 +552,7 @@ {"Uninstall","நிறுவல் நீக்க"}. {"Unregister an XMPP account","ஒரு எக்ச்எம்பிபி கணக்கை பதிவு செய்யவும்"}. {"Unregister","பதிவு செய்யப்படாதது"}. -{"Unsupported element","ஆதரிக்கப்படாத <குறியீட்டு/> உறுப்பு"}. +{"Unsupported element","ஆதரிக்கப்படாத உறுப்பு"}. {"Unsupported version","ஆதரிக்கப்படாத பதிப்பு"}. {"Update message of the day (don't send)","அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. {"Update message of the day on all hosts (don't send)","எல்லா ஓச்ட்களிலும் அன்றைய செய்தியைப் புதுப்பிக்கவும் (அனுப்ப வேண்டாம்)"}. @@ -584,6 +588,7 @@ {"Visitor","பார்வையாளர்"}. {"Voice requests are disabled in this conference","இந்த மாநாட்டில் குரல் கோரிக்கைகள் முடக்கப்பட்டுள்ளன"}. {"Voice request","குரல் கோரிக்கை"}. +{"Web client which allows to join the room anonymously","அநாமதேயமாக அறையில் சேர அனுமதிக்கும் வலை கிளையண்ட்"}. {"Wednesday","புதன்கிழமை"}. {"When a new subscription is processed and whenever a subscriber comes online","புதிய சந்தா செயலாக்கப்படும் போது, சந்தாதாரர் ஆன்லைனில் வரும்போதெல்லாம்"}. {"When a new subscription is processed","புதிய சந்தா செயலாக்கப்படும் போது"}. From ef35d19ff17528a674a1e7568ac8931236fd5824 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 10 Jul 2025 09:19:48 +0200 Subject: [PATCH 1206/1302] =?UTF-8?q?Update=20Ukrainian=20translation=20(t?= =?UTF-8?q?hanks=20to=20=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=93?= =?UTF-8?q?=D0=BE=D1=80=D0=BF=D0=B8=D0=BD=D1=96=D1=87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/uk.msg | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/priv/msgs/uk.msg b/priv/msgs/uk.msg index 520ec8a5e..cf950ac73 100644 --- a/priv/msgs/uk.msg +++ b/priv/msgs/uk.msg @@ -149,7 +149,7 @@ {"Failed to process option '~s'","Не вдалося обробити параметр \"~s\""}. {"Family Name","Прізвище"}. {"FAQ Entry","Запис в ЧаПи"}. -{"February","лютого"}. +{"February","Лютого"}. {"File larger than ~w bytes","Файл більший, ніж ~w байт"}. {"Fill in the form to search for any matching XMPP User","Заповніть форму для пошуку будь-якого відповідного користувача XMPP"}. {"Friday","П'ятниця"}. @@ -209,14 +209,14 @@ {"It is not allowed to send private messages of type \"groupchat\"","Не дозволяється надсилати приватні повідомлення типу \"groupchat\""}. {"It is not allowed to send private messages to the conference","Не дозволяється надсилати приватні повідомлення в конференцію"}. {"Jabber ID","Jabber ID"}. -{"January","січня"}. +{"January","Січня"}. {"JID normalization denied by service policy","Створювати конференцію заборонено політикою служби"}. {"JID normalization failed","Помилка нормалізації JID"}. {"Joined MIX channels of ~ts","Приєднався до каналів MIX ~ts"}. {"Joined MIX channels:","Приєднався до каналів MIX:"}. {"joins the room","увійшов(ла) в кімнату"}. -{"July","липня"}. -{"June","червня"}. +{"July","Липня"}. +{"June","Червня"}. {"Just created","Щойно створено"}. {"Last Activity","Останнє підключення"}. {"Last login","Останнє підключення"}. @@ -313,12 +313,12 @@ {"Node","Вузол"}. {"None","Немає"}. {"Not allowed","Не дозволяється"}. -{"Not Found","не знайдено"}. +{"Not Found","Не знайдено"}. {"Not subscribed","Не підписаний"}. {"Notify subscribers when items are removed from the node","Повідомляти абонентів про видалення публікацій із збірника"}. {"Notify subscribers when the node configuration changes","Повідомляти абонентів про зміни в конфігурації збірника"}. {"Notify subscribers when the node is deleted","Повідомляти абонентів про видалення збірника"}. -{"November","листопада"}. +{"November","Листопада"}. {"Number of answers required","Кількість необхідних відповідей"}. {"Number of occupants","Кількість присутніх"}. {"Number of Offline Messages","Кількість автономних повідомлень"}. @@ -397,7 +397,7 @@ {"Recipient is not in the conference room","Адресата немає в конференції"}. {"Register an XMPP account","Зареєструвати XMPP-запис"}. {"Register","Реєстрація"}. -{"Remote copy","не зберігаеться локально"}. +{"Remote copy","Віддалене копіювання"}. {"Remove a hat from a user","Зняти шапку з користувача"}. {"Remove User","Видалити користувача"}. {"Replaced by new connection","Замінено новим з'єднанням"}. @@ -435,7 +435,7 @@ {"Send announcement to all online users","Надіслати сповіщення всім підключеним користувачам"}. {"Send announcement to all users on all hosts","Надіслати сповіщення до усіх користувачів на усіх хостах"}. {"Send announcement to all users","Надіслати сповіщення всім користувачам"}. -{"September","вересня"}. +{"September","Вересня"}. {"Server:","Сервер:"}. {"Service list retrieval timed out","Час очікування отримання списку послуг минув"}. {"Session state copying timed out","Час очікування копіювання стану сеансу минув"}. @@ -551,7 +551,7 @@ {"Unexpected error condition: ~p","Умова несподіваної помилки: ~p"}. {"Uninstall","Видалити"}. {"Unregister an XMPP account","Видалити обліковий запис XMPP"}. -{"Unregister","Видалити"}. +{"Unregister","Скасувати реєстрацію"}. {"Unsupported element","Непідтримуваний елемент "}. {"Unsupported version","Непідтримувана версія"}. {"Update message of the day (don't send)","Оновити повідомлення дня (не надсилати)"}. @@ -582,7 +582,7 @@ {"Value 'set' of 'type' attribute is not allowed","Значення 'set' атрибута 'type' не допускається"}. {"vCard User Search","Пошук користувачів по vCard"}. {"View joined MIX channels","Перегляд приєднаних каналів MIX"}. -{"Virtual Hosts","віртуальні хости"}. +{"Virtual Hosts","Віртуальні хости"}. {"Visitors are not allowed to change their nicknames in this room","Відвідувачам не дозволяється змінювати псевдонім в цій кімнаті"}. {"Visitors are not allowed to send messages to all occupants","Відвідувачам не дозволяється надсилати повідомлення всім присутнім"}. {"Visitor","Відвідувач"}. From c0fc6091b1f716598434812f67acb316e024a0c5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 10 Jul 2025 09:44:52 +0200 Subject: [PATCH 1207/1302] Annotate ejabberd version of new modules, options, commands --- src/ejabberd_options_doc.erl | 6 +++++- src/ext_mod.erl | 2 +- src/mod_antispam.erl | 2 +- src/mod_conversejs.erl | 5 +++-- src/mod_matrix_gw.erl | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 375de0ca2..dabfae06d 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -718,7 +718,7 @@ doc() -> ?T("Define aliases for existing vhosts managed by ejabberd. " "An alias may be a regexp expression. " "This option is only consulted by the 'ejabberd_http' listener."), - note => "added in 25.xx", + note => "added in 25.07", example => ["hosts:", " - domain.tld", @@ -1213,18 +1213,22 @@ doc() -> "is 'closeold'.")}}, {rest_proxy, #{value => "Host", + note => "added in 25.07", desc => ?T("Address of a HTTP Connect proxy used by modules issuing rest calls " "(like ejabberd_oauth_rest)")}}, {rest_proxy_port, #{value => "1..65535", + note => "added in 25.07", desc => ?T("Port of a HTTP Connect proxy used by modules issuing rest calls " "(like ejabberd_oauth_rest)")}}, {rest_proxy_username, #{value => "string()", + note => "added in 25.07", desc => ?T("Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls " "(like ejabberd_oauth_rest)")}}, {rest_proxy_password, #{value => "string()", + note => "added in 25.07", desc => ?T("Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls " "(like ejabberd_oauth_rest)")}}, {router_cache_life_time, diff --git a/src/ext_mod.erl b/src/ext_mod.erl index d735b3a55..fe21c5fed 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -149,7 +149,7 @@ get_commands_spec() -> tags = [modules], desc = "Upgrade the running code of an installed module", longdesc = "In practice, this uninstalls, cleans the compiled files, and installs the module", - note = "improved in 25.xx", + note = "improved in 25.07", module = ?MODULE, function = upgrade, args_desc = ["Module name"], args_example = [<<"mod_rest">>], diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index c2472c601..9d8091268 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -182,7 +182,7 @@ mod_doc() -> "Traffic classified as spam is rejected with an error " "(and an '[info]' message is logged) unless the sender " "is subscribed to the recipient's presence."), - note => "added in 25.xx", + note => "added in 25.07", opts => [{access_spam, #{value => ?T("Access"), diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 8f2bcc5e7..c8ba4eb63 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -327,9 +327,10 @@ mod_doc() -> ?T("Make sure either _`mod_bosh`_ or _`listen.md#ejabberd_http_ws|ejabberd_http_ws`_ " "are enabled in at least one 'request_handlers'."), "", ?T("When 'conversejs_css' and 'conversejs_script' are 'auto', " - "by default they point to the public Converse client.") + "by default they point to the public Converse client."), "", + ?T("This module is available since ejabberd 21.12.") ], - note => "added in 21.12 and improved in 22.05", + note => "improved in 25.07", example => [{?T("Manually setup WebSocket url, and use the public Converse client:"), ["listen:", diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index bfd3951b8..adeb7965e 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -993,7 +993,7 @@ mod_doc() -> [?T("https://matrix.org/[Matrix] gateway. "), ?T("Erlang/OTP 25 or higher is required to use this module."), ?T("This module is available since ejabberd 24.02.")], - note => "improved in 25.03", + note => "improved in 25.07", example => ["listen:", " -", From e4d424bf56c265cf28a21021c92ea4ab3fb7de6d Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Thu, 10 Jul 2025 11:44:13 +0200 Subject: [PATCH 1208/1302] Add auth_password_types_hidden_in_scram1 option This option allows disabling some auth mechanisms to be offered in SASL1 features. This makes adding new password types easier, by ensuring that new password use will be offered only to clients that have new type stored (SASL2 clients that send us user info before features need to be sent), but not to clients where we don't know if they have new passwords. --- rebar.config | 2 +- rebar.lock | 2 +- src/ejabberd_c2s.erl | 41 +++++++++++++++++++++++------------- src/ejabberd_option.erl | 8 +++++++ src/ejabberd_options.erl | 3 +++ src/ejabberd_options_doc.erl | 11 ++++++++++ 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/rebar.config b/rebar.config index 192c7b18c..16cde2cc4 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "9b028c110083e4d979d88c286873d0abf08fa532"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "b474d673938856a546265cd5db1139244bfb95f3"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index caac96087..c42e49c40 100644 --- a/rebar.lock +++ b/rebar.lock @@ -35,7 +35,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"9b028c110083e4d979d88c286873d0abf08fa532"}}, + {ref,"b474d673938856a546265cd5db1139244bfb95f3"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1852dde32..1a03adc9f 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -440,21 +440,32 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St end, %% I re-created it from cyrsasl ets magic, but I think it's wrong %% TODO: need to check before 18.09 release - lists:filter( - fun(<<"ANONYMOUS">>) -> - ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer); - (<<"DIGEST-MD5">>) -> Digest; - (<<"SCRAM-SHA-1">>) -> ShaAv; - (<<"SCRAM-SHA-1-PLUS">>) -> ShaAv andalso Encrypted; - (<<"SCRAM-SHA-256">>) -> Sha256Av; - (<<"SCRAM-SHA-256-PLUS">>) -> Sha256Av andalso Encrypted; - (<<"SCRAM-SHA-512">>) -> Sha512Av; - (<<"SCRAM-SHA-512-PLUS">>) -> Sha512Av andalso Encrypted; - (<<"PLAIN">>) -> true; - (<<"X-OAUTH2">>) -> [ejabberd_auth_anonymous] /= ejabberd_auth:auth_modules(LServer); - (<<"EXTERNAL">>) -> maps:get(tls_verify, State, false); - (_) -> false - end, Mechs -- Mechs1). + Mechs2 = lists:filter( + fun(<<"ANONYMOUS">>) -> + ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer); + (<<"DIGEST-MD5">>) -> Digest; + (<<"SCRAM-SHA-1">>) -> ShaAv; + (<<"SCRAM-SHA-1-PLUS">>) -> ShaAv andalso Encrypted; + (<<"SCRAM-SHA-256">>) -> Sha256Av; + (<<"SCRAM-SHA-256-PLUS">>) -> Sha256Av andalso Encrypted; + (<<"SCRAM-SHA-512">>) -> Sha512Av; + (<<"SCRAM-SHA-512-PLUS">>) -> Sha512Av andalso Encrypted; + (<<"PLAIN">>) -> true; + (<<"X-OAUTH2">>) -> [ejabberd_auth_anonymous] /= ejabberd_auth:auth_modules(LServer); + (<<"EXTERNAL">>) -> maps:get(tls_verify, State, false); + (_) -> false + end, Mechs -- Mechs1), + case ejabberd_option:auth_password_types_hidden_in_scram1() of + [] -> Mechs2; + List -> + Mechs3 = lists:foldl( + fun(plain, Acc) -> Acc -- [<<"PLAIN">>]; + (scram_sha1, Acc) -> Acc -- [<<"SCRAM-SHA-1">>, <<"SCRAM-SHA-1-PLUS">>]; + (scram_sha256, Acc) -> Acc -- [<<"SCRAM-SHA-256">>, <<"SCRAM-SHA-256-PLUS">>]; + (scram_sha512, Acc) -> Acc -- [<<"SCRAM-SHA-512">>, <<"SCRAM-SHA-512-PLUS">>] + end, Mechs2, List), + {Mechs3, Mechs2} + end. sasl_options(#{lserver := LServer}) -> case ejabberd_option:disable_sasl_scram_downgrade_protection(LServer) of diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 3a69da867..4361571f8 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -18,6 +18,7 @@ -export([auth_method/0, auth_method/1]). -export([auth_opts/0, auth_opts/1]). -export([auth_password_format/0, auth_password_format/1]). +-export([auth_password_types_hidden_in_scram1/0, auth_password_types_hidden_in_scram1/1]). -export([auth_scram_hash/0, auth_scram_hash/1]). -export([auth_stored_password_types/0, auth_stored_password_types/1]). -export([auth_use_cache/0, auth_use_cache/1]). @@ -263,6 +264,13 @@ auth_password_format() -> auth_password_format(Host) -> ejabberd_config:get_option({auth_password_format, Host}). +-spec auth_password_types_hidden_in_scram1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_scram1() -> + auth_password_types_hidden_in_scram1(global). +-spec auth_password_types_hidden_in_scram1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_scram1(Host) -> + ejabberd_config:get_option({auth_password_types_hidden_in_scram1, Host}). + -spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'. auth_scram_hash() -> auth_scram_hash(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 521687aa2..ee3acfc58 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -79,6 +79,8 @@ opt_type(auth_opts) -> end; opt_type(auth_stored_password_types) -> econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); +opt_type(auth_password_types_hidden_in_scram1) -> + econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); opt_type(auth_password_format) -> econf:enum([plain, scram]); opt_type(auth_scram_hash) -> @@ -564,6 +566,7 @@ options() -> {auth_password_format, plain}, {auth_scram_hash, sha}, {auth_stored_password_types, []}, + {auth_password_types_hidden_in_scram1, []}, {auth_external_user_exists_check, true}, {auth_use_cache, fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index dabfae06d..98f170c96 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -392,6 +392,17 @@ doc() -> "SASL PLAIN and SASL SCRAM-SHA-1/256/512(-PLUS). The SCRAM variant " "depends on the _`auth_scram_hash`_ option."), "", ?T("The default value is 'plain'."), ""]}}, + + {auth_password_types_hidden_in_scram1, + #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + note => "added in 25.07", + desc => + ?T("List of password types that should not be offered in SCRAM1 authenticatication. " + "Because SCRAM1, unlike SCRAM2, can't have list of available mechanisms tailored to " + "individual user, it's possible that offered mechanisms will not be compatible " + "with stored password, especially if new password type was added recently. " + "This option allows disabling offering some mechanisms in SASL1, to a time until new " + "password type will be available for all users.")}}, {auth_scram_hash, #{value => "sha | sha256 | sha512", desc => From 4a666167568282bcd757f0219a3694ed98cc3010 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 11 Jul 2025 12:06:40 +0200 Subject: [PATCH 1209/1302] mod_antispam: Mention usage of CONFIG_PATH predefined keyword --- src/mod_antispam.erl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mod_antispam.erl b/src/mod_antispam.erl index 9d8091268..b2e7c5937 100644 --- a/src/mod_antispam.erl +++ b/src/mod_antispam.erl @@ -226,6 +226,9 @@ mod_doc() -> "are classified as spam if sender is not in recipient's roster. " "This list of domains gets merged with the one retrieved " "by an RTBL host if any given. " + "Use an absolute path, or the '@CONFIG_PATH@' " + "https://docs.ejabberd.im/admin/configuration/file-format/#predefined-keywords[predefined keyword] " + "if the file is available in the configuration directory. " "The default value is 'none'.")}}, {spam_dump_file, #{value => ?T("false | true | Path"), @@ -248,6 +251,9 @@ mod_doc() -> "are classified as spam as well. " "Furthermore, the sender's JID will be cached, " "so that future traffic originating from that JID will also be classified as spam. " + "Use an absolute path, or the '@CONFIG_PATH@' " + "https://docs.ejabberd.im/admin/configuration/file-format/#predefined-keywords[predefined keyword] " + "if the file is available in the configuration directory. " "The default value is 'none'.")}}, {spam_urls_file, #{value => ?T("none | Path"), @@ -257,6 +263,9 @@ mod_doc() -> "Messages containing at least one of the listed URLs are classified as spam. " "Furthermore, the sender's JID will be cached, " "so that future traffic originating from that JID will be classified as spam as well. " + "Use an absolute path, or the '@CONFIG_PATH@' " + "https://docs.ejabberd.im/admin/configuration/file-format/#predefined-keywords[predefined keyword] " + "if the file is available in the configuration directory. " "The default value is 'none'.")}}, {whitelist_domains_file, #{value => ?T("none | Path"), @@ -266,6 +275,9 @@ mod_doc() -> "If either it is in 'spam_domains_file' or more realistically " "in a domain sent by a RTBL host (see option 'rtbl_services') " "then this domain will be ignored and stanzas from there won't be blocked. " + "Use an absolute path, or the '@CONFIG_PATH@' " + "https://docs.ejabberd.im/admin/configuration/file-format/#predefined-keywords[predefined keyword] " + "if the file is available in the configuration directory. " "The default value is 'none'.")}}], example => ["modules:", From 67c3df05b3012384d48f5270b1fd3e91f41bd9bb Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 19 Jun 2025 16:44:57 +0200 Subject: [PATCH 1210/1302] ext_mod: Add temporary workaround for zip including absolute path --- src/ext_mod.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index fe21c5fed..ee6ca2ecd 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -49,6 +49,7 @@ -include("logger.hrl"). -include("translate.hrl"). -include_lib("xmpp/include/xmpp.hrl"). +-include_lib("stdlib/include/zip.hrl"). -define(REPOS, "git@github.com:processone/ejabberd-contrib.git"). @@ -399,6 +400,23 @@ extract(tar, {ok, _, Body}, DestDir) -> extract(_, {error, Reason}, _) -> {error, Reason}; extract(zip, Zip, DestDir) -> + {ok, DirList} = zip:list_dir(Zip), + Offending = + lists:filter(fun (#zip_comment{}) -> + false; + (#zip_file{name = Filename}) -> + absolute == filename:pathtype(Filename) + end, + DirList), + case Offending of + [] -> + extract(zip_verified, Zip, DestDir); + _ -> + Filenames = [F#zip_file.name || F <- Offending], + ?ERROR_MSG("The zip file includes absolute file paths:~n ~p", [Filenames]), + {error, {zip_absolute_path, Filenames}} + end; +extract(zip_verified, Zip, DestDir) -> case zip:extract(Zip, [{cwd, DestDir}]) of {ok, _} -> ok; Error -> Error From c0de52c967ac9edfd9b06db09a9d52575e031605 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 11 Jul 2025 11:07:15 +0200 Subject: [PATCH 1211/1302] Update man page to 25.07 --- man/ejabberd.yml.5 | 275 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 260 insertions(+), 15 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index 2bb711cc7..b449b423a 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 04/16/2025 +.\" Date: 07/11/2025 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "04/16/2025" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "07/11/2025" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.04/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.07/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 25\&.04\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 25\&.07\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR .RS 4 @@ -461,6 +461,12 @@ option\&. .sp The default value is \fIplain\fR\&. .PP +\fBauth_password_types_hidden_in_scram1 🟤\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. List of password types that should not be offered in SCRAM1 authenticatication\&. Because SCRAM1, unlike SCRAM2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&. +.RE +.PP \fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR .RS 4 Hash algorithm that should be used to store password in @@ -951,6 +957,34 @@ List of one or more option\&. .RE .PP +\fBhosts_alias 🟤\fR: \fI{Alias: Host}\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Define aliases for existing vhosts managed by ejabberd\&. An alias may be a regexp expression\&. This option is only consulted by the +\fIejabberd_http\fR +listener\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +hosts: + \- domain\&.tld + \- example\&.org + +hosts_alias: + xmpp\&.domain\&.tld: domain\&.tld + jabber\&.domain\&.tld: domain\&.tld + mytest\&.net: example\&.org + "exa*": example\&.org +.fi +.if n \{\ +.RE +.\} +.RE +.PP \fBinclude_config_file\fR: \fI[Filename, \&.\&.\&.] | {Filename: Options}\fR .RS 4 Read and @@ -1246,7 +1280,7 @@ This option can be used to tune tick time parameter of .RS 4 Whether to use the \fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/25\&.04/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/25\&.07/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1513,6 +1547,30 @@ XMPP Core: section 7\&.7\&.2\&.2\&. The default value is \fIcloseold\fR\&. .RE .PP +\fBrest_proxy 🟤\fR: \fIHost\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Address of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_password 🟤\fR: \fIstring()\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_port 🟤\fR: \fI1\&.\&.65535\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Port of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP +\fBrest_proxy_username 🟤\fR: \fIstring()\fR +.RS 4 +\fINote\fR +about this option: added in 25\&.07\&. Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) +.RE +.PP \fBrouter_cache_life_time\fR: \fItimeout()\fR .RS 4 Same as @@ -2043,9 +2101,13 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 25\&.04\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 25\&.07\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp +def:ad\-hoc command +.sp +: Command that can be executed by an XMPP client using XEP\-0050\&. +.sp This module implements XEP\-0050: Ad\-Hoc Commands\&. It\(cqs an auxiliary module and is only needed by some of the other modules\&. .sp .it 1 an-trap @@ -2066,7 +2128,7 @@ Provide the Commands item in the Service Discovery\&. Default value: .sp \fINote\fR about this option: added in 25\&.03\&. .sp -Execute API Commands in a XMPP client using XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. +Execute (def:API commands) in a XMPP client using XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR (to execute the commands), and recommends \fImod_disco\fR (to discover the commands)\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -2078,7 +2140,7 @@ Execute API Commands in a XMPP client using XEP\-0050: Ad\-Hoc Commands\&. This .PP \fBdefault_version\fR: \fIinteger() | string()\fR .RS 4 -What API version to use\&. If setting an ejabberd version, it will use the latest API version that was available in that ejabberd version\&. For example, setting +What API version to use\&. If setting an ejabberd version, it will use the latest API version that was available in that (def:c2s) ejabberd version\&. For example, setting \fI"24\&.06"\fR in this option implies \fI2\fR\&. The default value is the latest version\&. @@ -2393,6 +2455,134 @@ Same as top\-level option, but applied to this module only\&. .RE .RE +.SS "mod_antispam 🟤" +.sp +\fINote\fR about this option: added in 25\&.07\&. +.sp +Filter spam messages and subscription requests received from remote servers based on Real\-Time Block Lists (RTBL), lists of known spammer JIDs and/or URLs mentioned in spam messages\&. Traffic classified as spam is rejected with an error (and an \fI[info]\fR message is logged) unless the sender is subscribed to the recipient\(cqs presence\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBaccess_spam\fR: \fIAccess\fR +.RS 4 +Access rule that controls what accounts may receive spam messages\&. If the rule returns +\fIallow\fR +for a given recipient, spam messages aren\(cqt rejected for that recipient\&. The default value is +\fInone\fR, which means that all recipients are subject to spam filtering verification\&. +.RE +.PP +\fBcache_size\fR: \fIpos_integer()\fR +.RS 4 +Maximum number of JIDs that will be cached due to sending spam URLs\&. If that limit is exceeded, the least recently used entries are removed from the cache\&. Setting this option to +\fI0\fR +disables the caching feature\&. Note that separate caches are used for each virtual host, and that the caches aren\(cqt distributed across cluster nodes\&. The default value is +\fI10000\fR\&. +.RE +.PP +\fBrtbl_services\fR: \fI[Service]\fR +.RS 4 +Query a RTBL service to get domains to block, as provided by +xmppbl\&.org\&. Please note right now this option only supports one service in that list\&. For blocking spam and abuse on MUC channels, please use +\fImod_muc_rtbl\fR +for now\&. If only the host is provided, the default node names will be assumed\&. If the node name is different than +\fIspam_source_domains\fR, you can setup the custom node name with the option +\fIspam_source_domains_node\fR\&. The default value is an empty list of services\&. +.sp +\fBExample\fR: +.sp +.if n \{\ +.RS 4 +.\} +.nf +rtbl_services: + \- pubsub\&.server1\&.localhost: + spam_source_domains_node: actual_custom_pubsub_node +.fi +.if n \{\ +.RE +.\} +.RE +.PP +\fBspam_domains_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of known spam domains, one domain per line\&. Messages and subscription requests sent from one of the listed domains are classified as spam if sender is not in recipient\(cqs roster\&. This list of domains gets merged with the one retrieved by an RTBL host if any given\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBspam_dump_file\fR: \fIfalse | true | Path\fR +.RS 4 +Path to the file to store blocked messages\&. Use an absolute path, or the +\fI@LOG_PATH@\fR +predefined keyword +to store logs in the same place that the other ejabberd log files\&. If set to +\fIfalse\fR, it doesn\(cqt dump stanzas, which is the default\&. If set to +\fItrue\fR, it stores in +\fI"@LOG_PATH@/spam_dump_@HOST@\&.log"\fR\&. +.RE +.PP +\fBspam_jids_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of known spammer JIDs, one JID per line\&. Messages and subscription requests sent from one of the listed JIDs are classified as spam\&. Messages containing at least one of the listed JIDsare classified as spam as well\&. Furthermore, the sender\(cqs JID will be cached, so that future traffic originating from that JID will also be classified as spam\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBspam_urls_file\fR: \fInone | Path\fR +.RS 4 +Path to a plain text file containing a list of URLs known to be mentioned in spam message bodies\&. Messages containing at least one of the listed URLs are classified as spam\&. Furthermore, the sender\(cqs JID will be cached, so that future traffic originating from that JID will be classified as spam as well\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.PP +\fBwhitelist_domains_file\fR: \fInone | Path\fR +.RS 4 +Path to a file containing a list of domains to whitelist from being blocked, one per line\&. If either it is in +\fIspam_domains_file\fR +or more realistically in a domain sent by a RTBL host (see option +\fIrtbl_services\fR) then this domain will be ignored and stanzas from there won\(cqt be blocked\&. Use an absolute path, or the +\fI@CONFIG_PATH@\fR +predefined keyword +if the file is available in the configuration directory\&. The default value is +\fInone\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_antispam: + rtbl_services: + \- xmppbl\&.org + spam_jids_file: "@CONFIG_PATH@/spam_jids\&.txt" + spam_dump_file: "@LOG_PATH@/spam/host\-@HOST@\&.log" +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_auth_fast" .sp \fINote\fR about this option: added in 24\&.12\&. @@ -2844,9 +3034,9 @@ modules: .RE .\} .RE -.SS "mod_conversejs" +.SS "mod_conversejs 🟤" .sp -\fINote\fR about this option: added in 21\&.12 and improved in 22\&.05\&. +\fINote\fR about this option: improved in 25\&.07\&. .sp This module serves a simple page for the Converse XMPP web browser client\&. .sp @@ -2856,6 +3046,8 @@ Make sure either \fImod_bosh\fR or \fIlisten\&.md#ejabberd_http_ws|ejabberd_http .sp When \fIconversejs_css\fR and \fIconversejs_script\fR are \fIauto\fR, by default they point to the public Converse client\&. .sp +This module is available since ejabberd 21\&.12\&. +.sp .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 @@ -3956,9 +4148,9 @@ When this option is disabled, for each individual subscriber a separate mucsub m \fIfalse\fR\&. .RE .RE -.SS "mod_matrix_gw" +.SS "mod_matrix_gw 🟤" .sp -\fINote\fR about this option: improved in 25\&.03\&. +\fINote\fR about this option: improved in 25\&.07\&. .sp Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&. .sp @@ -6427,6 +6619,59 @@ modules: .RE .\} .RE +.SS "mod_pubsub_serverinfo 🟤" +.sp +\fINote\fR about this option: added in 25\&.07\&. +.sp +This module adds support for XEP\-0485: PubSub Server Information to expose S2S information over the Pub/Sub service\&. +.sp +Active S2S connections are published to a local PubSub node\&. Currently the node name is hardcoded as \fI"serverinfo"\fR\&. +.sp +Connections that support this feature are exposed with their domain names, otherwise they are shown as anonymous nodes\&. At startup a list of well known public servers is fetched\&. Those are not shown as anonymous even if they don\(cqt support this feature\&. +.sp +Please note that the module only shows S2S connections established while the module is running\&. If you install the module at runtime, run \fIstop_s2s_connections\fR API or restart ejabberd to force S2S reconnections that the module will detect and publish\&. +.sp +This module depends on \fImod_pubsub\fR and \fImod_disco\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBpubsub_host\fR: \fIundefined | string()\fR +.RS 4 +Use this local PubSub host to advertise S2S connections\&. This must be a host local to this service handled by +\fImod_pubsub\fR\&. This option is only needed if your configuration has more than one host in mod_pubsub\(cqs +\fIhosts\fR +option\&. The default value is the first host defined in mod_pubsub +\fIhosts\fR +option\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +modules: + mod_pubsub_serverinfo: + pubsub_host: custom\&.pubsub\&.domain\&.local +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_push" .sp This module implements the XMPP server\(cqs part of the push notification solution specified in XEP\-0357: Push Notifications\&. It does not generate, for example, APNS or FCM notifications directly\&. Instead, it\(cqs designed to work with so\-called "app servers" operated by third\-party vendors of mobile apps\&. Those app servers will usually trigger notification delivery to the user\(cqs mobile device using platform\-dependent backend services such as FCM or APNS\&. @@ -8372,7 +8617,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 25\&.04\&. +This section describes listeners options of ejabberd 25\&.07\&. .sp TODO .SH "AUTHOR" @@ -8380,13 +8625,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 25\&.04\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 25\&.07\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.04/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.07/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From 64a210842e58f5f0d1e894f54e9fb1d7cc346bc8 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Fri, 11 Jul 2025 11:10:17 +0200 Subject: [PATCH 1212/1302] Use tagged dependencies --- mix.exs | 8 ++--- mix.lock | 32 +++++++++--------- rebar.config | 30 ++++++++--------- rebar.lock | 94 +++++++++++++++++++++++++--------------------------- 4 files changed, 80 insertions(+), 84 deletions(-) diff --git a/mix.exs b/mix.exs index f667a834a..d400ffd53 100644 --- a/mix.exs +++ b/mix.exs @@ -120,17 +120,17 @@ defmodule Ejabberd.MixProject do {:dialyxir, "~> 1.2", only: [:test], runtime: false}, {:eimp, "~> 1.0"}, {:ex_doc, "~> 0.31", only: [:edoc], runtime: false}, - {:fast_tls, git: "https://github.com/processone/fast_tls.git", ref: "d6ed64546e4f0d92d6a334827ea36b0c38307878", override: true}, - {:fast_xml, git: "https://github.com/processone/fast_xml", ref: "72e1c1b2eef84804399095704f2d729d5df8f02e", override: true}, + {:fast_tls, "~> 1.1.24"}, + {:fast_xml, "~> 1.1.56"}, {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "27a590789add30ff507a49ffd440eeeb28c96ce5", override: true}, + {:p1_acme, "~> 1.0.27"}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "9b028c110083e4d979d88c286873d0abf08fa532", override: true}, + {:xmpp, ">= 1.11.0"}, {:yconf, ">= 1.0.18"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 8df7f78f8..6872d4bc9 100644 --- a/mix.lock +++ b/mix.lock @@ -1,19 +1,19 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, - "cache_tab": {:hex, :cache_tab, "1.0.31", "e4097b50a6f373ab1e0a5f01bab0bef6626771a4cd6c93404ed6d54810e11fbc", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8582b60a4a09b247ef86355ba9e07fce9e11edc0345a775c9171f971c72b6351"}, + "cache_tab": {:hex, :cache_tab, "1.0.33", "e2542afb34f17ee3ca19d2b0f546a074922c2b99fb6b2acfb38160d7d0336ec3", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "4258009eb050b22aabe0c848e230bba58401a6895c58c2ff74dfb635e3c35900"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "eimp": {:hex, :eimp, "1.0.24", "853db317ba394d479d2f1181e0b0135a3cd3c2c7eb48ff6cfb035a28dd354b14", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7d61432eb8a45659c0be475f44e75eeb651743aa64a1de8adf785cdad81961ad"}, + "eimp": {:hex, :eimp, "1.0.26", "c0b05f32e35629c4d9bcfb832ff879a92b0f92b19844bc7835e0a45635f2899a", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d96d4e8572b9dfc40f271e47f0cb1d8849373bc98a21223268781765ed52044c"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "esip": {:hex, :esip, "1.0.57", "4b14e4832d08b9ffc10d855b5d10b3083232b1d53deb4c046679496ce85569c4", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.17", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "19c357e1817b1e04792ef359bf900400f3e6d0e5ade929fd72f88ea9b44af2ed"}, + "esip": {:hex, :esip, "1.0.58", "96bf0c07271f86f03f42778d4a1237099baec0714d00b0b815eb42d0007f73b4", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "e0f4204a5ede0fa7d00da3cc42f6440aa362bac7faf536f71ea29fa3f0fa7c75"}, "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, - "ezlib": {:hex, :ezlib, "1.0.13", "3c7f62862850a241159c10b218ecf580bce54d0890601b65144dacc2633be2b0", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "9ee62ab3f8ed55a0fd11a9569fcb8e458683f95575417272192b069f092abfbb"}, - "fast_tls": {:hex, :fast_tls, "1.1.22", "44356b256afad4399c2fc5059a3066669dafd8bd4e4e796c9c1cf8910ddd265e", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e65779aefb7ab15c4755230fef8077e687d20cc5a3984a5974f9f657e8e2485b"}, - "fast_xml": {:git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e", [ref: "72e1c1b2eef84804399095704f2d729d5df8f02e"]}, - "fast_yaml": {:hex, :fast_yaml, "1.0.37", "f71d472fbf787ccd161b914d1eb486116a0f4f2e835337a378fbd31b59d2e74b", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8de868721bf7e2172414f7d3148ede0f3c922b496455cd625dd5c4429515a769"}, + "ezlib": {:hex, :ezlib, "1.0.15", "d74f5df191784744726a5b1ae9062522c606334f11086363385eb3b772d91357", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd14ba6c12521af5cfe6923e73e3d545f4a0897dc66bfab5287fbb7ae3962eab"}, + "fast_tls": {:hex, :fast_tls, "1.1.24", "5492125689e3d84c101323a0bff3d3996b92a903832530fe4f0935ed30b1b7d1", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "fff88ada39fad10464567a160643f4529ef4aed49d156919f5d1f415b6cdbbb6"}, + "fast_xml": {:hex, :fast_xml, "1.1.57", "31efc0f9bceda92069704f7a25830407da5dc3dad1272b810d6f2e13e73cc11a", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "eec34e90adacafe467d5ddab635a014ded73b98b4061554b2d1972173d929c39"}, + "fast_yaml": {:hex, :fast_yaml, "1.0.39", "2e71168091949bab0e5f583b340a99072b4d22d93eb86624e7850a12b1517be4", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "24c7b9ab9e2b9269d64e45f4a2a1280966adb17d31e63365cfd3ee277fb0a78d"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jiffy": {:hex, :jiffy, "1.1.2", "a9b6c9a7ec268e7cf493d028f0a4c9144f59ccb878b1afe42841597800840a1b", [:rebar3], [], "hexpm", "bb61bc42a720bbd33cb09a410e48bb79a61012c74cb8b3e75f26d988485cf381"}, @@ -22,18 +22,18 @@ "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "mqtree": {:hex, :mqtree, "1.0.17", "82f54b8f2d22b4445db1d6cccb7fe9ead049d61410c29e32475f3ceb3ee62a89", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "5fe8b7cf8fbc4783d0fceb94654ac2bbf3242a58cd0397d249ded8ae021be2a3"}, + "mqtree": {:hex, :mqtree, "1.0.19", "d769c25f898810725fc7db0dbffe5f72098647048b1be2e6d772f1c2f31d8476", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c81065715c49a1882812f80a5ae2d842e80dd3f2d130530df35990248bf8ce3c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "27a590789add30ff507a49ffd440eeeb28c96ce5", [ref: "27a590789add30ff507a49ffd440eeeb28c96ce5"]}, + "p1_acme": {:hex, :p1_acme, "1.0.27", "43d565cf0e22da51033ae329e773ccad0f44c4cb9c7199d0863f522e570f2767", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "aa64b6a8856b1a229a128bea27631de2e1a2219835e3a833fa11137143a8d773"}, "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.32", "3f95d7e3413fc8f0be80abb4be1a0d7f67066a36905085cd5a423145598b0cb0", [:rebar3], [{:xmpp, "~> 1.10.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "268b01e8f4eb75c211a31495a25c2815c549aecce2f0df1a161c6e0a2cde061e"}, - "p1_utils": {:hex, :p1_utils, "1.0.26", "67b0c4ac9fa3ba3ef563b31aa111b0a004439a37fac85e027f1c3617e1c7ec6c", [:rebar3], [], "hexpm", "d0379e8c1156b98bd64f8129c1de022fcca4f2fdb7486ce73bf0ed2c3376b04c"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.34", "d36bd0d6c9765a47d075a87ad5e3fc3bfd153bdc4c07a1217b9979f33f73e9aa", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "cb0e32e086c9c35d0e3e966e3863d832737c7b4d2b5f147316a465c0b243ea7f"}, + "p1_utils": {:hex, :p1_utils, "1.0.28", "9a7088a98d788b4c4880fd3c82d0c135650db13f2e4ef7e10db179791bc94d59", [:rebar3], [], "hexpm", "c49bd44bc4a40ad996691af826dd7e0aa56d4d0cd730817190a1f84d1a7f0033"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, - "stringprep": {:hex, :stringprep, "1.0.31", "fa1688c156dd271722aa18c423a4163e710d2f4f475ad0bc220910df669b53af", [:rebar3], [{:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e9699c88e8db16b3a41f0e45ac6874a4da81a6e4854a77d76ede6d09b08e3530"}, - "stun": {:hex, :stun, "1.2.17", "c54614a592812ea125a2e6827aac5a438571b591616426ec1419ba9b48252f54", [:rebar3], [{:fast_tls, "1.1.22", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "6b318244c21e8524a9aae3ac9a05cd8234ee994c1c2c815de68d306086ad768d"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "74ed2d87222d3bd5e86f7c41daaa28fae59d4995", [ref: "74ed2d87222d3bd5e86f7c41daaa28fae59d4995"]}, - "yconf": {:hex, :yconf, "1.0.18", "e565edc8aabb8164c3bebc86969095d296ad315dcbb46af65dccbc6c71eae0f6", [:rebar3], [{:fast_yaml, "1.0.37", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "fa950ec6503f92d6417fb8cc1d982403f041697e8e1bbf4d4588fb919b9562ea"}, + "stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"}, + "stun": {:hex, :stun, "1.2.20", "62a149cea122a78a104b9e064a12d9e33105b26c23168ecf3aea6e0c26de0748", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "79e49f826a4f7d522c939ab633d935c79d7d6b229e4cb7e05f62f33b50177414"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, + "xmpp": {:hex, :xmpp, "1.11.0", "a3158c486c9b86a7090c361d876db622381f4312ede8c125d7a52ad390387932", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "34a191d6a3b74e8f0a42346f859e2cab5b3a2ae7e5c28f392e5cb56612e7ce85"}, + "yconf": {:hex, :yconf, "1.0.20", "f2b38db613fa826966e8d22bdc3e3ebae46919f2a27ab149a5a086c1d99d3bbd", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "f2b3d730756fc2e4afd1c0b0ab6efb99f0e448952d25dc15ed75ac1635bf8882"}, } diff --git a/rebar.config b/rebar.config index 16cde2cc4..1e297608b 100644 --- a/rebar.config +++ b/rebar.config @@ -26,8 +26,8 @@ {if_version_below, "24", {base64url, "~> 1.0", {git, "https://github.com/dvv/base64url", {tag, "1.0.1"}}} }}, - {cache_tab, "~> 1.0.31", {git, "https://github.com/processone/cache_tab", {tag, "1.0.31"}}}, - {eimp, "~> 1.0.24", {git, "https://github.com/processone/eimp", {tag, "1.0.24"}}}, + {cache_tab, "~> 1.0.33", {git, "https://github.com/processone/cache_tab", {tag, "1.0.33"}}}, + {eimp, "~> 1.0.26", {git, "https://github.com/processone/eimp", {tag, "1.0.26"}}}, {if_var_true, pam, {epam, "~> 1.0.14", {git, "https://github.com/processone/epam", {tag, "1.0.14"}}}}, {if_var_true, redis, @@ -41,12 +41,12 @@ {eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}} }}}, {if_var_true, sip, - {esip, "~> 1.0.57", {git, "https://github.com/processone/esip", {tag, "1.0.57"}}}}, + {esip, "~> 1.0.58", {git, "https://github.com/processone/esip", {tag, "1.0.58"}}}}, {if_var_true, zlib, - {ezlib, "~> 1.0.13", {git, "https://github.com/processone/ezlib", {tag, "1.0.13"}}}}, - {fast_tls, "~> 1.1.22", {git, "https://github.com/processone/fast_tls", "d6ed64546e4f0d92d6a334827ea36b0c38307878"}}, - {fast_xml, "~> 1.1.55", {git, "https://github.com/processone/fast_xml", "72e1c1b2eef84804399095704f2d729d5df8f02e"}}, - {fast_yaml, "~> 1.0.37", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.37"}}}, + {ezlib, "~> 1.0.15", {git, "https://github.com/processone/ezlib", {tag, "1.0.15"}}}}, + {fast_tls, "~> 1.1.24", {git, "https://github.com/processone/fast_tls", {tag, "1.1.24"}}}, + {fast_xml, "~> 1.1.57", {git, "https://github.com/processone/fast_xml", {tag, "1.1.57"}}}, + {fast_yaml, "~> 1.0.39", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.39"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {if_version_below, "27", {jiffy, "~> 1.1.1", {git, "https://github.com/davisp/jiffy", {tag, "1.1.1"}}} @@ -63,22 +63,22 @@ {luerl, "1.0.0", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}}, {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, - {mqtree, "~> 1.0.17", {git, "https://github.com/processone/mqtree", {tag, "1.0.17"}}}, - {p1_acme, "~> 1.0.25", {git, "https://github.com/processone/p1_acme", "27a590789add30ff507a49ffd440eeeb28c96ce5"}}, + {mqtree, "~> 1.0.19", {git, "https://github.com/processone/mqtree", {tag, "1.0.19"}}}, + {p1_acme, "~> 1.0.27", {git, "https://github.com/processone/p1_acme", {tag, "1.0.27"}}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.32", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.32"}}}}, - {p1_utils, "~> 1.0.26", {git, "https://github.com/processone/p1_utils", {tag, "1.0.26"}}}, + {p1_pgsql, "~> 1.1.34", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.34"}}}}, + {p1_utils, "~> 1.0.28", {git, "https://github.com/processone/p1_utils", {tag, "1.0.28"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, - {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, + {stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}}, {if_var_true, stun, - {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "b474d673938856a546265cd5db1139244bfb95f3"}}, - {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} + {stun, "~> 1.2.20", {git, "https://github.com/processone/stun", {tag, "1.2.20"}}}}, + {xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", {tag, "1.11.0"}}}, + {yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", {tag, "1.0.20"}}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index c42e49c40..761354510 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,90 +1,86 @@ {"1.2.0", [{<<"base64url">>,{pkg,<<"base64url">>,<<"1.0.1">>},1}, - {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.31">>},0}, - {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.24">>},0}, + {<<"cache_tab">>,{pkg,<<"cache_tab">>,<<"1.0.33">>},0}, + {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.26">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.57">>},0}, - {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.13">>},0}, - {<<"fast_tls">>, - {git,"https://github.com/processone/fast_tls", - {ref,"d6ed64546e4f0d92d6a334827ea36b0c38307878"}}, - 0}, - {<<"fast_xml">>, - {git,"https://github.com/processone/fast_xml", - {ref,"72e1c1b2eef84804399095704f2d729d5df8f02e"}}, - 0}, - {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.37">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.58">>},0}, + {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.15">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.24">>},0}, + {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.57">>},0}, + {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.39">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, - {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.18">>},0}, - {<<"p1_acme">>, - {git,"https://github.com/processone/p1_acme", - {ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}}, - 0}, + {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.27">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.33">>},0}, - {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.34">>},0}, + {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.28">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, - {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.32">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0}, + {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.20">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"b474d673938856a546265cd5db1139244bfb95f3"}}, - 0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.0">>},0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.20">>},0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, - {<<"cache_tab">>, <<"E4097B50A6F373AB1E0A5F01BAB0BEF6626771A4CD6C93404ED6D54810E11FBC">>}, - {<<"eimp">>, <<"853DB317BA394D479D2F1181E0B0135A3CD3C2C7EB48FF6CFB035A28DD354B14">>}, + {<<"cache_tab">>, <<"E2542AFB34F17EE3CA19D2B0F546A074922C2B99FB6B2ACFB38160D7D0336EC3">>}, + {<<"eimp">>, <<"C0B05F32E35629C4D9BCFB832FF879A92B0F92B19844BC7835E0A45635F2899A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, - {<<"esip">>, <<"4B14E4832D08B9FFC10D855B5D10B3083232B1D53DEB4C046679496CE85569C4">>}, - {<<"ezlib">>, <<"3C7F62862850A241159C10B218ECF580BCE54D0890601B65144DACC2633BE2B0">>}, - {<<"fast_yaml">>, <<"F71D472FBF787CCD161B914D1EB486116A0F4F2E835337A378FBD31B59D2E74B">>}, + {<<"esip">>, <<"96BF0C07271F86F03F42778D4A1237099BAEC0714D00B0B815EB42D0007F73B4">>}, + {<<"ezlib">>, <<"D74F5DF191784744726A5B1AE9062522C606334F11086363385EB3B772D91357">>}, + {<<"fast_tls">>, <<"5492125689E3D84C101323A0BFF3D3996B92A903832530FE4F0935ED30B1B7D1">>}, + {<<"fast_xml">>, <<"31EFC0F9BCEDA92069704F7A25830407DA5DC3DAD1272B810D6F2E13E73CC11A">>}, + {<<"fast_yaml">>, <<"2E71168091949BAB0E5F583B340A99072B4D22D93EB86624E7850A12B1517BE4">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, - {<<"mqtree">>, <<"B004E80BBEE5BC49E774B839F88162BDFF5CB654ABBDB79C8381AE4B13510A1B">>}, + {<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>}, + {<<"p1_acme">>, <<"43D565CF0E22DA51033AE329E773CCAD0F44C4CB9C7199D0863F522E570F2767">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"585F720C76B9BD27C5313DBB2C3CAD0CA19AB4493DE0B34A7496B51B65F5613A">>}, - {<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>}, + {<<"p1_pgsql">>, <<"D36BD0D6C9765A47D075A87AD5E3FC3BFD153BDC4C07A1217B9979F33F73E9AA">>}, + {<<"p1_utils">>, <<"9A7088A98D788B4C4880FD3C82D0C135650DB13F2E4EF7E10DB179791BC94D59">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, - {<<"stringprep">>, <<"63FD7FF5417A4A48DB6BB529C83D678361D34188367C1B13B3EAF39ACDEDB8E5">>}, - {<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>}, + {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, + {<<"stun">>, <<"62A149CEA122A78A104B9E064A12D9E33105B26C23168ECF3AEA6E0C26DE0748">>}, {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, - {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, + {<<"xmpp">>, <<"A3158C486C9B86A7090C361D876DB622381F4312EDE8C125D7A52AD390387932">>}, + {<<"yconf">>, <<"F2B38DB613FA826966E8D22BDC3E3EBAE46919F2A27AB149A5A086C1D99D3BBD">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, - {<<"cache_tab">>, <<"8582B60A4A09B247EF86355BA9E07FCE9E11EDC0345A775C9171F971C72B6351">>}, - {<<"eimp">>, <<"7D61432EB8A45659C0BE475F44E75EEB651743AA64A1DE8ADF785CDAD81961AD">>}, + {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, + {<<"eimp">>, <<"D96D4E8572B9DFC40F271E47F0CB1D8849373BC98A21223268781765ED52044C">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, - {<<"esip">>, <<"19C357E1817B1E04792EF359BF900400F3E6D0E5ADE929FD72F88EA9B44AF2ED">>}, - {<<"ezlib">>, <<"9EE62AB3F8ED55A0FD11A9569FCB8E458683F95575417272192B069F092ABFBB">>}, - {<<"fast_yaml">>, <<"8DE868721BF7E2172414F7D3148EDE0F3C922B496455CD625DD5C4429515A769">>}, + {<<"esip">>, <<"E0F4204A5EDE0FA7D00DA3CC42F6440AA362BAC7FAF536F71EA29FA3F0FA7C75">>}, + {<<"ezlib">>, <<"DD14BA6C12521AF5CFE6923E73E3D545F4A0897DC66BFAB5287FBB7AE3962EAB">>}, + {<<"fast_tls">>, <<"FFF88ADA39FAD10464567A160643F4529EF4AED49D156919F5D1F415B6CDBBB6">>}, + {<<"fast_xml">>, <<"EEC34E90ADACAFE467D5DDAB635A014DED73B98B4061554B2D1972173D929C39">>}, + {<<"fast_yaml">>, <<"24C7B9AB9E2B9269D64E45F4A2A1280966ADB17D31E63365CFD3EE277FB0A78D">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, - {<<"mqtree">>, <<"F73827CECF9A310670F7D7909FC88EAB40B45290FE48E5C7E45AB7235B29B919">>}, + {<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>}, + {<<"p1_acme">>, <<"AA64B6A8856B1A229A128BEA27631DE2E1A2219835E3A833FA11137143A8D773">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"3FB6A9617DB146419D420FFE7E94B9179A7CB5063D9C9450EF8B13FDAD2A709F">>}, - {<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>}, + {<<"p1_pgsql">>, <<"CB0E32E086C9C35D0E3E966E3863D832737C7B4D2B5F147316A465C0B243EA7F">>}, + {<<"p1_utils">>, <<"C49BD44BC4A40AD996691AF826DD7E0AA56D4D0CD730817190A1F84D1A7F0033">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, - {<<"stringprep">>, <<"6069CB059F5D18A312C42E5F374E9DE415DF68F7E3090C9C1A5505E2A8532710">>}, - {<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>}, + {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, + {<<"stun">>, <<"79E49F826A4F7D522C939AB633D935C79D7D6B229E4CB7E05F62F33B50177414">>}, {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, - {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} + {<<"xmpp">>, <<"34A191D6A3B74E8F0A42346F859E2CAB5B3A2AE7E5C28F392E5CB56612E7CE85">>}, + {<<"yconf">>, <<"F2B3D730756FC2E4AFD1C0B0AB6EFB99F0E448952D25DC15ED75AC1635BF8882">>}]} ]. From 9f6ff515ff1b3af82b72bcf010ea41e3dd042994 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 11 Jul 2025 12:46:21 +0200 Subject: [PATCH 1213/1302] Fix documentation for commands implemented in modules with several behaviours --- src/ejabberd_commands_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl index b4780fee9..79bfe6147 100644 --- a/src/ejabberd_commands_doc.erl +++ b/src/ejabberd_commands_doc.erl @@ -398,7 +398,7 @@ gen_doc(#ejabberd_commands{name=Name, tags=Tags, desc=Desc, longdesc=LongDesc, TagsText = ?RAW(string:join(["_`"++atom_to_list(Tag)++"`_" || Tag <- Tags], ", ")), IsDefinerMod = case Definer of unknown -> false; - _ -> lists:member(gen_mod, proplists:get_value(behaviour, Definer:module_info(attributes))) + _ -> lists:member(gen_mod, lists:flatten(proplists:get_all_values(behaviour, Definer:module_info(attributes)))) end, ModuleText = case IsDefinerMod of true -> From 3834a47a3913fd6ef5b082d6b1ce808be90c7726 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 11 Jul 2025 13:09:31 +0200 Subject: [PATCH 1214/1302] CHANGELOG.md: Update to 25.07 --- CHANGELOG.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d8b291b..36721ea39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,74 @@ +## Version 25.07 + +#### Security fix + +- `ext_mod`: Add temporary workaround for zip including absolute path + +#### Compilation + +- Raise the minimum Elixir tested version to 1.14.0 ([#4281](https://github.com/processone/ejabberd/issues/4281)) +- Raise Erlang/OTP minimum requirement to 25.0 ([#4281](https://github.com/processone/ejabberd/issues/4281)) +- `configure.ac`: Allow to specify minimal erlang version using `--with-min-erlang` +- `Makefile.in`: Add target `test-` +- `rebar3-format.sh`: Replace csplit with perl +- Container: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4 +- Installers: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4, libexpat 2.7.1, OpenSSL 3.5.1 + +#### Configuration and Tests + +- Add `rest_proxy*` options to configure proxy used by rest module +- `ejabberd_c2s`: Add `auth_password_types_hidden_in_scram1` option +- `ejabberd_http`: Remove unused `default_host` option and state element +- `ejabberd_http`: New option `hosts_alias` and function `resolve_host_alias/1` ([#4400](https://github.com/processone/ejabberd/issues/4400)) +- New predefined keywords: `CONFIG_PATH` and `LOG_PATH` +- Fix macro used in string options when defined in env var +- Use auxiliary function to get `$HOME`, use Mnesia directory when not set ([#4402](https://github.com/processone/ejabberd/issues/4402)) +- `ejabberd_config`: Better `lists:uniq` substitute +- Tests: update readme and compose to work with current sw versions +- Update Elvis to 4.1.1, fix some warnings and enable their tests + +#### Erlang/OTP 28 support + +- Add workaround in `p1_acme` for Jose 1.11.10 not supporting OTP 28 `ecPrivkeyVer1` ([#4393](https://github.com/processone/ejabberd/issues/4393)) +- Bump `fast_xml` and `xmpp` for improved Erlang/OTP 28 support +- Bump `xmpp` and `p1_acme` patched with Erlang/OTP 28 support +- Fix `make options` in Erlang/OTP 28 ([#4352](https://github.com/processone/ejabberd/issues/4352)) +- Fix crash in `rebar3 cover` with Erlang/OTP 28 ([#4353](https://github.com/processone/ejabberd/issues/4353)) +- Rebar/Rebar3: Update binaries to work with Erlang/OTP 25-28 ([#4354](https://github.com/processone/ejabberd/issues/4354)) +- CI and Runtime: Add Erlang/OTP 28 to the versions matrix + +#### SQL + +- Fix mnesia to sql exporter after changes to auth tables +- Update code for switching to new schema type to users table changes +- Add mssql specific implementation of `delete_old_mam_messages` +- Make `delete_old_mam_messages_batch` work with sqlite +- `ejabberd_sm_sql`: Use misc:encode_pid/1 +- `mysql.sql`: Fix typo in commit 7862c6a when creating users table +- `pg.sql`: Fix missing comma in postgres schema ([#4409](https://github.com/processone/ejabberd/issues/4409)) + +#### Core and Modules + +- `ejabberd_s2s_in`: Allow S2S connections to accept client certificates that have only server purpose ([#4392](https://github.com/processone/ejabberd/issues/4392)) +- `ext_mod`: Recommend to write README.md instead txt (processone/ejabberd-contrib#363) +- `ext_mod`: Support library path installed from Debian (processone/ejabberd-contrib#363) +- `ext_mod`: When upgrading module, clean also the compiled directories +- `gen_mod`: Add support to prepare module stopping before actually stopping any module +- `mod_antispam`: Imported from ejabberd-contrib and improved ([#4373](https://github.com/processone/ejabberd/issues/4373)) +- `mod_auth_fast`: Clear tokens on kick, change pass and unregister ([#4397](https://github.com/processone/ejabberd/issues/4397))([#4398](https://github.com/processone/ejabberd/issues/4398))([#4399](https://github.com/processone/ejabberd/issues/4399)) +- `mod_conversejs`: Add link in WebAdmin to local Converse if configured +- `mod_mam`: Present mam full text search in xep-431 compatible way +- `mod_mam_mnesia`: Handle objects that don't need conversion in `transform/0` +- `mod_matrix_gw`: Don't send empty messages in Matrix rooms ([#4385](https://github.com/processone/ejabberd/issues/4385)) +- `mod_matrix_gw`: Support older Matrix rooms versions starting from version 4 +- `mod_matrix_gw`: When encoding JSON, handle term that is key-value list ([#4379](https://github.com/processone/ejabberd/issues/4379)) +- `mod_matrix_gw_s2s`: Fix key validation in `check_signature` +- `mod_mix` and `mod_muc_rtbl`: Support list of IDs in `pubsub-items-retract` (processone/xmpp#100) +- `mod_pubsub_serverinfo`: Imported module from ejabberd-contrib ([#4408](https://github.com/processone/ejabberd/issues/4408)) +- `mod_register`: Normalize username when determining if user want to change pass +- `mod_register`: Strip query data when returning errors +- WebAdmin: New hooks `webadmin_menu_system` to add items to system menu + ## Version 25.04 #### Security fixes From a64aa9e28076674d24d070f10938bef0e2e3edb1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 11 Jul 2025 13:10:44 +0200 Subject: [PATCH 1215/1302] Set version to 25.07 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 12e3ae463..d31d254fc 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.04` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.07` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) AC_ARG_WITH(min-erlang, AS_HELP_STRING([--with-min-erlang=version],[set minimal required erlang version, default to OTP25]), From a17c2c166de11b8e19342df145bcbc6a18d6c868 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 16 Jul 2025 12:19:51 +0200 Subject: [PATCH 1216/1302] Fix issue with filtering duplicates in auth_mnesia:get_users() Previous version was only correct when data to process was sorted, which was not always the case. This add common implementation of lists:uniq in misc that works also on Users = mnesia:dirty_select(passwd, [{#passwd{us = '$1', _ = '_'}, [{'==', {element, 2, '$1'}, Server}], ['$1']}]), - {_, Res} = lists:foldl( - fun({U, S, _}, {{U2, S2}, _} = Acc) when U == U2 andalso S == S2 -> - Acc; - ({U, S, _}, {_, Res}) -> - {{U, S}, [{U, S} | Res]} - end, {{none, none}, []}, Users), - Res; + misc:lists_uniq([{U, S} || {U, S, _} <- Users]); get_users(Server, [{from, Start}, {to, End}]) when is_integer(Start) and is_integer(End) -> get_users(Server, [{limit, End - Start + 1}, {offset, Start}]); diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index c4c01eb79..4ad1afd3f 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -631,22 +631,7 @@ callback_modules(external) -> end end, beams(external)); callback_modules(all) -> - lists_uniq(callback_modules(local) ++ callback_modules(external)). - --ifdef(OTP_BELOW_25). -lists_uniq(List) -> - {Res, _} = lists:foldr( - fun(El, {Result, Existing} = Acc) -> - case maps:is_key(El, Existing) of - true -> Acc; - _ -> {[El | Result], Existing#{El => true}} - end - end, {[], #{}}, List), - Res. --else. -lists_uniq(List) -> - lists:uniq(List). --endif. + misc:lists_uniq(callback_modules(local) ++ callback_modules(external)). -spec validators(module(), [atom()], [any()]) -> econf:validators(). validators(Mod, Disallowed, DK) -> diff --git a/src/misc.erl b/src/misc.erl index 0ddc85996..55c1cec29 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -46,7 +46,8 @@ json_encode/1, json_decode/1, set_proc_label/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, - semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1]). + semver_to_xxyy/1, logical_processors/0, get_mucsub_event_type/1, + lists_uniq/1]). %% Deprecated functions -export([decode_base64/1, encode_base64/1]). @@ -819,3 +820,21 @@ set_proc_label(_Label) -> set_proc_label(Label) -> proc_lib:set_label(Label). -endif. + +-ifdef(OTP_BELOW_25). +-spec lists_uniq([term()]) -> [term()]. +lists_uniq(List) -> + lists_uniq_int(List, #{}). + +lists_uniq_int([El | Rest], Existing) -> + case maps:is_key(El, Existing) of + true -> lists_uniq_int(Rest, Existing); + _ -> [El | lists_uniq_int(Rest, Existing#{El => true})] + end; +lists_uniq_int([], _) -> + []. +-else. +-spec lists_uniq([term()]) -> [term()]. +lists_uniq(List) -> + lists:uniq(List). +-endif. From 8ce8f67c062dd32cf97ff7791f012940a9a110d7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 15 Jul 2025 10:36:24 +0200 Subject: [PATCH 1217/1302] misc: Add workaround for Json library not able to handle empty list --- src/misc.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/misc.erl b/src/misc.erl index 55c1cec29..32a85a8d1 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -146,6 +146,9 @@ json_encode({[{_Key, _Value} | _]} = Term) -> (Val, Encoder) -> json:encode_value(Val, Encoder) end)); +json_encode({[]}) -> + %% Jiffy was able to handle this case, but Json library does not + <<"{}">>; json_encode(Term) -> iolist_to_binary(json:encode(Term)). json_decode(Bin) -> From 850d0976601d46e50ff39b2456678e312fabde60 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 15 Jul 2025 12:18:11 +0200 Subject: [PATCH 1218/1302] ejabberd_auth: Handle case running check_password when account is banned --- src/ejabberd_auth.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index ecb102bb8..0c5d2fc69 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -237,6 +237,7 @@ check_password(User, AuthzId, Server, Password, Digest, DigestGen) -> case check_password_with_authmodule( User, AuthzId, Server, Password, Digest, DigestGen) of {true, _AuthModule} -> true; + {false, _ErrorAtom, _Reason} -> false; false -> false end. From 9d17a160b645677554056fa350df4aef9555edf6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 14 Jul 2025 13:45:35 +0200 Subject: [PATCH 1219/1302] mod_admin_extra: No need to change password in ban_account (#4415) When ban details are stored in private storage, ejabberd_auth reads them and prevents user login, so there's no need to modify the account password. --- src/mod_admin_extra.erl | 99 +++++++++++++---------------------------- 1 file changed, 32 insertions(+), 67 deletions(-) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index defe90cb4..d473091ae 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -268,14 +268,14 @@ get_commands_spec() -> #ejabberd_commands{name = ban_account, tags = [accounts], desc = "Ban an account", longdesc = "This command kicks the account sessions, " - "sets a random password, and stores ban details in the " - "account private storage. " + "stores ban details in the account private storage, " + "which blocks login to the account. " "This command requires _`mod_private`_ to be enabled. " "Check also _`get_ban_details`_ API " "and _`unban_account`_ API.", module = ?MODULE, function = ban_account_v2, version = 2, - note = "improved in 24.06", + note = "improved in 25.xx", args = [{user, binary}, {host, binary}, {reason, binary}], args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>], args_desc = ["User name to ban", "Server name", @@ -301,7 +301,7 @@ get_commands_spec() -> {"lastdate", "2024-04-22T08:39:12Z"}, {"lastreason", "Connection reset by peer"}]}, #ejabberd_commands{name = unban_account, tags = [accounts], - desc = "Revert the ban from an account: set back the old password", + desc = "Remove the ban from an account", longdesc = "Check _`ban_account`_ API.", module = ?MODULE, function = unban_account, version = 2, @@ -1189,27 +1189,29 @@ prepare_reason(Reason) when is_binary(Reason) -> %% Ban account v2 ban_account_v2(User, Host, ReasonText) -> - case gen_mod:is_loaded(Host, mod_private) of - false -> + IsPrivateEnabled = gen_mod:is_loaded(Host, mod_private), + Exists = ejabberd_auth:user_exists(User, Host), + IsBanned = is_banned(User, Host), + case {IsPrivateEnabled, Exists, IsBanned} of + {true, true, false} -> + ban_account_v2_b(User, Host, ReasonText); + {false, _, _} -> mod_private_is_required_but_disabled; - true -> - case is_banned(User, Host) of - true -> - account_was_already_banned; - false -> - ban_account_v2_b(User, Host, ReasonText) - end + {_, false, _} -> + account_does_not_exist; + {_, _, true} -> + account_was_already_banned; + {_, _, _} -> + other_error end. ban_account_v2_b(User, Host, ReasonText) -> Reason = prepare_reason(ReasonText), - Pass = ejabberd_auth:get_password_s(User, Host), Last = get_last(User, Host), BanDate = xmpp_util:encode_timestamp(erlang:timestamp()), Hash = get_hash_value(User, Host), - BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate, Hash), + BanPrivateXml = build_ban_xmlel(Reason, Last, BanDate, Hash), ok = private_set2(User, Host, BanPrivateXml), - ok = set_random_password_v2(User, Host), kick_sessions(User, Host, Reason), ok. @@ -1217,36 +1219,16 @@ get_hash_value(User, Host) -> Cookie = misc:atom_to_binary(erlang:get_cookie()), misc:term_to_base64(crypto:hash(sha256, <>)). -set_random_password_v2(User, Server) -> - NewPass = p1_rand:get_string(), - ok = ejabberd_auth:set_password(User, Server, NewPass). - -build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate, Hash) -> - PassEls = build_pass_els(Pass), +build_ban_xmlel(Reason, {LastDate, LastReason}, BanDate, Hash) -> #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}], children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]}, - #xmlel{name = <<"password">>, attrs = [], children = PassEls}, #xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]}, #xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]}, #xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]}, #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, Hash}]} ]}. -build_pass_els(Pass) when is_binary(Pass) -> - [{xmlcdata, Pass}]; -build_pass_els(#scram{storedkey = StoredKey, - serverkey = ServerKey, - salt = Salt, - hash = Hash, - iterationcount = IterationCount}) -> - [#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]}, - #xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]}, - #xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]}, - #xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]}, - #xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]} - ]. - %% %% Get ban details @@ -1286,43 +1268,26 @@ is_banned(User, Host) -> %% Unban account unban_account(User, Host) -> - case gen_mod:is_loaded(Host, mod_private) of - false -> + IsPrivateEnabled = gen_mod:is_loaded(Host, mod_private), + Exists = ejabberd_auth:user_exists(User, Host), + IsBanned = is_banned(User, Host), + case {IsPrivateEnabled, Exists, IsBanned} of + {true, true, true} -> + unban_account2(User, Host); + {false, _, _} -> mod_private_is_required_but_disabled; - true -> - case is_banned(User, Host) of - false -> - account_was_not_banned; - true -> - unban_account2(User, Host) - end + {_, false, _} -> + account_does_not_exist; + {_, _, false} -> + account_was_not_banned; + {_, _, _} -> + other_error end. unban_account2(User, Host) -> - OldPass = get_oldpass(User, Host), - ok = ejabberd_auth:set_password(User, Host, OldPass), UnBanPrivateXml = build_unban_xmlel(), private_set2(User, Host, UnBanPrivateXml). -get_oldpass(User, Host) -> - [El] = private_get2(User, Host, <<"banned">>, <<"jabber:ejabberd:banned">>), - Pass = fxml:get_subtag(El, <<"password">>), - get_pass(Pass). - -get_pass(#xmlel{children = [{xmlcdata, Pass}]}) -> - Pass; -get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) -> - StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>), - ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>), - Salt = fxml:get_subtag_cdata(Pass, <<"salt">>), - Hash = fxml:get_subtag_cdata(Pass, <<"hash">>), - IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>), - #scram{storedkey = StoredKey, - serverkey = ServerKey, - salt = Salt, - hash = binary_to_existing_atom(Hash, latin1), - iterationcount = binary_to_integer(IterationCount)}. - build_unban_xmlel() -> #xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"jabber:ejabberd:banned">>}]}. From 45a6aed57f9abef0fbedbe913c0cf26b7ffdbce8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Jul 2025 18:08:23 +0200 Subject: [PATCH 1220/1302] mod_admin_extra: Run sm_kick_user event when kicking account (#4415) This is important when running the ban_account command and mod_auth_fast is enabled, as the client may store auth tokens that bypass the banning stored in private storage and enforced by ejabberd_auth. --- src/mod_admin_extra.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index d473091ae..fc2cf8c51 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1158,6 +1158,7 @@ ban_account(User, Host, ReasonText) -> ok. kick_sessions(User, Server, Reason) -> + ejabberd_hooks:run(sm_kick_user, Server, [User, Server]), lists:map( fun(Resource) -> kick_this_session(User, Server, Resource, Reason) From 9b6f0aeb3c1a1419c14dbbdafe4df409fedb782c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 21 Jul 2025 13:38:11 +0200 Subject: [PATCH 1221/1302] make-binaries: Disable Linux-PAM's logind support Make sure Linux-PAM doesn't attempt to include logind support. This avoids a build failure in case the host system has systemd's development headers installed. --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 tools/make-binaries diff --git a/tools/make-binaries b/tools/make-binaries old mode 100755 new mode 100644 index 21d7b605a..94635189a --- a/tools/make-binaries +++ b/tools/make-binaries @@ -629,8 +629,8 @@ build_deps() info "Building Linux-PAM $pam_vsn for $arch ..." cd "$target_src_dir/$pam_dir" $configure --prefix="$prefix" --includedir="$prefix/include/security" \ - --enable-static --disable-shared --disable-doc --disable-examples \ - --enable-db=no \ + --enable-static --disable-shared --disable-logind --disable-doc \ + --disable-examples --enable-db=no \ CFLAGS="$CFLAGS -O3 -fPIC -Wno-error=implicit-function-declaration" make make install From 73a8fbdfb5ffbbe219ab270875f98999f3fb146c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 21 Jul 2025 13:42:07 +0200 Subject: [PATCH 1222/1302] make-binaries: Re-add executable bit --- tools/make-binaries | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/make-binaries diff --git a/tools/make-binaries b/tools/make-binaries old mode 100644 new mode 100755 From 214b76f7632d2e88da6a74a7dd23e032cf17d6c1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 16 Jul 2025 20:35:25 +0200 Subject: [PATCH 1223/1302] ejabberd_doc: Document commands tags for modules --- src/ejabberd_doc.erl | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index ce030c221..a54c460f0 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -24,6 +24,7 @@ %% API -export([man/0, man/1, have_a2x/0]). +-include("ejabberd_commands.hrl"). -include("translate.hrl"). %%%=================================================================== @@ -46,7 +47,8 @@ man(Lang) -> DocOpts = maps:get(opts, Map, []), Example = maps:get(example, Map, []), Note = maps:get(note, Map, []), - {[{M, Descr, DocOpts, #{example => Example, note => Note}}|Mods], SubMods}; + Apitags = get_module_apitags(M), + {[{M, Descr, DocOpts, #{example => Example, note => Note, apitags => Apitags}}|Mods], SubMods}; #{opts := DocOpts} -> {ParentMod, Backend} = strip_backend_suffix(M), {Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)}; @@ -113,7 +115,8 @@ man(Lang) -> format_versions(Lang, Example) ++ [io_lib:nl()] ++ tr_multi(Lang, Descr) ++ [io_lib:nl()] ++ opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++ - format_example(0, Lang, Example) + format_example(0, Lang, Example) ++ [io_lib:nl()] ++ + format_apitags(Lang, Example) end, lists:keysort(1, ModDoc1)), ListenOptions = [io_lib:nl(), @@ -190,6 +193,34 @@ format_versions(_Lang, #{note := Note}) when Note /= [] -> format_versions(_, _) -> []. +%% @format-begin +get_module_apitags(M) -> + AllCommands = ejabberd_commands:get_commands_definition(), + Tags = [C#ejabberd_commands.tags || C <- AllCommands, C#ejabberd_commands.module == M], + TagsClean = + lists:sort( + misc:lists_uniq( + lists:flatten(Tags))), + TagsStrings = [atom_to_list(C) || C <- TagsClean], + TagFiltering = + fun ("internal") -> + false; + ([$v | Rest]) -> + {error, no_integer} == string:to_integer(Rest); + (_) -> + true + end, + TagsFiltered = lists:filter(TagFiltering, TagsStrings), + TagsUrls = + [["_`../../developer/ejabberd-api/admin-tags.md#", C, "|", C, "`_"] || C <- TagsFiltered], + lists:join(", ", TagsUrls). + +format_apitags(_Lang, #{apitags := TagsString}) when TagsString /= "" -> + ["**API Tags:** ", TagsString]; +format_apitags(_, _) -> + []. +%% @format-end + format_desc(Lang, #{desc := Desc}) -> tr_multi(Lang, Desc). From d269e32c3a49c5c756f1f19fbd93e050c363cddf Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 18 Jul 2025 12:58:25 +0200 Subject: [PATCH 1224/1302] ejabberd_config: Improve warning message about unsupported ram_db type --- src/ejabberd_config.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 4ad1afd3f..8ec4fccf4 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -264,30 +264,31 @@ version() -> -spec default_db(binary() | global, module()) -> atom(). default_db(Host, Module) -> - default_db(default_db, Host, Module, mnesia). + default_db(default_db, db_type, Host, Module, mnesia). -spec default_db(binary() | global, module(), atom()) -> atom(). default_db(Host, Module, Default) -> - default_db(default_db, Host, Module, Default). + default_db(default_db, db_type, Host, Module, Default). -spec default_ram_db(binary() | global, module()) -> atom(). default_ram_db(Host, Module) -> - default_db(default_ram_db, Host, Module, mnesia). + default_db(default_ram_db, ram_db_type, Host, Module, mnesia). -spec default_ram_db(binary() | global, module(), atom()) -> atom(). default_ram_db(Host, Module, Default) -> - default_db(default_ram_db, Host, Module, Default). + default_db(default_ram_db, ram_db_type, Host, Module, Default). --spec default_db(default_db | default_ram_db, binary() | global, module(), atom()) -> atom(). -default_db(Opt, Host, Mod, Default) -> +-spec default_db(default_db | default_ram_db, db_type | ram_db_type, binary() | global, module(), atom()) -> atom(). +default_db(Opt, ModOpt, Host, Mod, Default) -> Type = get_option({Opt, Host}), DBMod = list_to_atom(atom_to_list(Mod) ++ "_" ++ atom_to_list(Type)), case code:ensure_loaded(DBMod) of {module, _} -> Type; {error, _} -> ?WARNING_MSG("Module ~ts doesn't support database '~ts' " - "defined in option '~ts', using " - "'~ts' as fallback", [Mod, Type, Opt, Default]), + "defined in toplevel option '~ts': will use the value " + "set in ~ts option '~ts', or '~ts' as fallback", + [Mod, Type, Opt, Mod, ModOpt, Default]), Default end. From 355eb5dfdec4451c81ab6b479e7eff6acfdab7a5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 18 Jul 2025 15:52:07 +0200 Subject: [PATCH 1225/1302] Improve documentation of toplevel options default_db and default_ram_db --- src/ejabberd_options_doc.erl | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 98f170c96..a8d5962f5 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -109,14 +109,20 @@ doc() -> desc => ?T("_`database.md#default-database|Default database`_ " "to store persistent data in ejabberd. " - "Modules and other components (e.g. authentication) " - "may have its own value. The default value is 'mnesia'.")}}, + "Some components can be configured with specific toplevel options " + "like _`oauth_db_type`_. " + "Many modules can be configured with specific module options, " + "usually named `db_type`. " + "The default value is 'mnesia'.")}}, {default_ram_db, #{value => "mnesia | redis | sql", desc => ?T("Default volatile (in-memory) storage for ejabberd. " - "Modules and other components (e.g. session management) " - "may have its own value. The default value is 'mnesia'.")}}, + "Some components can be configured with specific toplevel options " + "like _`router_db_type`_ and _`sm_db_type`_. " + "Some modules can be configured with specific module options, " + "usually named `ram_db_type`. " + "The default value is 'mnesia'.")}}, {queue_type, #{value => "ram | file", desc => From b1c3baa7bd283fe4616d4a9862fecd4f01e5bcea Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Jul 2025 16:51:46 +0200 Subject: [PATCH 1226/1302] Bump p1_acme to fix 'AttributePKCS-10' and OTP 28 (processone/p1_acme#4) --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index d400ffd53..7fe59ec88 100644 --- a/mix.exs +++ b/mix.exs @@ -125,7 +125,7 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, "~> 1.0.27"}, + {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "edff1396d44d7bde6b2b44ca5b0dd91450319a3e", override: true}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index 6872d4bc9..60edccdc5 100644 --- a/mix.lock +++ b/mix.lock @@ -24,7 +24,7 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mqtree": {:hex, :mqtree, "1.0.19", "d769c25f898810725fc7db0dbffe5f72098647048b1be2e6d772f1c2f31d8476", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c81065715c49a1882812f80a5ae2d842e80dd3f2d130530df35990248bf8ce3c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "p1_acme": {:hex, :p1_acme, "1.0.27", "43d565cf0e22da51033ae329e773ccad0f44c4cb9c7199d0863f522e570f2767", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "aa64b6a8856b1a229a128bea27631de2e1a2219835e3a833fa11137143a8d773"}, + "p1_acme": {:git, "https://github.com/processone/p1_acme", "edff1396d44d7bde6b2b44ca5b0dd91450319a3e", [ref: "edff1396d44d7bde6b2b44ca5b0dd91450319a3e"]}, "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.34", "d36bd0d6c9765a47d075a87ad5e3fc3bfd153bdc4c07a1217b9979f33f73e9aa", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "cb0e32e086c9c35d0e3e966e3863d832737c7b4d2b5f147316a465c0b243ea7f"}, diff --git a/rebar.config b/rebar.config index 1e297608b..cfe938a9d 100644 --- a/rebar.config +++ b/rebar.config @@ -64,7 +64,7 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.19", {git, "https://github.com/processone/mqtree", {tag, "1.0.19"}}}, - {p1_acme, "~> 1.0.27", {git, "https://github.com/processone/p1_acme", {tag, "1.0.27"}}}, + {p1_acme, "~> 1.0.27", {git, "https://github.com/processone/p1_acme", "edff1396d44d7bde6b2b44ca5b0dd91450319a3e"}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, diff --git a/rebar.lock b/rebar.lock index 761354510..342bbe80f 100644 --- a/rebar.lock +++ b/rebar.lock @@ -14,7 +14,10 @@ {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0}, - {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.27">>},0}, + {<<"p1_acme">>, + {git,"https://github.com/processone/p1_acme", + {ref,"edff1396d44d7bde6b2b44ca5b0dd91450319a3e"}}, + 0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.34">>},0}, @@ -43,7 +46,6 @@ {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>}, - {<<"p1_acme">>, <<"43D565CF0E22DA51033AE329E773CCAD0F44C4CB9C7199D0863F522E570F2767">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_pgsql">>, <<"D36BD0D6C9765A47D075A87AD5E3FC3BFD153BDC4C07A1217B9979F33F73E9AA">>}, @@ -71,7 +73,6 @@ {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>}, - {<<"p1_acme">>, <<"AA64B6A8856B1A229A128BEA27631DE2E1A2219835E3A833FA11137143A8D773">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_pgsql">>, <<"CB0E32E086C9C35D0E3E966E3863D832737C7B4D2B5F147316A465C0B243EA7F">>}, From 99b75396ad4d14b4e12a033ae245369b3e2c4dcd Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 22 Jul 2025 10:06:15 +0200 Subject: [PATCH 1227/1302] ejabberd_listener: Log error when cannot set definitive unix socket (#4422) --- src/ejabberd_listener.erl | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 05e1c1661..8a69e9124 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -113,11 +113,12 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> {Port, SockOpts} end, ExtraOpts2 = lists:keydelete(send_timeout, 1, ExtraOpts), - case gen_udp:open(Port2, [binary, + case {gen_udp:open(Port2, [binary, {active, false}, {reuseaddr, true} | - ExtraOpts2]) of - {ok, Socket} -> + ExtraOpts2]), + set_definitive_udsocket(Port, Opts)} of + {{ok, Socket}, ok} -> set_definitive_udsocket(Port, Opts), misc:set_proc_label({?MODULE, udp, Port}), case inet:sockname(Socket) of @@ -143,14 +144,17 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end; - {error, Reason} = Err -> + {{error, Reason}, _} = Err -> + report_socket_error(Reason, EndPoint, Module), + proc_lib:init_ack(Err); + {_, {error, Reason}} = Err -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end; init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> - case listen_tcp(Port, SockOpts) of - {ok, ListenSocket} -> - set_definitive_udsocket(Port, Opts), + case {listen_tcp(Port, SockOpts), + set_definitive_udsocket(Port, Opts)} of + {{ok, ListenSocket}, ok} -> case inet:sockname(ListenSocket) of {ok, {Addr, Port1}} -> proc_lib:init_ack({ok, self()}), @@ -178,7 +182,10 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end; - {error, Reason} = Err -> + {{error, Reason}, _} = Err -> + report_socket_error(Reason, EndPoint, Module), + proc_lib:init_ack(Err); + {_, {error, Reason}} = Err -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end. @@ -216,7 +223,7 @@ listen_tcp(Port, SockOpts) -> setup_provisional_udsocket_dir(DefinitivePath) -> ProvisionalPath = get_provisional_udsocket_path(DefinitivePath), - ?DEBUG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", + ?INFO_MSG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", [ProvisionalPath, DefinitivePath]), ProvisionalPath. @@ -233,6 +240,8 @@ get_definitive_udsocket_path(ProvisionalPath) -> {term, Path} = misc:base64_to_term(PathBase64), relative_socket_to_mnesia(Path). +-spec set_definitive_udsocket(binary(), opts()) -> ok | {error, file:posix() | badarg}. + set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> Prov = get_provisional_udsocket_path(Path), Usd = maps:get(unix_socket, Opts), From f7002c31f00c0a3666afcf2051ea5d1804f987a5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 22 Jul 2025 13:30:14 +0200 Subject: [PATCH 1228/1302] Fix some typos in previous commit (#4422) --- src/ejabberd_listener.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 8a69e9124..aad6ecd86 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -119,7 +119,6 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> ExtraOpts2]), set_definitive_udsocket(Port, Opts)} of {{ok, Socket}, ok} -> - set_definitive_udsocket(Port, Opts), misc:set_proc_label({?MODULE, udp, Port}), case inet:sockname(Socket) of {ok, {Addr, Port1}} -> @@ -144,10 +143,10 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end; - {{error, Reason}, _} = Err -> + {{error, Reason} = Err, _} -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err); - {_, {error, Reason}} = Err -> + {_, {error, Reason} = Err} -> report_socket_error(Reason, EndPoint, Module), proc_lib:init_ack(Err) end; @@ -240,7 +239,7 @@ get_definitive_udsocket_path(ProvisionalPath) -> {term, Path} = misc:base64_to_term(PathBase64), relative_socket_to_mnesia(Path). --spec set_definitive_udsocket(binary(), opts()) -> ok | {error, file:posix() | badarg}. +-spec set_definitive_udsocket(integer() | binary(), opts()) -> ok | {error, file:posix() | badarg}. set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> Prov = get_provisional_udsocket_path(Path), @@ -280,7 +279,7 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> ok end, file:rename(Prov, FinalPath); -set_definitive_udsocket(_Port, _Opts) -> +set_definitive_udsocket(Port, _Opts) when is_integer(Port) -> ok. relative_socket_to_mnesia(Path1) -> From 6214e0385d696880cdb19404d085b15889161b2f Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 23 Jul 2025 19:40:18 +0200 Subject: [PATCH 1229/1302] Report db failures from mod_muc:restore_room --- src/mod_muc.erl | 13 ++++++------- src/mod_muc_admin.erl | 34 ++++++++++++++++++++++++++++++---- src/mod_muc_sql.erl | 6 ++++-- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 7a8a9f9ea..0ca31cb80 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -97,7 +97,7 @@ -callback import(binary(), binary(), [binary()]) -> ok. -callback store_room(binary(), binary(), binary(), list(), list()|undefined) -> {atomic, any()}. -callback store_changes(binary(), binary(), binary(), list()) -> {atomic, any()}. --callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error. +-callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error | {error, atom()}. -callback forget_room(binary(), binary(), binary()) -> {atomic, any()}. -callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean(). -callback get_rooms(binary(), binary()) -> [#muc_room{}]. @@ -591,20 +591,17 @@ extract_password(#iq{} = IQ) -> false end. --spec unhibernate_room(binary(), binary(), binary()) -> {ok, pid()} | error. +-spec unhibernate_room(binary(), binary(), binary()) -> {ok, pid()} | {error, notfound | db_failure | term()}. unhibernate_room(ServerHost, Host, Room) -> unhibernate_room(ServerHost, Host, Room, true). --spec unhibernate_room(binary(), binary(), binary(), boolean()) -> {ok, pid()} | error. +-spec unhibernate_room(binary(), binary(), binary(), boolean()) -> {ok, pid()} | {error, notfound | db_failure | term()}. unhibernate_room(ServerHost, Host, Room, ResetHibernationTime) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), case RMod:find_online_room(ServerHost, Room, Host) of error -> Proc = procname(ServerHost, {Room, Host}), - case ?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime}, 20000) of - {ok, _} = R -> R; - _ -> error - end; + ?GEN_SERVER:call(Proc, {unhibernate, Room, Host, ResetHibernationTime}, 20000); {ok, _} = R2 -> R2 end. @@ -888,6 +885,8 @@ load_room(RMod, Host, ServerHost, Room, ResetHibernationTime) -> case restore_room(ServerHost, Host, Room) of error -> {error, notfound}; + {error, _} = Err -> + Err; Opts0 -> Mod = gen_mod:db_mod(ServerHost, mod_muc), case proplists:get_bool(persistent, Opts0) of diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 77b46b275..9a8ab60b1 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1289,6 +1289,8 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> {error, _} -> throw({error, "Unable to start room"}) end; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "Room already exists"}) end. @@ -1307,6 +1309,8 @@ destroy_room(Name1, Service1) -> case get_room_pid_validate(Name1, Service1, <<"service">>) of {room_not_found, _, _} -> throw({error, "Room doesn't exists"}); + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); {Pid, _, _} -> mod_muc_room:destroy(Pid), ok @@ -1698,6 +1702,8 @@ change_room_option(Name, Service, OptionString, ValueString) -> case get_room_pid_validate(Name, Service, <<"service">>) of {room_not_found, _, _} -> throw({error, "Room not found"}); + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); {Pid, _, _} -> {Option, Value} = format_room_option(OptionString, ValueString), change_room_option(Pid, Option, Value) @@ -1823,25 +1829,29 @@ parse_nodes(_, _) -> throw({error, "Invalid 'subscribers' - unknown node name used"}). -spec get_room_pid_validate(binary(), binary(), binary()) -> - {pid() | room_not_found, binary(), binary()}. + {pid() | room_not_found | db_failure, binary(), binary()}. get_room_pid_validate(Name, Service, ServiceArg) -> Name2 = validate_room(Name), {ServerHost, Service2} = validate_muc2(Service, ServiceArg), case mod_muc:unhibernate_room(ServerHost, Service2, Name2) of - error -> + {error, notfound} -> {room_not_found, Name2, Service2}; + {error, db_failure} -> + {db_failure, Name2, Service2}; {ok, Pid} -> {Pid, Name2, Service2} end. %% @doc Get the Pid of an existing MUC room, or 'room_not_found'. --spec get_room_pid(binary(), binary()) -> pid() | room_not_found | invalid_service | unknown_service. +-spec get_room_pid(binary(), binary()) -> pid() | room_not_found | db_failure | invalid_service | unknown_service. get_room_pid(Name, Service) -> try get_room_serverhost(Service) of ServerHost -> case mod_muc:unhibernate_room(ServerHost, Service, Name) of - error -> + {error, notfound} -> room_not_found; + {error, db_failure} -> + db_failure; {ok, Pid} -> Pid end @@ -1954,6 +1964,8 @@ get_room_affiliations(Name, Service) -> ({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)-> {Uname, Domain, Aff, <<>>} end, Affiliations); + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist."}) end. @@ -1975,6 +1987,8 @@ get_room_affiliations_v3(Name, Service) -> Jid = makeencode(Uname, Domain), {Jid, Aff, <<>>} end, Affiliations); + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist."}) end. @@ -1993,6 +2007,8 @@ get_room_history(Name, Service) -> _ -> throw({error, "Unable to fetch room state."}) end; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist."}) end. @@ -2012,6 +2028,8 @@ get_room_affiliation(Name, Service, JID) -> {ok, StateData} = mod_muc_room:get_state(Pid), UserJID = jid:decode(JID), mod_muc_room:get_affiliation(UserJID, StateData); + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist."}) end. @@ -2052,6 +2070,8 @@ set_room_affiliation(Name, Service, JID, AffiliationString) -> {error, _} -> throw({error, "Unable to perform change"}) end; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "Room doesn't exists"}) end. @@ -2084,6 +2104,8 @@ subscribe_room(User, Nick, Room, NodeList) -> {error, Reason} -> throw({error, binary_to_list(Reason)}) end; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist"}) end @@ -2129,6 +2151,8 @@ unsubscribe_room(User, Room) -> {error, Reason} -> throw({error, binary_to_list(Reason)}) end; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist"}) end @@ -2146,6 +2170,8 @@ get_subscribers(Name, Host) -> {Pid, _, _} when is_pid(Pid) -> {ok, JIDList} = mod_muc_room:get_subscribers(Pid), [jid:encode(jid:remove_resource(J)) || J <- JIDList]; + {db_failure, _Name, _Host} -> + throw({error, "Database error"}); _ -> throw({error, "The room does not exist"}) end. diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index b6cff55d1..f99325f87 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -220,10 +220,12 @@ restore_room(LServer, Host, Name) -> Opts2 = lists:keystore(subscribers, 1, OptsD, {subscribers, SubData}), mod_muc:opts_to_binary(Opts2); _ -> - error + {error, db_failure} end; + {selected, _} -> + error; _ -> - error + {error, db_failure} end. forget_room(LServer, Host, Name) -> From 1a9b147baf76ce6f7f216c49bd74312f24b41f5d Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 23 Jul 2025 20:20:51 +0200 Subject: [PATCH 1230/1302] Report db failures in mod_muc_mnesia:restore_room --- src/mod_muc_mnesia.erl | 4 +++- src/mod_muc_sql.erl | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index 9e16a56c5..65c37a7ab 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -76,9 +76,11 @@ store_room(_LServer, Host, Name, Opts, _) -> mnesia:transaction(F). restore_room(_LServer, Host, Name) -> - case catch mnesia:dirty_read(muc_room, {Name, Host}) of + try mnesia:dirty_read(muc_room, {Name, Host}) of [#muc_room{opts = Opts}] -> Opts; _ -> error + catch + _:_ -> {error, db_failure} end. forget_room(_LServer, Host, Name) -> diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index f99325f87..31c8703c1 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -220,7 +220,7 @@ restore_room(LServer, Host, Name) -> Opts2 = lists:keystore(subscribers, 1, OptsD, {subscribers, SubData}), mod_muc:opts_to_binary(Opts2); _ -> - {error, db_failure} + {error, db_failure} end; {selected, _} -> error; From fe8710fe00a2b87e56f3b0d237c41f1159c78ea3 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Fri, 25 Jul 2025 09:35:10 +0200 Subject: [PATCH 1231/1302] Rename auth_password_types_hidden_in_scram1 option to auth_password_types_hidden_in_sasl1 Also add migration code from old name --- src/ejabberd_c2s.erl | 2 +- src/ejabberd_config_transformer.erl | 2 ++ src/ejabberd_option.erl | 14 +++++++------- src/ejabberd_options.erl | 4 ++-- src/ejabberd_options_doc.erl | 6 +++--- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1a03adc9f..f0f225bee 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -455,7 +455,7 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St (<<"EXTERNAL">>) -> maps:get(tls_verify, State, false); (_) -> false end, Mechs -- Mechs1), - case ejabberd_option:auth_password_types_hidden_in_scram1() of + case ejabberd_option:auth_password_types_hidden_in_sasl1() of [] -> Mechs2; List -> Mechs3 = lists:foldl( diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl index 362bbecea..1aed7c6a8 100644 --- a/src/ejabberd_config_transformer.erl +++ b/src/ejabberd_config_transformer.erl @@ -230,6 +230,8 @@ filter(_Host, captcha_host, _, _) -> filter(_Host, route_subdomains, _, _) -> warn_removed_option(route_subdomains, s2s_access), false; +filter(_Host, auth_password_types_hidden_in_scram1, Val, _) -> + {true, {auth_password_types_hidden_in_sasl1, Val}}; filter(Host, modules, ModOpts, State) -> NoDialbackHosts = maps:get(remove_s2s_dialback, State, []), ModOpts1 = lists:filter( diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 4361571f8..775ea14c9 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -18,7 +18,7 @@ -export([auth_method/0, auth_method/1]). -export([auth_opts/0, auth_opts/1]). -export([auth_password_format/0, auth_password_format/1]). --export([auth_password_types_hidden_in_scram1/0, auth_password_types_hidden_in_scram1/1]). +-export([auth_password_types_hidden_in_sasl1/0, auth_password_types_hidden_in_sasl1/1]). -export([auth_scram_hash/0, auth_scram_hash/1]). -export([auth_stored_password_types/0, auth_stored_password_types/1]). -export([auth_use_cache/0, auth_use_cache/1]). @@ -264,12 +264,12 @@ auth_password_format() -> auth_password_format(Host) -> ejabberd_config:get_option({auth_password_format, Host}). --spec auth_password_types_hidden_in_scram1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. -auth_password_types_hidden_in_scram1() -> - auth_password_types_hidden_in_scram1(global). --spec auth_password_types_hidden_in_scram1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. -auth_password_types_hidden_in_scram1(Host) -> - ejabberd_config:get_option({auth_password_types_hidden_in_scram1, Host}). +-spec auth_password_types_hidden_in_sasl1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_sasl1() -> + auth_password_types_hidden_in_sasl1(global). +-spec auth_password_types_hidden_in_sasl1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_sasl1(Host) -> + ejabberd_config:get_option({auth_password_types_hidden_in_sasl1, Host}). -spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'. auth_scram_hash() -> diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index ee3acfc58..609d75b93 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -79,7 +79,7 @@ opt_type(auth_opts) -> end; opt_type(auth_stored_password_types) -> econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); -opt_type(auth_password_types_hidden_in_scram1) -> +opt_type(auth_password_types_hidden_in_sasl1) -> econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); opt_type(auth_password_format) -> econf:enum([plain, scram]); @@ -566,7 +566,7 @@ options() -> {auth_password_format, plain}, {auth_scram_hash, sha}, {auth_stored_password_types, []}, - {auth_password_types_hidden_in_scram1, []}, + {auth_password_types_hidden_in_sasl1, []}, {auth_external_user_exists_check, true}, {auth_use_cache, fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index a8d5962f5..56e2633c3 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -399,12 +399,12 @@ doc() -> "depends on the _`auth_scram_hash`_ option."), "", ?T("The default value is 'plain'."), ""]}}, - {auth_password_types_hidden_in_scram1, + {auth_password_types_hidden_in_sasl1, #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", note => "added in 25.07", desc => - ?T("List of password types that should not be offered in SCRAM1 authenticatication. " - "Because SCRAM1, unlike SCRAM2, can't have list of available mechanisms tailored to " + ?T("List of password types that should not be offered in SASL1 authenticatication. " + "Because SASL1, unlike SASL2, can't have list of available mechanisms tailored to " "individual user, it's possible that offered mechanisms will not be compatible " "with stored password, especially if new password type was added recently. " "This option allows disabling offering some mechanisms in SASL1, to a time until new " From 7647b77225da5ecbd67c5930f80953dbe25a6d1a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 24 Jul 2025 11:55:23 +0200 Subject: [PATCH 1232/1302] Runtime: Raise the minimum Erlang tested to Erlang/OTP 24 The Erlang containers from versions 20-23 use Debian Buster, and require the debian repositories to install some development libraries. The Debian Buster repositories are no longer available, which means that we can no longer perform any test with Erlang 20-23. --- .github/workflows/runtime.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 4236a22be..90eee1fc2 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -31,9 +31,11 @@ jobs: strategy: fail-fast: false matrix: - otp: ['20', '25', '26', '27', '28'] + otp: ['24', '25', '26', '27', '28'] rebar: ['rebar', 'rebar3'] exclude: + - otp: '24' + rebar: 'rebar' - otp: '27' rebar: 'rebar' - otp: '28' From 4cd3c657e22ca3987a5eaff6c1a5c6e2ef773477 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Jul 2025 10:34:02 +0200 Subject: [PATCH 1233/1302] ejabberd_listener: Try to create provisional socket in final directory (#4422) and if that path is too long, then try HOME directory, if that's too long too, throw error explaining the problem. By the way, cutting the base64 string to 107 is a bad idea, as it encodes the final path, which would get lost and crash. --- src/ejabberd_listener.erl | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index aad6ecd86..f82b1e05b 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -224,13 +224,26 @@ setup_provisional_udsocket_dir(DefinitivePath) -> ProvisionalPath = get_provisional_udsocket_path(DefinitivePath), ?INFO_MSG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s", [ProvisionalPath, DefinitivePath]), - ProvisionalPath. + ProvisionalPathAbsolute = relative_socket_to_mnesia(ProvisionalPath), + create_base_dir(ProvisionalPathAbsolute), + ProvisionalPathAbsolute. get_provisional_udsocket_path(Path) -> PathBase64 = misc:term_to_base64(Path), PathBuild = filename:join(misc:get_home(), PathBase64), - %% Shorthen the path, a long path produces a crash when opening the socket. - binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}). + DestPath = filename:join(filename:dirname(Path), PathBase64), + case {byte_size(DestPath) > 107, byte_size(PathBuild) > 107} of + {false, _} -> + DestPath; + {true, false} -> + ?INFO_MSG("The provisional Unix Domain Socket path ~ts is longer than 107, let's use home directory instead which is ~p", [DestPath, byte_size(PathBuild)]), + PathBuild; + {true, true} -> + ?ERROR_MSG("The Unix Domain Socket path ~ts is too long, " + "and I cannot create the provisional file safely. " + "Please configure a shorter path and try again.", [Path]), + throw({error_socket_path_too_long, Path}) + end. get_definitive_udsocket_path(<<"unix", _>> = Unix) -> Unix; @@ -271,17 +284,20 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) -> end end, FinalPath = relative_socket_to_mnesia(Path), - FinalPathDir = filename:dirname(FinalPath), - case file:make_dir(FinalPathDir) of - ok -> - file:change_mode(FinalPathDir, 8#00700); - _ -> - ok - end, + create_base_dir(FinalPath), file:rename(Prov, FinalPath); set_definitive_udsocket(Port, _Opts) when is_integer(Port) -> ok. +create_base_dir(Path) -> + Dirname = filename:dirname(Path), + case file:make_dir(Dirname) of + ok -> + file:change_mode(Dirname, 8#00700); + _ -> + ok + end. + relative_socket_to_mnesia(Path1) -> case filename:pathtype(Path1) of absolute -> From 43919217276dc64888f42411d674fb23301ac6de Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Jul 2025 17:37:15 +0200 Subject: [PATCH 1234/1302] ejabberd_config: New predefined keyword HOST_URL_ENCODE --- src/ejabberd_config.erl | 2 +- src/mod_http_upload.erl | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 8ec4fccf4..8c10041e5 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -508,7 +508,7 @@ get_predefined_keywords(Host) -> global -> []; _ -> - [{<<"HOST">>, Host}] + [{<<"HOST">>, Host}, {<<"HOST_URL_ENCODE">>, misc:url_encode(Host)}] end, Home = misc:get_home(), ConfigDirPath = diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index e47da035e..51d5830af 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -319,8 +319,10 @@ mod_doc() -> desc => ?T("This option specifies the initial part of the PUT URLs " "used for file uploads. The keyword '@HOST@' is replaced " - "with the virtual host name. NOTE: different virtual " - "hosts cannot use the same PUT URL. " + "with the virtual host name. " + "And '@HOST_URL_ENCODE@' is replaced with the host name encoded for URL, " + "useful when your virtual hosts contain non-latin characters. " + "NOTE: different virtual hosts cannot use the same PUT URL. " "The default value is '\"https://@HOST@:5443/upload\"'.")}}, {get_url, #{value => ?T("URL"), From fbfd41c16ed9907f7d2ccdb6ce46b8338e1d623e Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 23 Jul 2025 20:34:24 +0200 Subject: [PATCH 1235/1302] misc: uri_decode/1 moved here from ejabberd_http and prosody2ejabberd --- src/ejabberd_http.erl | 15 +-------------- src/misc.erl | 21 +++++++++++++++++++++ src/prosody2ejabberd.erl | 14 ++------------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 0ca7d3ff0..b8e05dec4 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -718,7 +718,7 @@ file_format_error(Reason) -> url_decode_q_split_normalize(Path) -> {NPath, Query} = url_decode_q_split(Path), LPath = normalize_path([NPE - || NPE <- str:tokens(path_decode(NPath), <<"/">>)]), + || NPE <- str:tokens(misc:uri_decode(NPath), <<"/">>)]), {LPath, Query}. % Code below is taken (with some modifications) from the yaws webserver, which @@ -746,19 +746,6 @@ url_decode_q_split(<>, Acc) when H /= 0 -> url_decode_q_split(<<>>, Ack) -> {path_norm_reverse(Ack), <<>>}. -%% @doc Decode a part of the URL and return string() -path_decode(Path) -> path_decode(Path, <<>>). - -path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> - Hex = list_to_integer([Hi, Lo], 16), - if Hex == 0 -> exit(badurl); - true -> ok - end, - path_decode(Tail, <>); -path_decode(<>, Acc) when H /= 0 -> - path_decode(T, <>); -path_decode(<<>>, Acc) -> Acc. - path_norm_reverse(<<"/", T/binary>>) -> start_dir(0, <<"/">>, T); path_norm_reverse(T) -> start_dir(0, <<"">>, T). diff --git a/src/misc.erl b/src/misc.erl index 32a85a8d1..ce1b7c636 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -43,6 +43,7 @@ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4, get_my_ipv4_address/0, get_my_ipv6_address/0, parse_ip_mask/1, crypto_hmac/3, crypto_hmac/4, uri_parse/1, uri_parse/2, uri_quote/1, + uri_decode/1, json_encode/1, json_decode/1, set_proc_label/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, delete_dir/1, @@ -125,6 +126,26 @@ uri_quote(Data) -> uri_string:quote(Data). -endif. +%% @doc Decode a part of the URL and return string() +%% -spec url_decode(binary()) -> bitstring(). + +-ifdef(OTP_BELOW_24). +uri_decode(Path) -> uri_decode(Path, <<>>). + +uri_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> + Hex = list_to_integer([Hi, Lo], 16), + if Hex == 0 -> exit(badurl); + true -> ok + end, + uri_decode(Tail, <>); +uri_decode(<>, Acc) when H /= 0 -> + uri_decode(T, <>); +uri_decode(<<>>, Acc) -> Acc. +-else. +uri_decode(Path) -> + uri_string:percent_decode(Path). +-endif. + -ifdef(USE_OLD_CRYPTO_HMAC). crypto_hmac(Type, Key, Data) -> crypto:hmac(Type, Key, Data). crypto_hmac(Type, Key, Data, MacL) -> crypto:hmac(Type, Key, Data, MacL). diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index cbfb49cd4..045abdf90 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -87,8 +87,8 @@ convert_dir(Path, Host, Type) -> case eval_file(FilePath) of {ok, Data} -> Name = iolist_to_binary(filename:rootname(File)), - convert_data(url_decode(Host), Type, - url_decode(Name), Data); + convert_data(misc:uri_decode(Host), Type, + misc:uri_decode(Name), Data); Err -> Err end @@ -410,16 +410,6 @@ convert_privacy_item({_, Item}) -> match_presence_in = MatchPresIn, match_presence_out = MatchPresOut}. -url_decode(Encoded) -> - url_decode(Encoded, <<>>). -url_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) -> - Hex = list_to_integer([Hi, Lo], 16), - url_decode(Tail, <>); -url_decode(<>, Acc) -> - url_decode(Tail, <>); -url_decode(<<>>, Acc) -> - Acc. - decode_pubsub_host(Host) -> try jid:decode(Host) of #jid{luser = <<>>, lserver = LServer} -> LServer; From e5da1efea498d9dc04c649344e490e294e1975c1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 23 Jul 2025 21:21:28 +0200 Subject: [PATCH 1236/1302] misc: Move uri_parse/1 to yconf and merge with yconf:parse_uri/1 --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 11 ++++++----- src/misc.erl | 38 ++------------------------------------ 5 files changed, 11 insertions(+), 44 deletions(-) diff --git a/mix.exs b/mix.exs index 7fe59ec88..70a9c5fc9 100644 --- a/mix.exs +++ b/mix.exs @@ -131,7 +131,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:xmpp, ">= 1.11.0"}, - {:yconf, ">= 1.0.18"}] + {:yconf, git: "https://github.com/processone/yconf", ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 60edccdc5..288453f3a 100644 --- a/mix.lock +++ b/mix.lock @@ -35,5 +35,5 @@ "stun": {:hex, :stun, "1.2.20", "62a149cea122a78a104b9e064a12d9e33105b26c23168ecf3aea6e0c26de0748", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "79e49f826a4f7d522c939ab633d935c79d7d6b229e4cb7e05f62f33b50177414"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "xmpp": {:hex, :xmpp, "1.11.0", "a3158c486c9b86a7090c361d876db622381f4312ede8c125d7a52ad390387932", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "34a191d6a3b74e8f0a42346f859e2cab5b3a2ae7e5c28f392e5cb56612e7ce85"}, - "yconf": {:hex, :yconf, "1.0.20", "f2b38db613fa826966e8d22bdc3e3ebae46919f2a27ab149a5a086c1d99d3bbd", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "f2b3d730756fc2e4afd1c0b0ab6efb99f0e448952d25dc15ed75ac1635bf8882"}, + "yconf": {:git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", [ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"]}, } diff --git a/rebar.config b/rebar.config index cfe938a9d..039fdd40a 100644 --- a/rebar.config +++ b/rebar.config @@ -78,7 +78,7 @@ {if_var_true, stun, {stun, "~> 1.2.20", {git, "https://github.com/processone/stun", {tag, "1.2.20"}}}}, {xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", {tag, "1.11.0"}}}, - {yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", {tag, "1.0.20"}}} + {yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index 342bbe80f..921129904 100644 --- a/rebar.lock +++ b/rebar.lock @@ -28,7 +28,10 @@ {<<"stun">>,{pkg,<<"stun">>,<<"1.2.20">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.0">>},0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.20">>},0}]}. + {<<"yconf">>, + {git,"https://github.com/processone/yconf", + {ref,"c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}}, + 0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -55,8 +58,7 @@ {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, {<<"stun">>, <<"62A149CEA122A78A104B9E064A12D9E33105B26C23168ECF3AEA6E0C26DE0748">>}, {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, - {<<"xmpp">>, <<"A3158C486C9B86A7090C361D876DB622381F4312EDE8C125D7A52AD390387932">>}, - {<<"yconf">>, <<"F2B38DB613FA826966E8D22BDC3E3EBAE46919F2A27AB149A5A086C1D99D3BBD">>}]}, + {<<"xmpp">>, <<"A3158C486C9B86A7090C361D876DB622381F4312EDE8C125D7A52AD390387932">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, @@ -82,6 +84,5 @@ {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, {<<"stun">>, <<"79E49F826A4F7D522C939AB633D935C79D7D6B229E4CB7E05F62F33B50177414">>}, {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, - {<<"xmpp">>, <<"34A191D6A3B74E8F0A42346F859E2CAB5B3A2AE7E5C28F392E5CB56612E7CE85">>}, - {<<"yconf">>, <<"F2B3D730756FC2E4AFD1C0B0AB6EFB99F0E448952D25DC15ED75AC1635BF8882">>}]} + {<<"xmpp">>, <<"34A191D6A3B74E8F0A42346F859E2CAB5B3A2AE7E5C28F392E5CB56612E7CE85">>}]} ]. diff --git a/src/misc.erl b/src/misc.erl index ce1b7c636..87f8b24e6 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -73,45 +73,11 @@ -type distance_cache() :: #{{string(), string()} => non_neg_integer()}. -spec uri_parse(binary()|string()) -> {ok, string(), string(), string(), number(), string(), string()} | {error, term()}. --ifdef(USE_OLD_HTTP_URI). -uri_parse(URL) when is_binary(URL) -> - uri_parse(binary_to_list(URL)); uri_parse(URL) -> - uri_parse(URL, []). + yconf:parse_uri(URL). -uri_parse(URL, Protocols) when is_binary(URL) -> - uri_parse(binary_to_list(URL), Protocols); uri_parse(URL, Protocols) -> - case http_uri:parse(URL, [{scheme_defaults, Protocols}]) of - {ok, {Scheme, UserInfo, Host, Port, Path, Query}} -> - {ok, atom_to_list(Scheme), UserInfo, Host, Port, Path, Query}; - {error, _} = E -> - E - end. - --else. -uri_parse(URL) when is_binary(URL) -> - uri_parse(binary_to_list(URL)); -uri_parse(URL) -> - uri_parse(URL, [{http, 80}, {https, 443}]). - -uri_parse(URL, Protocols) when is_binary(URL) -> - uri_parse(binary_to_list(URL), Protocols); -uri_parse(URL, Protocols) -> - case uri_string:parse(URL) of - #{scheme := Scheme, host := Host, port := Port, path := Path} = M1 -> - {ok, Scheme, maps:get(userinfo, M1, ""), Host, Port, Path, maps:get(query, M1, "")}; - #{scheme := Scheme, host := Host, path := Path} = M2 -> - case lists:keyfind(list_to_atom(Scheme), 1, Protocols) of - {_, Port} -> - {ok, Scheme, maps:get(userinfo, M2, ""), Host, Port, Path, maps:get(query, M2, "")}; - _ -> - {error, unknown_protocol} - end; - {error, Atom, _} -> - {error, Atom} - end. --endif. + yconf:parse_uri(URL, Protocols). -ifdef(OTP_BELOW_25). -ifdef(OTP_BELOW_24). From bba1a1e3ca14befcbe0d3412a45ed019c67fe766 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 23 Jul 2025 20:36:31 +0200 Subject: [PATCH 1237/1302] mod_http_upload: Encode URLs into IDNA when showing to XMPP client (#3519) --- src/mod_http_upload.erl | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index 51d5830af..c4397741e 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -533,7 +533,8 @@ process(LocalPath, #request{method = Method, host = Host, ip = IP}) [Method, encode_addr(IP), Host]), http_response(404); process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP, - length = Length} = Request) -> + length = Length} = Request0) -> + Request = Request0#request{host = redecode_url(Host)}, {Proc, Slot} = parse_http_request(Request), try gen_server:call(Proc, {use_slot, Slot, Length}, ?CALL_TIMEOUT) of {ok, Path, FileMode, DirMode, GetPrefix, Thumbnail, CustomHeaders} -> @@ -573,9 +574,10 @@ process(_LocalPath, #request{method = 'PUT', host = Host, ip = IP, [encode_addr(IP), Host, Error]), http_response(500) end; -process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request) +process(_LocalPath, #request{method = Method, host = Host, ip = IP} = Request0) when Method == 'GET'; Method == 'HEAD' -> + Request = Request0#request{host = redecode_url(Host)}, {Proc, [_UserDir, _RandDir, FileName] = Slot} = parse_http_request(Request), try gen_server:call(Proc, get_conf, ?CALL_TIMEOUT) of {ok, DocRoot, CustomHeaders} -> @@ -909,8 +911,8 @@ mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS, Query) -> GetURL = str:join([GetPrefix | Slot], <<$/>>), mk_slot(PutURL, GetURL, XMLNS, Query); mk_slot(PutURL, GetURL, XMLNS, Query) -> - PutURL1 = <<(misc:url_encode(PutURL))/binary, Query/binary>>, - GetURL1 = misc:url_encode(GetURL), + PutURL1 = <<(reencode_url(PutURL))/binary, Query/binary>>, + GetURL1 = reencode_url(GetURL), case XMLNS of ?NS_HTTP_UPLOAD_0 -> #upload_slot_0{get = GetURL1, put = PutURL1, xmlns = XMLNS}; @@ -918,6 +920,18 @@ mk_slot(PutURL, GetURL, XMLNS, Query) -> #upload_slot{get = GetURL1, put = PutURL1, xmlns = XMLNS} end. +reencode_url(UrlString) -> + {ok, _, _, Host, _, _, _} = yconf:parse_uri(UrlString), + HostDecoded = misc:uri_decode(Host), + HostIdna = idna:encode(HostDecoded), + re:replace(UrlString, Host, HostIdna, [{return, binary}]). + +redecode_url(UrlString) -> + {ok, _, _, HostIdna, _, _, _} = yconf:parse_uri(<<"http://", UrlString/binary>>), + HostDecoded = idna:decode(HostIdna), + Host = misc:uri_quote(HostDecoded), + re:replace(UrlString, HostIdna, Host, [{return, binary}]). + -spec make_user_string(jid(), sha1 | node) -> binary(). make_user_string(#jid{luser = U, lserver = S}, sha1) -> str:sha(<>); From 053fd26994a78b464bc773906b0186b1b584f7c9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 25 Jul 2025 22:34:26 +0200 Subject: [PATCH 1238/1302] econf: If a host in configuration is encoded IDNA, decode it (#3519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example: hosts: - localhost - locälhost3 - xn--loclhost4-x2a all them are converted to utf8: ejabberd_option:hosts(). [<<"localhost">>, <<"locälhost3"/utf8>>, <<"locälhost4"/utf8>>] --- src/econf.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/econf.erl b/src/econf.erl index bcee46224..5bc4f4599 100644 --- a/src/econf.erl +++ b/src/econf.erl @@ -482,6 +482,8 @@ domain() -> non_empty(binary()), fun(Val) -> try jid:tolower(jid:decode(Val)) of + {<<"">>, <<"xn--", _/binary>> = Domain, <<"">>} -> + unicode:characters_to_binary(idna:decode(binary_to_list(Domain)), utf8); {<<"">>, Domain, <<"">>} -> Domain; _ -> fail({bad_domain, Val}) catch _:{bad_jid, _} -> From f150419891e7eedfd20ad63435388836d7cb864d Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Wed, 30 Jul 2025 22:44:54 +0200 Subject: [PATCH 1239/1302] CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action Updates this GitHub Action that's used to execute XMPP-based interop tests from v1.5.0 to v1.6.0. In this update, 524 new tests were added (more than doubling the amount of tests that previously existed). --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c747e16b..0c7828fea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: - name: Run XMPP Interoperability Tests against CI server. if: matrix.otp == '27' continue-on-error: true - uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.5.0 + uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.0 with: domain: 'localhost' adminAccountUsername: 'admin' From e709f99b473540ea60795cb0ab9b7cc3c653f277 Mon Sep 17 00:00:00 2001 From: marc0s Date: Fri, 11 Jul 2025 12:09:36 +0200 Subject: [PATCH 1240/1302] fix: unsubscribe users from members-only rooms when expelled Fixes #4410 --- src/mod_muc_room.erl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 64e7e0a2d..a7b33bb31 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3221,6 +3221,7 @@ process_item_change(Item, SD, UJID) -> true -> send_kickban_presence(UJID, JID, Reason, 321, none, SD), maybe_send_affiliation(JID, none, SD), + unsubscribe_from_room(JID, SD), SD1 = set_affiliation(JID, none, SD), set_role(JID, none, SD1); _ -> @@ -3237,6 +3238,7 @@ process_item_change(Item, SD, UJID) -> {JID, affiliation, outcast, Reason} -> send_kickban_presence(UJID, JID, Reason, 301, outcast, SD), maybe_send_affiliation(JID, outcast, SD), + unsubscribe_from_room(JID, SD), {result, undefined, SD2} = process_iq_mucsub(JID, #iq{type = set, @@ -3279,6 +3281,30 @@ process_item_change(Item, SD, UJID) -> {error, xmpp:err_internal_server_error()} end. +-spec unsubscribe_from_room(jid(), state()) -> ok | error. +unsubscribe_from_room(JID, SD) -> + case SD#state.config#config.members_only of + false -> + ok; + true -> + case mod_muc:unhibernate_room(SD#state.server_host, SD#state.host, SD#state.room) of + {error, Reason0} -> + error; + {ok, Pid} -> + _UnsubPid = + spawn(fun() -> + case unsubscribe(Pid, JID) of + ok -> + ok; + {error, Reason} -> + ?WARNING_MSG("Failed to automatically unsubscribe expelled member from room: ~ts", + [Reason]), + error + end + end) + end + end. + -spec find_changed_items(jid(), affiliation(), role(), [muc_item()], binary(), state(), [admin_action()]) -> {result, [admin_action()]}. From dacfad61d88cb281c1295ec9ed05268623ec0bc0 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Tue, 5 Aug 2025 10:08:14 +0200 Subject: [PATCH 1241/1302] Fix format of passwords updates triggered by mod_scram_upgrade --- src/mod_scram_upgrade.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index 7699006de..4bf335248 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -108,8 +108,10 @@ c2s_handle_sasl2_task_data({_, #{user := User, server := Server, StoredKey = scram:stored_key(Algo, scram:client_key(Algo, SaltedPassword)), ServerKey = scram:server_key(Algo, SaltedPassword), ejabberd_auth:set_password_instance(User, Server, - #scram{hash = Algo, iterationcount = Iter, salt = Salt, - serverkey = ServerKey, storedkey = StoredKey}), + #scram{hash = Algo, iterationcount = Iter, + salt = base64:encode(Salt), + serverkey = base64:encode(ServerKey), + storedkey = base64:encode(StoredKey)}), State2 = maps:remove(scram_upgrade, State), InlineEls2 = lists:keydelete(sasl_upgrade, 1, InlineEls), {State3, NewEls, Results} = ejabberd_c2s:handle_sasl2_inline(InlineEls2, State2), From f594620c68fe2da6fcd622bb7bbf4403779d4d03 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Tue, 5 Aug 2025 10:08:14 +0200 Subject: [PATCH 1242/1302] Only offer upgrades to methods that aren't already stored --- src/ejabberd_c2s.erl | 4 ++-- src/mod_auth_fast.erl | 4 ++-- src/mod_carboncopy.erl | 4 ++-- src/mod_scram_upgrade.erl | 24 ++++++++++++++++++------ src/mod_stream_mgmt.erl | 4 ++-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index f0f225bee..ef9312ef5 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -416,8 +416,8 @@ unauthenticated_stream_features(#{lserver := LServer}) -> authenticated_stream_features(#{lserver := LServer}) -> ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]). -inline_stream_features(#{lserver := LServer}) -> - ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer]). +inline_stream_features(#{lserver := LServer} = State) -> + ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], [], []}, [LServer, State]). sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) -> Type = ejabberd_auth:store_type(LServer), diff --git a/src/mod_auth_fast.erl b/src/mod_auth_fast.erl index c26c21072..c72153267 100644 --- a/src/mod_auth_fast.erl +++ b/src/mod_auth_fast.erl @@ -29,7 +29,7 @@ -export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). -export([mod_doc/0]). %% Hooks --export([c2s_inline_features/2, c2s_handle_sasl2_inline/1, +-export([c2s_inline_features/3, c2s_handle_sasl2_inline/1, get_tokens/3, get_mechanisms/1, remove_user_tokens/2]). -include_lib("xmpp/include/xmpp.hrl"). @@ -131,7 +131,7 @@ get_tokens(LServer, LUser, UA) -> {{Type, CreatedAt < ToRefresh}, Token} end, Mod:get_tokens(LServer, LUser, ua_hash(UA))). -c2s_inline_features({Sasl, Bind, Extra}, Host) -> +c2s_inline_features({Sasl, Bind, Extra}, Host, _State) -> {Sasl ++ [#fast{mechs = get_mechanisms(Host)}], Bind, Extra}. gen_token(#{sasl2_ua_id := UA, server := Server, user := User}) -> diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index b2e1ff544..d040d948f 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -38,7 +38,7 @@ iq_handler/1, disco_features/5, depends/2, mod_options/1, mod_doc/0]). -export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1, - c2s_inline_features/2, c2s_handle_bind2_inline/1]). + c2s_inline_features/3, c2s_handle_bind2_inline/1]). %% For debugging purposes -export([list/2]). @@ -145,7 +145,7 @@ c2s_session_resumed(State) -> c2s_session_opened(State) -> maps:remove(carboncopy, State). -c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) -> +c2s_inline_features({Sasl, Bind, Extra} = Acc, Host, _State) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> {Sasl, [#bind2_feature{var = ?NS_CARBONS_2} | Bind], Extra}; diff --git a/src/mod_scram_upgrade.erl b/src/mod_scram_upgrade.erl index 4bf335248..37af47b46 100644 --- a/src/mod_scram_upgrade.erl +++ b/src/mod_scram_upgrade.erl @@ -27,7 +27,7 @@ -export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]). -export([mod_doc/0]). %% Hooks --export([c2s_inline_features/2, c2s_handle_sasl2_inline/1, +-export([c2s_inline_features/3, c2s_handle_sasl2_inline/1, c2s_handle_sasl2_task_next/4, c2s_handle_sasl2_task_data/3]). -include_lib("xmpp/include/xmpp.hrl"). @@ -76,11 +76,23 @@ mod_doc() -> " - sha256", " - sha512"]}. -c2s_inline_features({Sasl, Bind, Extra}, Host) -> - Methods = lists:map( - fun(sha256) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-256">>}; - (sha512) -> #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-512">>} - end, mod_scram_upgrade_opt:offered_upgrades(Host)), +c2s_inline_features({Sasl, Bind, Extra}, Host, State) -> + KnowTypes = case State of + #{sasl2_password_fun := Fun} -> + case Fun(<<>>) of + {Pass, _} -> lists:filtermap( + fun(#scram{hash = sha256}) -> {true, sha256}; + (#scram{hash = sha512}) -> {true, sha512}; + (_) -> false + end, Pass); + _ -> [] + end; + _ -> [] + end, + Methods = lists:filtermap( + fun(sha256) -> {true, #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-256">>}}; + (sha512) -> {true, #sasl_upgrade{cdata = <<"UPGR-SCRAM-SHA-512">>}} + end, mod_scram_upgrade_opt:offered_upgrades(Host) -- KnowTypes), {Sasl, Bind, Methods ++ Extra}. c2s_handle_sasl2_inline({State, Els, _Results} = Acc) -> diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl index 359d2601e..f3a641a7a 100644 --- a/src/mod_stream_mgmt.erl +++ b/src/mod_stream_mgmt.erl @@ -33,7 +33,7 @@ c2s_authenticated_packet/2, c2s_unauthenticated_packet/2, c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2, c2s_handle_send/3, c2s_handle_info/2, c2s_handle_cast/2, - c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/2, + c2s_handle_call/3, c2s_handle_recv/3, c2s_inline_features/3, c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3, c2s_handle_bind2_inline/1]). %% adjust pending session timeout / access queue @@ -122,7 +122,7 @@ c2s_stream_features(Acc, Host) -> Acc end. -c2s_inline_features({Sasl, Bind, Extra} = Acc, Host) -> +c2s_inline_features({Sasl, Bind, Extra} = Acc, Host, _State) -> case gen_mod:is_loaded(Host, ?MODULE) of true -> {[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl], From 10f6723f00782d48e19b46350807cd09279fa9a3 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Thu, 7 Aug 2025 12:57:24 +0200 Subject: [PATCH 1243/1302] Prevent loops in xml_compress:decode with corrupted data --- src/xml_compress.erl | 101 +++++++++++++++++++------------------ tools/xml_compress_gen.erl | 35 +++++++------ 2 files changed, 73 insertions(+), 63 deletions(-) diff --git a/src/xml_compress.erl b/src/xml_compress.erl index a85ec56b2..21b9044a0 100644 --- a/src/xml_compress.erl +++ b/src/xml_compress.erl @@ -480,8 +480,11 @@ encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) -> decode(<<$<, _/binary>> = Data, _J1, _J2) -> fxml_stream:parse_element(Data); decode(<<1:8, Rest/binary>>, J1, J2) -> - {El, _} = decode(Rest, <<"jabber:client">>, J1, J2), - El. + try decode(Rest, <<"jabber:client">>, J1, J2, false) of + {El, _} -> El + catch throw:loop_detected -> + {error, {loop_detected, <<"Compressed data corrupted">>}} + end. decode_string(Data) -> case Data of @@ -489,35 +492,37 @@ decode_string(Data) -> {Str, Rest}; <<1:2, L1:6, 0:2, L2:6, Rest/binary>> -> L = L2*64 + L1, - <> = Rest, + <> = Rest, {Str, Rest2}; <<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> -> L = (L3*64 + L2)*64 + L1, - <> = Rest, + <> = Rest, {Str, Rest2} end. -decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) -> +decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2, _) -> {Text, Rest2} = decode_string(Rest), {{xmlcdata, Text}, Rest2}; -decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) -> +decode_child(<<2:8, Rest/binary>>, PNs, J1, J2, _) -> {Name, Rest2} = decode_string(Rest), {Attrs, Rest3} = decode_attrs(Rest2), {Children, Rest4} = decode_children(Rest3, PNs, J1, J2), {{xmlel, Name, Attrs, Children}, Rest4}; -decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) -> +decode_child(<<3:8, Rest/binary>>, PNs, J1, J2, _) -> {Ns, Rest2} = decode_string(Rest), {Name, Rest3} = decode_string(Rest2), {Attrs, Rest4} = decode_attrs(Rest3), {Children, Rest5} = decode_children(Rest4, Ns, J1, J2), {{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5}; -decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) -> +decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2, _) -> {stop, Rest}; -decode_child(Other, PNs, J1, J2) -> - decode(Other, PNs, J1, J2). +decode_child(_Other, _PNs, _J1, _J2, true) -> + throw(loop_detected); +decode_child(Other, PNs, J1, J2, _) -> + decode(Other, PNs, J1, J2, true). decode_children(Data, PNs, J1, J2) -> - prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data). + prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2, false) end, Data). decode_attr(<<1:8, Rest/binary>>) -> {Name, Rest2} = decode_string(Rest), @@ -545,7 +550,7 @@ add_ns(Ns, Ns, Attrs) -> add_ns(_, Ns, Attrs) -> [{<<"xmlns">>, Ns} | Attrs]. -decode(<<5:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<5:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"eu.siacs.conversations.axolotl">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -563,12 +568,12 @@ decode(<<5:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"key">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<12:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<12:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"eu.siacs.conversations.axolotl">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"encrypted">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<13:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<13:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"eu.siacs.conversations.axolotl">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -581,17 +586,17 @@ decode(<<13:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"header">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<14:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<14:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"eu.siacs.conversations.axolotl">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"iv">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<15:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<15:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"eu.siacs.conversations.axolotl">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"payload">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<6:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<6:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"jabber:client">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -636,7 +641,7 @@ decode(<<6:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"message">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<8:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<8:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"jabber:client">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = prefix_map(fun (<<9:8, Rest5/binary>>) -> @@ -650,25 +655,25 @@ decode(<<8:8, Rest/binary>>, PNs, J1, J2) -> 32,104,116,116,112,115,58,47,47,99,111,110,118,101,114,115, 97,116,105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Rest5}; (Other) -> - decode_child(Other, Ns, J1, J2) + decode_child(Other, Ns, J1, J2, false) end, Rest2), {{xmlel, <<"body">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<31:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<31:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"jabber:client">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"subject">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<32:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<32:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"jabber:client">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"thread">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<7:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<7:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:hints">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"store">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<10:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<10:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:sid:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -681,7 +686,7 @@ decode(<<10:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"origin-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<22:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<22:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:sid:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -697,12 +702,12 @@ decode(<<22:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"stanza-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<11:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<11:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:chat-markers:0">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"markable">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<20:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<20:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:chat-markers:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -724,7 +729,7 @@ decode(<<20:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"displayed">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<24:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<24:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:chat-markers:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -737,7 +742,7 @@ decode(<<24:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<16:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<16:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:eme:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -757,7 +762,7 @@ decode(<<16:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"encryption">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<17:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<17:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:delay">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -775,7 +780,7 @@ decode(<<17:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"delay">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<18:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<18:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/address">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -796,12 +801,12 @@ decode(<<18:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"address">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<19:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<19:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/address">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"addresses">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<21:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<21:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:mam:tmp">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -817,12 +822,12 @@ decode(<<21:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"archived">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<23:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<23:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:receipts">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"request">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<25:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<25:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:receipts">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -835,17 +840,17 @@ decode(<<25:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<26:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<26:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/chatstates">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"active">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<39:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<39:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/chatstates">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"composing">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<27:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<27:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/muc#user">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -861,17 +866,17 @@ decode(<<27:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"invite">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<28:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<28:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/muc#user">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"reason">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<29:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<29:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/muc#user">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<30:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<30:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"jabber:x:conference">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -886,12 +891,12 @@ decode(<<30:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<33:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<33:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/pubsub#event">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"event">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<34:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<34:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/pubsub#event">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -904,7 +909,7 @@ decode(<<34:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"item">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<35:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<35:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"http://jabber.org/protocol/pubsub#event">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -919,7 +924,7 @@ decode(<<35:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"items">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<36:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<36:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"p1:push:custom">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -935,12 +940,12 @@ decode(<<36:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<37:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<37:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"p1:pushed">>, {Attrs, Rest2} = decode_attrs(Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(<<38:8, Rest/binary>>, PNs, J1, J2) -> +decode(<<38:8, Rest/binary>>, PNs, J1, J2, _) -> Ns = <<"urn:xmpp:message-correct:0">>, {Attrs, Rest2} = prefix_map(fun (<<3:8, Rest3/binary>>) -> @@ -953,6 +958,6 @@ decode(<<38:8, Rest/binary>>, PNs, J1, J2) -> end, Rest), {Children, Rest6} = decode_children(Rest2, Ns, J1, J2), {{xmlel, <<"replace">>, add_ns(PNs, Ns, Attrs), Children}, Rest6}; -decode(Other, PNs, J1, J2) -> - decode_child(Other, PNs, J1, J2). +decode(Other, PNs, J1, J2, Loop) -> + decode_child(Other, PNs, J1, J2, Loop). diff --git a/tools/xml_compress_gen.erl b/tools/xml_compress_gen.erl index 63101ced2..d331d7533 100644 --- a/tools/xml_compress_gen.erl +++ b/tools/xml_compress_gen.erl @@ -93,41 +93,46 @@ gen_decode(Dev, Data, VerId) -> io:format(Dev, "decode(<<$<, _/binary>> = Data, _J1, _J2) ->~n" " fxml_stream:parse_element(Data);~n" "decode(<<~s, Rest/binary>>, J1, J2) ->~n" - " {El, _} = decode(Rest, <<\"jabber:client\">>, J1, J2),~n" - " El.~n~n", [VerId]), + " try decode(Rest, <<\"jabber:client\">>, J1, J2, false) of~n" + " {El, _} -> El~n" + " catch throw:loop_detected ->~n" + " {error, {loop_detected, <<\"Compressed data corrupted\">>}}~n" + " end.~n~n", [VerId]), io:format(Dev, "decode_string(Data) ->~n" " case Data of~n" " <<0:2, L:6, Str:L/binary, Rest/binary>> ->~n" " {Str, Rest};~n" " <<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->~n" " L = L2*64 + L1,~n" - " <> = Rest,~n" + " <> = Rest,~n" " {Str, Rest2};~n" " <<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->~n" " L = (L3*64 + L2)*64 + L1,~n" - " <> = Rest,~n" + " <> = Rest,~n" " {Str, Rest2}~n" " end.~n~n", []), - io:format(Dev, "decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->~n" + io:format(Dev, "decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2, _) ->~n" " {Text, Rest2} = decode_string(Rest),~n" " {{xmlcdata, Text}, Rest2};~n", []), - io:format(Dev, "decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->~n" + io:format(Dev, "decode_child(<<2:8, Rest/binary>>, PNs, J1, J2, _) ->~n" " {Name, Rest2} = decode_string(Rest),~n" " {Attrs, Rest3} = decode_attrs(Rest2),~n" " {Children, Rest4} = decode_children(Rest3, PNs, J1, J2),~n" " {{xmlel, Name, Attrs, Children}, Rest4};~n", []), - io:format(Dev, "decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->~n" + io:format(Dev, "decode_child(<<3:8, Rest/binary>>, PNs, J1, J2, _) ->~n" " {Ns, Rest2} = decode_string(Rest),~n" " {Name, Rest3} = decode_string(Rest2),~n" " {Attrs, Rest4} = decode_attrs(Rest3),~n" " {Children, Rest5} = decode_children(Rest4, Ns, J1, J2),~n" " {{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};~n", []), - io:format(Dev, "decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->~n" + io:format(Dev, "decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2, _) ->~n" " {stop, Rest};~n", []), - io:format(Dev, "decode_child(Other, PNs, J1, J2) ->~n" - " decode(Other, PNs, J1, J2).~n~n", []), + io:format(Dev, "decode_child(_Other, _PNs, _J1, _J2, true) ->~n" + " throw(loop_detected);~n", []), + io:format(Dev, "decode_child(Other, PNs, J1, J2, _) ->~n" + " decode(Other, PNs, J1, J2, true).~n~n", []), io:format(Dev, "decode_children(Data, PNs, J1, J2) ->~n" - " prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).~n~n", []), + " prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2, false) end, Data).~n~n", []), io:format(Dev, "decode_attr(<<1:8, Rest/binary>>) ->~n" " {Name, Rest2} = decode_string(Rest),~n" " {Val, Rest3} = decode_string(Rest2),~n" @@ -153,7 +158,7 @@ gen_decode(Dev, Data, VerId) -> fun({Ns, Els}) -> lists:foreach( fun({Name, Id, Attrs, Text}) -> - io:format(Dev, "decode(<<~s, Rest/binary>>, PNs, J1, J2) ->~n" + io:format(Dev, "decode(<<~s, Rest/binary>>, PNs, J1, J2, _) ->~n" " Ns = ~p,~n", [Id, Ns]), case Attrs of [] -> @@ -209,14 +214,14 @@ gen_decode(Dev, Data, VerId) -> end, Text), io:format(Dev, " (Other) ->~n" - " decode_child(Other, Ns, J1, J2)~n" + " decode_child(Other, Ns, J1, J2, false)~n" " end, Rest2),~n", []) end, io:format(Dev, " {{xmlel, ~p, add_ns(PNs, Ns, Attrs), Children}, Rest6};~n", [Name]) end, Els) end, Data), - io:format(Dev, "decode(Other, PNs, J1, J2) ->~n" - " decode_child(Other, PNs, J1, J2).~n~n", []). + io:format(Dev, "decode(Other, PNs, J1, J2, Loop) ->~n" + " decode_child(Other, PNs, J1, J2, Loop).~n~n", []). gen_encode(Dev, Data, VerId) -> From 31cb4b06e4d88287c2390311dec1d854bc1d52b3 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 11 Aug 2025 19:40:17 +0300 Subject: [PATCH 1244/1302] Matrix gateway updates - Partially rewritten state resolution - Support for Hydra rooms - Use double colon for separating a matrix server from a room ID in JID with Hydra rooms - Partially rewritten mod_matrix_gw_s2s - Add notary_servers option --- include/mod_matrix_gw.hrl | 3 +- src/mod_matrix_gw.erl | 43 ++- src/mod_matrix_gw_opt.erl | 7 + src/mod_matrix_gw_room.erl | 711 ++++++++++++++++++++++++++----------- src/mod_matrix_gw_s2s.erl | 322 +++++++++-------- 5 files changed, 718 insertions(+), 368 deletions(-) diff --git a/include/mod_matrix_gw.hrl b/include/mod_matrix_gw.hrl index 3b4b6a41f..cdb272e8e 100644 --- a/include/mod_matrix_gw.hrl +++ b/include/mod_matrix_gw.hrl @@ -31,5 +31,6 @@ knock_restricted_join_rule :: boolean(), enforce_int_power_levels :: boolean(), implicit_room_creator :: boolean(), - updated_redaction_rules :: boolean() + updated_redaction_rules :: boolean(), + hydra :: boolean() }). diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index adeb7965e..d9dddbee1 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -611,22 +611,28 @@ parse_auth4(<<>>, Key, Val, Ts) -> prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event, RoomVersion) -> - Event2 = + Keys = case RoomVersion#room_version.updated_redaction_rules of false -> - maps:with( - [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, - <<"state_key">>, <<"content">>, <<"hashes">>, - <<"signatures">>, <<"depth">>, <<"prev_events">>, - <<"prev_state">>, <<"auth_events">>, <<"origin">>, - <<"origin_server_ts">>, <<"membership">>], Event); + [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, + <<"state_key">>, <<"content">>, <<"hashes">>, + <<"signatures">>, <<"depth">>, <<"prev_events">>, + <<"prev_state">>, <<"auth_events">>, <<"origin">>, + <<"origin_server_ts">>, <<"membership">>]; true -> - maps:with( - [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, - <<"state_key">>, <<"content">>, <<"hashes">>, - <<"signatures">>, <<"depth">>, <<"prev_events">>, - <<"auth_events">>, <<"origin_server_ts">>], Event) + [<<"event_id">>, <<"type">>, <<"room_id">>, <<"sender">>, + <<"state_key">>, <<"content">>, <<"hashes">>, + <<"signatures">>, <<"depth">>, <<"prev_events">>, + <<"auth_events">>, <<"origin_server_ts">>] end, + Keys2 = + case {RoomVersion#room_version.hydra, Type} of + {true, <<"m.room.create">>} -> + lists:delete(<<"room_id">>, Keys); + _ -> + Keys + end, + Event2 = maps:with(Keys2, Event), Content2 = case Type of <<"m.room.member">> -> @@ -976,7 +982,9 @@ mod_opt_type(key) -> crypto:generate_key(eddsa, ed25519, Key2) end; mod_opt_type(matrix_id_as_jid) -> - econf:bool(). + econf:bool(); +mod_opt_type(notary_servers) -> + econf:list(econf:host()). -spec mod_options(binary()) -> [{key, {binary(), binary()}} | {atom(), any()}]. @@ -986,7 +994,8 @@ mod_options(Host) -> {host, <<"matrix.", Host/binary>>}, {key_name, <<"">>}, {key, {<<"">>, <<"">>}}, - {matrix_id_as_jid, false}]. + {matrix_id_as_jid, false}, + {notary_servers, []}]. mod_doc() -> #{desc => @@ -1042,7 +1051,11 @@ mod_doc() -> "Matrix user '@user:matrixdomain.tld', the client must send a message " "to the JID 'user%matrixdomain.tld@matrix.myxmppdomain.tld', where " "'matrix.myxmppdomain.tld' is the JID of the gateway service as set by the " - "'host' option. The default is 'false'.")}} + "'host' option. The default is 'false'.")}}, + {notary_servers, + #{value => "[Server, ...]", + desc => + ?T("A list of notary servers.")}} ] }. -endif. diff --git a/src/mod_matrix_gw_opt.erl b/src/mod_matrix_gw_opt.erl index dbfc8526d..9e03c7072 100644 --- a/src/mod_matrix_gw_opt.erl +++ b/src/mod_matrix_gw_opt.erl @@ -8,6 +8,7 @@ -export([key_name/1]). -export([matrix_domain/1]). -export([matrix_id_as_jid/1]). +-export([notary_servers/1]). -spec host(gen_mod:opts() | global | binary()) -> binary(). host(Opts) when is_map(Opts) -> @@ -39,3 +40,9 @@ matrix_id_as_jid(Opts) when is_map(Opts) -> matrix_id_as_jid(Host) -> gen_mod:get_module_opt(Host, mod_matrix_gw, matrix_id_as_jid). +-spec notary_servers(gen_mod:opts() | global | binary()) -> [binary()]. +notary_servers(Opts) when is_map(Opts) -> + gen_mod:get_opt(notary_servers, Opts); +notary_servers(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, notary_servers). + diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 6ad3b7c3c..344aecc60 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -89,6 +89,7 @@ room_id :: binary(), room_jid :: jid(), room_version :: #room_version{}, + via :: binary | undefined, events = #{}, latest_events = sets:new([{version, 2}]), nonlatest_events = sets:new([{version, 2}]), @@ -105,6 +106,8 @@ -define(ROOM_TOPIC, <<"m.room.topic">>). -define(ROOM_ALIASES, <<"m.room.aliases">>). +-define(CREATOR_PL, (1 bsl 53)). + -define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF). -define(MAX_TXN_RETRIES, 5). @@ -183,14 +186,14 @@ route(#presence{from = From, to = #jid{luser = <>} = To, C == $# -> Host = ejabberd_config:get_myname(), case room_id_from_xmpp(Host, To#jid.luser) of - {ok, RoomID} -> + {ok, RoomID, Via} -> case From#jid.lserver of Host -> case Type of available -> case get_room_pid(Host, RoomID) of {ok, Pid} -> - gen_statem:cast(Pid, {join, From, Packet}); + gen_statem:cast(Pid, {join, From, Packet, Via}); {error, _} = Error -> ?DEBUG("join failed ~p", [{From, To, Error}]), ok @@ -227,7 +230,7 @@ route(#message{from = From, to = #jid{luser = <>} = To, ok; Text -> case room_id_from_xmpp(Host, To#jid.luser) of - {ok, RoomID} -> + {ok, RoomID, _Via} -> case From#jid.lserver of Host -> case user_id_from_jid(From, Host) of @@ -719,7 +722,7 @@ handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, D ?INFO_MSG("bad join user id: ~p", [{UserID, UserJID}]), {stop, normal} end; -handle_event(cast, {join, UserJID, Packet}, _State, Data) -> +handle_event(cast, {join, UserJID, Packet, Via}, _State, Data) -> Host = Data#data.host, {LUser, LServer, LResource} = jid:tolower(UserJID), case Data#data.kind of @@ -763,91 +766,95 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) -> {ok, UserID} -> %% TODO: async RoomID = Data#data.room_id, - {ok, MatrixServer} = - case binary:split(RoomID, <<":">>) of - [_, MS] -> {ok, MS}; - _ -> error - end, - MakeJoinRes = - mod_matrix_gw:send_request( - Host, get, MatrixServer, - [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, - RoomID, UserID], - [{<<"ver">>, V} || V <- supported_versions()], - none, - [{timeout, 5000}], - [{sync, true}, - {body_format, binary}]), - ?DEBUG("make_join ~p~n", [MakeJoinRes]), - case MakeJoinRes of - {ok, {{_, 200, _}, _Headers, Body}} -> - try misc:json_decode(Body) of - #{<<"event">> := Event, - <<"room_version">> := SRoomVersion} -> - case binary_to_room_version(SRoomVersion) of - false -> - ?DEBUG("unsupported room version on make_join: ~p", [MakeJoinRes]), - {stop, normal}; - #room_version{} = RoomVersion -> - JoinTS = erlang:system_time(millisecond), - Origin = mod_matrix_gw_opt:matrix_domain(Host), - Event2 = - Event#{<<"origin">> => Origin, - <<"origin_server_ts">> => JoinTS}, - CHash = mod_matrix_gw:content_hash(Event2), - Event3 = - Event2#{<<"hashes">> => - #{<<"sha256">> => - mod_matrix_gw:base64_encode(CHash)}}, - Event4 = mod_matrix_gw:sign_event(Host, Event3, RoomVersion), - EventID = mod_matrix_gw:get_event_id(Event4, RoomVersion), - SendJoinRes = - mod_matrix_gw:send_request( - Data#data.host, put, MatrixServer, - [<<"_matrix">>, <<"federation">>, - <<"v2">>, <<"send_join">>, - RoomID, EventID], - [], - Event4, - [{connect_timeout, 5000}, - {timeout, 60000}], - [{sync, true}, - {body_format, binary}]), - RoomJID = jid:remove_resource(xmpp:get_to(Packet)), - ?DEBUG("send_join ~p~n", [SendJoinRes]), - process_send_join_res( - MatrixServer, SendJoinRes, RoomVersion, - Data#data{ - kind = - #multi{users = - #{{LUser, LServer} => - #{LResource => #multi_user{join_ts = JoinTS, - room_jid = RoomJID}}}}, - room_version = RoomVersion}) + case Via of + MatrixServer when is_binary(MatrixServer) -> + MakeJoinRes = + mod_matrix_gw:send_request( + Host, get, MatrixServer, + [<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>, + RoomID, UserID], + [{<<"ver">>, V} || V <- supported_versions()], + none, + [{timeout, 5000}], + [{sync, true}, + {body_format, binary}]), + ?DEBUG("make_join ~p~n", [MakeJoinRes]), + case MakeJoinRes of + {ok, {{_, 200, _}, _Headers, Body}} -> + try misc:json_decode(Body) of + #{<<"event">> := Event, + <<"room_version">> := SRoomVersion} -> + case binary_to_room_version(SRoomVersion) of + false -> + ?DEBUG("unsupported room version on make_join: ~p", [MakeJoinRes]), + {stop, normal}; + #room_version{} = RoomVersion -> + JoinTS = erlang:system_time(millisecond), + Origin = mod_matrix_gw_opt:matrix_domain(Host), + Event2 = + Event#{<<"origin">> => Origin, + <<"origin_server_ts">> => JoinTS}, + CHash = mod_matrix_gw:content_hash(Event2), + Event3 = + Event2#{<<"hashes">> => + #{<<"sha256">> => + mod_matrix_gw:base64_encode(CHash)}}, + Event4 = mod_matrix_gw:sign_event(Host, Event3, RoomVersion), + EventID = mod_matrix_gw:get_event_id(Event4, RoomVersion), + SendJoinRes = + mod_matrix_gw:send_request( + Data#data.host, put, MatrixServer, + [<<"_matrix">>, <<"federation">>, + <<"v2">>, <<"send_join">>, + RoomID, EventID], + [], + Event4, + [{connect_timeout, 5000}, + {timeout, 60000}], + [{sync, true}, + {body_format, binary}]), + RoomJID = jid:remove_resource(xmpp:get_to(Packet)), + ?DEBUG("send_join ~p~n", [SendJoinRes]), + process_send_join_res( + MatrixServer, SendJoinRes, RoomVersion, + Data#data{ + kind = + #multi{users = + #{{LUser, LServer} => + #{LResource => #multi_user{join_ts = JoinTS, + room_jid = RoomJID}}}}, + room_version = RoomVersion}) + end; + _JSON -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + Txt = <<"received bad JSON on make_join">>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} + catch + _:_ -> + ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), + Txt = <<"received bad JSON on make_join">>, + Err = xmpp:err_bad_request(Txt, Lang), + ejabberd_router:route_error(Packet, Err), + {stop, normal} end; - _JSON -> - ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), - Txt = <<"received bad JSON on make_join">>, + {ok, {{_, 400, _}, _Headers, Body}} -> + ?DEBUG("failed make_join: ~p", [MakeJoinRes]), + Txt = <<"make_join failed: ", Body/binary>>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), - {stop, normal} - catch - _:_ -> - ?DEBUG("received bad JSON on make_join: ~p", [MakeJoinRes]), - Txt = <<"received bad JSON on make_join">>, + {stop, normal}; + _ -> + ?DEBUG("failed make_join: ~p", [MakeJoinRes]), + Txt = <<"make_join failed">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), {stop, normal} end; - {ok, {{_, 400, _}, _Headers, Body}} -> - ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = <<"make_join failed: ", Body/binary>>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err), - {stop, normal}; - _ -> - ?DEBUG("failed make_join: ~p", [MakeJoinRes]), - Txt = <<"make_join failed">>, + undefined -> + ?DEBUG("don't know which server to connect to", []), + Txt = <<"unknown remote server">>, Err = xmpp:err_bad_request(Txt, Lang), ejabberd_router:route_error(Packet, Err), {stop, normal} @@ -1234,13 +1241,25 @@ check_event_auth(Event, StateMap, Data) -> RoomVersion = Data#data.room_version, case Event#event.type of ?ROOM_CREATE -> - case maps:size(StateMap) of - 0 -> - RDomain = mod_matrix_gw:get_id_domain_exn(Data#data.room_id), - SDomain = mod_matrix_gw:get_id_domain_exn(Event#event.sender), + case {maps:size(StateMap), Event#event.prev_events} of + {0, []} -> + Check12 = + case RoomVersion#room_version.hydra of + true -> + case Event#event.json of + #{<<"room_id">> := _} -> + false; + _ -> + true + end; + false -> + RDomain = mod_matrix_gw:get_id_domain_exn(Data#data.room_id), + SDomain = mod_matrix_gw:get_id_domain_exn(Event#event.sender), + RDomain == SDomain + end, if - RDomain == SDomain -> - %% TODO: check version + Check12 -> + %% TODO: check content.room_version case RoomVersion#room_version.implicit_room_creator of false -> case Event#event.json of @@ -1251,7 +1270,28 @@ check_event_auth(Event, StateMap, Data) -> false end; true -> - true + case RoomVersion#room_version.hydra of + true -> + case Event#event.json of + #{<<"content">> := + #{<<"additional_creators">> := Creators}} when is_list(Creators) -> + lists:foreach( + fun(C) -> + case check_user_id(C) of + true -> ok; + false -> error(not_allowed) + end + end, Creators), + true; + #{<<"content">> := + #{<<"additional_creators">> := _}} -> + false; + _ -> + true + end; + false -> + true + end end; true -> false @@ -1577,7 +1617,8 @@ check_event_power_level(Event, StateMap, Data) -> {ok, #event{json = #{<<"content">> := C}}} -> C; _ -> #{} end, - RequiredLevel = get_event_power_level(Event#event.type, PLContent), + RequiredLevel = get_event_power_level( + Event#event.type, Event#event.state_key, PLContent), UserLevel = get_user_power_level(Event#event.sender, StateMap, Data), if UserLevel >= RequiredLevel -> @@ -1591,11 +1632,18 @@ check_event_power_level(Event, StateMap, Data) -> false end. -get_event_power_level(Type, PL) -> - case PL of - #{Type := Level} -> get_int(Level); - #{<<"events_default">> := Level} -> get_int(Level); - _ -> 0 +get_event_power_level(Type, StateKey, PL) -> + case {StateKey, PL} of + {_, #{Type := Level}} -> + get_int(Level); + {undefined, #{<<"events_default">> := Level}} -> + get_int(Level); + {undefined, _} -> + 0; + {StateKey, #{<<"state_default">> := Level}} when is_binary(StateKey) -> + get_int(Level); + {StateKey, _} when is_binary(StateKey) -> + 50 end. get_user_power_level(User, StateMap, Data) -> @@ -1605,26 +1653,67 @@ get_user_power_level(User, StateMap, Data) -> {ok, #event{json = #{<<"content">> := C}}} -> C; _ -> #{} end, - case PL of - #{<<"users">> := #{User := Level}} -> get_int(Level); - #{<<"users_default">> := Level} -> get_int(Level); + IsCreator = + case {RoomVersion#room_version.hydra, + RoomVersion#room_version.implicit_room_creator, + statemap_find({?ROOM_CREATE, <<"">>}, StateMap, Data)} of + {false, false, + {ok, #event{json = #{<<"content">> := #{<<"creator">> := User}}}}} -> + true; + {false, true, {ok, #event{sender = User}}} -> + true; + {true, _, {ok, #event{sender = User}}} -> + true; + {true, _, + {ok, #event{ + json = #{<<"content">> := + #{<<"additional_creators">> := Creators}}}}} + when is_list(Creators) -> + lists:member(User, Creators); + _ -> + false + end, + case {RoomVersion#room_version.hydra, + IsCreator, + PL} of + {true, true, _} -> + ?CREATOR_PL; + {_, _, #{<<"users">> := #{User := Level}}} -> get_int(Level); + {_, _, #{<<"users_default">> := Level}} -> get_int(Level); + {_, true, _} -> + 100; _ -> - case {RoomVersion#room_version.implicit_room_creator, - statemap_find({?ROOM_CREATE, <<"">>}, StateMap, Data)} of - {false, - {ok, #event{json = #{<<"content">> := #{<<"creator">> := User}}}}} -> - 100; - {true, {ok, #event{sender = User}}} -> - 100; - _ -> - 0 - end + 0 end. check_event_auth_power_levels(Event, StateMap, Data) -> try case Event#event.json of #{<<"content">> := NewPL = #{<<"users">> := Users}} when is_map(Users) -> + case (Data#data.room_version)#room_version.hydra of + false -> + ok; + true -> + case statemap_find({?ROOM_CREATE, <<"">>}, StateMap, Data) of + {ok, #event{sender = C} = E} -> + Creators = + case E#event.json of + #{<<"content">> := + #{<<"additional_creators">> := ACs}} -> + [C | ACs]; + _ -> + [C] + end, + case maps:size(maps:with(Creators, Users)) > 0 of + true -> + error(creators_in_pl); + false -> + ok + end; + _ -> + error(missed_create_event) + end + end, CheckKeys = case (Data#data.room_version)#room_version.limit_notifications_power_levels of false -> @@ -1796,6 +1885,7 @@ fill_event(JSON, Data) -> end end, compute_event_auth_keys(JSON, Data#data.room_version))), + ?DEBUG("auth_events ~p", [{AuthEvents, compute_event_auth_keys(JSON, Data#data.room_version)}]), {JSON#{<<"auth_events">> => AuthEvents, <<"depth">> => Depth2, <<"origin">> => MatrixServer, @@ -1964,6 +2054,7 @@ process_pdu(Host, Origin, PDU) -> Event = json_to_event(PDU, RoomVersion), case check_event_signature(Host, Event) of true -> + ?DEBUG("process pdu: ~p~n", [PDU]), {SeenEvents, MissedEvents} = partition_missed_events(Pid, Event#event.prev_events), ?DEBUG("seen/missed: ~p~n", [{SeenEvents, MissedEvents}]), @@ -2221,16 +2312,24 @@ resolve_state_maps([], _Data) -> resolve_state_maps([StateMap], _Data) -> StateMap; resolve_state_maps(StateMaps, Data) -> - {Unconflicted, Conflicted} = calculate_conflict(StateMaps), + {Unconflicted, Conflicted0} = calculate_conflict(StateMaps), + Conflicted1 = lists:append(maps:values(Conflicted0)), + Conflicted = + case (Data#data.room_version)#room_version.hydra of + false -> + Conflicted1; + true -> + calculate_conflicted_subgraph(Conflicted1, Data) + end, ?DEBUG("confl ~p~n", [{Unconflicted, Conflicted}]), - case maps:size(Conflicted) of - 0 -> + case Conflicted of + [] -> Unconflicted; _ -> AuthDiff = calculate_auth_diff(StateMaps, Data), ?DEBUG("auth diff ~p~n", [AuthDiff]), FullConflictedSet = - maps:from_list([{E, []} || E <- lists:append([AuthDiff | maps:values(Conflicted)])]), + maps:from_list([{E, []} || E <- AuthDiff ++ Conflicted]), ?DEBUG("fcs ~p~n", [FullConflictedSet]), %% TODO: test PowerEvents = @@ -2241,7 +2340,15 @@ resolve_state_maps(StateMaps, Data) -> end, maps:keys(FullConflictedSet)), SortedPowerEvents = lexicographic_toposort(PowerEvents, FullConflictedSet, Data), ?DEBUG("spe ~p~n", [SortedPowerEvents]), - StateMap = iterative_auth_checks(SortedPowerEvents, Unconflicted, Data), + StateMap = + case (Data#data.room_version)#room_version.hydra of + false -> + iterative_auth_checks(SortedPowerEvents, Unconflicted, Data); + true -> + maps:merge( + Unconflicted, + iterative_auth_checks(SortedPowerEvents, #{}, Data)) + end, PowerEventsSet = maps:from_list([{E, []} || E <- SortedPowerEvents]), OtherEvents = lists:filter(fun(E) -> not maps:is_key(E, PowerEventsSet) end, maps:keys(FullConflictedSet)), @@ -2276,6 +2383,70 @@ calculate_conflict(StateMaps) -> end end, {#{}, #{}}, Keys). +calculate_conflicted_subgraph([], _Data) -> + []; +calculate_conflicted_subgraph(Events, Data) -> + MinDepth = + lists:min( + [(maps:get(EID, Data#data.events))#event.depth || EID <- Events]), + AuthEvents = + lists:append( + [(maps:get(EID, Data#data.events))#event.auth_events || EID <- Events]), + Used0 = + maps:from_list([{E, true} || E <- Events]), + {Res, _Used} = + lists:foldl( + fun(EID, {_Res, Used} = Acc) -> + case maps:is_key(EID, Used) of + false -> + calculate_conflicted_subgraph_dfs(EID, Acc, MinDepth, Data); + true -> + Acc + end + end, {Events, Used0}, AuthEvents), + Res. + +calculate_conflicted_subgraph_dfs(EventID, {Res, Used}, MinDepth, Data) -> + case maps:find(EventID, Data#data.events) of + error -> + {Res, Used}; + {ok, Event} when Event#event.depth < MinDepth -> + {Res, Used}; + {ok, Event} -> + Used2 = Used#{EventID => gray}, + {Res8, Used8, Reachable} = + lists:foldl( + fun(_ID, {_Res3, _Used3, true} = Acc) -> + Acc; + (ID, {Res3, Used3, false}) -> + {Res4, Used4} = + case maps:get(ID, Used3, white) of + white -> + calculate_conflicted_subgraph_dfs(ID, {Res3, Used3}, MinDepth, Data); + _ -> + {Res3, Used3} + end, + case maps:get(ID, Used4, white) of + gray -> + error(loop_in_auth_chain); + true -> + {Res4, Used4, true}; + _ -> + {Res4, Used4, false} + end + end, {Res, Used2, false}, Event#event.auth_events), + Used9 = Used8#{EventID => Reachable}, + Res9 = + case Reachable of + true -> + [EventID | Res8]; + false -> + Res8 + end, + {Res9, Used9} + end. + + %% TODO: not optimal calculate_auth_diff(StateMaps, Data) -> N = length(StateMaps), @@ -2345,25 +2516,25 @@ is_power_event(_) -> false. lexicographic_toposort(EventIDs, EventSet, Data) -> - Used = + {Used, Rev} = lists:foldl( - fun(EventID, Used) -> + fun(EventID, {Used, Rev} = Acc) -> case maps:is_key(EventID, EventSet) of true -> case maps:is_key(EventID, Used) of false -> - lexicographic_toposort_prepare(EventID, Used, EventSet, Data); + lexicographic_toposort_prepare(EventID, Used, Rev, EventSet, Data); true -> - Used + Acc end; false -> - Used + Acc end - end, #{}, EventIDs), - IncomingCnt = + end, {#{}, #{}}, EventIDs), + ?DEBUG("rev ~p~n", [Rev]), + OutgoingCnt = maps:fold( fun(EventID, _, Acc) -> - Event = maps:get(EventID, Data#data.events), lists:foldl( fun(EID, Acc2) -> case maps:is_key(EID, Acc2) of @@ -2373,7 +2544,7 @@ lexicographic_toposort(EventIDs, EventSet, Data) -> false -> Acc2 end - end, Acc, Event#event.auth_events) + end, Acc, maps:get(EventID, Rev, [])) end, maps:map(fun(_, _) -> 0 end, Used), Used), Current = maps:fold( @@ -2383,64 +2554,63 @@ lexicographic_toposort(EventIDs, EventSet, Data) -> gb_trees:enter({-PowerLevel, Event#event.origin_server_ts, EventID}, [], Acc); (_, _, Acc) -> Acc - end, gb_trees:empty(), IncomingCnt), - IncomingCnt2 = maps:filter(fun(_, 0) -> false; (_, _) -> true end, IncomingCnt), - lexicographic_toposort_loop(Current, IncomingCnt2, [], Data). + end, gb_trees:empty(), OutgoingCnt), + OutgoingCnt2 = maps:filter(fun(_, 0) -> false; (_, _) -> true end, OutgoingCnt), + lexicographic_toposort_loop(Current, OutgoingCnt2, Rev, [], Data). -lexicographic_toposort_prepare(EventID, Used, EventSet, Data) -> +lexicographic_toposort_prepare(EventID, Used, Rev, EventSet, Data) -> Event = maps:get(EventID, Data#data.events), Used2 = Used#{EventID => []}, - Used4 = - lists:foldl( - fun(EID, Used3) -> - case maps:is_key(EID, EventSet) of - true -> - case maps:is_key(EID, Used3) of - false -> - lexicographic_toposort_prepare(EID, Used3, EventSet, Data); - true -> - Used3 - end; - false -> - Used3 - end - end, Used2, Event#event.auth_events), - Used4. + lists:foldl( + fun(EID, {Used3, Rev3} = Acc) -> + case maps:is_key(EID, EventSet) of + true -> + Rev4 = maps:update_with( + EID, + fun(Es) -> [EventID | Es] end, [EventID], Rev3), + case maps:is_key(EID, Used3) of + false -> + lexicographic_toposort_prepare(EID, Used3, Rev4, EventSet, Data); + true -> + {Used3, Rev4} + end; + false -> + Acc + end + end, {Used2, Rev}, Event#event.auth_events). -lexicographic_toposort_loop(Current, IncomingCnt, Res, Data) -> - %?DEBUG("toposort ~p", [{gb_trees:to_list(Current), IncomingCnt, Res}]), +lexicographic_toposort_loop(Current, OutgoingCnt, Rev, Res, Data) -> + %?DEBUG("toposort ~p", [{gb_trees:to_list(Current), OutgoingCnt, Res}]), case gb_trees:is_empty(Current) of true -> - case maps:size(IncomingCnt) of + case maps:size(OutgoingCnt) of 0 -> - Res; + lists:reverse(Res); _ -> error(loop_in_auth_chain) end; false -> {{_, _, EventID}, _, Current2} = gb_trees:take_smallest(Current), - Event = maps:get(EventID, Data#data.events), - %?DEBUG("toposort ev ~p", [Event]), - {IncomingCnt2, Current3} = + {OutgoingCnt2, Current3} = lists:foldl( - fun(EID, {InCnt, Cur} = Acc) -> - case maps:is_key(EID, InCnt) of + fun(EID, {OutCnt, Cur} = Acc) -> + case maps:is_key(EID, OutCnt) of true -> - C = maps:get(EID, InCnt) - 1, + C = maps:get(EID, OutCnt) - 1, case C of 0 -> E = maps:get(EID, Data#data.events), PowerLevel = get_sender_power_level(EID, Data), Cur2 = gb_trees:enter({-PowerLevel, E#event.origin_server_ts, EID}, [], Cur), - {maps:remove(EID, InCnt), Cur2}; + {maps:remove(EID, OutCnt), Cur2}; _ -> - {maps:put(EID, C, InCnt), Cur} + {maps:put(EID, C, OutCnt), Cur} end; false -> Acc end - end, {IncomingCnt, Current2}, Event#event.auth_events), - lexicographic_toposort_loop(Current3, IncomingCnt2, [EventID | Res], Data) + end, {OutgoingCnt, Current2}, maps:get(EventID, Rev, [])), + lexicographic_toposort_loop(Current3, OutgoingCnt2, Rev, [EventID | Res], Data) end. get_sender_power_level(EventID, Data) -> @@ -2453,28 +2623,21 @@ get_sender_power_level(EventID, Data) -> _ -> maps:get(PowerEventID, Data#data.events) end, Sender = Event#event.sender, - case PowerEvent of - undefined -> - lists:foldl( - fun(EID, Acc) -> - E = maps:get(EID, Data#data.events), - case {RoomVersion#room_version.implicit_room_creator, E} of - {false, - #event{type = ?ROOM_CREATE, state_key = <<"">>, - json = #{<<"content">> := - #{<<"creator">> := Sender}}}} -> - 100; - {true, - #event{type = ?ROOM_CREATE, state_key = <<"">>, - sender = Sender}} -> - 100; - _ -> - Acc - end - end, 0, Event#event.auth_events); - #event{json = #{<<"content">> := #{<<"users">> := #{Sender := Level}}}} -> + IsCreator = is_creator(EventID, Sender, Data), + case {RoomVersion#room_version.hydra, + IsCreator, + PowerEvent} of + {true, true, _} -> + ?CREATOR_PL; + {_, true, undefined} -> + 100; + {_, false, undefined} -> + 0; + {_, _, + #event{json = #{<<"content">> := #{<<"users">> := #{Sender := Level}}}}} -> get_int(Level); - #event{json = #{<<"content">> := #{<<"users_default">> := Level}}} -> + {_, _, + #event{json = #{<<"content">> := #{<<"users_default">> := Level}}}} -> get_int(Level); _ -> 0 @@ -2549,6 +2712,52 @@ find_power_level_event(EventID, Data) -> PLID end, undefined, Event#event.auth_events). +find_create_event(EventID, Data) -> + Event = maps:get(EventID, Data#data.events), + lists:foldl( + fun(EID, undefined) -> + E = maps:get(EID, Data#data.events), + case E of + #event{type = ?ROOM_CREATE, state_key = <<"">>} -> E; + _ -> undefined + end; + (_, Create) -> + Create + end, undefined, Event#event.auth_events). + +is_creator(EventID, User, Data) -> + case find_create_event(EventID, Data) of + undefined -> + false; + CreateEvent -> + RoomVersion = Data#data.room_version, + case {RoomVersion#room_version.hydra, + RoomVersion#room_version.implicit_room_creator, + CreateEvent} of + {false, false, + #event{type = ?ROOM_CREATE, state_key = <<"">>, + json = #{<<"content">> := + #{<<"creator">> := User}}}} -> + true; + {false, true, + #event{type = ?ROOM_CREATE, state_key = <<"">>, + sender = User}} -> + true; + {true, _, + #event{type = ?ROOM_CREATE, state_key = <<"">>, + sender = User}} -> + true; + {true, _, + #event{ + json = #{<<"content">> := + #{<<"additional_creators">> := Creators}}}} + when is_list(Creators) -> + lists:member(User, Creators); + _ -> + false + end + end. + binary_to_room_version(<<"4">>) -> #room_version{id = <<"4">>, @@ -2562,7 +2771,8 @@ binary_to_room_version(<<"4">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"5">>) -> #room_version{id = <<"5">>, @@ -2576,7 +2786,8 @@ binary_to_room_version(<<"5">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"6">>) -> #room_version{id = <<"6">>, @@ -2590,7 +2801,8 @@ binary_to_room_version(<<"6">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"7">>) -> #room_version{id = <<"7">>, @@ -2604,7 +2816,8 @@ binary_to_room_version(<<"7">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"8">>) -> #room_version{id = <<"8">>, @@ -2618,7 +2831,8 @@ binary_to_room_version(<<"8">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"9">>) -> #room_version{id = <<"9">>, @@ -2632,7 +2846,8 @@ binary_to_room_version(<<"9">>) -> knock_restricted_join_rule = false, enforce_int_power_levels = false, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"10">>) -> #room_version{id = <<"10">>, @@ -2646,7 +2861,8 @@ binary_to_room_version(<<"10">>) -> knock_restricted_join_rule = true, enforce_int_power_levels = true, implicit_room_creator = false, - updated_redaction_rules = false + updated_redaction_rules = false, + hydra = false }; binary_to_room_version(<<"11">>) -> #room_version{id = <<"11">>, @@ -2660,27 +2876,81 @@ binary_to_room_version(<<"11">>) -> knock_restricted_join_rule = true, enforce_int_power_levels = true, implicit_room_creator = true, - updated_redaction_rules = true + updated_redaction_rules = true, + hydra = false + }; +binary_to_room_version(<<"org.matrix.hydra.11">>) -> + #room_version{id = <<"org.matrix.hydra.11">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = true, + knock_restricted_join_rule = true, + enforce_int_power_levels = true, + implicit_room_creator = true, + updated_redaction_rules = true, + hydra = true + }; +binary_to_room_version(<<"12">>) -> + #room_version{id = <<"12">>, + enforce_key_validity = true, + special_case_aliases_auth = false, + strict_canonicaljson = true, + limit_notifications_power_levels = true, + knock_join_rule = true, + restricted_join_rule = true, + restricted_join_rule_fix = true, + knock_restricted_join_rule = true, + enforce_int_power_levels = true, + implicit_room_creator = true, + updated_redaction_rules = true, + hydra = true }; binary_to_room_version(_) -> false. supported_versions() -> [<<"4">>, <<"5">>, <<"6">>, <<"7">>, <<"8">>, <<"9">>, - <<"10">>, <<"11">>]. + <<"10">>, <<"11">>, <<"org.matrix.hydra.11">>, <<"12">>]. json_to_event(#{<<"type">> := Type, - <<"room_id">> := RoomID, <<"depth">> := Depth, - <<"auth_events">> := AuthEvents, + <<"auth_events">> := AuthEvents0, <<"sender">> := Sender, <<"prev_events">> := PrevEvents, <<"origin_server_ts">> := OriginServerTS} = JSON, RoomVersion) when is_binary(Type), is_integer(Depth), - is_list(AuthEvents) -> - StateKey = maps:get(<<"state_key">>, JSON, undefined), + is_list(AuthEvents0) -> EventID = mod_matrix_gw:get_event_id(JSON, RoomVersion), + {RoomID, AuthEvents} = + case RoomVersion#room_version.hydra of + true -> + case {maps:get(<<"room_id">>, JSON, undefined), Type} of + {undefined, ?ROOM_CREATE} -> + <<$$, S/binary>> = EventID, + {<<$!, S/binary>>, AuthEvents0}; + {undefined, _} -> + throw(missed_room_id); + {RID, ?ROOM_CREATE} when is_binary(RID) -> + throw(room_id_in_create); + {<<$!, S/binary>> = RID, _} -> + CreateEvent = <<$$, S/binary>>, + case lists:member(CreateEvent, AuthEvents0) of + true -> + throw(create_in_auth_events); + false -> + ok + end, + {RID, [CreateEvent | AuthEvents0]} + end; + false -> + {maps:get(<<"room_id">>, JSON), AuthEvents0} + end, + StateKey = maps:get(<<"state_key">>, JSON, undefined), case RoomVersion#room_version.strict_canonicaljson of true -> case mod_matrix_gw:is_canonical_json(JSON) of @@ -3299,10 +3569,16 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, <<"content">> := #{<<"membership">> := Membership} = Content, <<"state_key">> := StateKey}, RoomVersion) -> - Common = [{?ROOM_CREATE, <<"">>}, - {?ROOM_POWER_LEVELS, <<"">>}, - {?ROOM_MEMBER, Sender}, - {?ROOM_MEMBER, StateKey}], + Common1 = [{?ROOM_POWER_LEVELS, <<"">>}, + {?ROOM_MEMBER, Sender}, + {?ROOM_MEMBER, StateKey}], + Common = + case RoomVersion#room_version.hydra of + false -> + [{?ROOM_CREATE, <<"">>} | Common1]; + true -> + Common1 + end, case Membership of <<"join">> -> case Content of @@ -3324,10 +3600,16 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER, _ -> Common end; -compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}, _RoomVersion) -> - [{?ROOM_CREATE, <<"">>}, - {?ROOM_POWER_LEVELS, <<"">>}, - {?ROOM_MEMBER, Sender}]. +compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}, RoomVersion) -> + Common1 = + [{?ROOM_POWER_LEVELS, <<"">>}, + {?ROOM_MEMBER, Sender}], + case RoomVersion#room_version.hydra of + false -> + [{?ROOM_CREATE, <<"">>} | Common1]; + true -> + Common1 + end. update_client(#data{kind = #direct{client_state = undefined, @@ -3404,15 +3686,15 @@ send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event, IRS) -> User = <<$#, R/binary, $%, S/binary>>, case jid:nodeprep(User) of error -> - room_id_to_xmpp(RoomID); + room_id_to_xmpp(RoomID, Origin); _ -> {ok, User} end; _ -> - room_id_to_xmpp(RoomID) + room_id_to_xmpp(RoomID, Origin) end; _ -> - room_id_to_xmpp(RoomID) + room_id_to_xmpp(RoomID, Origin) end, RoomJID = jid:make(EscRoomID, ServiceHost), Invite = #muc_invite{to = undefined, from = SenderJID}, @@ -3428,6 +3710,9 @@ send_muc_invite(Host, Origin, RoomID, Sender, UserID, Event, IRS) -> end. room_id_to_xmpp(RoomID) -> + room_id_to_xmpp(RoomID, undefined). + +room_id_to_xmpp(RoomID, Origin) -> case RoomID of <<$!, Parts/binary>> -> case binary:split(Parts, <<":">>) of @@ -3436,6 +3721,16 @@ room_id_to_xmpp(RoomID) -> <> = R, HR = integer_to_binary(IR, 16), {ok, <<$!, HR/binary, $%, S/binary>>}; + [R] -> + Len = 8 * size(R), + <> = R, + HR = integer_to_binary(IR, 16), + case Origin of + undefined -> + {ok, <<$!, HR/binary>>}; + S when is_binary(S) -> + {ok, <<$!, HR/binary, $%, $%, S/binary>>} + end; _ -> error end; _ -> @@ -3446,11 +3741,21 @@ room_id_from_xmpp(Host, RID) -> case RID of <<$!, Parts/binary>> -> case binary:split(Parts, <<"%">>) of + [R, <<$%, S/binary>>] -> + IR = binary_to_integer(R, 16), + Len = size(R) * 4, + RoomID = <>, + {ok, <<$!, RoomID/binary>>, S}; [R, S] -> IR = binary_to_integer(R, 16), Len = size(R) * 4, RoomID = <>, - {ok, <<$!, RoomID/binary, $:, S/binary>>}; + {ok, <<$!, RoomID/binary, $:, S/binary>>, S}; + [R] -> + IR = binary_to_integer(R, 16), + Len = size(R) * 4, + RoomID = <>, + {ok, <<$!, RoomID/binary>>, undefined}; _ -> error end; <<$#, Parts/binary>> -> @@ -3459,7 +3764,7 @@ room_id_from_xmpp(Host, RID) -> Alias = <<$#, R/binary, $:, S/binary>>, case resolve_alias(Host, S, Alias) of {ok, <<$!, _/binary>> = RoomID} -> - {ok, RoomID}; + {ok, RoomID, S}; error -> error end; diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index e9ea5bb99..975d39369 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -44,12 +44,23 @@ {to :: binary(), pid :: pid()}). +-record(pending, + {request_id :: any(), + servers :: [binary()], + key_queue = []}). + +-record(wait, + {timer_ref :: reference(), + last :: integer}). + -record(data, {host :: binary(), matrix_server :: binary(), matrix_host_port :: {binary(), integer()} | undefined, keys = #{}, - key_queue = #{}}). + state :: #pending{} | #wait{}}). + +-define(KEYS_REQUEST_TIMEOUT, 600000). %%%=================================================================== %%% API @@ -227,8 +238,11 @@ init([Host, MatrixServer]) -> #matrix_s2s{to = MatrixServer, pid = self()}), {ok, state_name, - #data{host = Host, - matrix_server = MatrixServer}}. + request_keys( + MatrixServer, + #data{host = Host, + matrix_server = MatrixServer, + state = #wait{timer_ref = undefined, last = 0}})}. %%-------------------------------------------------------------------- %% @private @@ -246,7 +260,7 @@ init([Host, MatrixServer]) -> handle_event({call, From}, get_matrix_host_port, _State, Data) -> case Data#data.matrix_host_port of undefined -> - Result = do_get_matrix_host_port(Data), + Result = do_get_matrix_host_port(Data#data.matrix_server), Data2 = Data#data{matrix_host_port = Result}, {keep_state, Data2, [{reply, From, Result}]}; Result -> @@ -254,104 +268,59 @@ handle_event({call, From}, get_matrix_host_port, _State, Data) -> end; handle_event({call, From}, {get_key, KeyID}, State, Data) -> case maps:find(KeyID, Data#data.keys) of - {ok, {ok, _, _} = Result} -> - {keep_state, Data, [{reply, From, Result}]}; - {ok, error = Result} -> - {keep_state, Data, [{reply, From, Result}]}; - {ok, pending} -> - KeyQueue = maps:update_with( - KeyID, - fun(Xs) -> - [From | Xs] - end, - [From], - Data#data.key_queue), - {next_state, State, - Data#data{key_queue = KeyQueue}, []}; + {ok, {Key, ValidUntil}} -> + {keep_state, Data, [{reply, From, {ok, Key, ValidUntil}}]}; error -> - {MHost, MPort} = do_get_matrix_host_port(Data), - URL = <<"https://", MHost/binary, - ":", (integer_to_binary(MPort))/binary, - "/_matrix/key/v2/server/", KeyID/binary>>, - Self = self(), - httpc:request(get, {URL, []}, - [{timeout, 5000}], - [{sync, false}, - {receiver, - fun({_RequestId, Result}) -> - gen_statem:cast( - Self, {key_reply, KeyID, Result}) - end}]), - Keys = (Data#data.keys)#{KeyID => pending}, - KeyQueue = maps:update_with( - KeyID, - fun(Xs) -> - [From | Xs] - end, - [From], - Data#data.key_queue), - {next_state, State, - Data#data{keys = Keys, - key_queue = KeyQueue}, - []} + case Data#data.state of + #pending{key_queue = KeyQueue} = St -> + KeyQueue2 = [{From, KeyID} | KeyQueue], + {next_state, State, + Data#data{state = St#pending{key_queue = KeyQueue2}}, []}; + #wait{timer_ref = TimerRef, last = Last} -> + TS = erlang:system_time(millisecond), + if + Last + ?KEYS_REQUEST_TIMEOUT =< TS -> + Data2 = request_keys(Data#data.matrix_server, Data), + #pending{key_queue = KeyQueue} = St = Data2#data.state, + KeyQueue2 = [{From, KeyID} | KeyQueue], + {next_state, State, + Data2#data{state = St#pending{key_queue = KeyQueue2}}, []}; + true -> + Timeout = + case erlang:read_timer(TimerRef) of + false -> + Last + ?KEYS_REQUEST_TIMEOUT - TS; + Left -> + erlang:cancel_timer(TimerRef), + min(Left, + Last + ?KEYS_REQUEST_TIMEOUT - TS) + end, + TRef = erlang:start_timer(Timeout, self(), []), + {next_state, State, + Data#data{state = #wait{timer_ref = TRef, + last = Last}}, + [{reply, From, error}]} + end + end end; -handle_event(cast, {query, AuthParams, _Query, _JSON, _Request} = Msg, - State, Data) -> - #{<<"key">> := KeyID} = AuthParams, - case maps:find(KeyID, Data#data.keys) of - {ok, {ok, VerifyKey, _ValidUntil}} -> - Data2 = process_unverified_query( - KeyID, VerifyKey, Msg, Data), - {next_state, State, Data2, []}; - {ok, error} -> - %TODO - {next_state, State, Data, []}; - {ok, pending} -> - KeyQueue = maps:update_with( - KeyID, - fun(Xs) -> - [Msg | Xs] - end, - [Msg], - Data#data.key_queue), - {next_state, State, - Data#data{key_queue = KeyQueue}, []}; - error -> - {MHost, MPort} = do_get_matrix_host_port(Data), - URL = <<"https://", MHost/binary, - ":", (integer_to_binary(MPort))/binary, - "/_matrix/key/v2/server/", KeyID/binary>>, - Self = self(), - httpc:request(get, {URL, []}, - [{timeout, 5000}], - [{sync, false}, - {receiver, - fun({_RequestId, Result}) -> - gen_statem:cast( - Self, {key_reply, KeyID, Result}) - end}]), - Keys = (Data#data.keys)#{KeyID => pending}, - KeyQueue = maps:update_with( - KeyID, - fun(Xs) -> - [Msg | Xs] - end, - [Msg], - Data#data.key_queue), - {next_state, State, - Data#data{keys = Keys, - key_queue = KeyQueue}, - []} - end; -handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> - KeyVal = +handle_event(cast, {key_reply, RequestID, HTTPResult}, State, + #data{state = #pending{request_id = RequestID, + servers = Servers, + key_queue = KeyQueue} = St} = Data) -> + TS = erlang:system_time(millisecond), + Res = case HTTPResult of {{_, 200, _}, _, SJSON} -> try - JSON = misc:json_decode(SJSON), - ?DEBUG("key ~p~n", [JSON]), + JSON1 = misc:json_decode(SJSON), + JSON = + case JSON1 of + #{<<"server_keys">> := [J]} -> J; + _ -> JSON1 + end, + ?DEBUG("keys ~p~n", [JSON]), #{<<"verify_keys">> := VerifyKeys} = JSON, - #{KeyID := KeyData} = VerifyKeys, + {KeyID, KeyData, _} = maps:next(maps:iterator(VerifyKeys)), #{<<"key">> := SKey} = KeyData, VerifyKey = mod_matrix_gw:base64_decode(SKey), ?DEBUG("key ~p~n", [VerifyKey]), @@ -366,22 +335,77 @@ handle_event(cast, {key_reply, KeyID, HTTPResult}, State, Data) -> ValidUntil2 = min(ValidUntil, erlang:system_time(millisecond) + timer:hours(24 * 7)), - {ok, VerifyKey, ValidUntil2} + OldKeysJSON = + case JSON of + #{<<"old_verify_keys">> := OldKeysJ} + when is_map(OldKeysJ) -> + OldKeysJ; + _ -> + #{} + end, + OldKeys = + maps:filtermap( + fun(_KID, + #{<<"key">> := SK, + <<"expired_ts">> := Exp}) + when is_integer(Exp), + is_binary(SK) -> + {true, {mod_matrix_gw:base64_decode(SK), + Exp}}; + (_, _) -> false + end, OldKeysJSON), + NewKeys = + maps:filtermap( + fun(_KID, + #{<<"key">> := SK}) + when is_binary(SK) -> + {true, {mod_matrix_gw:base64_decode(SK), + ValidUntil2}}; + (_, _) -> false + end, VerifyKeys), + {ok, maps:merge(OldKeys, NewKeys), ValidUntil2} catch _:_ -> - error + {ok, Data#data.keys, TS + ?KEYS_REQUEST_TIMEOUT} end; _ -> - error + case Servers of + [] -> + {ok, Data#data.keys, TS + ?KEYS_REQUEST_TIMEOUT}; + [S | Servers1] -> + {error, + request_keys( + S, + Data#data{state = St#pending{servers = Servers1}})} + end end, - Keys = (Data#data.keys)#{KeyID => KeyVal}, - Froms = maps:get(KeyID, Data#data.key_queue, []), - KeyQueue = maps:remove(KeyID, Data#data.key_queue), - Data2 = Data#data{keys = Keys, - key_queue = KeyQueue}, - Replies = lists:map(fun(From) -> {reply, From, KeyVal} end, Froms), - ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), - {next_state, State, Data2, Replies}; + case Res of + {ok, Keys, ValidTS} -> + Replies = + lists:map( + fun({From, KeyID}) -> + case maps:find(KeyID, Keys) of + {ok, {Key, KeyValidUntil}} -> + {reply, From, {ok, Key, KeyValidUntil}}; + error -> + {reply, From, error} + end + end, + KeyQueue), + TimerRef = erlang:start_timer(max(ValidTS - TS, ?KEYS_REQUEST_TIMEOUT), + self(), []), + Data2 = Data#data{keys = Keys, + state = #wait{timer_ref = TimerRef, + last = TS}}, + ?DEBUG("KEYS ~p~n", [{Keys, Data2}]), + {next_state, State, Data2, Replies}; + {error, Data2} -> + {next_state, State, Data2, []} + end; +handle_event(info, {timeout, TimerRef, []}, State, + #data{state = #wait{timer_ref = TimerRef}} = Data) -> + Data2 = request_keys(Data#data.matrix_server, Data), + {next_state, State, Data2, []}; handle_event(cast, Msg, State, Data) -> ?WARNING_MSG("Unexpected cast: ~p", [Msg]), {next_state, State, Data, []}; @@ -427,8 +451,7 @@ callback_mode() -> %%% Internal functions %%%=================================================================== -do_get_matrix_host_port(Data) -> - MatrixServer = Data#data.matrix_server, +do_get_matrix_host_port(MatrixServer) -> case binary:split(MatrixServer, <<":">>) of [Addr] -> case inet:parse_address(binary_to_list(Addr)) of @@ -530,47 +553,48 @@ check_signature(JSON, SignatureName, KeyID, VerifyKey) -> false end. -%process_unverified_queries(KeyID, Data) -> -% case maps:find(KeyID, Data#data.keys) of -% {ok, {ok, VerifyKey, _ValidUntil}} -> -% Queue = maps:get(KeyID, Data#data.key_queue, []), -% KeyQueue = maps:remove(KeyID, Data#data.key_queue), -% Data2 = Data#data{key_queue = KeyQueue}, -% lists:foldl( -% fun(Query, DataAcc) -> -% process_unverified_query(KeyID, VerifyKey, Query, DataAcc) -% end, Data2, Queue); -% _ -> -% %% TODO -% Data -% end. - -process_unverified_query( - KeyID, VerifyKey, {query, AuthParams, _Query, Content, Request} = _Msg, Data) -> - Destination = mod_matrix_gw_opt:matrix_domain(Data#data.host), - #{<<"sig">> := Sig} = AuthParams, - JSON = #{<<"method">> => atom_to_binary(Request#request.method, latin1), - <<"uri">> => Request#request.raw_path, - <<"origin">> => Data#data.matrix_server, - <<"destination">> => Destination, - <<"signatures">> => #{ - Data#data.matrix_server => #{KeyID => Sig} - } - }, - JSON2 = - case Content of - none -> JSON; - _ -> - JSON#{<<"content">> => Content} +request_keys(Via, Data) -> + {MHost, MPort} = do_get_matrix_host_port(Via), + URL = + case Data#data.matrix_server of + Via -> + <<"https://", MHost/binary, + ":", (integer_to_binary(MPort))/binary, + "/_matrix/key/v2/server">>; + MatrixServer -> + <<"https://", MHost/binary, + ":", (integer_to_binary(MPort))/binary, + "/_matrix/key/v2/query/", MatrixServer/binary>> end, - case check_signature(JSON2, Data#data.matrix_server, KeyID, VerifyKey) of - true -> - todo_remove_me; - %process_query(Msg, Data); - false -> - ?WARNING_MSG("Failed authentication: ~p", [JSON]), - %% TODO - Data + Self = self(), + {ok, RequestID} = + httpc:request(get, {URL, []}, + [{timeout, 5000}], + [{sync, false}, + {receiver, + fun({RequestId, Result}) -> + gen_statem:cast( + Self, {key_reply, RequestId, Result}) + end}]), + case Data#data.state of + #pending{request_id = OldReqID} = St -> + case OldReqID of + undefined -> + ok; + _ -> + httpc:cancel_request(OldReqID) + end, + Data#data{state = St#pending{request_id = RequestID}}; + #wait{timer_ref = TimerRef} -> + case TimerRef of + undefined -> + ok; + _ -> + erlang:cancel_timer(TimerRef) + end, + NotaryServers = mod_matrix_gw_opt:notary_servers(Data#data.host), + Data#data{state = #pending{request_id = RequestID, + servers = NotaryServers}} end. -endif. From 5edba59b24fbaacfc972e6384fc41d8e653f5bbe Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 11 Aug 2025 20:24:59 +0300 Subject: [PATCH 1245/1302] Fix dialyzer errors --- src/mod_matrix_gw_s2s.erl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/mod_matrix_gw_s2s.erl b/src/mod_matrix_gw_s2s.erl index 975d39369..241b78ef6 100644 --- a/src/mod_matrix_gw_s2s.erl +++ b/src/mod_matrix_gw_s2s.erl @@ -51,7 +51,7 @@ -record(wait, {timer_ref :: reference(), - last :: integer}). + last :: integer()}). -record(data, {host :: binary(), @@ -242,7 +242,7 @@ init([Host, MatrixServer]) -> MatrixServer, #data{host = Host, matrix_server = MatrixServer, - state = #wait{timer_ref = undefined, last = 0}})}. + state = #wait{timer_ref = make_ref(), last = 0}})}. %%-------------------------------------------------------------------- %% @private @@ -586,12 +586,7 @@ request_keys(Via, Data) -> end, Data#data{state = St#pending{request_id = RequestID}}; #wait{timer_ref = TimerRef} -> - case TimerRef of - undefined -> - ok; - _ -> - erlang:cancel_timer(TimerRef) - end, + erlang:cancel_timer(TimerRef), NotaryServers = mod_matrix_gw_opt:notary_servers(Data#data.host), Data#data{state = #pending{request_id = RequestID, servers = NotaryServers}} From 903e6b70b4797efba23ae244042216bcbe11f65f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Aug 2025 11:36:38 +0200 Subject: [PATCH 1246/1302] mod_matrix_gw: Document what room versions are supported since when --- src/mod_matrix_gw.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index d9dddbee1..dd1ccc2e2 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -1000,9 +1000,12 @@ mod_options(Host) -> mod_doc() -> #{desc => [?T("https://matrix.org/[Matrix] gateway. "), + ?T("Supports room versions 9, 10 and 11 since ejabberd 25.03; " + "room versions 4 and higher since ejabberd 25.07; " + "room version 12 (hydra rooms) since ejabberd 25.xx. "), ?T("Erlang/OTP 25 or higher is required to use this module."), ?T("This module is available since ejabberd 24.02.")], - note => "improved in 25.07", + note => "improved in 25.xx", example => ["listen:", " -", From 48e66317516bb5a6734d62319acadca5ef0b97cc Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Aug 2025 09:54:39 +0200 Subject: [PATCH 1247/1302] mod_muc_room: Fix warning about unused variable --- src/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index a7b33bb31..b4148b33f 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3288,7 +3288,7 @@ unsubscribe_from_room(JID, SD) -> ok; true -> case mod_muc:unhibernate_room(SD#state.server_host, SD#state.host, SD#state.room) of - {error, Reason0} -> + {error, _Reason0} -> error; {ok, Pid} -> _UnsubPid = From 7815463ba09fd0ec0bc973947a6770b2ec5b65f4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 1 Aug 2025 10:51:10 +0200 Subject: [PATCH 1248/1302] ejabberd.yml.example: Use HOST_URL_ENCODE to handle case when vhost is non-latin1 --- ejabberd.yml.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 070194412..b9df7799b 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -179,7 +179,7 @@ modules: mod_fail2ban: {} mod_http_api: {} mod_http_upload: - put_url: https://@HOST@:5443/upload + put_url: https://@HOST_URL_ENCODE@:5443/upload custom_headers: "Access-Control-Allow-Origin": "https://@HOST@" "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" From 7065cb69f1009c3cc145986bcdd5d8dbb31180f9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 31 Mar 2025 17:37:11 +0200 Subject: [PATCH 1249/1302] ejabberdctl: New "mnesia_change" command, a frontend to mnesia_change_nodename --- ejabberdctl.template | 124 ++++++++++++++++++++++++++++++++++++++-- src/ejabberd_ctl.erl | 11 ++++ src/ejabberd_mnesia.erl | 7 ++- 3 files changed, 134 insertions(+), 8 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 9d91e261c..58a0d8be6 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -318,6 +318,13 @@ check_start() # allow sync calls wait_status() { + wait_status_node "$ERLANG_NODE" $1 $2 $3 +} + +wait_status_node() +{ + CONNECT_NODE=$1 + shift # args: status try delay # return: 0 OK, 1 KO timeout="$2" @@ -329,9 +336,9 @@ wait_status() status="$1" else exec_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null + -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi done @@ -340,19 +347,26 @@ wait_status() exec_other_command() { + exec_other_command_node $ERLANG_NODE "$@" +} + +exec_other_command_node() +{ + CONNECT_NODE=$1 + shift if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then exec_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" + -extra "$CONNECT_NODE" $NO_TIMEOUT "$@" result=$? case $result in 3) help;; *) :;; esac - exit $result + return $result else exec_ctl_over_http_socket "$@" fi @@ -393,6 +407,103 @@ cd "$SPOOL_DIR" || { exit 6 } +printe() +{ + printf "\n" + printf "\e[1;40;32m==> %s\e[0m\n" "$1" +} + +## Function copied from tools/make-installers +user_agrees() +{ + question="$*" + + if [ -t 0 ] + then + printe "$question (y/n) [n]" + read -r response + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]|'') + return 1 + ;; + *) + echo 'Please respond with "yes" or "no".' + user_agrees "$question" + ;; + esac + else # Assume 'yes' if not running interactively. + return 0 + fi +} + +mnesia_change() +{ + ERLANG_NODE_OLD="$1" + [ "$ERLANG_NODE_OLD" = "" ] \ + && echo "Error: Please provide the old erlang node name, for example:" \ + && echo " ejabberdctl mnesia_change ejabberd@oldmachine" \ + && exit 1 + + SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/ + OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup + NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup + + printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'" + + [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP" + + if ! user_agrees "Do you want to proceed?" + then + echo 'Operation aborted.' + exit 1 + fi + + printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..." + exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached + wait_status_node $ERLANG_NODE_OLD 0 30 2 + result=$? + case $result in + 1) echo "There was a problem starting ejabberd with the old erlang node name. " \ + && echo "Check for log errors in $EJABBERD_LOG_PATH" \ + && exit $result;; + *) :;; + esac + exec_other_command_node $ERLANG_NODE_OLD "status" + + printe "Making backup of old database to file $OLDFILE ..." + mkdir $SPOOL_DIR_BACKUP + exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE" + + printe "Changing node name in new backup file $NEWFILE ..." + exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE" + + printe "Stopping old ejabberd..." + exec_other_command_node $ERLANG_NODE_OLD "stop" + wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd + + printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..." + mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP + + printe "Starting ejabberd with new node name $ERLANG_NODE ..." + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + wait_status 0 30 2 + exec_other_command "status" + + printe "Installing fallback of new mnesia..." + exec_other_command install_fallback "$NEWFILE" + + printe "Stopping new ejabberd..." + exec_other_command "stop" + wait_status 3 30 2 && stop_epmd + + printe "Finished, now you can start ejabberd normally" +} + # main case $1 in start) @@ -452,6 +563,9 @@ case $1 in set_dist_client wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout ;; + mnesia_change) + mnesia_change $2 + ;; *) set_dist_client exec_other_command "$@" diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 72aac3780..d2bad4f19 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -1148,6 +1148,17 @@ get_commands_spec() -> desc = "Get list of commands, or help of a command (only ejabberdctl)", longdesc = "This command is exclusive for the ejabberdctl command-line script, " "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = mnesia_change, tags = [ejabberdctl, mnesia], + desc = "Change the erlang node name in the mnesia database (only ejabberdctl)", + longdesc = "This command internally calls the _`mnesia_change_nodename`_ API. " + "This is a special command that starts and stops ejabberd several times: " + "do not attempt to run this command when ejabberd is running. " + "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend.", + note = "added in 25.xx", + args = [{old_node_name, string}], + args_desc = ["Old erlang node name"], + args_example = ["ejabberd@oldmachine"]}, #ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia], desc = "Show information of Mnesia system (only ejabberdctl)", note = "renamed in 24.02", diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index f3191b910..5b82dbb50 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -80,10 +80,11 @@ init([]) -> Schema = read_schema_file(), {ok, #state{schema = Schema}}; false -> - ?CRITICAL_MSG("Node name mismatch: I'm [~ts], " - "the database is owned by ~p", [MyNode, DbNodes]), + ?CRITICAL_MSG("Erlang node name mismatch: I'm running in node [~ts], " + "but the mnesia database is owned by ~p", [MyNode, DbNodes]), ?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg " - "or change node name in Mnesia", []), + "or change node name in Mnesia by running: " + "ejabberdctl mnesia_change ~ts", [hd(DbNodes)]), {stop, node_name_mismatch} end. From d70ac7f7c542be8f04377d6574fe23668beeafa2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Aug 2025 10:30:57 +0200 Subject: [PATCH 1250/1302] ejabberd_logger: Print log lines colorized in console when using rebar3 --- include/logger.hrl | 36 +++++++++++++++++++++++++++++++----- src/ejabberd_logger.erl | 14 +++++++++----- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/include/logger.hrl b/include/logger.hrl index 27c5ffdc8..e41ab73dd 100644 --- a/include/logger.hrl +++ b/include/logger.hrl @@ -39,20 +39,46 @@ -else. -include_lib("kernel/include/logger.hrl"). +-define(CLEAD, "\e[1"). % bold +-define(CMID, "\e[0"). % normal +-define(CCLEAN, "\e[0m"). % clean + +-define(CDEFAULT, ";49;95m"). % light magenta +-define(CDEBUG, ";49;90m"). % dark gray +-define(CINFO, ";49;92m"). % green +-define(CWARNING, ";49;93m"). % light yellow +-define(CERROR, ";49;91m"). % light magenta +-define(CCRITICAL,";49;31m"). % light red + -define(DEBUG(Format, Args), - begin ?LOG_DEBUG(Format, Args), ok end). + begin ?LOG_DEBUG(Format, Args, + #{clevel => ?CLEAD ++ ?CDEBUG, + ctext => ?CMID ++ ?CDEBUG}), + ok end). -define(INFO_MSG(Format, Args), - begin ?LOG_INFO(Format, Args), ok end). + begin ?LOG_INFO(Format, Args, + #{clevel => ?CLEAD ++ ?CINFO, + ctext => ?CCLEAN}), + ok end). -define(WARNING_MSG(Format, Args), - begin ?LOG_WARNING(Format, Args), ok end). + begin ?LOG_WARNING(Format, Args, + #{clevel => ?CLEAD ++ ?CWARNING, + ctext => ?CMID ++ ?CWARNING}), + ok end). -define(ERROR_MSG(Format, Args), - begin ?LOG_ERROR(Format, Args), ok end). + begin ?LOG_ERROR(Format, Args, + #{clevel => ?CLEAD ++ ?CERROR, + ctext => ?CMID ++ ?CERROR}), + ok end). -define(CRITICAL_MSG(Format, Args), - begin ?LOG_CRITICAL(Format, Args), ok end). + begin ?LOG_CRITICAL(Format, Args, + #{clevel => ?CLEAD++ ?CCRITICAL, + ctext => ?CMID ++ ?CCRITICAL}), + ok end). -endif. %% Use only when trying to troubleshoot test problem with ExUnit diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl index 746925b29..c002914bf 100644 --- a/src/ejabberd_logger.erl +++ b/src/ejabberd_logger.erl @@ -47,6 +47,8 @@ -export_type([loglevel/0]). +-include("logger.hrl"). + %%%=================================================================== %%% API %%%=================================================================== @@ -383,19 +385,21 @@ console_template() -> false -> [time, " [", level, "] " | msg()] end. +msg() -> + [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, + msg, io_lib:nl()]. -else. console_template() -> - [time, " [", level, "] " | msg()]. + [time, " ", ?CLEAD, ?CDEFAULT, clevel, "[", level, "] ", ?CMID, ?CDEFAULT, ctext | msg()]. +msg() -> + [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, + msg, ?CCLEAN, io_lib:nl()]. -endif. file_template() -> [time, " [", level, "] ", pid, {mfa, ["@", mfa, {line, [":", line], []}], []}, " " | msg()]. -msg() -> - [{logger_formatter, [[logger_formatter, title], ":", io_lib:nl()], []}, - msg, io_lib:nl()]. - -spec reopen_log() -> ok. reopen_log() -> ok. From 97e1b419a0189b4a5a87f0fc6ef709af799beaa0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Aug 2025 11:13:15 +0200 Subject: [PATCH 1251/1302] mod_providers: New module to serve easily XMPP Providers files --- src/ejabberd_shaper.erl | 1 + src/mod_providers.erl | 462 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 463 insertions(+) create mode 100644 src/mod_providers.erl diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl index af0b3faba..d45f1dcd8 100644 --- a/src/ejabberd_shaper.erl +++ b/src/ejabberd_shaper.erl @@ -21,6 +21,7 @@ -export([start_link/0, new/1, update/2, match/3, get_max_rate/1]). -export([reload_from_config/0]). +-export([read_shaper_rules/2]). -export([validator/1, shaper_rules_validator/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, diff --git a/src/mod_providers.erl b/src/mod_providers.erl new file mode 100644 index 000000000..86493ec2f --- /dev/null +++ b/src/mod_providers.erl @@ -0,0 +1,462 @@ +%%%------------------------------------------------------------------- +%%% File : mod_providers.erl +%%% Author : Badlop +%%% Purpose : Serve xmpp-provider-v2.json files as described by XMPP Providers +%%% Created : 7 August 2025 by Badlop +%%% +%%% +%%% ejabberd, Copyright (C) 2025 ProcessOne +%%% +%%% 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 2 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, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%%| Definitions + +%% This module is based in mod_host_meta.erl + +%% @format-begin + +-module(mod_providers). + +-author('badlop@process-one.net'). + +-behaviour(gen_mod). + +-export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, mod_options/1, depends/2]). +-export([mod_doc/0]). + +-include("logger.hrl"). + +-include_lib("xmpp/include/xmpp.hrl"). + +-include("ejabberd_http.hrl"). +-include("ejabberd_web_admin.hrl"). +-include("translate.hrl"). + +%%-------------------------------------------------------------------- +%%| gen_mod callbacks + +start(_Host, _Opts) -> + report_hostmeta_listener(), + ok. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + report_hostmeta_listener(), + ok. + +depends(_Host, _Opts) -> + []. + +%%-------------------------------------------------------------------- +%%| HTTP handlers + +process([], + #request{method = 'GET', + host = Host, + path = Path}) -> + case lists:last(Path) of + <<"xmpp-provider-v2.json">> -> + file_json(Host) + end; +process(_Path, _Request) -> + {404, [], "Not Found"}. + +%%-------------------------------------------------------------------- +%%| JSON + +file_json(Host) -> + Content = + #{website => build_urls(Host, website), + alternativeJids => gen_mod:get_module_opt(Host, ?MODULE, alternativeJids), + busFactor => gen_mod:get_module_opt(Host, ?MODULE, busFactor), + organization => gen_mod:get_module_opt(Host, ?MODULE, organization), + passwordReset => get_password_url(Host), + serverTesting => gen_mod:get_module_opt(Host, ?MODULE, serverTesting), + maximumHttpFileUploadTotalSize => get_upload_size(Host), + maximumHttpFileUploadStorageTime => get_upload_time(Host), + maximumMessageArchiveManagementStorageTime => + gen_mod:get_module_opt(Host, ?MODULE, maximumMessageArchiveManagementStorageTime), + professionalHosting => gen_mod:get_module_opt(Host, ?MODULE, professionalHosting), + freeOfCharge => gen_mod:get_module_opt(Host, ?MODULE, freeOfCharge), + legalNotice => build_urls(Host, legalNotice), + serverLocations => gen_mod:get_module_opt(Host, ?MODULE, serverLocations), + since => gen_mod:get_module_opt(Host, ?MODULE, since)}, + {200, + [html, + {<<"Content-Type">>, <<"application/json">>}, + {<<"Access-Control-Allow-Origin">>, <<"*">>}], + [misc:json_encode(Content)]}. + +%%-------------------------------------------------------------------- +%%| Upload Size + +get_upload_size(Host) -> + case gen_mod:get_module_opt(Host, ?MODULE, maximumHttpFileUploadTotalSize) of + default_value -> + get_upload_size_mhuq(Host); + I when is_integer(I) -> + I + end. + +get_upload_size_mhuq(Host) -> + case gen_mod:is_loaded(Host, mod_http_upload_quota) of + true -> + Access = gen_mod:get_module_opt(Host, mod_http_upload_quota, access_hard_quota), + Rules = ejabberd_shaper:read_shaper_rules(Access, Host), + get_upload_size_rules(Rules); + false -> + 0 + end. + +get_upload_size_rules(Rules) -> + case lists:keysearch([{acl, all}], 2, Rules) of + {value, {Size, _}} -> + Size; + false -> + 0 + end. + +%%-------------------------------------------------------------------- +%%| Upload Time + +get_upload_time(Host) -> + case gen_mod:get_module_opt(Host, ?MODULE, maximumHttpFileUploadStorageTime) of + default_value -> + get_upload_time_mhuq(Host); + I when is_integer(I) -> + I + end. + +get_upload_time_mhuq(Host) -> + case gen_mod:is_loaded(Host, mod_http_upload_quota) of + true -> + case gen_mod:get_module_opt(Host, mod_http_upload_quota, max_days) of + infinity -> + 0; + I when is_integer(I) -> + I + end; + false -> + 0 + end. + +%%-------------------------------------------------------------------- +%%| Password URL + +get_password_url(Host) -> + build_urls(Host, get_password_url2(Host)). + +get_password_url2(Host) -> + case gen_mod:get_module_opt(Host, ?MODULE, passwordReset) of + default_value -> + get_password_url3(Host); + U when is_binary(U) -> + U + end. + +get_password_url3(Host) -> + case find_handler_port_path2(any, mod_register_web) of + [] -> + <<"">>; + [{ThisTls, Port, Path} | _] -> + Protocol = + case ThisTls of + false -> + <<"http">>; + true -> + <<"https">> + end, + <>))/binary, + "/">> + end. + +%% TODO Ya hay otra funciona como esta +find_handler_port_path2(Tls, Module) -> + lists:filtermap(fun ({{Port, _, _}, + ejabberd_http, + #{tls := ThisTls, request_handlers := Handlers}}) + when (Tls == any) or (Tls == ThisTls) -> + case lists:keyfind(Module, 2, Handlers) of + false -> + false; + {Path, Module} -> + {true, {ThisTls, Port, Path}} + end; + (_) -> + false + end, + ets:tab2list(ejabberd_listener)). + +%%-------------------------------------------------------------------- +%%| Build URLs + +build_urls(Host, Option) when is_atom(Option) -> + build_urls(Host, gen_mod:get_module_opt(Host, ?MODULE, Option)); +build_urls(_Host, <<"">>) -> + #{}; +build_urls(Host, Url) when not is_atom(Url) -> + Languages = gen_mod:get_module_opt(Host, ?MODULE, languages), + maps:from_list([{L, misc:expand_keyword(<<"@LANGUAGE_URL@">>, Url, L)} + || L <- Languages]). + +find_handler_port_path(Tls, Module) -> + lists:filtermap(fun ({{Port, _, _}, + ejabberd_http, + #{tls := ThisTls, request_handlers := Handlers}}) + when is_integer(Port) and ((Tls == any) or (Tls == ThisTls)) -> + case lists:keyfind(Module, 2, Handlers) of + false -> + false; + {Path, Module} -> + {true, {ThisTls, Port, Path}} + end; + (_) -> + false + end, + ets:tab2list(ejabberd_listener)). + +report_hostmeta_listener() -> + case {find_handler_port_path(false, ?MODULE), find_handler_port_path(true, ?MODULE)} of + {[], []} -> + ?CRITICAL_MSG("It seems you enabled ~p in 'modules' but forgot to " + "add it as a request_handler in an ejabberd_http " + "listener.", + [?MODULE]); + {[_ | _], _} -> + ?WARNING_MSG("Apparently ~p is enabled in a request_handler in a " + "non-encrypted ejabberd_http listener. If this is " + "not desired, enable 'tls' in that " + "listener, or setup a proxy encryption mechanism.", + [?MODULE]); + {[], [_ | _]} -> + ok + end. + +%%-------------------------------------------------------------------- +%%| Options + +mod_opt_type(languages) -> + econf:list( + econf:binary()); +mod_opt_type(website) -> + econf:binary(); +mod_opt_type(alternativeJids) -> + econf:list( + econf:domain(), [unique]); +mod_opt_type(busFactor) -> + econf:int(); +mod_opt_type(organization) -> + econf:enum([company, + 'commercial person', + 'private person', + governmental, + 'non-governmental']); +mod_opt_type(passwordReset) -> + econf:binary(); +mod_opt_type(serverTesting) -> + econf:bool(); +mod_opt_type(maximumHttpFileUploadTotalSize) -> + econf:int(); +mod_opt_type(maximumHttpFileUploadStorageTime) -> + econf:int(); +mod_opt_type(maximumMessageArchiveManagementStorageTime) -> + econf:int(); +mod_opt_type(professionalHosting) -> + econf:bool(); +mod_opt_type(freeOfCharge) -> + econf:bool(); +mod_opt_type(legalNotice) -> + econf:binary(); +mod_opt_type(serverLocations) -> + econf:list( + econf:binary()); +mod_opt_type(since) -> + econf:binary(). + +mod_options(Host) -> + [{languages, [ejabberd_option:language(Host)]}, + {website, <<"">>}, + {alternativeJids, []}, + {busFactor, -1}, + {organization, ''}, + {passwordReset, default_value}, + {serverTesting, false}, + {maximumHttpFileUploadTotalSize, default_value}, + {maximumHttpFileUploadStorageTime, default_value}, + {maximumMessageArchiveManagementStorageTime, 0}, + {professionalHosting, false}, + {freeOfCharge, false}, + {legalNotice, <<"">>}, + {serverLocations, []}, + {since, <<"">>}]. + +%%-------------------------------------------------------------------- +%%| Doc + +mod_doc() -> + #{desc => + [?T("This module serves JSON provider files API v2 as described by " + "https://providers.xmpp.net/provider-file-generator/[XMPP Providers]."), + "", + ?T("It attempts to fill some properties gathering values automatically from your existing ejabberd configuration. Try enabling the module, check what values are displayed, and then customize using the options."), + "", + ?T("To use this module, in addition to adding it to the 'modules' " + "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " + "_`listen-options.md#request_handlers|request_handlers`_. " + "Notice you should set in _`listen.md#ejabberd_http|ejabberd_http`_ " + "the option _`listen-options.md#tls|tls`_ enabled.")], + note => "added in 25.xx", + opts => + [{languages, + #{value => "[string()]", + desc => + ?T("List of language codes that your pages are available. " + "Some options define URL where the keyword '@LANGUAGE_URL@' " + "will be replaced with each of those language codes. " + "The default value is a list with the language set in the " + "option _`language`_, for example: '[en]'.")}}, + {website, + #{value => "string()", + desc => + ?T("Provider website. " + "The keyword '@LANGUAGE_URL@' is replaced with each language. " + "The default value is '\"\"'.")}}, + {alternativeJids, + #{value => "[string()]", + desc => + ?T("List of JIDs (XMPP server domains) a provider offers for " + "registration other than its main JID. " + "The default value is '[]'.")}}, + {busFactor, + #{value => "integer()", + desc => + ?T("Bus factor of the XMPP service (i.e., the minimum number of " + "team members that the service could not survive losing) or '-1' for n/a. " + "The default value is '-1'.")}}, + {organization, + #{value => "string()", + desc => + ?T("Type of organization providing the XMPP service. " + "Allowed values are: 'company', '\"commercial person\"', '\"private person\"', " + "'governmental', '\"non-governmental\"' or '\"\"'. " + "The default value is '\"\"'.")}}, + {passwordReset, + #{value => "string()", + desc => + ?T("Password reset web page (per language) used for an automatic password reset " + "(e.g., via email) or describing how to manually reset a password " + "(e.g., by contacting the provider). " + "The keyword '@LANGUAGE_URL@' is replaced with each language. " + "The default value is an URL built automatically " + "if _`mod_register_web`_ is configured as a 'request_handler', " + "or '\"\"' otherwise.")}}, + {serverTesting, + #{value => "true | false", + desc => + ?T("Whether tests against the provider's server are allowed " + "(e.g., certificate checks and uptime monitoring). " + "The default value is 'false'.")}}, + {maximumHttpFileUploadTotalSize, + #{value => "integer()", + desc => + ?T("Maximum size of all shared files in total per user (number in megabytes (MB), " + "'0' for no limit or '-1' for less than 1 MB). " + "Attention: MB is used instead of MiB (e.g., 104,857,600 bytes = 100 MiB ≈ 104 MB). " + "This property is not about the maximum size of each shared file, " + "which is already retrieved via XMPP. " + "The default value is the value of the shaper value " + "of option 'access_hard_quota' " + "from module _`mod_http_upload_quota`_, or '0' otherwise.")}}, + {maximumHttpFileUploadStorageTime, + #{value => "integer()", + desc => + ?T("Maximum storage duration of each shared file " + "(number in days, '0' for no limit or '-1' for less than 1 day). " + "The default value is the same as option 'max_days' " + "from module _`mod_http_upload_quota`_, or '0' otherwise.")}}, + {maximumMessageArchiveManagementStorageTime, + #{value => "integer()", + desc => + ?T("Maximum storage duration of each exchanged message " + "(number in days, '0' for no limit or '-1' for less than 1 day). " + "The default value is '0'.")}}, + {professionalHosting, + #{value => "true | false", + desc => + ?T("Whether the XMPP server is hosted with good internet connection speed, " + "uninterruptible power supply, access protection and regular backups. " + "The default value is 'false'.")}}, + {freeOfCharge, + #{value => "true | false", + desc => + ?T("Whether the XMPP service can be used for free. " + "The default value is 'false'.")}}, + {legalNotice, + #{value => "string()", + desc => + ?T("Legal notice web page (per language). " + "The keyword '@LANGUAGE_URL@' is replaced with each language. " + "The default value is '\"\"'.")}}, + {serverLocations, + #{value => "[string()]", + desc => + ?T("List of language codes of Server/Backup locations. " + "The default value is an empty list: '[]'.")}}, + {since, + #{value => "string()", + desc => + ?T("Date since the XMPP service is available. " + "The default value is an empty string: '\"\"'.")}}], + example => + ["listen:", + " -", + " port: 443", + " module: ejabberd_http", + " tls: true", + " request_handlers:", + " /.well-known/xmpp-provider-v2.json: mod_providers", + "", + "modules:", + " mod_providers:", + " alternativeJids: [\"example1.com\", \"example2.com\"]", + " busFactor: 1", + " freeOfCharge: true", + " languages: [ag, ao, bg, en]", + " legalNotice: \"http://@HOST@/legal/@LANGUAGE_URL@/\"", + " maximumHttpFileUploadStorageTime: 0", + " maximumHttpFileUploadTotalSize: 0", + " maximumMessageArchiveManagementStorageTime: 0", + " organization: \"non-governmental\"", + " passwordReset: \"http://@HOST@/reset/@LANGUAGE_URL@/\"", + " professionalHosting: true", + " serverLocations: [ao, bg]", + " serverTesting: true", + " since: \"2025-12-31\"", + " website: \"http://@HOST@/website/@LANGUAGE_URL@/\""]}. + +%%-------------------------------------------------------------------- + +%%| vim: set foldmethod=marker foldmarker=%%|,%%-: From ce828163af6de102f08e27972897453ec39f484e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 21 Jul 2025 10:13:37 +0200 Subject: [PATCH 1252/1302] Bump Erlang/OTP version to 27.3.4.2 --- .github/container/Dockerfile | 2 +- CONTAINER.md | 2 +- tools/make-binaries | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index 6cc9f885e..ec9a4881a 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,5 @@ #' Define default build variables -ARG OTP_VSN='27.3.4.1' +ARG OTP_VSN='27.3.4.2' ARG ELIXIR_VSN='1.18.4' ARG UID='9000' ARG USER='ejabberd' diff --git a/CONTAINER.md b/CONTAINER.md index ed6596aa6..b17d4fb40 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.4.1-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.4.2-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | diff --git a/tools/make-binaries b/tools/make-binaries index 94635189a..c3afcbe59 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.7.1' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.5.1' -otp_vsn='27.3.4.1' +otp_vsn='27.3.4.2' elixir_vsn='1.18.4' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' From fd9c929e378be5c497627c1aac5f8860c63f2e20 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 5 Aug 2025 16:25:09 +0200 Subject: [PATCH 1253/1302] Bump OpenSSL version to 3.5.2 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index c3afcbe59..51a348d16 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -70,7 +70,7 @@ termcap_vsn='1.3.1' expat_vsn='2.7.1' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.5.1' +ssl_vsn='3.5.2' otp_vsn='27.3.4.2' elixir_vsn='1.18.4' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. From 212a5ded6e7ed743b068e42f5fb0ca75dd09f516 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 10:20:02 +0000 Subject: [PATCH 1254/1302] build(deps): bump actions/download-artifact from 4 to 5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/installers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 1e7f3e761..9c4da3058 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -74,7 +74,7 @@ jobs: if: github.ref_type == 'tag' steps: - name: Download packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: ejabberd-packages - name: Draft Release From 517776acd4cf911a70716cc086efbe2619515a26 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Aug 2025 18:11:04 +0200 Subject: [PATCH 1255/1302] COMPILE.md: Mention dependencies and add link to Docs (#4431) --- COMPILE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/COMPILE.md b/COMPILE.md index 42f30a6f2..2eb8a1739 100644 --- a/COMPILE.md +++ b/COMPILE.md @@ -65,6 +65,11 @@ To configure the compilation, features, install paths... ./configure --help +The build tool automatically downloads and compiles the +erlang libraries that [ejabberd depends on][docs-repo]. + +[docs-repo]: https://docs.ejabberd.im/developer/repositories/ + Install in the System --------------------- From 41318e45a5f802f4314561abdb2471434379b9ad Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Aug 2025 13:52:25 +0200 Subject: [PATCH 1256/1302] mod_conversejs: Add option conversejs_plugins (#4413) --- src/mod_conversejs.erl | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index c8ba4eb63..280bd8088 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -64,6 +64,7 @@ process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1 CSS = get_file_url(Host, conversejs_css, <>, <<"https://cdn.conversejs.org/dist/converse.min.css">>), + PluginsHtml = get_plugins_html(Host, RawPath), Init = [{<<"discover_connection_methods">>, false}, {<<"default_domain">>, Domain}, {<<"domain_placeholder">>, Domain}, @@ -89,7 +90,8 @@ process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1 <<"">>, <<"">>, - <<"">>, + <<"">> + ] ++ PluginsHtml ++ [ <<"">>, <<"">>, <<"">> + end, + gen_mod:get_module_opt(Host, ?MODULE, conversejs_plugins)). + %%---------------------------------------------------------------------- %% WebAdmin link and autologin %%---------------------------------------------------------------------- @@ -305,6 +327,8 @@ mod_opt_type(conversejs_script) -> econf:binary(); mod_opt_type(conversejs_css) -> econf:binary(); +mod_opt_type(conversejs_plugins) -> + econf:list(econf:binary()); mod_opt_type(default_domain) -> econf:host(). @@ -315,6 +339,7 @@ mod_options(Host) -> {conversejs_resources, undefined}, {conversejs_options, []}, {conversejs_script, auto}, + {conversejs_plugins, []}, {conversejs_css, auto}]. mod_doc() -> @@ -345,6 +370,7 @@ mod_doc() -> "modules:", " mod_bosh: {}", " mod_conversejs:", + " conversejs_plugins: [\"libsignal\"]", " websocket_url: \"ws://@HOST@:5280/websocket\""]}, {?T("Host Converse locally and let auto detection of WebSocket and Converse URLs:"), ["listen:", @@ -358,7 +384,9 @@ mod_doc() -> "", "modules:", " mod_conversejs:", - " conversejs_resources: \"/home/ejabberd/conversejs-9.0.0/package/dist\""]}, + " conversejs_resources: \"/home/ejabberd/conversejs-x.y.z/package/dist\"", + " conversejs_plugins: [\"libsignal-protocol.min.js\"]", + " # File path is: /home/ejabberd/conversejs-x.y.z/package/dist/plugins/libsignal-protocol.min.js"]}, {?T("Configure some additional options for Converse"), ["modules:", " mod_conversejs:", @@ -410,6 +438,15 @@ mod_doc() -> "See https://conversejs.org/docs/html/configuration.html[Converse configuration]. " "Only boolean, integer and string values are supported; " "lists are not supported.")}}, + {conversejs_plugins, + #{value => ?T("[Filename]"), + desc => + ?T("List of additional local files to include as scripts in the homepage. " + "Please make sure those files are available in the path specified in " + "'conversejs_resources' option, in subdirectory 'plugins/'. " + "If using the public Converse client, then '\"libsignal\"' " + "gets replaced with the URL of the public library. " + "The default value is '[]'.")}}, {conversejs_script, #{value => ?T("auto | URL"), desc => From 51af393baab737d5834ee281e29be3c7235d920b Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 15 Aug 2025 04:23:34 +0300 Subject: [PATCH 1257/1302] Add leave_timeout mod_matrix_gw option (#4386) --- src/mod_matrix_gw.erl | 13 +++++-- src/mod_matrix_gw_opt.erl | 7 ++++ src/mod_matrix_gw_room.erl | 77 +++++++++++++++++++++++++------------- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index dd1ccc2e2..baafd64dc 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -984,7 +984,9 @@ mod_opt_type(key) -> mod_opt_type(matrix_id_as_jid) -> econf:bool(); mod_opt_type(notary_servers) -> - econf:list(econf:host()). + econf:list(econf:host()); +mod_opt_type(leave_timeout) -> + econf:non_neg_int(). -spec mod_options(binary()) -> [{key, {binary(), binary()}} | {atom(), any()}]. @@ -995,7 +997,8 @@ mod_options(Host) -> {key_name, <<"">>}, {key, {<<"">>, <<"">>}}, {matrix_id_as_jid, false}, - {notary_servers, []}]. + {notary_servers, []}, + {leave_timeout, 0}]. mod_doc() -> #{desc => @@ -1058,7 +1061,11 @@ mod_doc() -> {notary_servers, #{value => "[Server, ...]", desc => - ?T("A list of notary servers.")}} + ?T("A list of notary servers.")}}, + {leave_timeout, + #{value => "integer()", + desc => + ?T("Delay in seconds between a user leaving a MUC room and sending 'leave' Matrix event.")}} ] }. -endif. diff --git a/src/mod_matrix_gw_opt.erl b/src/mod_matrix_gw_opt.erl index 9e03c7072..974aafd48 100644 --- a/src/mod_matrix_gw_opt.erl +++ b/src/mod_matrix_gw_opt.erl @@ -6,6 +6,7 @@ -export([host/1]). -export([key/1]). -export([key_name/1]). +-export([leave_timeout/1]). -export([matrix_domain/1]). -export([matrix_id_as_jid/1]). -export([notary_servers/1]). @@ -28,6 +29,12 @@ key_name(Opts) when is_map(Opts) -> key_name(Host) -> gen_mod:get_module_opt(Host, mod_matrix_gw, key_name). +-spec leave_timeout(gen_mod:opts() | global | binary()) -> non_neg_integer(). +leave_timeout(Opts) when is_map(Opts) -> + gen_mod:get_opt(leave_timeout, Opts); +leave_timeout(Host) -> + gen_mod:get_module_opt(Host, mod_matrix_gw, leave_timeout). + -spec matrix_domain(gen_mod:opts() | global | binary()) -> binary(). matrix_domain(Opts) when is_map(Opts) -> gen_mod:get_opt(matrix_domain, Opts); diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 344aecc60..1525b99dd 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -81,7 +81,9 @@ room_jid :: jid()}). -record(multi, - {users :: #{{binary(), binary()} => #{binary() => #multi_user{}}}}). + {users :: #{{binary(), binary()} => + ({online, #{binary() => #multi_user{}}} | + {offline, reference()})}}). -record(data, {host :: binary(), @@ -726,7 +728,7 @@ handle_event(cast, {join, UserJID, Packet, Via}, _State, Data) -> Host = Data#data.host, {LUser, LServer, LResource} = jid:tolower(UserJID), case Data#data.kind of - #multi{users = #{{LUser, LServer} := #{LResource := _}}} -> + #multi{users = #{{LUser, LServer} := {online, #{LResource := _}}}} -> {keep_state_and_data, []}; #multi{} = Kind -> case user_id_from_jid(UserJID, Host) of @@ -740,7 +742,10 @@ handle_event(cast, {join, UserJID, Packet, Via}, _State, Data) -> Users = Kind#multi.users, Resources = case Users of - #{{LUser, LServer} := Rs} -> Rs; + #{{LUser, LServer} := {online, Rs}} -> Rs; + #{{LUser, LServer} := {offline, TimerRef}} -> + erlang:cancel_timer(TimerRef), + #{}; _ -> #{} end, RoomJID = jid:remove_resource(xmpp:get_to(Packet)), @@ -750,9 +755,10 @@ handle_event(cast, {join, UserJID, Packet, Via}, _State, Data) -> Kind#multi{ users = Users#{{LUser, LServer} => - Resources#{LResource => - #multi_user{join_ts = JoinTS, - room_jid = RoomJID}}}}}, + {online, + Resources#{LResource => + #multi_user{join_ts = JoinTS, + room_jid = RoomJID}}}}}}, {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; error -> ?INFO_MSG("bad join user id: ~p", [UserJID]), @@ -821,8 +827,9 @@ handle_event(cast, {join, UserJID, Packet, Via}, _State, Data) -> kind = #multi{users = #{{LUser, LServer} => - #{LResource => #multi_user{join_ts = JoinTS, - room_jid = RoomJID}}}}, + {online, + #{LResource => #multi_user{join_ts = JoinTS, + room_jid = RoomJID}}}}}, room_version = RoomVersion}) end; _JSON -> @@ -871,22 +878,19 @@ handle_event(cast, {leave, UserJID}, _State, Data) -> Host = Data#data.host, {LUser, LServer, LResource} = jid:tolower(UserJID), case Data#data.kind of - #multi{users = #{{LUser, LServer} := #{LResource := _} = Resources} = Users} -> + #multi{users = #{{LUser, LServer} := {online, #{LResource := _} = Resources}} = Users} -> Resources2 = maps:remove(LResource, Resources), if Resources2 == #{} -> - Users2 = maps:remove({LUser, LServer}, Users), + LeaveTimeout = mod_matrix_gw_opt:leave_timeout(Host) * 1000, + TimerRef = erlang:start_timer(LeaveTimeout, self(), + {leave, LUser, LServer}), + Users2 = Users#{{LUser, LServer} => {offline, TimerRef}}, Kind = (Data#data.kind)#multi{users = Users2}, Data2 = Data#data{kind = Kind}, - {ok, UserID} = user_id_from_jid(UserJID, Host), - JSON = #{<<"content">> => - #{<<"membership">> => <<"leave">>}, - <<"sender">> => UserID, - <<"state_key">> => UserID, - <<"type">> => ?ROOM_MEMBER}, - {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; + {keep_state, Data2, []}; true -> - Users2 = Users#{{LUser, LServer} => Resources2}, + Users2 = Users#{{LUser, LServer} => {online, Resources2}}, Kind = (Data#data.kind)#multi{users = Users2}, Data2 = Data#data{kind = Kind}, {keep_state, Data2, []} @@ -1007,6 +1011,23 @@ handle_event(info, {resend_txn, Server}, _State, Data) -> _ -> {keep_state, Data, []} end; +handle_event(info, {timeout, TimerRef, {leave, LUser, LServer}}, State, Data) -> + Host = Data#data.host, + case Data#data.kind of + #multi{users = #{{LUser, LServer} := {offline, TimerRef}} = Users} -> + Users2 = maps:remove({LUser, LServer}, Users), + Kind = (Data#data.kind)#multi{users = Users2}, + Data2 = Data#data{kind = Kind}, + {ok, UserID} = user_id_from_jid(jid:make(LUser, LServer), Host), + JSON = #{<<"content">> => + #{<<"membership">> => <<"leave">>}, + <<"sender">> => UserID, + <<"state_key">> => UserID, + <<"type">> => ?ROOM_MEMBER}, + {keep_state, Data2, [{next_event, cast, {add_event, JSON}}]}; + _ -> + {next_state, State, Data, []} + end; handle_event(info, Info, State, Data) -> ?WARNING_MSG("Unexpected info: ~p", [Info]), {next_state, State, Data, []}. @@ -3109,7 +3130,7 @@ notify_event_xmpp( <<$@, SenderUser/binary>> -> ?DEBUG("notify xmpp ~p", [Users]), maps:fold( - fun({LUser, LServer}, Resources, ok) -> + fun({LUser, LServer}, {online, Resources}, ok) -> maps:fold( fun(LResource, #multi_user{join_ts = JoinTS, room_jid = RoomJID}, ok) @@ -3135,7 +3156,9 @@ notify_event_xmpp( Msg, Data#data.room_jid, TimeStamp), ejabberd_router:route(TSMsg); (_, _, _) -> ok - end, ok, Resources) + end, ok, Resources); + (_, _, ok) -> + ok end, ok, Users), Data; _ -> @@ -3150,7 +3173,7 @@ notify_event_xmpp( #jid{} = SenderJID -> <<$@, SenderUser/binary>> = Sender, maps:fold( - fun({LUser, LServer}, Resources, ok) -> + fun({LUser, LServer}, {online, Resources}, ok) -> maps:fold( fun(LResource, #multi_user{join_ts = JoinTS, room_jid = RoomJID}, ok) @@ -3206,7 +3229,9 @@ notify_event_xmpp( false -> ok end; (_, _, _) -> ok - end, ok, Resources) + end, ok, Resources); + (_, _, ok) -> + ok end, ok, Users), Data; error -> @@ -3223,7 +3248,7 @@ notify_event_xmpp( case StateKey of <<$@, RUser/binary>> -> maps:fold( - fun({LUser, LServer}, Resources, ok) -> + fun({LUser, LServer}, {online, Resources}, ok) -> maps:fold( fun(LResource, #multi_user{join_ts = JoinTS, room_jid = RoomJID}, ok) @@ -3239,13 +3264,15 @@ notify_event_xmpp( }, ejabberd_router:route(Pres); (_, _, _) -> ok - end, ok, Resources) + end, ok, Resources); + (_, _, ok) -> + ok end, ok, Users), case user_id_to_jid(StateKey, Data) of #jid{} = RJID -> US = {RJID#jid.luser, RJID#jid.lserver}, case Users of - #{US := Resources} -> + #{US := {online, Resources}} -> JoinTS = maps:fold( fun(_, #multi_user{join_ts = TS}, Acc) -> From a02c75aa086d1b34958347e8c98249a8386be11f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 15 Aug 2025 04:45:08 +0300 Subject: [PATCH 1258/1302] Add support for null values in is_canonical_json (thanks to snoopcatt) (#4421) --- src/mod_matrix_gw.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index baafd64dc..5f9705422 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -744,6 +744,8 @@ is_canonical_json(B) when is_binary(B) -> true; is_canonical_json(B) when is_boolean(B) -> true; +is_canonical_json(null) -> + true; is_canonical_json(Map) when is_map(Map) -> maps:fold( fun(_K, V, true) -> From 8b61cf074232994c53c66a09545e814f6174e703 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 15 Aug 2025 04:50:33 +0300 Subject: [PATCH 1259/1302] Don't send empty direct Matrix messages (thanks to snoopcatt) (#4420) --- src/mod_matrix_gw_room.erl | 202 +++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 99 deletions(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 1525b99dd..4928c7582 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -265,105 +265,109 @@ route(#message{from = From, to = To, body = Body} = _Pkt) -> Host = ejabberd_config:get_myname(), case user_id_from_jid(To, Host) of {ok, ToMatrixID} -> - Key = {{From#jid.luser, From#jid.lserver}, ToMatrixID}, - Text = xmpp:get_text(Body), - case mnesia:dirty_read(matrix_direct, Key) of - [#matrix_direct{room_id = RoomID}] -> - ?DEBUG("msg ~p~n", [{RoomID, From, ToMatrixID, Text}]), - case get_existing_room_pid(Host, RoomID) of - {ok, Pid} -> - MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - FromMatrixID = - <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, - JSON = - #{<<"content">> => - #{<<"body">> => Text, - <<"msgtype">> => <<"m.text">>}, - <<"sender">> => FromMatrixID, - <<"type">> => ?ROOM_MESSAGE}, - gen_statem:cast(Pid, {add_event, JSON}), - ok; - {error, _} -> - %%TODO - ok - end; - _ -> - RoomID = new_room_id(), - ?DEBUG("new room id ~p~n", [RoomID]), - case get_room_pid(Host, RoomID) of - {ok, Pid} -> - MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), - FromMatrixID = - <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, - gen_statem:cast(Pid, {create, MatrixServer, RoomID, - FromMatrixID, ToMatrixID}), - JSONs = - [#{<<"content">> => - #{<<"creator">> => FromMatrixID, - <<"room_version">> => <<"9">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => <<"">>, - <<"type">> => ?ROOM_CREATE}, - #{<<"content">> => - #{<<"membership">> => <<"join">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => FromMatrixID, - <<"type">> => ?ROOM_MEMBER}, - #{<<"content">> => - #{<<"ban">> => 50, - <<"events">> => - #{<<"m.room.avatar">> => 50, - <<"m.room.canonical_alias">> => 50, - <<"m.room.encryption">> => 100, - <<"m.room.history_visibility">> => 100, - <<"m.room.name">> => 50, - <<"m.room.power_levels">> => 100, - <<"m.room.server_acl">> => 100, - <<"m.room.tombstone">> => 100}, - <<"events_default">> => 0, - <<"historical">> => 100, - <<"invite">> => 0, - <<"kick">> => 50, - <<"redact">> => 50, - <<"state_default">> => 50, - <<"users">> => - #{FromMatrixID => 100, - ToMatrixID => 100}, - <<"users_default">> => 0}, - <<"sender">> => FromMatrixID, - <<"state_key">> => <<"">>, - <<"type">> => ?ROOM_POWER_LEVELS}, - #{<<"content">> => #{<<"join_rule">> => <<"invite">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => <<"">>, - <<"type">> => ?ROOM_JOIN_RULES}, - #{<<"content">> => #{<<"history_visibility">> => <<"shared">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => <<"">>, - <<"type">> => ?ROOM_HISTORY_VISIBILITY}, - #{<<"content">> => #{<<"guest_access">> => <<"can_join">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => <<"">>, - <<"type">> => <<"m.room.guest_access">>}, - #{<<"content">> => - #{<<"is_direct">> => true, - <<"membership">> => <<"invite">>}, - <<"sender">> => FromMatrixID, - <<"state_key">> => ToMatrixID, - <<"type">> => ?ROOM_MEMBER}, - #{<<"content">> => - #{<<"body">> => Text, - <<"msgtype">> => <<"m.text">>}, - <<"sender">> => FromMatrixID, - <<"type">> => ?ROOM_MESSAGE} - ], - lists:foreach(fun(JSON) -> - gen_statem:cast(Pid, {add_event, JSON}) - end, JSONs), - ok; - {error, _} -> - %%TODO - ok + case xmpp:get_text(Body) of + <<"">> -> + ok; + Text -> + Key = {{From#jid.luser, From#jid.lserver}, ToMatrixID}, + case mnesia:dirty_read(matrix_direct, Key) of + [#matrix_direct{room_id = RoomID}] -> + ?DEBUG("msg ~p~n", [{RoomID, From, ToMatrixID, Text}]), + case get_existing_room_pid(Host, RoomID) of + {ok, Pid} -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + FromMatrixID = + <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, + JSON = + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>}, + <<"sender">> => FromMatrixID, + <<"type">> => ?ROOM_MESSAGE}, + gen_statem:cast(Pid, {add_event, JSON}), + ok; + {error, _} -> + %%TODO + ok + end; + _ -> + RoomID = new_room_id(), + ?DEBUG("new room id ~p~n", [RoomID]), + case get_room_pid(Host, RoomID) of + {ok, Pid} -> + MatrixServer = mod_matrix_gw_opt:matrix_domain(Host), + FromMatrixID = + <<$@, (From#jid.luser)/binary, $:, MatrixServer/binary>>, + gen_statem:cast(Pid, {create, MatrixServer, RoomID, + FromMatrixID, ToMatrixID}), + JSONs = + [#{<<"content">> => + #{<<"creator">> => FromMatrixID, + <<"room_version">> => <<"9">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_CREATE}, + #{<<"content">> => + #{<<"membership">> => <<"join">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => FromMatrixID, + <<"type">> => ?ROOM_MEMBER}, + #{<<"content">> => + #{<<"ban">> => 50, + <<"events">> => + #{<<"m.room.avatar">> => 50, + <<"m.room.canonical_alias">> => 50, + <<"m.room.encryption">> => 100, + <<"m.room.history_visibility">> => 100, + <<"m.room.name">> => 50, + <<"m.room.power_levels">> => 100, + <<"m.room.server_acl">> => 100, + <<"m.room.tombstone">> => 100}, + <<"events_default">> => 0, + <<"historical">> => 100, + <<"invite">> => 0, + <<"kick">> => 50, + <<"redact">> => 50, + <<"state_default">> => 50, + <<"users">> => + #{FromMatrixID => 100, + ToMatrixID => 100}, + <<"users_default">> => 0}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_POWER_LEVELS}, + #{<<"content">> => #{<<"join_rule">> => <<"invite">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_JOIN_RULES}, + #{<<"content">> => #{<<"history_visibility">> => <<"shared">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => ?ROOM_HISTORY_VISIBILITY}, + #{<<"content">> => #{<<"guest_access">> => <<"can_join">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => <<"">>, + <<"type">> => <<"m.room.guest_access">>}, + #{<<"content">> => + #{<<"is_direct">> => true, + <<"membership">> => <<"invite">>}, + <<"sender">> => FromMatrixID, + <<"state_key">> => ToMatrixID, + <<"type">> => ?ROOM_MEMBER}, + #{<<"content">> => + #{<<"body">> => Text, + <<"msgtype">> => <<"m.text">>}, + <<"sender">> => FromMatrixID, + <<"type">> => ?ROOM_MESSAGE} + ], + lists:foreach(fun(JSON) -> + gen_statem:cast(Pid, {add_event, JSON}) + end, JSONs), + ok; + {error, _} -> + %%TODO + ok + end end end; error -> From 38b203feb1d5c584ed0f93208639445054033b42 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Aug 2025 11:28:39 +0200 Subject: [PATCH 1260/1302] ejabberd_listener: Use init_fail for errors as recommended by init_ack That is recommended since OTP 26, see https://www.erlang.org/doc/apps/stdlib/proc_lib.html#init_ack/2 Warning Do not use this function to return an error indicating that the process start failed. When doing so the start function can return before the failing process has exited, which may block VM resources required for a new start attempt to succeed. Use init_fail/2,3 for that purpose. --- src/ejabberd_listener.erl | 46 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index f82b1e05b..5528b3a7f 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -139,16 +139,13 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) -> {error, _} -> ok end; - {error, Reason} = Err -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err) + {error, Reason} -> + return_socket_error(Reason, EndPoint, Module) end; - {{error, Reason} = Err, _} -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err); - {_, {error, Reason} = Err} -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err) + {{error, Reason}, _} -> + return_socket_error(Reason, EndPoint, Module); + {_, {error, Reason} } -> + return_socket_error(Reason, EndPoint, Module) end; init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> case {listen_tcp(Port, SockOpts), @@ -177,16 +174,13 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) -> {error, _} -> ok end; - {error, Reason} = Err -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err) + {error, Reason} -> + return_socket_error(Reason, EndPoint, Module) end; - {{error, Reason}, _} = Err -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err); - {_, {error, Reason}} = Err -> - report_socket_error(Reason, EndPoint, Module), - proc_lib:init_ack(Err) + {{error, Reason}, _} -> + return_socket_error(Reason, EndPoint, Module); + {_, {error, Reason}} -> + return_socket_error(Reason, EndPoint, Module) end. -spec listen_tcp(inet:port_number(), [gen_tcp:option()]) -> @@ -608,10 +602,20 @@ config_reloaded() -> end end, New). --spec report_socket_error(inet:posix(), endpoint(), module()) -> ok. -report_socket_error(Reason, EndPoint, Module) -> + +-spec return_socket_error(inet:posix(), endpoint(), module()) -> ok. +return_socket_error(Reason, EndPoint, Module) -> ?ERROR_MSG("Failed to open socket at ~ts for ~ts: ~ts", - [format_endpoint(EndPoint), Module, format_error(Reason)]). + [format_endpoint(EndPoint), Module, format_error(Reason)]), + return_init_error(Reason). + +-ifdef(OTP_BELOW_26). +return_init_error(Reason) -> + proc_lib:init_ack({error, Reason}). +-else. +return_init_error(Reason) -> + proc_lib:init_fail({error, Reason}, {exit, normal}). +-endif. -spec format_error(inet:posix() | atom()) -> string(). format_error(Reason) -> From e1dc686ae78251f08813ca17e8642d1aeb281f2f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Aug 2025 15:12:41 +0200 Subject: [PATCH 1261/1302] mod_conversejs: Ensure assets_path ends in / as required by Converse (#4414) --- src/mod_conversejs.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 280bd8088..f6462e584 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -69,7 +69,7 @@ process([], #request{method = 'GET', host = Host, q = Query, raw_path = RawPath1 {<<"default_domain">>, Domain}, {<<"domain_placeholder">>, Domain}, {<<"registration_domain">>, Domain}, - {<<"assets_path">>, RawPath}, + {<<"assets_path">>, <>}, {<<"view_mode">>, <<"fullscreen">>} | ExtraOptions], Init2 = From 3183e2f7337f9fa1f016f99bd3cf5d0619ee161b Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Aug 2025 16:32:54 +0200 Subject: [PATCH 1262/1302] Fix dialyzer warnings in recent commit --- src/ejabberd_listener.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 5528b3a7f..9f0f5a162 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -602,8 +602,7 @@ config_reloaded() -> end end, New). - --spec return_socket_error(inet:posix(), endpoint(), module()) -> ok. +-spec return_socket_error(inet:posix(), endpoint(), module()) -> no_return(). return_socket_error(Reason, EndPoint, Module) -> ?ERROR_MSG("Failed to open socket at ~ts for ~ts: ~ts", [format_endpoint(EndPoint), Module, format_error(Reason)]), @@ -613,6 +612,7 @@ return_socket_error(Reason, EndPoint, Module) -> return_init_error(Reason) -> proc_lib:init_ack({error, Reason}). -else. +-spec return_init_error(inet:posix()) -> no_return(). return_init_error(Reason) -> proc_lib:init_fail({error, Reason}, {exit, normal}). -endif. From ff3d33dde491a6aa585a3f8be806360c20ce6ca0 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 18 Aug 2025 16:23:33 +0200 Subject: [PATCH 1263/1302] Bump xmpp version Allow for adding HTTP File Upload purposes support to ejabberd. --- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 11 ++++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/mix.exs b/mix.exs index 70a9c5fc9..e0bbd0a9c 100644 --- a/mix.exs +++ b/mix.exs @@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.11.0"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f", override: true}, {:yconf, git: "https://github.com/processone/yconf", ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 288453f3a..cdaac67b6 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"}, "stun": {:hex, :stun, "1.2.20", "62a149cea122a78a104b9e064a12d9e33105b26c23168ecf3aea6e0c26de0748", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "79e49f826a4f7d522c939ab633d935c79d7d6b229e4cb7e05f62f33b50177414"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, - "xmpp": {:hex, :xmpp, "1.11.0", "a3158c486c9b86a7090c361d876db622381f4312ede8c125d7a52ad390387932", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "34a191d6a3b74e8f0a42346f859e2cab5b3a2ae7e5c28f392e5cb56612e7ce85"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f", [ref: "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"]}, "yconf": {:git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", [ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"]}, } diff --git a/rebar.config b/rebar.config index 039fdd40a..a2ebae777 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}}, {if_var_true, stun, {stun, "~> 1.2.20", {git, "https://github.com/processone/stun", {tag, "1.2.20"}}}}, - {xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", {tag, "1.11.0"}}}, + {xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"}}, {yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}} ]}. diff --git a/rebar.lock b/rebar.lock index 921129904..66b130643 100644 --- a/rebar.lock +++ b/rebar.lock @@ -27,7 +27,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.20">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.0">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"}}, + 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", {ref,"c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}}, @@ -57,8 +60,7 @@ {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, {<<"stun">>, <<"62A149CEA122A78A104B9E064A12D9E33105B26C23168ECF3AEA6E0C26DE0748">>}, - {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, - {<<"xmpp">>, <<"A3158C486C9B86A7090C361D876DB622381F4312EDE8C125D7A52AD390387932">>}]}, + {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, @@ -83,6 +85,5 @@ {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, {<<"stun">>, <<"79E49F826A4F7D522C939AB633D935C79D7D6B229E4CB7E05F62F33B50177414">>}, - {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, - {<<"xmpp">>, <<"34A191D6A3B74E8F0A42346F859E2CAB5B3A2AE7E5C28F392E5CB56612E7CE85">>}]} + {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]} ]. From 654d4b81b15721f7a999dddc8cb24c14cc4333fa Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 19 Aug 2025 20:03:01 +0200 Subject: [PATCH 1264/1302] mod_register: Don't duplicate welcome message Originally, the welcome message was sent as type 'normal'. Apparently, some clients don't display 'normal' messages as expected (see #4246). To address that issue, commit 9a0ff13cc2c228e45e9def48f3e25122003c1e6f duplicated the welcome message as type 'chat'. However, we shouldn't send both formats. The 'normal' message is either ignored by the client, in which case it serves no purpose, or displayed, in which case the user would see the message twice. --- src/mod_register.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 3c32bc362..7302841ad 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -420,12 +420,7 @@ send_welcome_message(JID) -> to = JID, type = chat, subject = xmpp:mk_text(Subj), - body = xmpp:mk_text(<>)}), - ejabberd_router:route( - #message{from = jid:make(Host), - to = JID, - subject = xmpp:mk_text(Subj), - body = xmpp:mk_text(Body)}) + body = xmpp:mk_text(<>)}) end. send_registration_notifications(Mod, UJID, Source) -> From a46325166a2059ed9f2b0570fe4811ddb3210cb9 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 19 Aug 2025 20:09:17 +0200 Subject: [PATCH 1265/1302] mod_register: Don't duplicate welcome subject Don't include the configured welcome message subject with the body. If that's desired, the admin can simply configure it that way. But if it's undesired, there would be no way to avoid the subject duplication. --- src/mod_register.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 7302841ad..793c3c54d 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -420,7 +420,7 @@ send_welcome_message(JID) -> to = JID, type = chat, subject = xmpp:mk_text(Subj), - body = xmpp:mk_text(<>)}) + body = xmpp:mk_text(Body)}) end. send_registration_notifications(Mod, UJID, Source) -> From 74c810eeaa5ca1e7f44243bef7cdd92912c3d60f Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 20 Aug 2025 13:35:18 +0200 Subject: [PATCH 1266/1302] Tag dependencies --- mix.exs | 6 +++--- mix.lock | 14 +++++++------- rebar.config | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mix.exs b/mix.exs index e0bbd0a9c..630f0939a 100644 --- a/mix.exs +++ b/mix.exs @@ -125,13 +125,13 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:mqtree, "~> 1.0"}, - {:p1_acme, git: "https://github.com/processone/p1_acme", ref: "edff1396d44d7bde6b2b44ca5b0dd91450319a3e", override: true}, + {:p1_acme, ">= 1.0.28"}, {:p1_oauth2, "~> 0.6"}, {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f", override: true}, - {:yconf, git: "https://github.com/processone/yconf", ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", override: true}] + {:xmpp, ">= 1.11.1"}, + {:yconf, ">= 1.0.21"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index cdaac67b6..aca94aa44 100644 --- a/mix.lock +++ b/mix.lock @@ -7,11 +7,11 @@ "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "esip": {:hex, :esip, "1.0.58", "96bf0c07271f86f03f42778d4a1237099baec0714d00b0b815eb42d0007f73b4", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.20", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "e0f4204a5ede0fa7d00da3cc42f6440aa362bac7faf536f71ea29fa3f0fa7c75"}, + "esip": {:hex, :esip, "1.0.59", "eb202f8c62928193588091dfedbc545fe3274c34ecd209961f86dcb6c9ebce88", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "0bdf2e3c349dc0b144f173150329e675c6a51ac473d7a0b2e362245faad3fbe6"}, "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.15", "d74f5df191784744726a5b1ae9062522c606334f11086363385eb3b772d91357", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd14ba6c12521af5cfe6923e73e3d545f4a0897dc66bfab5287fbb7ae3962eab"}, - "fast_tls": {:hex, :fast_tls, "1.1.24", "5492125689e3d84c101323a0bff3d3996b92a903832530fe4f0935ed30b1b7d1", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "fff88ada39fad10464567a160643f4529ef4aed49d156919f5d1f415b6cdbbb6"}, + "fast_tls": {:hex, :fast_tls, "1.1.25", "da8ed6f05a2452121b087158b17234749f36704c1f2b74dc51db99a1e27ed5e8", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "59e183b5740e670e02b8aa6be673b5e7779e5fe5bfcc679fe2d4993d1949a821"}, "fast_xml": {:hex, :fast_xml, "1.1.57", "31efc0f9bceda92069704f7a25830407da5dc3dad1272b810d6f2e13e73cc11a", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "eec34e90adacafe467d5ddab635a014ded73b98b4061554b2d1972173d929c39"}, "fast_yaml": {:hex, :fast_yaml, "1.0.39", "2e71168091949bab0e5f583b340a99072b4d22d93eb86624e7850a12b1517be4", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "24c7b9ab9e2b9269d64e45f4a2a1280966adb17d31e63365cfd3ee277fb0a78d"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, @@ -24,16 +24,16 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mqtree": {:hex, :mqtree, "1.0.19", "d769c25f898810725fc7db0dbffe5f72098647048b1be2e6d772f1c2f31d8476", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c81065715c49a1882812f80a5ae2d842e80dd3f2d130530df35990248bf8ce3c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "p1_acme": {:git, "https://github.com/processone/p1_acme", "edff1396d44d7bde6b2b44ca5b0dd91450319a3e", [ref: "edff1396d44d7bde6b2b44ca5b0dd91450319a3e"]}, + "p1_acme": {:hex, :p1_acme, "1.0.28", "64d9c17f5412aa92d75b29206b2b984d734a4fe1b7eacb66c3d7a7c697ac612c", [:rebar3], [{:base64url, "~> 1.0", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~> 1.1.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~> 1.0.17", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "ce686986de3f9d5fd285afe87523cb45329a349c6c6be7acc1ed916725d46423"}, "p1_mysql": {:hex, :p1_mysql, "1.0.26", "574d07c9936c53b1ec3556db3cf064cc14a6c39039835b3d940471bfa5ac8e2b", [:rebar3], [], "hexpm", "ea138083f2c54719b9cf549dbf5802a288b0019ea3e5449b354c74cc03fafdec"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.14", "1c5f82535574de87e2059695ac4b91f8f9aebacbc1c80287dae6f02552d47aea", [:rebar3], [], "hexpm", "1fd3ac474e43722d9d5a87c6df8d36f698ed87af7bb81cbbb66361451d99ae8f"}, - "p1_pgsql": {:hex, :p1_pgsql, "1.1.34", "d36bd0d6c9765a47d075a87ad5e3fc3bfd153bdc4c07a1217b9979f33f73e9aa", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "cb0e32e086c9c35d0e3e966e3863d832737c7b4d2b5f147316a465c0b243ea7f"}, + "p1_pgsql": {:hex, :p1_pgsql, "1.1.35", "e13d89f14d717553e85c88a152ce77461916b013d88fcb851e354a0b332d4218", [:rebar3], [{:xmpp, "~> 1.11.0", [hex: :xmpp, repo: "hexpm", optional: false]}], "hexpm", "e99594446c411c660696795b062336f5c4bd800451d8f620bb4d4ce304e255c2"}, "p1_utils": {:hex, :p1_utils, "1.0.28", "9a7088a98d788b4c4880fd3c82d0c135650db13f2e4ef7e10db179791bc94d59", [:rebar3], [], "hexpm", "c49bd44bc4a40ad996691af826dd7e0aa56d4d0cd730817190a1f84d1a7f0033"}, "pkix": {:hex, :pkix, "1.0.10", "d3bfadf7b7cfe2a3636f1b256c9cce5f646a07ce31e57ee527668502850765a0", [:rebar3], [], "hexpm", "e02164f83094cb124c41b1ab28988a615d54b9adc38575f00f19a597a3ac5d0e"}, "sqlite3": {:hex, :sqlite3, "1.1.15", "e819defd280145c328457d7af897d2e45e8e5270e18812ee30b607c99cdd21af", [:rebar3], [], "hexpm", "3c0ba4e13322c2ad49de4e2ddd28311366adde54beae8dba9d9e3888f69d2857"}, "stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"}, - "stun": {:hex, :stun, "1.2.20", "62a149cea122a78a104b9e064a12d9e33105b26c23168ecf3aea6e0c26de0748", [:rebar3], [{:fast_tls, "1.1.24", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "79e49f826a4f7d522c939ab633d935c79d7d6b229e4cb7e05f62f33b50177414"}, + "stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, - "xmpp": {:git, "https://github.com/processone/xmpp", "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f", [ref: "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"]}, - "yconf": {:git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f", [ref: "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"]}, + "xmpp": {:hex, :xmpp, "1.11.1", "60181e7d3e8e48aa3b23b2792075cda37e2e507ec152490b866e61e5320cb1da", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "a5c933df904ab3cec15425da334e410ce84ec3ae7b81efe069e5db368a7b3716"}, + "yconf": {:hex, :yconf, "1.0.21", "dbae1589381e044529e112b7e0097c89d88df89e446ead53bd33e8d27e2bcc83", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "c524a5f1fd86875d85b469cc2e368c204f97cca1c3918736e21f5001c01d096c"}, } diff --git a/rebar.config b/rebar.config index a2ebae777..799197174 100644 --- a/rebar.config +++ b/rebar.config @@ -41,10 +41,10 @@ {eredis, "~> 1.7.1", {git, "https://github.com/Nordix/eredis/", {tag, "v1.7.1"}}} }}}, {if_var_true, sip, - {esip, "~> 1.0.58", {git, "https://github.com/processone/esip", {tag, "1.0.58"}}}}, + {esip, "~> 1.0.59", {git, "https://github.com/processone/esip", {tag, "1.0.59"}}}}, {if_var_true, zlib, {ezlib, "~> 1.0.15", {git, "https://github.com/processone/ezlib", {tag, "1.0.15"}}}}, - {fast_tls, "~> 1.1.24", {git, "https://github.com/processone/fast_tls", {tag, "1.1.24"}}}, + {fast_tls, "~> 1.1.25", {git, "https://github.com/processone/fast_tls", {tag, "1.1.25"}}}, {fast_xml, "~> 1.1.57", {git, "https://github.com/processone/fast_xml", {tag, "1.1.57"}}}, {fast_yaml, "~> 1.0.39", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.39"}}}, {idna, "~> 6.0", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, @@ -64,21 +64,21 @@ {luerl, "~> 1.2.0", {git, "https://github.com/rvirding/luerl", {tag, "1.2"}}} }}, {mqtree, "~> 1.0.19", {git, "https://github.com/processone/mqtree", {tag, "1.0.19"}}}, - {p1_acme, "~> 1.0.27", {git, "https://github.com/processone/p1_acme", "edff1396d44d7bde6b2b44ca5b0dd91450319a3e"}}, + {p1_acme, "~> 1.0.28", {git, "https://github.com/processone/p1_acme", {tag, "1.0.28"}}}, {if_var_true, mysql, {p1_mysql, "~> 1.0.26", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.26"}}}}, {p1_oauth2, "~> 0.6.14", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.14"}}}, {if_var_true, pgsql, - {p1_pgsql, "~> 1.1.34", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.34"}}}}, + {p1_pgsql, "~> 1.1.35", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.35"}}}}, {p1_utils, "~> 1.0.28", {git, "https://github.com/processone/p1_utils", {tag, "1.0.28"}}}, {pkix, "~> 1.0.10", {git, "https://github.com/processone/pkix", {tag, "1.0.10"}}}, {if_var_true, sqlite, {sqlite3, "~> 1.1.15", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}}}, {stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}}, {if_var_true, stun, - {stun, "~> 1.2.20", {git, "https://github.com/processone/stun", {tag, "1.2.20"}}}}, - {xmpp, "~> 1.11.0", {git, "https://github.com/processone/xmpp", "62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"}}, - {yconf, "~> 1.0.20", {git, "https://github.com/processone/yconf", "c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}} + {stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}}, + {xmpp, "~> 1.11.1", {git, "https://github.com/processone/xmpp", {tag, "1.11.1"}}}, + {yconf, "~> 1.0.21", {git, "https://github.com/processone/yconf", {tag, "1.0.21"}}} ]}. {gitonly_deps, [ejabberd_po]}. From 644d468b4ffc17d95c89e0262923953f12b5e15d Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 21 Aug 2025 09:51:24 +0200 Subject: [PATCH 1267/1302] Update registration test Adjust test case for commit 654d4b81b15721f7a999dddc8cb24c14cc4333fa. --- test/ejabberd_SUITE.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 4b2a7fccc..ca465689d 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -961,8 +961,6 @@ presence_broadcast(Config) -> sub_els = [#disco_info{node = Node}]} = recv_iq(Config), #message{type = chat, subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config), - #message{type = normal, - subject = [#text{lang = <<"en">>,data = <<"Welcome!">>}]} = recv_message(Config), #presence{from = JID, to = JID} = recv_presence(Config), send(Config, #iq{type = result, id = IQ#iq.id, to = JID, sub_els = [Info]}), From b8550e087e107c72fff459e75ab9bf470a9f7f78 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 16 Aug 2025 00:08:38 +0200 Subject: [PATCH 1268/1302] mod_conversejs: Ensure plugins URL is separated with / (#4413) --- src/mod_conversejs.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index f6462e584..c28151076 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -240,7 +240,7 @@ get_plugins_html(Host, RawPath) -> [?MODULE, Path, F]), <<"https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js">>; _ -> - fxml:crypt(<>) + fxml:crypt(<>) end, <<"">> end, From 98469678a0caca1f379cfd99de0b2e6701238aa9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 16 Aug 2025 00:51:07 +0200 Subject: [PATCH 1269/1302] ejabberd_listener: Add secret in temporary unix domain socket path (#4422) --- src/ejabberd_listener.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 9f0f5a162..507c6f4c6 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -223,7 +223,8 @@ setup_provisional_udsocket_dir(DefinitivePath) -> ProvisionalPathAbsolute. get_provisional_udsocket_path(Path) -> - PathBase64 = misc:term_to_base64(Path), + ReproducibleSecret = binary:part(crypto:hash(sha, misc:atom_to_binary(erlang:get_cookie())), 1, 8), + PathBase64 = misc:term_to_base64({ReproducibleSecret, Path}), PathBuild = filename:join(misc:get_home(), PathBase64), DestPath = filename:join(filename:dirname(Path), PathBase64), case {byte_size(DestPath) > 107, byte_size(PathBuild) > 107} of @@ -243,7 +244,7 @@ get_definitive_udsocket_path(<<"unix", _>> = Unix) -> Unix; get_definitive_udsocket_path(ProvisionalPath) -> PathBase64 = filename:basename(ProvisionalPath), - {term, Path} = misc:base64_to_term(PathBase64), + {term, {_, Path}} = misc:base64_to_term(PathBase64), relative_socket_to_mnesia(Path). -spec set_definitive_udsocket(integer() | binary(), opts()) -> ok | {error, file:posix() | badarg}. From dd5bbda2dc95ccf06c67b97e1b7a53c8067b997e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:34:48 +0000 Subject: [PATCH 1270/1302] build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/container.yml | 4 ++-- .github/workflows/installers.yml | 2 +- .github/workflows/runtime.yml | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c7828fea..8cab0feb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Test shell scripts if: matrix.otp == '27' diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 567a2c845..0bb169ccc 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -22,12 +22,12 @@ jobs: packages: write steps: - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Checkout ejabberd-contrib - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: repository: processone/ejabberd-contrib path: .ejabberd-modules/sources/ejabberd-contrib diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml index 9c4da3058..37c8983b4 100644 --- a/.github/workflows/installers.yml +++ b/.github/workflows/installers.yml @@ -41,7 +41,7 @@ jobs: gem install --no-document --user-install fpm echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Build binary archives diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml index 90eee1fc2..cd599fe83 100644 --- a/.github/workflows/runtime.yml +++ b/.github/workflows/runtime.yml @@ -46,7 +46,7 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Get old compatible Rebar binaries if: matrix.otp < 24 @@ -186,7 +186,7 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Prepare libraries run: | @@ -309,7 +309,7 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Prepare libraries run: | From 4a053807e04dac8a909f7f0c372e44f5e7f34c2e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 18 Aug 2025 10:39:32 +0200 Subject: [PATCH 1271/1302] build(deps-dev): bump dialyxir from 1.4.5 to 1.4.6 Bumps [dialyxir](https://github.com/jeremyjh/dialyxir) from 1.4.5 to 1.4.6. - [Release notes](https://github.com/jeremyjh/dialyxir/releases) - [Changelog](https://github.com/jeremyjh/dialyxir/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeremyjh/dialyxir/compare/1.4.5...1.4.6) --- updated-dependencies: - dependency-name: dialyxir dependency-version: 1.4.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index aca94aa44..11c9ba390 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.33", "e2542afb34f17ee3ca19d2b0f546a074922c2b99fb6b2acfb38160d7d0336ec3", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "4258009eb050b22aabe0c848e230bba58401a6895c58c2ff74dfb635e3c35900"}, - "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, + "dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "eimp": {:hex, :eimp, "1.0.26", "c0b05f32e35629c4d9bcfb832ff879a92b0f92b19844bc7835e0a45635f2899a", [:rebar3], [{:p1_utils, "~> 1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d96d4e8572b9dfc40f271e47f0cb1d8849373bc98a21223268781765ed52044c"}, "epam": {:hex, :epam, "1.0.14", "aa0b85d27f4ef3a756ae995179df952a0721237e83c6b79d644347b75016681a", [:rebar3], [], "hexpm", "2f3449e72885a72a6c2a843f561add0fc2f70d7a21f61456930a547473d4d989"}, From c508795ad42327337e297533d8aadf2a1f2badbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:24:34 +0000 Subject: [PATCH 1272/1302] build(deps): bump golang in /.github/container Bumps golang from 1.24-alpine to 1.25-alpine. --- updated-dependencies: - dependency-name: golang dependency-version: 1.25-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index ec9a4881a..b1c5aa5d1 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -9,7 +9,7 @@ ARG VERSION='master' ################################################################################ #' Compile ejabberdapi -FROM docker.io/golang:1.24-alpine AS api +FROM docker.io/golang:1.25-alpine AS api RUN go install -v \ github.com/processone/ejabberd-api/cmd/ejabberd@master \ && mv bin/ejabberd bin/ejabberdapi From 6ae48eb991811d049bf5ed42b242655a71d1d9da Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 09:27:12 +0200 Subject: [PATCH 1273/1302] Result of running "make options" --- src/mod_conversejs_opt.erl | 7 +++ src/mod_providers_opt.erl | 111 +++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/mod_providers_opt.erl diff --git a/src/mod_conversejs_opt.erl b/src/mod_conversejs_opt.erl index c8132bfab..37deac7ef 100644 --- a/src/mod_conversejs_opt.erl +++ b/src/mod_conversejs_opt.erl @@ -6,6 +6,7 @@ -export([bosh_service_url/1]). -export([conversejs_css/1]). -export([conversejs_options/1]). +-export([conversejs_plugins/1]). -export([conversejs_resources/1]). -export([conversejs_script/1]). -export([default_domain/1]). @@ -29,6 +30,12 @@ conversejs_options(Opts) when is_map(Opts) -> conversejs_options(Host) -> gen_mod:get_module_opt(Host, mod_conversejs, conversejs_options). +-spec conversejs_plugins(gen_mod:opts() | global | binary()) -> [binary()]. +conversejs_plugins(Opts) when is_map(Opts) -> + gen_mod:get_opt(conversejs_plugins, Opts); +conversejs_plugins(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, conversejs_plugins). + -spec conversejs_resources(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). conversejs_resources(Opts) when is_map(Opts) -> gen_mod:get_opt(conversejs_resources, Opts); diff --git a/src/mod_providers_opt.erl b/src/mod_providers_opt.erl new file mode 100644 index 000000000..ad398295f --- /dev/null +++ b/src/mod_providers_opt.erl @@ -0,0 +1,111 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_providers_opt). + +-export([alternativeJids/1]). +-export([busFactor/1]). +-export([freeOfCharge/1]). +-export([languages/1]). +-export([legalNotice/1]). +-export([maximumHttpFileUploadStorageTime/1]). +-export([maximumHttpFileUploadTotalSize/1]). +-export([maximumMessageArchiveManagementStorageTime/1]). +-export([organization/1]). +-export([passwordReset/1]). +-export([professionalHosting/1]). +-export([serverLocations/1]). +-export([serverTesting/1]). +-export([since/1]). +-export([website/1]). + +-spec alternativeJids(gen_mod:opts() | global | binary()) -> [binary()]. +alternativeJids(Opts) when is_map(Opts) -> + gen_mod:get_opt(alternativeJids, Opts); +alternativeJids(Host) -> + gen_mod:get_module_opt(Host, mod_providers, alternativeJids). + +-spec busFactor(gen_mod:opts() | global | binary()) -> integer(). +busFactor(Opts) when is_map(Opts) -> + gen_mod:get_opt(busFactor, Opts); +busFactor(Host) -> + gen_mod:get_module_opt(Host, mod_providers, busFactor). + +-spec freeOfCharge(gen_mod:opts() | global | binary()) -> boolean(). +freeOfCharge(Opts) when is_map(Opts) -> + gen_mod:get_opt(freeOfCharge, Opts); +freeOfCharge(Host) -> + gen_mod:get_module_opt(Host, mod_providers, freeOfCharge). + +-spec languages(gen_mod:opts() | global | binary()) -> [binary()]. +languages(Opts) when is_map(Opts) -> + gen_mod:get_opt(languages, Opts); +languages(Host) -> + gen_mod:get_module_opt(Host, mod_providers, languages). + +-spec legalNotice(gen_mod:opts() | global | binary()) -> binary(). +legalNotice(Opts) when is_map(Opts) -> + gen_mod:get_opt(legalNotice, Opts); +legalNotice(Host) -> + gen_mod:get_module_opt(Host, mod_providers, legalNotice). + +-spec maximumHttpFileUploadStorageTime(gen_mod:opts() | global | binary()) -> 'default_value' | integer(). +maximumHttpFileUploadStorageTime(Opts) when is_map(Opts) -> + gen_mod:get_opt(maximumHttpFileUploadStorageTime, Opts); +maximumHttpFileUploadStorageTime(Host) -> + gen_mod:get_module_opt(Host, mod_providers, maximumHttpFileUploadStorageTime). + +-spec maximumHttpFileUploadTotalSize(gen_mod:opts() | global | binary()) -> 'default_value' | integer(). +maximumHttpFileUploadTotalSize(Opts) when is_map(Opts) -> + gen_mod:get_opt(maximumHttpFileUploadTotalSize, Opts); +maximumHttpFileUploadTotalSize(Host) -> + gen_mod:get_module_opt(Host, mod_providers, maximumHttpFileUploadTotalSize). + +-spec maximumMessageArchiveManagementStorageTime(gen_mod:opts() | global | binary()) -> integer(). +maximumMessageArchiveManagementStorageTime(Opts) when is_map(Opts) -> + gen_mod:get_opt(maximumMessageArchiveManagementStorageTime, Opts); +maximumMessageArchiveManagementStorageTime(Host) -> + gen_mod:get_module_opt(Host, mod_providers, maximumMessageArchiveManagementStorageTime). + +-spec organization(gen_mod:opts() | global | binary()) -> '' | 'commercial person' | 'company' | 'governmental' | 'non-governmental' | 'private person'. +organization(Opts) when is_map(Opts) -> + gen_mod:get_opt(organization, Opts); +organization(Host) -> + gen_mod:get_module_opt(Host, mod_providers, organization). + +-spec passwordReset(gen_mod:opts() | global | binary()) -> 'default_value' | binary(). +passwordReset(Opts) when is_map(Opts) -> + gen_mod:get_opt(passwordReset, Opts); +passwordReset(Host) -> + gen_mod:get_module_opt(Host, mod_providers, passwordReset). + +-spec professionalHosting(gen_mod:opts() | global | binary()) -> boolean(). +professionalHosting(Opts) when is_map(Opts) -> + gen_mod:get_opt(professionalHosting, Opts); +professionalHosting(Host) -> + gen_mod:get_module_opt(Host, mod_providers, professionalHosting). + +-spec serverLocations(gen_mod:opts() | global | binary()) -> [binary()]. +serverLocations(Opts) when is_map(Opts) -> + gen_mod:get_opt(serverLocations, Opts); +serverLocations(Host) -> + gen_mod:get_module_opt(Host, mod_providers, serverLocations). + +-spec serverTesting(gen_mod:opts() | global | binary()) -> boolean(). +serverTesting(Opts) when is_map(Opts) -> + gen_mod:get_opt(serverTesting, Opts); +serverTesting(Host) -> + gen_mod:get_module_opt(Host, mod_providers, serverTesting). + +-spec since(gen_mod:opts() | global | binary()) -> binary(). +since(Opts) when is_map(Opts) -> + gen_mod:get_opt(since, Opts); +since(Host) -> + gen_mod:get_module_opt(Host, mod_providers, since). + +-spec website(gen_mod:opts() | global | binary()) -> binary(). +website(Opts) when is_map(Opts) -> + gen_mod:get_opt(website, Opts); +website(Host) -> + gen_mod:get_module_opt(Host, mod_providers, website). + From 2f3b9015e9b62fa0b1e756f808b008e217a10d77 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 12:54:08 +0200 Subject: [PATCH 1274/1302] Update module and options version notes --- src/ejabberd_ctl.erl | 2 +- src/mod_admin_extra.erl | 2 +- src/mod_matrix_gw.erl | 4 ++-- src/mod_providers.erl | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index d2bad4f19..88bd5e785 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -1155,7 +1155,7 @@ get_commands_spec() -> "do not attempt to run this command when ejabberd is running. " "This command is exclusive for the ejabberdctl command-line script, " "don't attempt to execute it using any other API frontend.", - note = "added in 25.xx", + note = "added in 25.08", args = [{old_node_name, string}], args_desc = ["Old erlang node name"], args_example = ["ejabberd@oldmachine"]}, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index fc2cf8c51..c3f605e59 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -275,7 +275,7 @@ get_commands_spec() -> "and _`unban_account`_ API.", module = ?MODULE, function = ban_account_v2, version = 2, - note = "improved in 25.xx", + note = "improved in 25.08", args = [{user, binary}, {host, binary}, {reason, binary}], args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>], args_desc = ["User name to ban", "Server name", diff --git a/src/mod_matrix_gw.erl b/src/mod_matrix_gw.erl index 5f9705422..54edafcf2 100644 --- a/src/mod_matrix_gw.erl +++ b/src/mod_matrix_gw.erl @@ -1007,10 +1007,10 @@ mod_doc() -> [?T("https://matrix.org/[Matrix] gateway. "), ?T("Supports room versions 9, 10 and 11 since ejabberd 25.03; " "room versions 4 and higher since ejabberd 25.07; " - "room version 12 (hydra rooms) since ejabberd 25.xx. "), + "room version 12 (hydra rooms) since ejabberd 25.08. "), ?T("Erlang/OTP 25 or higher is required to use this module."), ?T("This module is available since ejabberd 24.02.")], - note => "improved in 25.xx", + note => "improved in 25.08", example => ["listen:", " -", diff --git a/src/mod_providers.erl b/src/mod_providers.erl index 86493ec2f..24796b65d 100644 --- a/src/mod_providers.erl +++ b/src/mod_providers.erl @@ -328,7 +328,7 @@ mod_doc() -> "_`listen-options.md#request_handlers|request_handlers`_. " "Notice you should set in _`listen.md#ejabberd_http|ejabberd_http`_ " "the option _`listen-options.md#tls|tls`_ enabled.")], - note => "added in 25.xx", + note => "added in 25.08", opts => [{languages, #{value => "[string()]", From 6d63842ad34444e5b2fcbd2876213c74e357ed81 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 13:27:25 +0200 Subject: [PATCH 1275/1302] Fix typo in hu.msg string --- priv/msgs/hu.msg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/msgs/hu.msg b/priv/msgs/hu.msg index ca0dc5e77..32e3174d0 100644 --- a/priv/msgs/hu.msg +++ b/priv/msgs/hu.msg @@ -358,7 +358,7 @@ {"Too many child elements","Túl sok gyermekelem"}. {"Too many elements","Túl sok elem"}. {"Too many elements","Túl sok elem"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Túl sok (~p) sikertelen hitelesítés erről az IP-címről (~ts) A cím ~ts-kor lesz feloldva UTC szerint"}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","Túl sok (~p) sikertelen hitelesítés erről az IP-címről (~s) A cím ~s-kor lesz feloldva UTC szerint"}. {"Too many receiver fields were specified","Túl sok fogadómező lett meghatározva"}. {"Too many unacked stanzas","Túl sok nyugtázatlan stanza"}. {"Too many users in this conference","Túl sok felhasználó ebben a konferenciában"}. From b7bd0e196d42778cc5e32e345177309a5311239f Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 17:11:24 +0200 Subject: [PATCH 1276/1302] Update rebar.lock too --- rebar.lock | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/rebar.lock b/rebar.lock index 66b130643..371e08b40 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,9 +4,9 @@ {<<"eimp">>,{pkg,<<"eimp">>,<<"1.0.26">>},0}, {<<"epam">>,{pkg,<<"epam">>,<<"1.0.14">>},0}, {<<"eredis">>,{pkg,<<"eredis">>,<<"1.7.1">>},0}, - {<<"esip">>,{pkg,<<"esip">>,<<"1.0.58">>},0}, + {<<"esip">>,{pkg,<<"esip">>,<<"1.0.59">>},0}, {<<"ezlib">>,{pkg,<<"ezlib">>,<<"1.0.15">>},0}, - {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.24">>},0}, + {<<"fast_tls">>,{pkg,<<"fast_tls">>,<<"1.1.25">>},0}, {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.57">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.39">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, @@ -14,27 +14,18 @@ {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0}, - {<<"p1_acme">>, - {git,"https://github.com/processone/p1_acme", - {ref,"edff1396d44d7bde6b2b44ca5b0dd91450319a3e"}}, - 0}, + {<<"p1_acme">>,{pkg,<<"p1_acme">>,<<"1.0.28">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0}, - {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.34">>},0}, + {<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.35">>},0}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.28">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0}, - {<<"stun">>,{pkg,<<"stun">>,<<"1.2.20">>},0}, + {<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, - {<<"xmpp">>, - {git,"https://github.com/processone/xmpp", - {ref,"62e5c7f6c901e3cd1a2aaa1bd7ab0205ccb1b30f"}}, - 0}, - {<<"yconf">>, - {git,"https://github.com/processone/yconf", - {ref,"c59f94097af5b78a8c6c72a143dcefaa5b3f406f"}}, - 0}]}. + {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0}, + {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.21">>},0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -42,9 +33,9 @@ {<<"eimp">>, <<"C0B05F32E35629C4D9BCFB832FF879A92B0F92B19844BC7835E0A45635F2899A">>}, {<<"epam">>, <<"AA0B85D27F4EF3A756AE995179DF952A0721237E83C6B79D644347B75016681A">>}, {<<"eredis">>, <<"39E31AA02ADCD651C657F39AAFD4D31A9B2F63C6C700DC9CECE98D4BC3C897AB">>}, - {<<"esip">>, <<"96BF0C07271F86F03F42778D4A1237099BAEC0714D00B0B815EB42D0007F73B4">>}, + {<<"esip">>, <<"EB202F8C62928193588091DFEDBC545FE3274C34ECD209961F86DCB6C9EBCE88">>}, {<<"ezlib">>, <<"D74F5DF191784744726A5B1AE9062522C606334F11086363385EB3B772D91357">>}, - {<<"fast_tls">>, <<"5492125689E3D84C101323A0BFF3D3996B92A903832530FE4F0935ED30B1B7D1">>}, + {<<"fast_tls">>, <<"DA8ED6F05A2452121B087158B17234749F36704C1F2B74DC51DB99A1E27ED5E8">>}, {<<"fast_xml">>, <<"31EFC0F9BCEDA92069704F7A25830407DA5DC3DAD1272B810D6F2E13E73CC11A">>}, {<<"fast_yaml">>, <<"2E71168091949BAB0E5F583B340A99072B4D22D93EB86624E7850A12B1517BE4">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, @@ -52,24 +43,27 @@ {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>}, + {<<"p1_acme">>, <<"64D9C17F5412AA92D75B29206B2B984D734A4FE1B7EACB66C3D7A7C697AC612C">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, - {<<"p1_pgsql">>, <<"D36BD0D6C9765A47D075A87AD5E3FC3BFD153BDC4C07A1217B9979F33F73E9AA">>}, + {<<"p1_pgsql">>, <<"E13D89F14D717553E85C88A152CE77461916B013D88FCB851E354A0B332D4218">>}, {<<"p1_utils">>, <<"9A7088A98D788B4C4880FD3C82D0C135650DB13F2E4EF7E10DB179791BC94D59">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, - {<<"stun">>, <<"62A149CEA122A78A104B9E064A12D9E33105B26C23168ECF3AEA6E0C26DE0748">>}, - {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]}, + {<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>}, + {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, + {<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>}, + {<<"yconf">>, <<"DBAE1589381E044529E112B7E0097C89D88DF89E446EAD53BD33E8D27E2BCC83">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, {<<"eimp">>, <<"D96D4E8572B9DFC40F271E47F0CB1D8849373BC98A21223268781765ED52044C">>}, {<<"epam">>, <<"2F3449E72885A72A6C2A843F561ADD0FC2F70D7A21F61456930A547473D4D989">>}, {<<"eredis">>, <<"7C2B54C566FED55FEEF3341CA79B0100A6348FD3F162184B7ED5118D258C3CC1">>}, - {<<"esip">>, <<"E0F4204A5EDE0FA7D00DA3CC42F6440AA362BAC7FAF536F71EA29FA3F0FA7C75">>}, + {<<"esip">>, <<"0BDF2E3C349DC0B144F173150329E675C6A51AC473D7A0B2E362245FAAD3FBE6">>}, {<<"ezlib">>, <<"DD14BA6C12521AF5CFE6923E73E3D545F4A0897DC66BFAB5287FBB7AE3962EAB">>}, - {<<"fast_tls">>, <<"FFF88ADA39FAD10464567A160643F4529EF4AED49D156919F5D1F415B6CDBBB6">>}, + {<<"fast_tls">>, <<"59E183B5740E670E02B8AA6BE673B5E7779E5FE5BFCC679FE2D4993D1949A821">>}, {<<"fast_xml">>, <<"EEC34E90ADACAFE467D5DDAB635A014DED73B98B4061554B2D1972173D929C39">>}, {<<"fast_yaml">>, <<"24C7B9AB9E2B9269D64E45F4A2A1280966ADB17D31E63365CFD3EE277FB0A78D">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, @@ -77,13 +71,16 @@ {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>}, + {<<"p1_acme">>, <<"CE686986DE3F9D5FD285AFE87523CB45329A349C6C6BE7ACC1ED916725D46423">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, - {<<"p1_pgsql">>, <<"CB0E32E086C9C35D0E3E966E3863D832737C7B4D2B5F147316A465C0B243EA7F">>}, + {<<"p1_pgsql">>, <<"E99594446C411C660696795B062336F5C4BD800451D8F620BB4D4CE304E255C2">>}, {<<"p1_utils">>, <<"C49BD44BC4A40AD996691AF826DD7E0AA56D4D0CD730817190A1F84D1A7F0033">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, - {<<"stun">>, <<"79E49F826A4F7D522C939AB633D935C79D7D6B229E4CB7E05F62F33B50177414">>}, - {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]} + {<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>}, + {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, + {<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>}, + {<<"yconf">>, <<"C524A5F1FD86875D85B469CC2E368C204F97CCA1C3918736E21F5001C01D096C">>}]} ]. From 3887b6d93078678dec3ef255d4c13b5a79fa79e9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 16:43:37 +0200 Subject: [PATCH 1277/1302] Update man page to 25.08 --- man/ejabberd.yml.5 | 295 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 270 insertions(+), 25 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index b449b423a..aa42e20b2 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 07/11/2025 +.\" Date: 08/22/2025 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "07/11/2025" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "08/22/2025" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,12 +82,12 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.07/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" .sp -This section describes top level options of ejabberd 25\&.07\&. The options that changed in this version are marked with 🟤\&. +This section describes top level options of ejabberd 25\&.08\&. The options that changed in this version are marked with 🟤\&. .PP \fBaccess_rules\fR: \fI{AccessName: {allow|deny: ACLName|ACLDefinition}}\fR .RS 4 @@ -461,10 +461,10 @@ option\&. .sp The default value is \fIplain\fR\&. .PP -\fBauth_password_types_hidden_in_scram1 🟤\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR +\fBauth_password_types_hidden_in_sasl1\fR: \fI[plain | scram_sha1 | scram_sha256 | scram_sha512]\fR .RS 4 \fINote\fR -about this option: added in 25\&.07\&. List of password types that should not be offered in SCRAM1 authenticatication\&. Because SCRAM1, unlike SCRAM2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&. +about this option: added in 25\&.07\&. List of password types that should not be offered in SASL1 authenticatication\&. Because SASL1, unlike SASL2, can\(cqt have list of available mechanisms tailored to individual user, it\(cqs possible that offered mechanisms will not be compatible with stored password, especially if new password type was added recently\&. This option allows disabling offering some mechanisms in SASL1, to a time until new password type will be available for all users\&. .RE .PP \fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR @@ -703,13 +703,19 @@ A list of Erlang nodes to connect on ejabberd startup\&. This option is mostly i \fBdefault_db\fR: \fImnesia | sql\fR .RS 4 \fIdatabase\&.md#default\-database|Default database\fR -to store persistent data in ejabberd\&. Modules and other components (e\&.g\&. authentication) may have its own value\&. The default value is +to store persistent data in ejabberd\&. Some components can be configured with specific toplevel options like +\fIoauth_db_type\fR\&. Many modules can be configured with specific module options, usually named +db_type\&. The default value is \fImnesia\fR\&. .RE .PP \fBdefault_ram_db\fR: \fImnesia | redis | sql\fR .RS 4 -Default volatile (in\-memory) storage for ejabberd\&. Modules and other components (e\&.g\&. session management) may have its own value\&. The default value is +Default volatile (in\-memory) storage for ejabberd\&. Some components can be configured with specific toplevel options like +\fIrouter_db_type\fR +and +\fIsm_db_type\fR\&. Some modules can be configured with specific module options, usually named +ram_db_type\&. The default value is \fImnesia\fR\&. .RE .PP @@ -957,7 +963,7 @@ List of one or more option\&. .RE .PP -\fBhosts_alias 🟤\fR: \fI{Alias: Host}\fR +\fBhosts_alias\fR: \fI{Alias: Host}\fR .RS 4 \fINote\fR about this option: added in 25\&.07\&. Define aliases for existing vhosts managed by ejabberd\&. An alias may be a regexp expression\&. This option is only consulted by the @@ -1280,7 +1286,7 @@ This option can be used to tune tick time parameter of .RS 4 Whether to use the \fIdatabase\&.md#default\-and\-new\-schemas|new SQL schema\fR\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/25\&.07/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/25\&.08/sql\&. There are two schemas available\&. The default legacy schema stores one XMPP domain into one ejabberd database\&. The \fInew\fR schema can handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1547,25 +1553,25 @@ XMPP Core: section 7\&.7\&.2\&.2\&. The default value is \fIcloseold\fR\&. .RE .PP -\fBrest_proxy 🟤\fR: \fIHost\fR +\fBrest_proxy\fR: \fIHost\fR .RS 4 \fINote\fR about this option: added in 25\&.07\&. Address of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) .RE .PP -\fBrest_proxy_password 🟤\fR: \fIstring()\fR +\fBrest_proxy_password\fR: \fIstring()\fR .RS 4 \fINote\fR about this option: added in 25\&.07\&. Password used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) .RE .PP -\fBrest_proxy_port 🟤\fR: \fI1\&.\&.65535\fR +\fBrest_proxy_port\fR: \fI1\&.\&.65535\fR .RS 4 \fINote\fR about this option: added in 25\&.07\&. Port of a HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) .RE .PP -\fBrest_proxy_username 🟤\fR: \fIstring()\fR +\fBrest_proxy_username\fR: \fIstring()\fR .RS 4 \fINote\fR about this option: added in 25\&.07\&. Username used to authenticate to HTTP Connect proxy used by modules issuing rest calls (like ejabberd_oauth_rest) @@ -2101,7 +2107,7 @@ seconds\&. .RE .SH "MODULES" .sp -This section describes modules options of ejabberd 25\&.07\&. The modules that changed in this version are marked with 🟤\&. +This section describes modules options of ejabberd 25\&.08\&. The modules that changed in this version are marked with 🟤\&. .SS "mod_adhoc" .sp def:ad\-hoc command @@ -2261,6 +2267,8 @@ ejabberdctl srg_create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g1 .if n \{\ .RE .\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#erlang|erlang\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#last|last\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#private|private\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#session|session\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#shared_roster_group|shared_roster_group\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#stanza|stanza\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#statistics|statistics\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#vcard|vcard\fR .RE .SS "mod_admin_update_sql" .sp @@ -2455,7 +2463,7 @@ Same as top\-level option, but applied to this module only\&. .RE .RE -.SS "mod_antispam 🟤" +.SS "mod_antispam" .sp \fINote\fR about this option: added in 25\&.07\&. .sp @@ -3034,7 +3042,7 @@ modules: .RE .\} .RE -.SS "mod_conversejs 🟤" +.SS "mod_conversejs" .sp \fINote\fR about this option: improved in 25\&.07\&. .sp @@ -3080,6 +3088,17 @@ about this option: added in 22\&.05\&. Specify additional options to be passed t Converse configuration\&. Only boolean, integer and string values are supported; lists are not supported\&. .RE .PP +\fBconversejs_plugins\fR: \fI[Filename]\fR +.RS 4 +List of additional local files to include as scripts in the homepage\&. Please make sure those files are available in the path specified in +\fIconversejs_resources\fR +option, in subdirectory +\fIplugins/\fR\&. If using the public Converse client, then +\fI"libsignal"\fR +gets replaced with the URL of the public library\&. The default value is +\fI[]\fR\&. +.RE +.PP \fBconversejs_resources\fR: \fIPath\fR .RS 4 \fINote\fR @@ -3138,6 +3157,7 @@ listen: modules: mod_bosh: {} mod_conversejs: + conversejs_plugins: ["libsignal"] websocket_url: "ws://@HOST@:5280/websocket" .fi .if n \{\ @@ -3161,7 +3181,9 @@ listen: modules: mod_conversejs: - conversejs_resources: "/home/ejabberd/conversejs\-9\&.0\&.0/package/dist" + conversejs_resources: "/home/ejabberd/conversejs\-x\&.y\&.z/package/dist" + conversejs_plugins: ["libsignal\-protocol\&.min\&.js"] + # File path is: /home/ejabberd/conversejs\-x\&.y\&.z/package/dist/plugins/libsignal\-protocol\&.min\&.js .fi .if n \{\ .RE @@ -3424,6 +3446,8 @@ hour\&. The number of C2S authentication failures to trigger the IP ban\&. The default value is \fI20\fR\&. .RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#accounts|accounts\fR .RE .SS "mod_host_meta" .sp @@ -3784,7 +3808,9 @@ A name of the service in the Service Discovery\&. The default value is .RS 4 This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword \fI@HOST@\fR -is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is +is replaced with the virtual host name\&. And +\fI@HOST_URL_ENCODE@\fR +is replaced with the host name encoded for URL, useful when your virtual hosts contain non\-latin characters\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is \fI"https://@HOST@:5443/upload"\fR\&. .RE .PP @@ -4147,12 +4173,14 @@ option, but applied to this module only\&. When this option is disabled, for each individual subscriber a separate mucsub message is stored\&. With this option enabled, when a user fetches archive virtual mucsub, messages are generated from muc archives\&. The default value is \fIfalse\fR\&. .RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#mam|mam\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR .RE .SS "mod_matrix_gw 🟤" .sp -\fINote\fR about this option: improved in 25\&.07\&. +\fINote\fR about this option: improved in 25\&.08\&. .sp -Matrix gateway\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&. +Matrix gateway\&. Supports room versions 9, 10 and 11 since ejabberd 25\&.03; room versions 4 and higher since ejabberd 25\&.07; room version 12 (hydra rooms) since ejabberd 25\&.08\&. Erlang/OTP 25 or higher is required to use this module\&. This module is available since ejabberd 24\&.02\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -4182,6 +4210,13 @@ Value of the matrix signing key, in base64\&. Name of the matrix signing key\&. .RE .PP +\fBleave_timeout\fR: \fIinteger()\fR +.RS 4 +Delay in seconds between a user leaving a MUC room and sending +\fIleave\fR +Matrix event\&. +.RE +.PP \fBmatrix_domain\fR: \fIDomain\fR .RS 4 Specify a domain in the Matrix federation\&. The keyword @@ -4206,6 +4241,11 @@ is the JID of the gateway service as set by the option\&. The default is \fIfalse\fR\&. .RE +.PP +\fBnotary_servers\fR: \fI[Server, \&.\&.\&.]\fR +.RS 4 +A list of notary servers\&. +.RE .RE .sp .it 1 an-trap @@ -5170,6 +5210,8 @@ about this option: added in 22\&.05\&. How many users can be subscribed to a roo API\&. The default value is \fI50\fR\&. .RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc|muc\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_room|muc_room\fR, \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#muc_sub|muc_sub\fR .RE .SS "mod_muc_log" .sp @@ -5747,6 +5789,8 @@ modules: .if n \{\ .RE .\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#offline|offline\fR .RE .SS "mod_ping" .sp @@ -5990,6 +6034,8 @@ Same as top\-level \fIuse_cache\fR option, but applied to this module only\&. .RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#private|private\fR .RE .SS "mod_privilege" .sp @@ -6157,6 +6203,199 @@ modules: .RE .\} .RE +.SS "mod_providers 🟤" +.sp +\fINote\fR about this option: added in 25\&.08\&. +.sp +This module serves JSON provider files API v2 as described by XMPP Providers\&. +.sp +It attempts to fill some properties gathering values automatically from your existing ejabberd configuration\&. Try enabling the module, check what values are displayed, and then customize using the options\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → \fIlisten\-options\&.md#request_handlers|request_handlers\fR\&. Notice you should set in \fIlisten\&.md#ejabberd_http|ejabberd_http\fR the option \fIlisten\-options\&.md#tls|tls\fR enabled\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBalternativeJids\fR: \fI[string()]\fR +.RS 4 +List of JIDs (XMPP server domains) a provider offers for registration other than its main JID\&. The default value is +\fI[]\fR\&. +.RE +.PP +\fBbusFactor\fR: \fIinteger()\fR +.RS 4 +Bus factor of the XMPP service (i\&.e\&., the minimum number of team members that the service could not survive losing) or +\fI\-1\fR +for n/a\&. The default value is +\fI\-1\fR\&. +.RE +.PP +\fBfreeOfCharge\fR: \fItrue | false\fR +.RS 4 +Whether the XMPP service can be used for free\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBlanguages\fR: \fI[string()]\fR +.RS 4 +List of language codes that your pages are available\&. Some options define URL where the keyword +\fI@LANGUAGE_URL@\fR +will be replaced with each of those language codes\&. The default value is a list with the language set in the option +\fIlanguage\fR, for example: +\fI[en]\fR\&. +.RE +.PP +\fBlegalNotice\fR: \fIstring()\fR +.RS 4 +Legal notice web page (per language)\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is +\fI""\fR\&. +.RE +.PP +\fBmaximumHttpFileUploadStorageTime\fR: \fIinteger()\fR +.RS 4 +Maximum storage duration of each shared file (number in days, +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 day)\&. The default value is the same as option +\fImax_days\fR +from module +\fImod_http_upload_quota\fR, or +\fI0\fR +otherwise\&. +.RE +.PP +\fBmaximumHttpFileUploadTotalSize\fR: \fIinteger()\fR +.RS 4 +Maximum size of all shared files in total per user (number in megabytes (MB), +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 MB)\&. Attention: MB is used instead of MiB (e\&.g\&., 104,857,600 bytes = 100 MiB H 104 MB)\&. This property is not about the maximum size of each shared file, which is already retrieved via XMPP\&. The default value is the value of the shaper value of option +\fIaccess_hard_quota\fR +from module +\fImod_http_upload_quota\fR, or +\fI0\fR +otherwise\&. +.RE +.PP +\fBmaximumMessageArchiveManagementStorageTime\fR: \fIinteger()\fR +.RS 4 +Maximum storage duration of each exchanged message (number in days, +\fI0\fR +for no limit or +\fI\-1\fR +for less than 1 day)\&. The default value is +\fI0\fR\&. +.RE +.PP +\fBorganization\fR: \fIstring()\fR +.RS 4 +Type of organization providing the XMPP service\&. Allowed values are: +\fIcompany\fR, +\fI"commercial person"\fR, +\fI"private person"\fR, +\fIgovernmental\fR, +\fI"non\-governmental"\fR +or +\fI""\fR\&. The default value is +\fI""\fR\&. +.RE +.PP +\fBpasswordReset\fR: \fIstring()\fR +.RS 4 +Password reset web page (per language) used for an automatic password reset (e\&.g\&., via email) or describing how to manually reset a password (e\&.g\&., by contacting the provider)\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is an URL built automatically if +\fImod_register_web\fR +is configured as a +\fIrequest_handler\fR, or +\fI""\fR +otherwise\&. +.RE +.PP +\fBprofessionalHosting\fR: \fItrue | false\fR +.RS 4 +Whether the XMPP server is hosted with good internet connection speed, uninterruptible power supply, access protection and regular backups\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBserverLocations\fR: \fI[string()]\fR +.RS 4 +List of language codes of Server/Backup locations\&. The default value is an empty list: +\fI[]\fR\&. +.RE +.PP +\fBserverTesting\fR: \fItrue | false\fR +.RS 4 +Whether tests against the provider\(cqs server are allowed (e\&.g\&., certificate checks and uptime monitoring)\&. The default value is +\fIfalse\fR\&. +.RE +.PP +\fBsince\fR: \fIstring()\fR +.RS 4 +Date since the XMPP service is available\&. The default value is an empty string: +\fI""\fR\&. +.RE +.PP +\fBwebsite\fR: \fIstring()\fR +.RS 4 +Provider website\&. The keyword +\fI@LANGUAGE_URL@\fR +is replaced with each language\&. The default value is +\fI""\fR\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 443 + module: ejabberd_http + tls: true + request_handlers: + /\&.well\-known/xmpp\-provider\-v2\&.json: mod_providers + +modules: + mod_providers: + alternativeJids: ["example1\&.com", "example2\&.com"] + busFactor: 1 + freeOfCharge: true + languages: [ag, ao, bg, en] + legalNotice: "http://@HOST@/legal/@LANGUAGE_URL@/" + maximumHttpFileUploadStorageTime: 0 + maximumHttpFileUploadTotalSize: 0 + maximumMessageArchiveManagementStorageTime: 0 + organization: "non\-governmental" + passwordReset: "http://@HOST@/reset/@LANGUAGE_URL@/" + professionalHosting: true + serverLocations: [ao, bg] + serverTesting: true + since: "2025\-12\-31" + website: "http://@HOST@/website/@LANGUAGE_URL@/" +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_proxy65" .sp This module implements XEP\-0065: SOCKS5 Bytestreams\&. It allows ejabberd to act as a file transfer proxy between two XMPP clients\&. @@ -6618,8 +6857,10 @@ modules: .if n \{\ .RE .\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR .RE -.SS "mod_pubsub_serverinfo 🟤" +.SS "mod_pubsub_serverinfo" .sp \fINote\fR about this option: added in 25\&.07\&. .sp @@ -6741,6 +6982,8 @@ Same as top\-level \fIuse_cache\fR option, but applied to this module only\&. .RE +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#purge|purge\fR .RE .SS "mod_push_keepalive" .sp @@ -7090,6 +7333,8 @@ modules: .if n \{\ .RE .\} +.sp +\fBAPI Tags:\fR \fI\&.\&./\&.\&./developer/ejabberd\-api/admin\-tags\&.md#roster|roster\fR .RE .SS "mod_s2s_bidi" .sp @@ -8617,7 +8862,7 @@ Should the operating system be revealed or not\&. The default value is .RE .SH "LISTENERS" .sp -This section describes listeners options of ejabberd 25\&.07\&. +This section describes listeners options of ejabberd 25\&.08\&. .sp TODO .SH "AUTHOR" @@ -8625,13 +8870,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 25\&.07\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 25\&.08\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.07/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/25\&.08/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From ce668bef1471ea381b903b1b86f2d07228965e40 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Aug 2025 17:37:26 +0200 Subject: [PATCH 1278/1302] Container: Apply some improvements from ejabberd source code Applied: - ejabberd.yml.example: Use HOST_URL_ENCODE to handle case when vhost is non-latin1 - ejabberdctl: Improve explanation how to stop ejabberd in live mode - ejabberdctl: New "mnesia_change" command, a frontend to mnesia_change_nodename --- .github/container/ejabberd.yml.example | 2 +- .github/container/ejabberdctl.template | 128 +++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 7 deletions(-) diff --git a/.github/container/ejabberd.yml.example b/.github/container/ejabberd.yml.example index 62dff50c9..2f63a2b64 100644 --- a/.github/container/ejabberd.yml.example +++ b/.github/container/ejabberd.yml.example @@ -206,7 +206,7 @@ modules: mod_fail2ban: {} mod_http_api: {} mod_http_upload: - put_url: https://@HOST@:5443/upload + put_url: https://@HOST_URL_ENCODE@:5443/upload custom_headers: "Access-Control-Allow-Origin": "https://@HOST@" "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" diff --git a/.github/container/ejabberdctl.template b/.github/container/ejabberdctl.template index 228f8707c..b1f1d2179 100755 --- a/.github/container/ejabberdctl.template +++ b/.github/container/ejabberdctl.template @@ -195,7 +195,9 @@ livewarning() echo "Please be extremely cautious with your actions," echo "and exit immediately if you are not completely sure." echo "" - echo "To exit and detach this shell from ejabberd, press:" + echo "To stop ejabberd gracefully:" + echo " ejabberd:stop()." + echo "To quit erlang immediately, press:" echo " control+g and then q" echo "" echo "--------------------------------------------------------------------" @@ -363,6 +365,13 @@ post_waiter_loop() # allow sync calls wait_status() { + wait_status_node "$ERLANG_NODE" $1 $2 $3 +} + +wait_status_node() +{ + CONNECT_NODE=$1 + shift # args: status try delay # return: 0 OK, 1 KO timeout="$2" @@ -374,9 +383,9 @@ wait_status() status="$1" else run_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null + -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi done @@ -385,19 +394,26 @@ wait_status() exec_other_command() { + exec_other_command_node $ERLANG_NODE "$@" +} + +exec_other_command_node() +{ + CONNECT_NODE=$1 + shift if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then run_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" + -extra "$CONNECT_NODE" $NO_TIMEOUT "$@" result=$? case $result in 3) help;; *) :;; esac - exit $result + return $result else exec_ctl_over_http_socket "$@" fi @@ -439,6 +455,103 @@ cd "$SPOOL_DIR" || { exit 6 } +printe() +{ + printf "\n" + printf "\e[1;40;32m==> %s\e[0m\n" "$1" +} + +## Function copied from tools/make-installers +user_agrees() +{ + question="$*" + + if [ -t 0 ] + then + printe "$question (y/n) [n]" + read -r response + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]|'') + return 1 + ;; + *) + echo 'Please respond with "yes" or "no".' + user_agrees "$question" + ;; + esac + else # Assume 'yes' if not running interactively. + return 0 + fi +} + +mnesia_change() +{ + ERLANG_NODE_OLD="$1" + [ "$ERLANG_NODE_OLD" = "" ] \ + && echo "Error: Please provide the old erlang node name, for example:" \ + && echo " ejabberdctl mnesia_change ejabberd@oldmachine" \ + && exit 1 + + SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/ + OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup + NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup + + printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'" + + [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP" + + if ! user_agrees "Do you want to proceed?" + then + echo 'Operation aborted.' + exit 1 + fi + + printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..." + exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached + wait_status_node $ERLANG_NODE_OLD 0 30 2 + result=$? + case $result in + 1) echo "There was a problem starting ejabberd with the old erlang node name. " \ + && echo "Check for log errors in $EJABBERD_LOG_PATH" \ + && exit $result;; + *) :;; + esac + exec_other_command_node $ERLANG_NODE_OLD "status" + + printe "Making backup of old database to file $OLDFILE ..." + mkdir $SPOOL_DIR_BACKUP + exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE" + + printe "Changing node name in new backup file $NEWFILE ..." + exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE" + + printe "Stopping old ejabberd..." + exec_other_command_node $ERLANG_NODE_OLD "stop" + wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd + + printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..." + mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP + + printe "Starting ejabberd with new node name $ERLANG_NODE ..." + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + wait_status 0 30 2 + exec_other_command "status" + + printe "Installing fallback of new mnesia..." + exec_other_command install_fallback "$NEWFILE" + + printe "Stopping new ejabberd..." + exec_other_command "stop" + wait_status 3 30 2 && stop_epmd + + printe "Finished, now you can start ejabberd normally" +} + # main case $1 in start) @@ -501,6 +614,9 @@ case $1 in set_dist_client wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout ;; + mnesia_change) + mnesia_change $2 + ;; post_waiter) post_waiter_waiting ;; From cae7850a7076a7c74ad4e38a03a85760a591ff8e Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Aug 2025 10:50:40 +0200 Subject: [PATCH 1279/1302] CHANGELOG.md: Update to 25.08 --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36721ea39..cadfc1c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,55 @@ +## Version 25.08 + +#### API Commands + +- `ban_account`: Run `sm_kick_user` event when kicking account ([#4415](https://github.com/processone/ejabberd/issues/4415)) +- `ban_account`: No need to change password ([#4415](https://github.com/processone/ejabberd/issues/4415)) +- `mnesia_change`: New command in `ejabberdctl` script that helps changing the mnesia node name + +#### Configuration + +- Rename `auth_password_types_hidden_in_scram1` option to `auth_password_types_hidden_in_sasl1` +- `econf`: If a host in configuration is encoded IDNA, decode it ([#3519](https://github.com/processone/ejabberd/issues/3519)) +- `ejabberd_config`: New predefined keyword `HOST_URL_ENCODE` +- `ejabberd.yml.example`: Use `HOST_URL_ENCODE` to handle case when vhost is non-latin1 +- `mod_conversejs`: Add option `conversejs_plugins` ([#4413](https://github.com/processone/ejabberd/issues/4413)) +- `mod_matrix_gw`: Add `leave_timeout` option ([#4386](https://github.com/processone/ejabberd/issues/4386)) + +#### Documentation and Tests + +- `COMPILE.md`: Mention dependencies and add link to Docs ([#4431](https://github.com/processone/ejabberd/issues/4431)) +- `ejabberd_doc`: Document commands tags for modules +- CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4425](https://github.com/processone/ejabberd/issues/4425)) +- Runtime: Raise the minimum Erlang tested to Erlang/OTP 24 + +#### Installers and Container + +- Bump Erlang/OTP version to 27.3.4.2 +- Bump OpenSSL version to 3.5.2 +- `make-binaries`: Disable Linux-PAM's `logind` support + +#### Core and Modules + +- Bump `p1_acme` to fix `'AttributePKCS-10'` and OTP 28 ([processone/p1_acme#4](https://github.com/processone/p1_acme/issues/4)) +- Prevent loops in `xml_compress:decode` with corrupted data +- `ejabberd_auth_mnesia`: Fix issue with filtering duplicates in `get_users()` +- `ejabberd_listener`: Add secret in temporary unix domain socket path ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_listener`: Log error when cannot set definitive unix socket ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_listener`: Try to create provisional socket in final directory ([#4422](https://github.com/processone/ejabberd/issues/4422)) +- `ejabberd_logger`: Print log lines colorized in console when using rebar3 +- `mod_conversejs`: Ensure assets_path ends in `/` as required by Converse ([#4414](https://github.com/processone/ejabberd/issues/4414)) +- `mod_conversejs`: Ensure plugins URL is separated with `/` ([#4413](https://github.com/processone/ejabberd/issues/4413)) +- `mod_http_upload`: Encode URLs into IDNA when showing to XMPP client ([#3519](https://github.com/processone/ejabberd/issues/3519)) +- `mod_matrix_gw`: Add support for null values in `is_canonical_json` ([#4421](https://github.com/processone/ejabberd/issues/4421)) +- `mod_matrix_gw`: Don't send empty direct Matrix messages ([#4420](https://github.com/processone/ejabberd/issues/4420)) +- `mod_matrix_gw`: Matrix gateway updates +- `mod_muc`: Report db failures when restoring rooms +- `mod_muc`: Unsubscribe users from members-only rooms when expelled ([#4412](https://github.com/processone/ejabberd/issues/4412)) +- `mod_providers`: New module to serve easily XMPP Providers files +- `mod_register`: Don't duplicate welcome subject and message +- `mod_scram_upgrade`: Fix format of passwords updates +- `mod_scram_upgrade`: Only offer upgrades to methods that aren't already stored + ## Version 25.07 #### Security fix From 00c75c3dc93c9800008fcfc2c35ad2247014231f Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 22 Aug 2025 11:15:33 +0200 Subject: [PATCH 1280/1302] Set version to 25.08 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d31d254fc..b91595dc5 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.07` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) +AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 25.08` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) AC_ARG_WITH(min-erlang, AS_HELP_STRING([--with-min-erlang=version],[set minimal required erlang version, default to OTP25]), From 3a36a722c50fd99d3d56ad44b995b32d8d424061 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 22 Aug 2025 14:44:48 +0300 Subject: [PATCH 1281/1302] Fix a bug in mod_matrix_gw_room:check_event_power_level/3 --- src/mod_matrix_gw_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_matrix_gw_room.erl b/src/mod_matrix_gw_room.erl index 4928c7582..65babf7e5 100644 --- a/src/mod_matrix_gw_room.erl +++ b/src/mod_matrix_gw_room.erl @@ -1659,7 +1659,7 @@ check_event_power_level(Event, StateMap, Data) -> get_event_power_level(Type, StateKey, PL) -> case {StateKey, PL} of - {_, #{Type := Level}} -> + {_, #{<<"events">> := #{Type := Level}}} -> get_int(Level); {undefined, #{<<"events_default">> := Level}} -> get_int(Level); From 60bdab4e5203d2e47b6353427b56e594a948c966 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Mon, 25 Aug 2025 10:43:31 +0200 Subject: [PATCH 1282/1302] CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action Updates this GitHub Action that's used to execute XMPP-based interop tests from v1.6.0 to v1.6.1. This is a bugfix release that should increase the stability / predictability of test execution. A notable change is that the file structure in which XMPP stanzas are generated (which is provided as debug output) has changed. They are still stored in the directory denoted by the logDir argument, but the file structure in that directory has changed somewhat. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cab0feb5..ebf9da68c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: - name: Run XMPP Interoperability Tests against CI server. if: matrix.otp == '27' continue-on-error: true - uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.0 + uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.6.1 with: domain: 'localhost' adminAccountUsername: 'admin' From bf262a6051619c46abbe2aa38be97b1bbadfdc32 Mon Sep 17 00:00:00 2001 From: Badlop Date: Sat, 23 Aug 2025 17:05:28 +0200 Subject: [PATCH 1283/1302] Fix markdown link to a command --- src/ejabberd_admin.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 4e7dfe739..5ec0ac051 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -274,7 +274,9 @@ get_commands_spec() -> `ejabberdctl` (or some `CTL_ON_` container environment variables) to run more commands afterwards, you may want to precede them with - the _`started`_ command to ensure the + the `started` + _`../../admin/guide/managing.md#ejabberdctl-commands|ejabberdctl command`_ + to ensure the clustering process has completed before proceeding. For example: `join_cluster ejabberd@main` > `started` > `list_cluster`.", From 066e0a8101e6e07712eecbf5d9c5c22c91b0787a Mon Sep 17 00:00:00 2001 From: Badlop Date: Sun, 24 Aug 2025 13:47:37 +0200 Subject: [PATCH 1284/1302] No need to test intermediate XML, and delete after conversion --- src/ejabberd_doc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_doc.erl b/src/ejabberd_doc.erl index a54c460f0..c2bf33922 100644 --- a/src/ejabberd_doc.erl +++ b/src/ejabberd_doc.erl @@ -437,7 +437,7 @@ run_a2x(Cwd, AsciiDocFile) -> {error, "a2x was not found: do you have 'asciidoc' installed?"}; {true, Path} -> Cmd = lists:flatten( - io_lib:format("~ts -f manpage ~ts -D ~ts", + io_lib:format("~ts --no-xmllint -f manpage ~ts -D ~ts", [Path, AsciiDocFile, Cwd])), case os:cmd(Cmd) of "" -> ok; From fd8aba6d4178560c57fe654edfb6e51429a027b3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Aug 2025 13:53:32 +0200 Subject: [PATCH 1285/1302] ext_mod: Print module status message after installation --- src/ext_mod.erl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ext_mod.erl b/src/ext_mod.erl index ee6ca2ecd..9d9b438c6 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -243,6 +243,7 @@ install(Package, Config) when is_binary(Package) -> ok -> code:add_pathsz([module_ebin_dir(Module)|module_deps_dirs(Module)]), ejabberd_config_reload(Config), + maybe_print_module_status(Module), copy_commit_json(Package, Attrs), ModuleRuntime = get_runtime_module_name(Module), case erlang:function_exported(ModuleRuntime, post_install, 0) of @@ -263,6 +264,14 @@ ejabberd_config_reload(Config) when is_list(Config) -> ejabberd_config_reload(undefined) -> ejabberd_config:reload(). +maybe_print_module_status(Module) -> + case get_module_status_el(Module) of + [_, {xmlcdata, String}] -> + io:format("~ts~n", [String]); + _ -> + ok + end. + uninstall(Module) when is_atom(Module) -> uninstall(misc:atom_to_binary(Module)); uninstall(Package) when is_binary(Package) -> From 2951281115256fd93a5953b224c35141e4524c18 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Aug 2025 16:56:51 +0200 Subject: [PATCH 1286/1302] mod_http_upload: Encode URL before parsing, as done before bba1a1e3c (#4450) --- src/mod_http_upload.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl index c4397741e..faa085811 100644 --- a/src/mod_http_upload.erl +++ b/src/mod_http_upload.erl @@ -921,7 +921,7 @@ mk_slot(PutURL, GetURL, XMLNS, Query) -> end. reencode_url(UrlString) -> - {ok, _, _, Host, _, _, _} = yconf:parse_uri(UrlString), + {ok, _, _, Host, _, _, _} = yconf:parse_uri(misc:url_encode(UrlString)), HostDecoded = misc:uri_decode(Host), HostIdna = idna:encode(HostDecoded), re:replace(UrlString, Host, HostIdna, [{return, binary}]). From 1d5b9bba1585fea98894cc2783ffa96ed7196424 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 29 Aug 2025 16:59:02 +0200 Subject: [PATCH 1287/1302] Get yconf that fixes problem introduced in recent commit (#4444) --- mix.exs | 2 +- mix.lock | 4 ++-- rebar.config | 2 +- rebar.lock | 11 ++++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/mix.exs b/mix.exs index 630f0939a..c5600b228 100644 --- a/mix.exs +++ b/mix.exs @@ -131,7 +131,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:xmpp, ">= 1.11.1"}, - {:yconf, ">= 1.0.21"}] + {:yconf, git: "https://github.com/processone/yconf", ref: "95692795a8a8d950ba560e5b07e6b80660557259", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 11c9ba390..9ad9a9e33 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "eredis": {:hex, :eredis, "1.7.1", "39e31aa02adcd651c657f39aafd4d31a9b2f63c6c700dc9cece98d4bc3c897ab", [:mix, :rebar3], [], "hexpm", "7c2b54c566fed55feef3341ca79b0100a6348fd3f162184b7ed5118d258c3cc1"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "esip": {:hex, :esip, "1.0.59", "eb202f8c62928193588091dfedbc545fe3274c34ecd209961f86dcb6c9ebce88", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.2.21", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "0bdf2e3c349dc0b144f173150329e675c6a51ac473d7a0b2e362245faad3fbe6"}, - "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, + "ex_doc": {:hex, :ex_doc, "0.38.3", "ddafe36b8e9fe101c093620879f6604f6254861a95133022101c08e75e6c759a", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "ecaa785456a67f63b4e7d7f200e8832fa108279e7eb73fd9928e7e66215a01f9"}, "exsync": {:hex, :exsync, "0.4.1", "0a14fe4bfcb80a509d8a0856be3dd070fffe619b9ba90fec13c58b316c176594", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "cefb22aa805ec97ffc5b75a4e1dc54bcaf781e8b32564bf74abbe5803d1b5178"}, "ezlib": {:hex, :ezlib, "1.0.15", "d74f5df191784744726a5b1ae9062522c606334f11086363385eb3b772d91357", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd14ba6c12521af5cfe6923e73e3d545f4a0897dc66bfab5287fbb7ae3962eab"}, "fast_tls": {:hex, :fast_tls, "1.1.25", "da8ed6f05a2452121b087158b17234749f36704c1f2b74dc51db99a1e27ed5e8", [:rebar3], [{:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "59e183b5740e670e02b8aa6be673b5e7779e5fe5bfcc679fe2d4993d1949a821"}, @@ -35,5 +35,5 @@ "stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "xmpp": {:hex, :xmpp, "1.11.1", "60181e7d3e8e48aa3b23b2792075cda37e2e507ec152490b866e61e5320cb1da", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "a5c933df904ab3cec15425da334e410ce84ec3ae7b81efe069e5db368a7b3716"}, - "yconf": {:hex, :yconf, "1.0.21", "dbae1589381e044529e112b7e0097c89d88df89e446ead53bd33e8d27e2bcc83", [:rebar3], [{:fast_yaml, "1.0.39", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "c524a5f1fd86875d85b469cc2e368c204f97cca1c3918736e21f5001c01d096c"}, + "yconf": {:git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259", [ref: "95692795a8a8d950ba560e5b07e6b80660557259"]}, } diff --git a/rebar.config b/rebar.config index 799197174..e3d42edce 100644 --- a/rebar.config +++ b/rebar.config @@ -78,7 +78,7 @@ {if_var_true, stun, {stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}}, {xmpp, "~> 1.11.1", {git, "https://github.com/processone/xmpp", {tag, "1.11.1"}}}, - {yconf, "~> 1.0.21", {git, "https://github.com/processone/yconf", {tag, "1.0.21"}}} + {yconf, ".*", {git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259"}} ]}. {gitonly_deps, [ejabberd_po]}. diff --git a/rebar.lock b/rebar.lock index 371e08b40..2547b4371 100644 --- a/rebar.lock +++ b/rebar.lock @@ -25,7 +25,10 @@ {<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0}, - {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.21">>},0}]}. + {<<"yconf">>, + {git,"https://github.com/processone/yconf", + {ref,"95692795a8a8d950ba560e5b07e6b80660557259"}}, + 0}]}. [ {pkg_hash,[ {<<"base64url">>, <<"F8C7F2DA04CA9A5D0F5F50258F055E1D699F0E8BF4CFDB30B750865368403CF6">>}, @@ -53,8 +56,7 @@ {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, {<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>}, {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, - {<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>}, - {<<"yconf">>, <<"DBAE1589381E044529E112B7E0097C89D88DF89E446EAD53BD33E8D27E2BCC83">>}]}, + {<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, @@ -81,6 +83,5 @@ {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, {<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>}, {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, - {<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>}, - {<<"yconf">>, <<"C524A5F1FD86875D85B469CC2E368C204F97CCA1C3918736E21F5001C01D096C">>}]} + {<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>}]} ]. From 4dea2f1eb66dca48052d2a7d616d2efaecac78cd Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 1 Sep 2025 17:51:25 +0200 Subject: [PATCH 1288/1302] Fix some Elvis reports --- elvis.config | 1 + src/mod_http_api.erl | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/elvis.config b/elvis.config index fb0f8b973..4ac4df9e1 100644 --- a/elvis.config +++ b/elvis.config @@ -16,6 +16,7 @@ {elvis_style, function_naming_convention, disable}, {elvis_style, god_modules, #{limit => 300}}, {elvis_style, invalid_dynamic_call, disable}, + {elvis_style, macro_names, disable}, {elvis_style, max_function_arity, disable}, % #{max_arity => 15}}, {elvis_style, nesting_level, disable}, {elvis_style, no_author, disable}, diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 68d5c4181..4d7e86777 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -145,12 +145,12 @@ process([Call | _], #request{method = 'POST', data = Data, ip = IPPort} = Req) - %% TODO We need to refactor to remove redundant error return formatting throw:{error, unknown_command} -> json_format({404, 44, <<"Command not found.">>}); - _:{error,{_,invalid_json}} = _Err -> - ?DEBUG("Bad Request: ~p", [_Err]), + _:{error,{_,invalid_json}} = Err -> + ?DEBUG("Bad Request: ~p", [Err]), badrequest_response(<<"Invalid JSON input">>); - ?EX_RULE(_Class, _Error, Stack) -> + ?EX_RULE(_Class, Error, Stack) -> StackTrace = ?EX_STACK(Stack), - ?DEBUG("Bad Request: ~p ~p", [_Error, StackTrace]), + ?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]), badrequest_response() end; process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> @@ -166,9 +166,9 @@ process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> %% TODO We need to refactor to remove redundant error return formatting throw:{error, unknown_command} -> json_format({404, 44, <<"Command not found.">>}); - ?EX_RULE(_, _Error, Stack) -> + ?EX_RULE(_, Error, Stack) -> StackTrace = ?EX_STACK(Stack), - ?DEBUG("Bad Request: ~p ~p", [_Error, StackTrace]), + ?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]), badrequest_response() end; process([_Call], #request{method = 'OPTIONS', data = <<>>}) -> From 19b71061244dab1979872ab244411b99035d6475 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Mon, 22 Sep 2025 17:49:33 +0200 Subject: [PATCH 1289/1302] Remove EX_RULE and EX_STACK macros As we no longer targer R19, we no longer need to carry those. --- include/ejabberd_stacktrace.hrl | 27 -------------- mix.exs | 1 - rebar.config | 1 - src/ejabberd_app.erl | 2 +- src/ejabberd_config.erl | 23 ++++++------ src/ejabberd_ctl.erl | 11 +++--- src/ejabberd_hooks.erl | 65 ++++++++++++++++---------------- src/ejabberd_http.erl | 10 ++--- src/ejabberd_iq.erl | 12 +++--- src/ejabberd_local.erl | 2 +- src/ejabberd_mnesia.erl | 19 +++++----- src/ejabberd_redis.erl | 9 +++-- src/ejabberd_router.erl | 13 ++++--- src/ejabberd_router_sql.erl | 21 ++++++----- src/ejabberd_s2s.erl | 12 +++--- src/ejabberd_sm.erl | 17 +++++---- src/ejabberd_sql.erl | 39 ++++++++++--------- src/gen_iq_handler.erl | 18 ++++----- src/gen_mod.erl | 66 +++++++++++++++++++-------------- src/mod_http_api.erl | 24 ++++++------ src/mod_mix.erl | 13 ++++--- src/mod_muc.erl | 18 ++++----- src/mod_muc_room.erl | 37 +++++++++--------- src/mod_proxy65_service.erl | 6 +-- src/mod_pubsub.erl | 36 +++++++++--------- src/mod_roster.erl | 2 +- src/mod_vcard.erl | 12 +++--- 27 files changed, 252 insertions(+), 264 deletions(-) delete mode 100644 include/ejabberd_stacktrace.hrl diff --git a/include/ejabberd_stacktrace.hrl b/include/ejabberd_stacktrace.hrl deleted file mode 100644 index e65c5264f..000000000 --- a/include/ejabberd_stacktrace.hrl +++ /dev/null @@ -1,27 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% -%%% ejabberd, Copyright (C) 2002-2025 ProcessOne -%%% -%%% 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 2 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, write to the Free Software Foundation, Inc., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --ifdef(DEPRECATED_GET_STACKTRACE). --define(EX_RULE(Class, Reason, Stack), Class:Reason:Stack). --define(EX_STACK(Stack), Stack). --else. --define(EX_RULE(Class, Reason, _), Class:Reason). --define(EX_STACK(_), erlang:get_stacktrace()). --endif. diff --git a/mix.exs b/mix.exs index c5600b228..5b5be84f5 100644 --- a/mix.exs +++ b/mix.exs @@ -85,7 +85,6 @@ defmodule Ejabberd.MixProject do result = [{:d, :ELIXIR_ENABLED}] ++ cond_options() ++ Enum.map(includes, fn (path) -> {:i, path} end) ++ - if_version_above(~c"20", [{:d, :DEPRECATED_GET_STACKTRACE}]) ++ if_version_above(~c"20", [{:d, :HAVE_URI_STRING}]) ++ if_version_above(~c"20", [{:d, :HAVE_ERL_ERROR}]) ++ if_version_below(~c"21", [{:d, :USE_OLD_HTTP_URI}]) ++ diff --git a/rebar.config b/rebar.config index e3d42edce..1d0f9a8db 100644 --- a/rebar.config +++ b/rebar.config @@ -126,7 +126,6 @@ {erl_opts, [nowarn_deprecated_function, {i, "include"}, - {if_version_above, "20", {d, 'DEPRECATED_GET_STACKTRACE'}}, {if_version_above, "20", {d, 'HAVE_ERL_ERROR'}}, {if_version_above, "20", {d, 'HAVE_URI_STRING'}}, {if_version_below, "21", {d, 'USE_OLD_HTTP_URI'}}, diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index a31d7b1a6..cbe74fb08 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -32,7 +32,7 @@ -export([start/2, prep_stop/1, stop/1]). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + %%% %%% Application API diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 8c10041e5..11173f9e8 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -52,7 +52,7 @@ {get_lang, 1}]). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -type option() :: atom() | {atom(), global | binary()}. -type error_reason() :: {merge_conflict, atom(), binary()} | @@ -169,14 +169,14 @@ get_option({O, Host} = Opt) -> T -> T end, try ets:lookup_element(Tab, Opt, 2) - catch ?EX_RULE(error, badarg, St) when Host /= global -> - StackTrace = ?EX_STACK(St), - Val = get_option({O, global}), - ?DEBUG("Option '~ts' is not defined for virtual host '~ts'. " - "This is a bug, please report it with the following " - "stacktrace included:~n** ~ts", - [O, Host, misc:format_exception(2, error, badarg, StackTrace)]), - Val + catch + error:badarg:StackTrace when Host /= global -> + Val = get_option({O, global}), + ?DEBUG("Option '~ts' is not defined for virtual host '~ts'. " + "This is a bug, please report it with the following " + "stacktrace included:~n** ~ts", + [O, Host, misc:format_exception(2, error, badarg, StackTrace)]), + Val end. -spec set_option(option(), term()) -> ok. @@ -802,8 +802,9 @@ load_file(File) -> Err -> abort(Err) end - catch ?EX_RULE(Class, Reason, St) -> - {error, {exception, Class, Reason, ?EX_STACK(St)}} + catch + Class:Reason:Stack -> + {error, {exception, Class, Reason, Stack}} end. -spec commit() -> ok. diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 88bd5e785..075854e40 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -39,7 +39,7 @@ -include("ejabberd_commands.hrl"). -include("ejabberd_http.hrl"). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -define(DEFAULT_VERSION, 1000000). @@ -331,11 +331,10 @@ try_call_command(Args, Auth, AccessCommands, Version) -> ?STATUS_ERROR}; throw:Error -> {io_lib:format("~p", [Error]), ?STATUS_ERROR}; - ?EX_RULE(A, Why, Stack) -> - StackTrace = ?EX_STACK(Stack), - {io_lib:format("Unhandled exception occurred executing the command:~n** ~ts", - [misc:format_exception(2, A, Why, StackTrace)]), - ?STATUS_ERROR} + A:Why:StackTrace -> + {io_lib:format("Unhandled exception occurred executing the command:~n** ~ts", + [misc:format_exception(2, A, Why, StackTrace)]), + ?STATUS_ERROR} end. -spec call_command(Args::[string()], diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 0fedc1dfc..8f378aa48 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -60,7 +60,7 @@ ). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -record(state, {}). -type subscriber() :: {Module :: atom(), Function :: atom(), InitArg :: any()}. @@ -455,17 +455,19 @@ safe_apply(Hook, Module, Function, Args) -> true -> apply(Module, Function, Args) end - catch ?EX_RULE(E, R, St) when E /= exit; R /= normal -> - Stack = ?EX_STACK(St), - ?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++ - string:join( - ["** ~ts"| - ["** Arg " ++ integer_to_list(I) ++ " = ~p" - || I <- lists:seq(1, length(Args))]], - "~n"), - [Hook, Module, Function, length(Args), - misc:format_exception(2, E, R, Stack)|Args]), - 'EXIT' + catch + E:R:Stack when E /= exit; R /= normal -> + ?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++ + string:join( + ["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p" + || I <- lists:seq(1, length(Args)) ]], + "~n"), + [Hook, + Module, + Function, + length(Args), + misc:format_exception(2, E, R, Stack) | Args]), + 'EXIT' end. -spec call_subscriber_list([subscriber()], binary() | global, atom(), {atom(), atom(), integer(), list()} | list(), subscriber_event(), [subscriber()]) -> any(). @@ -480,18 +482,20 @@ call_subscriber_list([{Mod, Func, InitArg} | SubscriberList], Host, Hook, Callba try apply(Mod, Func, SubscriberArgs) of State -> call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, [{Mod, Func, State} | Result]) - catch ?EX_RULE(E, R, St) when E /= exit; R /= normal -> - Stack = ?EX_STACK(St), - ?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++ - string:join( - ["** ~ts"| - ["** Arg " ++ integer_to_list(I) ++ " = ~p" - || I <- lists:seq(1, length(SubscriberArgs))]], - "~n"), - [Hook, Mod, Func, length(SubscriberArgs), - misc:format_exception(2, E, R, Stack)|SubscriberArgs]), - %% Do not append subscriber for next calls: - call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result) + catch + E:R:Stack when E /= exit; R /= normal -> + ?ERROR_MSG("Hook subscriber ~p crashed when running ~p:~p/~p:~n" ++ + string:join( + ["** ~ts" | [ "** Arg " ++ integer_to_list(I) ++ " = ~p" + || I <- lists:seq(1, length(SubscriberArgs)) ]], + "~n"), + [Hook, + Mod, + Func, + length(SubscriberArgs), + misc:format_exception(2, E, R, Stack) | SubscriberArgs]), + %% Do not append subscriber for next calls: + call_subscriber_list(SubscriberList, Host, Hook, CallbackOrArgs, Event, Result) end. %%%---------------------------------------------------------------------- @@ -709,13 +713,11 @@ run_event_handlers(TracingOpts, Hook, Host, Event, EventArgs, RunType) -> _ -> ok catch - ?EX_RULE(E, R, St) -> - Stack = ?EX_STACK(St), - ?ERROR_MSG( + E:R:Stack -> + ?ERROR_MSG( "(~0p|~ts|~0p) Tracing event '~0p' handler exception(~0p): ~0p: ~0p", - [Hook, Host, erlang:self(), EventHandler, E, R, Stack] - ), - ok + [Hook, Host, erlang:self(), EventHandler, E, R, Stack]), + ok end end, EventHandlerList @@ -885,8 +887,7 @@ tracing_output(#{output_function := OutputF}, Text, Args) -> _ -> ok catch - ?EX_RULE(E, R, St) -> - Stack = ?EX_STACK(St), + E:R:Stack -> ?ERROR_MSG("Tracing output function exception(~0p): ~0p: ~0p", [E, R, Stack]), ok end; diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index b8e05dec4..709585145 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -39,7 +39,7 @@ -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_http.hrl"). --include("ejabberd_stacktrace.hrl"). + -include_lib("kernel/include/file.hrl"). -record(state, {sockmod, @@ -372,11 +372,11 @@ process(Handlers, Request) -> try HandlerModule:process(LocalPath, Request) catch - ?EX_RULE(Class, Reason, Stack) -> + Class:Reason:Stack -> ?ERROR_MSG( - "HTTP handler crashed: ~s", - [misc:format_exception(2, Class, Reason, ?EX_STACK(Stack))]), - erlang:raise(Class, Reason, ?EX_STACK(Stack)) + "HTTP handler crashed: ~s", + [misc:format_exception(2, Class, Reason, Stack)]), + erlang:raise(Class, Reason, Stack) end end, ejabberd_hooks:run(http_request_debug, [{LocalPath, Request}]), diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl index 507820b20..5db539cab 100644 --- a/src/ejabberd_iq.erl +++ b/src/ejabberd_iq.erl @@ -36,7 +36,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -record(state, {expire = infinity :: timeout()}). -type state() :: #state{}. @@ -174,11 +174,11 @@ calc_checksum(Data) -> -spec callback(atom() | pid(), #iq{} | timeout, term()) -> any(). callback(undefined, IQRes, Fun) -> try Fun(IQRes) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts", - [xmpp:pp(IQRes), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to process iq response:~n~ts~n** ~ts", + [xmpp:pp(IQRes), + misc:format_exception(2, Class, Reason, StackTrace)]) end; callback(Proc, IQRes, Ctx) -> try diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 34735cd2c..5d9557415 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -48,7 +48,7 @@ -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("translate.hrl"). -record(state, {}). diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index 5b82dbb50..d9db27219 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -42,7 +42,7 @@ -define(NEED_RESET, [local_content, type]). -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -record(state, {tables = #{} :: tables(), schema = [] :: [{atom(), custom_schema()}]}). @@ -377,14 +377,15 @@ do_transform(OldAttrs, Attrs, Old) -> transform_fun(Module, Name) -> fun(Obj) -> try Module:transform(Obj) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to transform Mnesia table ~ts:~n" - "** Record: ~p~n" - "** ~ts", - [Name, Obj, - misc:format_exception(2, Class, Reason, StackTrace)]), - erlang:raise(Class, Reason, StackTrace) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to transform Mnesia table ~ts:~n" + "** Record: ~p~n" + "** ~ts", + [Name, + Obj, + misc:format_exception(2, Class, Reason, StackTrace)]), + erlang:raise(Class, Reason, StackTrace) end end. diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl index 27e8775e1..c0e61c0e6 100644 --- a/src/ejabberd_redis.erl +++ b/src/ejabberd_redis.erl @@ -48,7 +48,7 @@ -define(CALL_TIMEOUT, 60*1000). %% 60 seconds -include("logger.hrl"). --include("ejabberd_stacktrace.hrl"). + -record(state, {connection :: pid() | undefined, num :: pos_integer(), @@ -114,9 +114,10 @@ multi(F) -> {error, _} = Err -> Err; Result -> get_result(Result) end - catch ?EX_RULE(E, R, St) -> - erlang:erase(?TR_STACK), - erlang:raise(E, R, ?EX_STACK(St)) + catch + E:R:St -> + erlang:erase(?TR_STACK), + erlang:raise(E, R, St) end; _ -> erlang:error(nested_transaction) diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl index de088d8ba..236b5081a 100644 --- a/src/ejabberd_router.erl +++ b/src/ejabberd_router.erl @@ -70,7 +70,8 @@ -include("logger.hrl"). -include("ejabberd_router.hrl"). -include_lib("xmpp/include/xmpp.hrl"). --include("ejabberd_stacktrace.hrl"). + + -callback init() -> any(). -callback register_route(binary(), binary(), local_hint(), @@ -90,11 +91,11 @@ start_link() -> -spec route(stanza()) -> ok. route(Packet) -> try do_route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end. -spec route(jid(), jid(), xmlel() | stanza()) -> ok. diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl index ad120d82f..2d7631476 100644 --- a/src/ejabberd_router_sql.erl +++ b/src/ejabberd_router_sql.erl @@ -32,7 +32,8 @@ -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). -include("ejabberd_router.hrl"). --include("ejabberd_stacktrace.hrl"). + + %%%=================================================================== %%% API @@ -141,13 +142,13 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) -> local_hint = dec_local_hint(LocalHintS)}] catch _:{bad_node, _} -> []; - ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to decode row from 'route' table:~n" - "** Row = ~p~n" - "** Domain = ~ts~n" - "** ~ts", - [Row, Domain, - misc:format_exception(2, Class, Reason, StackTrace)]), - [] + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to decode row from 'route' table:~n" + "** Row = ~p~n" + "** Domain = ~ts~n" + "** ~ts", + [Row, + Domain, + misc:format_exception(2, Class, Reason, StackTrace)]), + [] end. diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 448773612..0876ca584 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -53,7 +53,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("translate.hrl"). -define(DEFAULT_MAX_S2S_CONNECTIONS_NUMBER, 1). @@ -249,11 +249,11 @@ handle_info({mnesia_system_event, {mnesia_up, Node}}, State) -> {noreply, State}; handle_info({route, Packet}, State) -> try route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) -> diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 296452736..d6d81fbbe 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -92,7 +92,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("ejabberd_commands.hrl"). -include("ejabberd_sm.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("translate.hrl"). -callback init() -> ok | {error, any()}. @@ -131,13 +131,14 @@ stop() -> %% @doc route arbitrary term to c2s process(es) route(To, Term) -> try do_route(To, Term), ok - catch ?EX_RULE(E, R, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route term to ~ts:~n" - "** Term = ~p~n" - "** ~ts", - [jid:encode(To), Term, - misc:format_exception(2, E, R, StackTrace)]) + catch + E:R:StackTrace -> + ?ERROR_MSG("Failed to route term to ~ts:~n" + "** Term = ~p~n" + "** ~ts", + [jid:encode(To), + Term, + misc:format_exception(2, E, R, StackTrace)]) end. -spec route(stanza()) -> ok. diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 0c29f039e..922fec14a 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -89,7 +89,7 @@ -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). --include("ejabberd_stacktrace.hrl"). + -record(state, {db_ref :: undefined | db_ref_pid() | odbc_connection_reference(), @@ -616,19 +616,20 @@ outer_transaction(F, NRestarts, _Reason) -> {atomic, Res} end catch - ?EX_RULE(throw, {aborted, Reason}, _) when NRestarts > 0 -> - maybe_restart_transaction(F, NRestarts, Reason, true); - ?EX_RULE(throw, {aborted, Reason}, Stack) when NRestarts =:= 0 -> - StackTrace = ?EX_STACK(Stack), - ?ERROR_MSG("SQL transaction restarts exceeded~n** " - "Restarts: ~p~n** Last abort reason: " - "~p~n** Stacktrace: ~p~n** When State " - "== ~p", - [?MAX_TRANSACTION_RESTARTS, Reason, - StackTrace, get(?STATE_KEY)]), - maybe_restart_transaction(F, NRestarts, Reason, true); - ?EX_RULE(_, Reason, _) -> - maybe_restart_transaction(F, 0, Reason, true) + throw:{aborted, Reason}:_ when NRestarts > 0 -> + maybe_restart_transaction(F, NRestarts, Reason, true); + throw:{aborted, Reason}:StackTrace when NRestarts =:= 0 -> + ?ERROR_MSG("SQL transaction restarts exceeded~n** " + "Restarts: ~p~n** Last abort reason: " + "~p~n** Stacktrace: ~p~n** When State " + "== ~p", + [?MAX_TRANSACTION_RESTARTS, + Reason, + StackTrace, + get(?STATE_KEY)]), + maybe_restart_transaction(F, NRestarts, Reason, true); + _:Reason:_ -> + maybe_restart_transaction(F, 0, Reason, true) end end. @@ -742,10 +743,9 @@ sql_query_internal(#sql_query{} = Query) -> {error, <<"terminated unexpectedly">>}; exit:{shutdown, _} -> {error, <<"shutdown">>}; - ?EX_RULE(Class, Reason, Stack) -> - StackTrace = ?EX_STACK(Stack), + Class:Reason:StackTrace -> ?ERROR_MSG("Internal error while processing SQL query:~n** ~ts", - [misc:format_exception(2, Class, Reason, StackTrace)]), + [misc:format_exception(2, Class, Reason, StackTrace)]), {error, <<"internal error">>} end, check_error(Res, Query); @@ -935,12 +935,11 @@ sql_query_format_res({selected, _, Rows}, SQLQuery) -> try [(SQLQuery#sql_query.format_res)(Row)] catch - ?EX_RULE(Class, Reason, Stack) -> - StackTrace = ?EX_STACK(Stack), + Class:Reason:StackTrace -> ?ERROR_MSG("Error while processing SQL query result:~n" "** Row: ~p~n** ~ts", [Row, - misc:format_exception(2, Class, Reason, StackTrace)]), + misc:format_exception(2, Class, Reason, StackTrace)]), [] end end, Rows), diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl index a02e9178a..c123f6383 100644 --- a/src/gen_iq_handler.erl +++ b/src/gen_iq_handler.erl @@ -37,7 +37,7 @@ -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + -type component() :: ejabberd_sm | ejabberd_local. @@ -111,14 +111,14 @@ process_iq(_Host, Module, Function, IQ) -> ejabberd_router:route(ResIQ); ignore -> ok - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to process iq:~n~ts~n** ~ts", - [xmpp:pp(IQ), - misc:format_exception(2, Class, Reason, StackTrace)]), - Txt = ?T("Module failed to handle the query"), - Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), - ejabberd_router:route_error(IQ, Err) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to process iq:~n~ts~n** ~ts", + [xmpp:pp(IQ), + misc:format_exception(2, Class, Reason, StackTrace)]), + Txt = ?T("Module failed to handle the query"), + Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), + ejabberd_router:route_error(IQ, Err) end. -spec process_iq(module(), atom(), iq()) -> ignore | iq(). diff --git a/src/gen_mod.erl b/src/gen_mod.erl index dc3ba3c56..667ff5055 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -44,7 +44,7 @@ -include("logger.hrl"). -include_lib("stdlib/include/ms_transform.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("ejabberd_commands.hrl"). -record(ejabberd_module, @@ -187,16 +187,20 @@ start_module(Host, Module, Opts, Order) -> ets:delete(ejabberd_modules, {Module, Host}), erlang:error({bad_return, Module, Err}) end - catch ?EX_RULE(Class, Reason, Stack) -> - StackTrace = ?EX_STACK(Stack), - ets:delete(ejabberd_modules, {Module, Host}), - ErrorText = format_module_error( - Module, start, 2, - Opts, Class, Reason, - StackTrace), - ?CRITICAL_MSG(ErrorText, []), - maybe_halt_ejabberd(), - erlang:raise(Class, Reason, StackTrace) + catch + Class:Reason:StackTrace -> + ets:delete(ejabberd_modules, {Module, Host}), + ErrorText = format_module_error( + Module, + start, + 2, + Opts, + Class, + Reason, + StackTrace), + ?CRITICAL_MSG(ErrorText, []), + maybe_halt_ejabberd(), + erlang:raise(Class, Reason, StackTrace) end. -spec reload_modules(binary()) -> ok. @@ -246,14 +250,18 @@ reload_module(Host, Module, NewOpts, OldOpts, Order) -> {ok, Pid} when is_pid(Pid) -> {ok, Pid}; Err -> erlang:error({bad_return, Module, Err}) end - catch ?EX_RULE(Class, Reason, Stack) -> - StackTrace = ?EX_STACK(Stack), - ErrorText = format_module_error( - Module, reload, 3, - NewOpts, Class, Reason, - StackTrace), + catch + Class:Reason:StackTrace -> + ErrorText = format_module_error( + Module, + reload, + 3, + NewOpts, + Class, + Reason, + StackTrace), ?CRITICAL_MSG(ErrorText, []), - erlang:raise(Class, Reason, StackTrace) + erlang:raise(Class, Reason, StackTrace) end; false -> ?WARNING_MSG("Module ~ts doesn't support reloading " @@ -346,14 +354,15 @@ prep_stop_module_keep_config(Host, Module) -> try Module:prep_stop(Host) of _ -> ok - catch ?EX_RULE(error, undef, _St) -> - ok; - ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), + catch + error:undef:_St -> + ok; + Class:Reason:StackTrace -> ?ERROR_MSG("Failed to prepare stop module ~ts at ~ts:~n** ~ts", - [Module, Host, + [Module, + Host, misc:format_exception(2, Class, Reason, StackTrace)]), - error + error end. -spec stop_module_keep_config(binary(), atom()) -> error | ok. @@ -371,12 +380,13 @@ stop_module_keep_config(Host, Module) -> _ -> ets:delete(ejabberd_modules, {Module, Host}), ok - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), + catch + Class:Reason:StackTrace -> ?ERROR_MSG("Failed to stop module ~ts at ~ts:~n** ~ts", - [Module, Host, + [Module, + Host, misc:format_exception(2, Class, Reason, StackTrace)]), - error + error end. -spec add_registrations(binary(), module(), [registration()]) -> ok. diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 4d7e86777..087be0e72 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -36,7 +36,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("ejabberd_http.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("translate.hrl"). -define(DEFAULT_API_VERSION, 1000000). @@ -148,8 +148,7 @@ process([Call | _], #request{method = 'POST', data = Data, ip = IPPort} = Req) - _:{error,{_,invalid_json}} = Err -> ?DEBUG("Bad Request: ~p", [Err]), badrequest_response(<<"Invalid JSON input">>); - ?EX_RULE(_Class, Error, Stack) -> - StackTrace = ?EX_STACK(Stack), + _Class:Error:StackTrace -> ?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]), badrequest_response() end; @@ -166,8 +165,7 @@ process([Call | _], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) -> %% TODO We need to refactor to remove redundant error return formatting throw:{error, unknown_command} -> json_format({404, 44, <<"Command not found.">>}); - ?EX_RULE(_, Error, Stack) -> - StackTrace = ?EX_STACK(Stack), + _:Error:StackTrace -> ?DEBUG("Bad Request: ~p ~p", [Error, StackTrace]), badrequest_response() end; @@ -257,13 +255,15 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> {400, misc:atom_to_binary(Error)}; throw:Msg when is_list(Msg); is_binary(Msg) -> {400, iolist_to_binary(Msg)}; - ?EX_RULE(Class, Error, Stack) -> - StackTrace = ?EX_STACK(Stack), - ?ERROR_MSG("REST API Error: " - "~ts(~p) -> ~p:~p ~p", - [Call, hide_sensitive_args(Args), - Class, Error, StackTrace]), - {500, <<"internal_error">>} + Class:Error:StackTrace -> + ?ERROR_MSG("REST API Error: " + "~ts(~p) -> ~p:~p ~p", + [Call, + hide_sensitive_args(Args), + Class, + Error, + StackTrace]), + {500, <<"internal_error">>} end. handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> diff --git a/src/mod_mix.erl b/src/mod_mix.erl index ced8ada75..25216b6fc 100644 --- a/src/mod_mix.erl +++ b/src/mod_mix.erl @@ -44,7 +44,8 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("logger.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + + -callback init(binary(), gen_mod:opts()) -> ok | {error, db_failure}. -callback set_channel(binary(), binary(), binary(), @@ -319,11 +320,11 @@ handle_cast(Request, State) -> handle_info({route, Packet}, State) -> try route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_info(Info, State) -> diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 0ca31cb80..ffb8c25d7 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -85,7 +85,7 @@ -include("mod_muc.hrl"). -include("mod_muc_room.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + -type state() :: #{hosts := [binary()], server_host := binary(), @@ -454,11 +454,11 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From, -spec handle_cast(term(), state()) -> {noreply, state()}. handle_cast({route_to_room, Packet}, #{server_host := ServerHost} = State) -> try route_to_room(Packet, ServerHost) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), + catch + Class:Reason:StackTrace -> ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_cast({room_destroyed, {Room, Host}, Pid}, @@ -483,11 +483,11 @@ handle_info({route, Packet}, #{server_host := ServerHost} = State) -> %% where mod_muc is not loaded. Such configuration %% is *highly* discouraged try route(Packet, ServerHost) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), + catch + Class:Reason:StackTrace -> ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_info({room_destroyed, {Room, Host}, Pid}, State) -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index b4148b33f..b2aa0d4c6 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -74,7 +74,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). -include("mod_muc_room.hrl"). --include("ejabberd_stacktrace.hrl"). + -define(MAX_USERS_DEFAULT_LIST, [5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]). @@ -1027,10 +1027,10 @@ terminate(Reason, _StateName, ok end end - catch ?EX_RULE(E, R, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Got exception on room termination:~n** ~ts", - [misc:format_exception(2, E, R, StackTrace)]) + catch + E:R:StackTrace -> + ?ERROR_MSG("Got exception on room termination:~n** ~ts", + [misc:format_exception(2, E, R, StackTrace)]) end. %%%---------------------------------------------------------------------- @@ -3266,19 +3266,20 @@ process_item_change(Item, SD, UJID) -> maybe_send_affiliation(JID, A, SD1), SD1 end - catch ?EX_RULE(E, R, St) -> - StackTrace = ?EX_STACK(St), - FromSuffix = case UJID of - #jid{} -> - JidString = jid:encode(UJID), - <<" from ", JidString/binary>>; - undefined -> - <<"">> - end, - ?ERROR_MSG("Failed to set item ~p~ts:~n** ~ts", - [Item, FromSuffix, - misc:format_exception(2, E, R, StackTrace)]), - {error, xmpp:err_internal_server_error()} + catch + E:R:StackTrace -> + FromSuffix = case UJID of + #jid{} -> + JidString = jid:encode(UJID), + <<" from ", JidString/binary>>; + undefined -> + <<"">> + end, + ?ERROR_MSG("Failed to set item ~p~ts:~n** ~ts", + [Item, + FromSuffix, + misc:format_exception(2, E, R, StackTrace)]), + {error, xmpp:err_internal_server_error()} end. -spec unsubscribe_from_room(jid(), state()) -> ok | error. diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index 692ad146d..2d170ed68 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -40,7 +40,7 @@ -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + -define(PROCNAME, ejabberd_mod_proxy65_service). @@ -86,8 +86,8 @@ terminate(_Reason, #state{myhosts = MyHosts}) -> handle_info({route, Packet}, State) -> try route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), + catch + Class:Reason:StackTrace -> ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", [xmpp:pp(Packet), misc:format_exception(2, Class, Reason, StackTrace)]) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 06f0af657..bd0aff20f 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -46,7 +46,7 @@ -include("pubsub.hrl"). -include("mod_roster.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("ejabberd_commands.hrl"). -define(STDTREE, <<"tree">>). @@ -748,11 +748,11 @@ handle_cast(Msg, State) -> handle_info({route, Packet}, State) -> try route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_info(Info, State) -> @@ -3819,9 +3819,9 @@ tree_action(Host, Function, Args) -> DBType = mod_pubsub_opt:db_type(ServerHost), Fun = fun () -> try tree_call(Host, Function, Args) - catch ?EX_RULE(Class, Reason, St) when DBType == sql -> - StackTrace = ?EX_STACK(St), - ejabberd_sql:abort({exception, Class, Reason, StackTrace}) + catch + Class:Reason:StackTrace when DBType == sql -> + ejabberd_sql:abort({exception, Class, Reason, StackTrace}) end end, Ret = case DBType of @@ -3919,15 +3919,15 @@ transaction(Host, Fun, Trans) -> do_transaction(ServerHost, Fun, Trans, DBType) -> F = fun() -> try Fun() - catch ?EX_RULE(Class, Reason, St) when (DBType == mnesia andalso - Trans == transaction) orelse - DBType == sql -> - StackTrace = ?EX_STACK(St), - Ex = {exception, Class, Reason, StackTrace}, - case DBType of - mnesia -> mnesia:abort(Ex); - sql -> ejabberd_sql:abort(Ex) - end + catch + Class:Reason:StackTrace when (DBType == mnesia andalso + Trans == transaction) orelse + DBType == sql -> + Ex = {exception, Class, Reason, StackTrace}, + case DBType of + mnesia -> mnesia:abort(Ex); + sql -> ejabberd_sql:abort(Ex) + end end end, Res = case DBType of diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 7765fc255..0f032cb02 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -61,7 +61,7 @@ -include("mod_roster.hrl"). -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("translate.hrl"). -define(ROSTER_CACHE, roster_cache). diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index a225ba23b..740ca41d2 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -51,7 +51,7 @@ -include_lib("xmpp/include/xmpp.hrl"). -include("mod_vcard.hrl"). -include("translate.hrl"). --include("ejabberd_stacktrace.hrl"). + -include("ejabberd_http.hrl"). -include("ejabberd_web_admin.hrl"). @@ -151,11 +151,11 @@ handle_cast(Cast, State) -> handle_info({route, Packet}, State) -> try route(Packet) - catch ?EX_RULE(Class, Reason, St) -> - StackTrace = ?EX_STACK(St), - ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", - [xmpp:pp(Packet), - misc:format_exception(2, Class, Reason, StackTrace)]) + catch + Class:Reason:StackTrace -> + ?ERROR_MSG("Failed to route packet:~n~ts~n** ~ts", + [xmpp:pp(Packet), + misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; handle_info(Info, State) -> From 4e909fc50d20490b5c889a1df884e04795c0aa81 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Sep 2025 19:26:23 +0200 Subject: [PATCH 1290/1302] make-binaries: Bump crosstool to 1.28.0 --- tools/make-binaries | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-binaries b/tools/make-binaries index 51a348d16..d058e0649 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -65,7 +65,7 @@ fi rel_name='ejabberd' rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:]') mix_vsn=$(mix_version "$rel_vsn") -crosstool_vsn='1.27.0' +crosstool_vsn='1.28.0' termcap_vsn='1.3.1' expat_vsn='2.7.1' zlib_vsn='1.3.1' From ef2e62a01cc724abbf1eb4d58762e343cf28cf13 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 10 Sep 2025 17:55:38 +0200 Subject: [PATCH 1291/1302] Bump Erlang/OTP version to 27.3.4.3 in installers and container --- .github/container/Dockerfile | 2 +- CONTAINER.md | 2 +- tools/make-binaries | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/container/Dockerfile b/.github/container/Dockerfile index b1c5aa5d1..1d238339a 100644 --- a/.github/container/Dockerfile +++ b/.github/container/Dockerfile @@ -1,5 +1,5 @@ #' Define default build variables -ARG OTP_VSN='27.3.4.2' +ARG OTP_VSN='27.3.4.3' ARG ELIXIR_VSN='1.18.4' ARG UID='9000' ARG USER='ejabberd' diff --git a/CONTAINER.md b/CONTAINER.md index b17d4fb40..fefd96733 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.4.2-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.4.3-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | diff --git a/tools/make-binaries b/tools/make-binaries index d058e0649..3e0d8d9c3 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -71,7 +71,7 @@ expat_vsn='2.7.1' zlib_vsn='1.3.1' yaml_vsn='0.2.5' ssl_vsn='3.5.2' -otp_vsn='27.3.4.2' +otp_vsn='27.3.4.3' elixir_vsn='1.18.4' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. png_vsn='1.6.45' From 1472caab50a041369dfb72a72523f3e26e7d1783 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 16 Sep 2025 20:26:46 +0200 Subject: [PATCH 1292/1302] make-binaries: Bump OpenSSL 3.5.3 and Expat 2.7.2 --- tools/make-binaries | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-binaries b/tools/make-binaries index 3e0d8d9c3..22ad3e98c 100755 --- a/tools/make-binaries +++ b/tools/make-binaries @@ -67,10 +67,10 @@ rel_vsn=$(git describe --tags | sed -e 's/-g.*//' -e 's/-/./' | tr -d '[:space:] mix_vsn=$(mix_version "$rel_vsn") crosstool_vsn='1.28.0' termcap_vsn='1.3.1' -expat_vsn='2.7.1' +expat_vsn='2.7.2' zlib_vsn='1.3.1' yaml_vsn='0.2.5' -ssl_vsn='3.5.2' +ssl_vsn='3.5.3' otp_vsn='27.3.4.3' elixir_vsn='1.18.4' pam_vsn='1.6.1' # Newer Linux-PAM versions use Meson, we don't support that yet. From dd270f99fc165093fd589872e6c1e5917fec3ae8 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 15 Sep 2025 20:15:37 +0200 Subject: [PATCH 1293/1302] container.md: Update versions used in ecs container image --- CONTAINER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTAINER.md b/CONTAINER.md index fefd96733..e96081112 100644 --- a/CONTAINER.md +++ b/CONTAINER.md @@ -1072,7 +1072,7 @@ Let's summarize the differences between both container images. Legend: | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | -| Software | Erlang/OTP 27.3.4.3-alpine
Elixir 1.18.4 | Alpine 3.19
Erlang/OTP 26.2
Elixir 1.15.7 | +| Software | Erlang/OTP 27.3.4.3-alpine
Elixir 1.18.4 | Alpine 3.22
Erlang/OTP 26.2
Elixir 1.18.3 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | | :black_square_button: **Additional content** | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | From e0eae52eae5e9d030036de25cf07c9097767fd9c Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 22 Sep 2025 13:44:20 +0200 Subject: [PATCH 1294/1302] mod_muc_room: Don't require password if user is owner of room --- src/mod_muc_room.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index b2aa0d4c6..c90fde530 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -2468,6 +2468,10 @@ check_password(owner, _Affiliation, _Packet, _From, _StateData) -> %% Don't check pass if user is owner in MUC service (access_admin option) true; +check_password(_ServiceAffiliation, owner, _Packet, _From, + _StateData) -> + %% Don't check pass if user is owner in this room + true; check_password(_ServiceAffiliation, Affiliation, Packet, From, StateData) -> case (StateData#state.config)#config.password_protected From 7eb09295a3e29cd6c30ee50b52e2f1632ea48411 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 19 Sep 2025 13:20:08 +0200 Subject: [PATCH 1295/1302] mod_block_strangers: Clarify access and catpcha documentation (#4221) --- src/mod_block_strangers.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl index c0a85faf1..fd4e9974d 100644 --- a/src/mod_block_strangers.erl +++ b/src/mod_block_strangers.erl @@ -281,9 +281,9 @@ mod_doc() -> #{value => ?T("AccessName"), desc => ?T("The option is supposed to be used when 'allow_local_users' " - "and 'allow_transports' are not enough. It's an ACL where " - "'deny' means the message will be rejected (or a CAPTCHA " - "would be generated for a presence, if configured), and " + "and 'allow_transports' are not enough. It's an Access Rule where " + "'deny' means the stanza will be rejected; there's an exception " + "if option 'captcha' is configured. And " "'allow' means the sender is whitelisted and the stanza " "will pass through. The default value is 'none', which " "means nothing is whitelisted.")}}, @@ -314,8 +314,8 @@ mod_doc() -> {captcha, #{value => "true | false", desc => - ?T("Whether to generate CAPTCHA or not in response to " - "messages from strangers. See also section " - "_`basic.md#captcha|CAPTCHA`_" + ?T("Whether to generate CAPTCHA challenges in response to " + "incoming presence subscription requests from strangers. " + "See also section _`basic.md#captcha|CAPTCHA`_" " of the Configuration Guide. " "The default value is 'false'.")}}]}. From fb572bf90176c0f15de0c467cc744b6b6d2ffcaa Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 16 Dec 2024 11:06:00 +0100 Subject: [PATCH 1296/1302] ejabberdctl: When ping returns pang, return also status code 1 (#4327) --- ejabberdctl.template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 58a0d8be6..0d124bead 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -553,7 +553,8 @@ case $1 in -noinput -hidden \ -eval 'net_kernel:connect_node('"'$PEER'"')' \ -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ - -s erlang halt -output text + -eval 'halt(case net_adm:ping('"'$PEER'"') of pong -> 0; pang -> 1 end).' \ + -output text ;; started) set_dist_client From 0283a501fa049ac5197c307c56899df27adf659f Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Wed, 24 Sep 2025 11:14:46 +0200 Subject: [PATCH 1297/1302] Add archive_muc_as_mucsub option in mod_mam This option can be used to enable archiving of incoming groupchat messages as mucsub events if user is subscribed to a room. --- src/mod_mam.erl | 49 +++++++++++++++++++++++++++- src/mod_mam_opt.erl | 7 ++++ src/mod_muc_room.erl | 78 +++++++++++++++++++++++++++++--------------- 3 files changed, 107 insertions(+), 27 deletions(-) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 33f361f47..c3d5ae435 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -914,6 +914,13 @@ process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang, -spec should_archive(message(), binary()) -> boolean(). should_archive(#message{type = error}, _LServer) -> false; +should_archive(#message{type = groupchat, meta = #{is_muc_subscriber := true}} = Msg, LServer) -> + case mod_mam_opt:archive_muc_as_mucsub(LServer) of + true -> + should_archive(Msg#message{type = chat}, LServer); + false -> + false + end; should_archive(#message{type = groupchat}, _LServer) -> false; should_archive(#message{meta = #{from_offline := true}}, _LServer) -> @@ -1101,6 +1108,31 @@ may_enter_room(From, MUCState) -> -spec store_msg(message(), binary(), binary(), jid(), send | recv) -> ok | pass | any(). +store_msg(#message{type = groupchat, from = From, to = To, meta = #{is_muc_subscriber := true}} = Pkt, LUser, LServer, _Peer, Dir) -> + BarePeer = jid:remove_resource(From), + StanzaId = xmpp:get_subtag(Pkt, #stanza_id{by = #jid{}}), + Id = case StanzaId of + #stanza_id{id = Id2} -> + Id2; + _ -> + p1_rand:get_string() + end, + Pkt2 = #message{ + to = To, + from = BarePeer, + id = Id, + sub_els = [#ps_event{ + items = #ps_items{ + node = ?NS_MUCSUB_NODES_MESSAGES, + items = [#ps_item{ + id = Id, + sub_els = [Pkt] + }] + } + }] + }, + Pkt3 = xmpp:put_meta(Pkt2, stanza_id, binary_to_integer(Id)), + store_msg(Pkt3, LUser, LServer, BarePeer, Dir); store_msg(Pkt, LUser, LServer, Peer, Dir) -> case get_prefs(LUser, LServer) of {ok, Prefs} -> @@ -1738,6 +1770,8 @@ mod_opt_type(clear_archive_on_room_destroy) -> econf:bool(); mod_opt_type(user_mucsub_from_muc_archive) -> econf:bool(); +mod_opt_type(archive_muc_as_mucsub) -> + econf:bool(); mod_opt_type(access_preferences) -> econf:acl(); mod_opt_type(db_type) -> @@ -1759,6 +1793,7 @@ mod_options(Host) -> {clear_archive_on_room_destroy, true}, {access_preferences, all}, {user_mucsub_from_muc_archive, false}, + {archive_muc_as_mucsub, false}, {db_type, ejabberd_config:default_db(Host, ?MODULE)}, {use_cache, ejabberd_option:use_cache(Host)}, {cache_size, ejabberd_option:cache_size(Host)}, @@ -1856,4 +1891,16 @@ mod_doc() -> "subscriber a separate mucsub message is stored. With this " "option enabled, when a user fetches archive virtual " "mucsub, messages are generated from muc archives. " - "The default value is 'false'.")}}]}. + "The default value is 'false'.") + }}, + {archive_muc_as_mucsub, + #{ + value => "true | false", + desc => + ?T("When this option is enabled incoming groupchat messages " + "for users that have mucsub subscription to a room from which " + "message originated will have those messages archived after being " + "converted to mucsub event messages." + "The default value is 'false'.") + }}] + }. diff --git a/src/mod_mam_opt.erl b/src/mod_mam_opt.erl index d8d970a13..940ddd183 100644 --- a/src/mod_mam_opt.erl +++ b/src/mod_mam_opt.erl @@ -4,6 +4,7 @@ -module(mod_mam_opt). -export([access_preferences/1]). +-export([archive_muc_as_mucsub/1]). -export([assume_mam_usage/1]). -export([cache_life_time/1]). -export([cache_missed/1]). @@ -22,6 +23,12 @@ access_preferences(Opts) when is_map(Opts) -> access_preferences(Host) -> gen_mod:get_module_opt(Host, mod_mam, access_preferences). +-spec archive_muc_as_mucsub(gen_mod:opts() | global | binary()) -> boolean(). +archive_muc_as_mucsub(Opts) when is_map(Opts) -> + gen_mod:get_opt(archive_muc_as_mucsub, Opts); +archive_muc_as_mucsub(Host) -> + gen_mod:get_module_opt(Host, mod_mam, archive_muc_as_mucsub). + -spec assume_mam_usage(gen_mod:opts() | global | binary()) -> boolean(). assume_mam_usage(Opts) when is_map(Opts) -> gen_mod:get_opt(assume_mam_usage, Opts); diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index c90fde530..85c1d2d69 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5588,29 +5588,34 @@ wrap(From, To, Packet, Node, Id) -> -spec send_wrapped_multiple(jid(), users(), stanza(), binary(), state()) -> ok. send_wrapped_multiple(From, Users, Packet, Node, State) -> - {Dir, Wra} = - maps:fold( - fun(_, #user{jid = To, last_presence = LP}, {Direct, Wrapped} = Res) -> - IsOffline = LP == undefined, - if IsOffline -> - LBareTo = jid:tolower(jid:remove_resource(To)), - case muc_subscribers_find(LBareTo, State#state.muc_subscribers) of - {ok, #subscriber{nodes = Nodes}} -> - case lists:member(Node, Nodes) of - true -> - {Direct, [To | Wrapped]}; - _ -> - %% TODO: check that this branch is never called - Res - end; - _ -> - Res - end; - true -> - {[To | Direct], Wrapped} - end - end, {[],[]}, Users), - case Dir of + {Dir, DirSub, Wra} = + maps:fold( + fun(_, #user{jid = To, last_presence = LP}, {Direct, DirectSub, Wrapped} = Res) -> + IsOffline = LP == undefined, + LBareTo = jid:tolower(jid:remove_resource(To)), + IsSub = case muc_subscribers_find(LBareTo, State#state.muc_subscribers) of + {ok, #subscriber{nodes = Nodes}} -> + lists:member(Node, Nodes); + _ -> false + end, + if + IsOffline -> + if + IsSub -> + {Direct, DirectSub, [To | Wrapped]}; + true -> + Res + end; + IsSub -> + {Direct, [To | DirectSub], Wrapped}; + true -> + {[To | Direct], DirectSub, Wrapped} + end + end, + {[], [], []}, + Users), + DirAll = Dir ++ DirSub, + case DirAll of [] -> ok; _ -> case Packet of @@ -5623,7 +5628,9 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> not lists:member(303, Codes)) of true -> ejabberd_router_multicast:route_multicast( - From, State#state.server_host, Dir, + From, + State#state.server_host, + DirAll, #presence{id = p1_rand:get_string(), type = unavailable}, false); false -> @@ -5635,8 +5642,27 @@ send_wrapped_multiple(From, Users, Packet, Node, State) -> _ -> ok end, - ejabberd_router_multicast:route_multicast(From, State#state.server_host, - Dir, Packet, false) + if + Dir /= [] -> + ejabberd_router_multicast:route_multicast(From, + State#state.server_host, + Dir, + Packet, + false); + true -> + ok + end, + if + DirSub /= [] -> + PacketSub = xmpp:put_meta(Packet, is_muc_subscriber, true), + ejabberd_router_multicast:route_multicast(From, + State#state.server_host, + DirSub, + PacketSub, + false); + true -> + ok + end end, case Wra of [] -> ok; From 48fb446f8cc3b1a52bc5070b4abfa149004d1eae Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Fri, 26 Sep 2025 10:21:41 +0200 Subject: [PATCH 1298/1302] Properly pass send_timeout option to listener sockets --- src/ejabberd_listener.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 507c6f4c6..d23f4f189 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -323,6 +323,8 @@ split_opts(Transport, Opts) -> {ModOpts, [{Opt, Val} | SockOpts]}; {backlog, _} -> {ModOpts, SockOpts}; + {send_timeout, _} -> + {ModOpts, [{Opt, Val} | SockOpts]}; _ -> {ModOpts#{Opt => Val}, SockOpts} end From c3a24ffdf81a988c19bcdb57fc8f3791a54f2501 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Sep 2025 19:04:45 +0200 Subject: [PATCH 1299/1302] Revert "mod_muc_room.hrl: Work around old Dialyzer bug" This reverts commit c4f6c9dfe765e017c5f2d83c8ab6311c97304250. --- include/mod_muc_room.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index 1e90a7912..949e910da 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -126,7 +126,7 @@ history = #lqueue{} :: lqueue(), subject = [] :: [text()], subject_author = {<<"">>, #jid{}} :: {binary(), jid()}, - hats_users = #{} :: map(), % FIXME on OTP 21+: #{ljid() => #{binary() => binary()}}, + hats_users = #{} :: #{ljid() => #{binary() => binary()}}, just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), room_shaper = none :: ejabberd_shaper:shaper(), From 2b7285e0b2423fb54f54cd41342d02658e178c57 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 8 Sep 2025 19:07:30 +0200 Subject: [PATCH 1300/1302] Update implementation of XEP-0317 Hats to version 0.3.1 (#4380) --- ejabberd.doap | 4 +- include/mod_muc_room.hrl | 3 +- mix.exs | 2 +- mix.lock | 2 +- rebar.config | 2 +- rebar.lock | 11 +- src/mod_muc.erl | 2 +- src/mod_muc_mnesia.erl | 38 +- src/mod_muc_room.erl | 749 ++++++++++++++++++++++++--------------- 9 files changed, 511 insertions(+), 302 deletions(-) diff --git a/ejabberd.doap b/ejabberd.doap index b48ad5892..bfd3f5636 100644 --- a/ejabberd.doap +++ b/ejabberd.doap @@ -578,10 +578,10 @@ - 0.2.0 + 0.3.1 21.12 complete - mod_muc_room, 0.2.0 since 25.03 + mod_muc_room, 0.3.1 since 25.xx diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index 949e910da..5f81fe026 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -126,7 +126,8 @@ history = #lqueue{} :: lqueue(), subject = [] :: [text()], subject_author = {<<"">>, #jid{}} :: {binary(), jid()}, - hats_users = #{} :: #{ljid() => #{binary() => binary()}}, + hats_defs = #{} :: #{binary() => {binary(), binary()}}, + hats_users = #{} :: #{ljid() => [binary()]}, just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), room_shaper = none :: ejabberd_shaper:shaper(), diff --git a/mix.exs b/mix.exs index 5b5be84f5..fcb3ac39e 100644 --- a/mix.exs +++ b/mix.exs @@ -129,7 +129,7 @@ defmodule Ejabberd.MixProject do {:p1_utils, "~> 1.0"}, {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, - {:xmpp, ">= 1.11.1"}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "e9d901ea84fd3910ad32b715853397eb1155b41c", override: true}, {:yconf, git: "https://github.com/processone/yconf", ref: "95692795a8a8d950ba560e5b07e6b80660557259", override: true}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index 9ad9a9e33..3eb53bce9 100644 --- a/mix.lock +++ b/mix.lock @@ -34,6 +34,6 @@ "stringprep": {:hex, :stringprep, "1.0.33", "22f42866b4f6f3c238ea2b9cb6241791184ddedbab55e94a025511f46325f3ca", [:rebar3], [{:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "96f8b30bc50887f605b33b46bca1d248c19a879319b8c482790e3b4da5da98c0"}, "stun": {:hex, :stun, "1.2.21", "735855314ad22cb7816b88597d2f5ca22e24aa5e4d6010a0ef3affb33ceed6a5", [:rebar3], [{:fast_tls, "1.1.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.28", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3d7fe8efb9d05b240a6aa9a6bf8b8b7bff2d802895d170443c588987dc1e12d9"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, - "xmpp": {:hex, :xmpp, "1.11.1", "60181e7d3e8e48aa3b23b2792075cda37e2e507ec152490b866e61e5320cb1da", [:rebar3], [{:ezlib, "~> 1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "~> 1.1.19", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "~> 1.1.51", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "~> 6.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "~> 1.0.26", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "~> 1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "a5c933df904ab3cec15425da334e410ce84ec3ae7b81efe069e5db368a7b3716"}, + "xmpp": {:git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c", [ref: "e9d901ea84fd3910ad32b715853397eb1155b41c"]}, "yconf": {:git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259", [ref: "95692795a8a8d950ba560e5b07e6b80660557259"]}, } diff --git a/rebar.config b/rebar.config index 1d0f9a8db..3d85402e3 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.33", {git, "https://github.com/processone/stringprep", {tag, "1.0.33"}}}, {if_var_true, stun, {stun, "~> 1.2.21", {git, "https://github.com/processone/stun", {tag, "1.2.21"}}}}, - {xmpp, "~> 1.11.1", {git, "https://github.com/processone/xmpp", {tag, "1.11.1"}}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "e9d901ea84fd3910ad32b715853397eb1155b41c"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", "95692795a8a8d950ba560e5b07e6b80660557259"}} ]}. diff --git a/rebar.lock b/rebar.lock index 2547b4371..d69fcec1e 100644 --- a/rebar.lock +++ b/rebar.lock @@ -24,7 +24,10 @@ {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.33">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.21">>},0}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, - {<<"xmpp">>,{pkg,<<"xmpp">>,<<"1.11.1">>},0}, + {<<"xmpp">>, + {git,"https://github.com/processone/xmpp", + {ref,"e9d901ea84fd3910ad32b715853397eb1155b41c"}}, + 0}, {<<"yconf">>, {git,"https://github.com/processone/yconf", {ref,"95692795a8a8d950ba560e5b07e6b80660557259"}}, @@ -55,8 +58,7 @@ {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"stringprep">>, <<"22F42866B4F6F3C238EA2B9CB6241791184DDEDBAB55E94A025511F46325F3CA">>}, {<<"stun">>, <<"735855314AD22CB7816B88597D2F5CA22E24AA5E4D6010A0EF3AFFB33CEED6A5">>}, - {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}, - {<<"xmpp">>, <<"60181E7D3E8E48AA3B23B2792075CDA37E2E507EC152490B866E61E5320CB1DA">>}]}, + {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>}]}, {pkg_hash_ext,[ {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"cache_tab">>, <<"4258009EB050B22AABE0C848E230BBA58401A6895C58C2FF74DFB635E3C35900">>}, @@ -82,6 +84,5 @@ {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"stringprep">>, <<"96F8B30BC50887F605B33B46BCA1D248C19A879319B8C482790E3B4DA5DA98C0">>}, {<<"stun">>, <<"3D7FE8EFB9D05B240A6AA9A6BF8B8B7BFF2D802895D170443C588987DC1E12D9">>}, - {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}, - {<<"xmpp">>, <<"A5C933DF904AB3CEC15425DA334E410CE84EC3AE7B81EFE069E5DB368A7B3716">>}]} + {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>}]} ]. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ffb8c25d7..592d18fae 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1766,7 +1766,7 @@ mod_doc() -> "The default value is an empty string.")}}, {enable_hats, #{value => "true | false", - note => "improved in 25.03", + note => "improved in 25.xx", desc => ?T("Allow extended roles as defined in XEP-0317 Hats. " "Check the _`../../tutorials/muc-hats.md|MUC Hats`_ tutorial. " diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl index 65c37a7ab..02ecb3ce8 100644 --- a/src/mod_muc_mnesia.erl +++ b/src/mod_muc_mnesia.erl @@ -429,11 +429,15 @@ need_transform({muc_room, {N, H}, _}) ?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []), true; need_transform({muc_room, {_N, _H}, Opts}) -> - case lists:keymember(allow_private_messages, 1, Opts) of - true -> + case {lists:keymember(allow_private_messages, 1, Opts), + lists:keymember(hats_defs, 1, Opts)} of + {true, _} -> ?INFO_MSG("Mnesia table 'muc_room' will be converted to allowpm", []), true; - false -> + {false, false} -> + ?INFO_MSG("Mnesia table 'muc_room' will be converted to Hats 0.3.0", []), + true; + {false, true} -> false end; @@ -459,7 +463,33 @@ transform(#muc_room{opts = Opts} = R) -> _ -> Opts end, - R#muc_room{opts = Opts2}; + Opts4 = + case lists:keyfind(hats_defs, 1, Opts2) of + false -> + {hats_users, HatsUsers} = lists:keyfind(hats_users, 1, Opts2), + {HatsDefs, HatsUsers2} = + lists:foldl(fun({Jid, UriTitleList}, {Defs, Assigns}) -> + Defs2 = + lists:foldl(fun({Uri, Title}, AccDef) -> + maps:put(Uri, {Title, <<"">>}, AccDef) + end, + Defs, + UriTitleList), + Assigns2 = + maps:put(Jid, + [Uri || {Uri, _Title} <- UriTitleList], + Assigns), + {Defs2, Assigns2} + end, + {maps:new(), maps:new()}, + HatsUsers), + Opts3 = + lists:keyreplace(hats_users, 1, Opts2, {hats_users, maps:to_list(HatsUsers2)}), + [{hats_defs, maps:to_list(HatsDefs)} | Opts3]; + {_, _} -> + Opts2 + end, + R#muc_room{opts = Opts4}; transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick} = R) -> R#muc_registered{us_host = {{iolist_to_binary(U), iolist_to_binary(S)}, iolist_to_binary(H)}, diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 85c1d2d69..64c91e4c0 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --protocol({xep, 317, '0.2.0', '21.12', "complete", "0.2.0 since 25.03"}). +-protocol({xep, 317, '0.3.1', '21.12', "complete", "0.3.1 since 25.xx"}). -protocol({xep, 410, '1.1.0', '18.12', "complete", ""}). -behaviour(p1_fsm). @@ -79,9 +79,14 @@ -define(MAX_USERS_DEFAULT_LIST, [5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]). --define(MUC_HAT_ADD_CMD, <<"urn:xmpp:hats:commands:don">>). --define(MUC_HAT_REMOVE_CMD, <<"urn:xmpp:hats:commands:doff">>). --define(MUC_HAT_LIST_CMD, <<"urn:xmpp:hats:commands:dlist">>). +-define(MUC_HAT_CREATE_CMD, <<"urn:xmpp:hats:commands:create">>). +-define(MUC_HAT_DESTROY_CMD, <<"urn:xmpp:hats:commands:destroy">>). +-define(MUC_HAT_LISTHATS_CMD, <<"urn:xmpp:hats:commands:list">>). + +-define(MUC_HAT_ASSIGN_CMD, <<"urn:xmpp:hats:commands:assign">>). +-define(MUC_HAT_UNASSIGN_CMD, <<"urn:xmpp:hats:commands:unassign">>). +-define(MUC_HAT_LISTUSERS_CMD,<<"urn:xmpp:hats:commands:list-assigned">>). + -define(MAX_HATS_USERS, 100). -define(MAX_HATS_PER_USER, 10). -define(CLEAN_ROOM_TIMEOUT, 30000). @@ -4258,11 +4263,10 @@ set_opts2([{Opt, Val} | Opts], StateData) -> StateData#state{subject_author = Val}; subject_author when is_binary(Val) -> % ejabberd 23.04 or older StateData#state{subject_author = {Val, #jid{}}}; + hats_defs -> + StateData#state{hats_defs = maps:from_list(Val)}; hats_users -> - Hats = maps:from_list( - lists:map(fun({U, H}) -> {U, maps:from_list(H)} end, - Val)), - StateData#state{hats_users = Hats}; + StateData#state{hats_users = maps:from_list(Val)}; hibernation_time -> StateData; Other -> ?INFO_MSG("Unknown MUC room option, will be discarded: ~p", [Other]), @@ -4343,9 +4347,8 @@ make_opts(StateData, Hibernation) -> {roles, maps:to_list(StateData#state.roles)}, {subject, StateData#state.subject}, {subject_author, StateData#state.subject_author}, - {hats_users, - lists:map(fun({U, H}) -> {U, maps:to_list(H)} end, - maps:to_list(StateData#state.hats_users))}, + {hats_defs, maps:to_list(StateData#state.hats_defs)}, + {hats_users, maps:to_list(StateData#state.hats_users)}, {hibernation_time, if Hibernation -> erlang:system_time(microsecond); true -> undefined end}, {subscribers, Subscribers}]. @@ -4461,6 +4464,10 @@ make_disco_info(From, StateData) -> true -> [?NS_MUCSUB]; false -> [] end + ++ case Config#config.enable_hats of + true -> [?NS_HATS]; + false -> [] + end ++ case gen_mod:is_loaded(StateData#state.server_host, mod_muc_occupantid) of true -> [?NS_OCCUPANT_ID]; @@ -4490,6 +4497,7 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang, DiscoInfo = make_disco_info(From, StateData), Extras = iq_disco_info_extras(Lang, StateData, false), {result, DiscoInfo#disco_info{xdata = [Extras]}}; + process_iq_disco_info(From, #iq{type = get, lang = Lang, sub_els = [#disco_info{node = ?NS_COMMANDS}]}, StateData) -> @@ -4507,9 +4515,25 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang, Txt = ?T("Node not found"), {error, xmpp:err_item_not_found(Txt, Lang)} end; + process_iq_disco_info(From, #iq{type = get, lang = Lang, - sub_els = [#disco_info{node = ?MUC_HAT_ADD_CMD}]}, - StateData) -> + sub_els = [#disco_info{node = Node}]}, + StateData) + when Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_LISTHATS_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD; + Node == ?MUC_HAT_LISTUSERS_CMD -> + NodeName = case Node of + ?MUC_HAT_CREATE_CMD -> ?T("Create a Hat"); + ?MUC_HAT_DESTROY_CMD -> ?T("Destroy a Hat"); + ?MUC_HAT_LISTHATS_CMD -> ?T("List Hats"); + ?MUC_HAT_ASSIGN_CMD -> ?T("Assign a Hat to a User"); + ?MUC_HAT_UNASSIGN_CMD -> ?T("Remove a Hat from a User"); + ?MUC_HAT_LISTUSERS_CMD -> ?T("List Users and their Hats") + end, + case (StateData#state.config)#config.enable_hats andalso is_admin(From, StateData) of @@ -4519,48 +4543,13 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang, identities = [#identity{category = <<"automation">>, type = <<"command-node">>, name = translate:translate( - Lang, ?T("Add a hat to a user"))}], - features = [?NS_COMMANDS]}}; - false -> - Txt = ?T("Node not found"), - {error, xmpp:err_item_not_found(Txt, Lang)} - end; -process_iq_disco_info(From, #iq{type = get, lang = Lang, - sub_els = [#disco_info{node = ?MUC_HAT_REMOVE_CMD}]}, - StateData) -> - case (StateData#state.config)#config.enable_hats andalso - is_admin(From, StateData) - of - true -> - {result, - #disco_info{ - identities = [#identity{category = <<"automation">>, - type = <<"command-node">>, - name = translate:translate( - Lang, ?T("Remove a hat from a user"))}], - features = [?NS_COMMANDS]}}; - false -> - Txt = ?T("Node not found"), - {error, xmpp:err_item_not_found(Txt, Lang)} - end; -process_iq_disco_info(From, #iq{type = get, lang = Lang, - sub_els = [#disco_info{node = ?MUC_HAT_LIST_CMD}]}, - StateData) -> - case (StateData#state.config)#config.enable_hats andalso - is_admin(From, StateData) - of - true -> - {result, - #disco_info{ - identities = [#identity{category = <<"automation">>, - type = <<"command-node">>, - name = translate:translate( - Lang, ?T("List users with hats"))}], + Lang, NodeName)}], features = [?NS_COMMANDS]}}; false -> Txt = ?T("Node not found"), {error, xmpp:err_item_not_found(Txt, Lang)} end; + process_iq_disco_info(From, #iq{type = get, lang = Lang, sub_els = [#disco_info{node = Node}]}, StateData) -> @@ -4623,8 +4612,15 @@ iq_disco_info_extras(Lang, StateData, Static) -> StateData#state.server_host, Fs5, [StateData]), + Fs7 = case (StateData#state.config)#config.enable_hats of + true -> + HatsHash = get_hats_hash(StateData), + [{'hats#hash', [HatsHash]} | Fs6]; + false -> + Fs6 + end, #xdata{type = result, - fields = muc_roominfo:encode(Fs6, Lang)}. + fields = muc_roominfo:encode(Fs7, Lang)}. -spec process_iq_disco_items(jid(), iq(), state()) -> {error, stanza_error()} | {result, disco_items()}. @@ -4657,15 +4653,27 @@ process_iq_disco_items(From, #iq{type = get, lang = Lang, {result, #disco_items{ items = [#disco_item{jid = StateData#state.jid, - node = ?MUC_HAT_ADD_CMD, + node = ?MUC_HAT_CREATE_CMD, name = translate:translate( - Lang, ?T("Add a hat to a user"))}, + Lang, ?T("Create a hat"))}, #disco_item{jid = StateData#state.jid, - node = ?MUC_HAT_REMOVE_CMD, + node = ?MUC_HAT_DESTROY_CMD, + name = translate:translate( + Lang, ?T("Destroy a hat"))}, + #disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_LISTHATS_CMD, + name = translate:translate( + Lang, ?T("List hats"))}, + #disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_ASSIGN_CMD, + name = translate:translate( + Lang, ?T("Assign a hat to a user"))}, + #disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_UNASSIGN_CMD, name = translate:translate( Lang, ?T("Remove a hat from a user"))}, #disco_item{jid = StateData#state.jid, - node = ?MUC_HAT_LIST_CMD, + node = ?MUC_HAT_LISTUSERS_CMD, name = translate:translate( Lang, ?T("List users with hats"))}]}}; false -> @@ -4675,9 +4683,12 @@ process_iq_disco_items(From, #iq{type = get, lang = Lang, process_iq_disco_items(From, #iq{type = get, lang = Lang, sub_els = [#disco_items{node = Node}]}, StateData) - when Node == ?MUC_HAT_ADD_CMD; - Node == ?MUC_HAT_REMOVE_CMD; - Node == ?MUC_HAT_LIST_CMD -> + when Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_LISTHATS_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD; + Node == ?MUC_HAT_LISTUSERS_CMD -> case (StateData#state.config)#config.enable_hats andalso is_admin(From, StateData) of @@ -4944,270 +4955,436 @@ get_mucroom_disco_items(StateData) -> end, [], StateData#state.nicks), #disco_items{items = Items}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Hats + +%% @format-begin + -spec process_iq_adhoc(jid(), iq(), state()) -> - {result, adhoc_command()} | - {result, adhoc_command(), state()} | - {error, stanza_error()}. + {result, adhoc_command()} | + {result, adhoc_command(), state()} | + {error, stanza_error()}. process_iq_adhoc(_From, #iq{type = get}, _StateData) -> {error, xmpp:err_bad_request()}; -process_iq_adhoc(From, #iq{type = set, lang = Lang1, - sub_els = [#adhoc_command{} = Request]}, - StateData) -> +process_iq_adhoc(From, + #iq{type = set, + lang = Lang1, + sub_els = [#adhoc_command{} = Request]}, + StateData) -> % Ad-Hoc Commands are used only for Hats here - case (StateData#state.config)#config.enable_hats andalso - is_admin(From, StateData) - of + case StateData#state.config#config.enable_hats andalso is_admin(From, StateData) of true -> - #adhoc_command{lang = Lang2, node = Node, - action = Action, xdata = XData} = Request, - Lang = case Lang2 of - <<"">> -> Lang1; - _ -> Lang2 - end, + #adhoc_command{lang = Lang2, + node = Node, + action = Action, + xdata = XData} = + Request, + Lang = + case Lang2 of + <<"">> -> + Lang1; + _ -> + Lang2 + end, case {Node, Action} of {_, cancel} -> {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{status = canceled, lang = Lang, - node = Node})}; - {?MUC_HAT_ADD_CMD, execute} -> - Form = - #xdata{ - title = translate:translate( - Lang, ?T("Add a hat to a user")), - type = form, - fields = - [#xdata_field{ - type = 'jid-single', - label = translate:translate(Lang, ?T("Jabber ID")), - required = true, - var = <<"jid">>}, - #xdata_field{ - type = 'text-single', - label = translate:translate(Lang, ?T("Hat title")), - var = <<"hat_title">>}, - #xdata_field{ - type = 'text-single', - label = translate:translate(Lang, ?T("Hat URI")), - required = true, - var = <<"hat_uri">>} - ]}, + xmpp_util:make_adhoc_response(Request, + #adhoc_command{status = canceled, + lang = Lang, + node = Node})}; + {Node, execute} + when Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_LISTHATS_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD; + Node == ?MUC_HAT_LISTUSERS_CMD -> + {Status, Form} = process_iq_adhoc_hats(Node, StateData, Lang), {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{ - status = executing, - xdata = Form})}; - {?MUC_HAT_ADD_CMD, complete} when XData /= undefined -> - JID = try - jid:decode(hd(xmpp_util:get_xdata_values( - <<"jid">>, XData))) - catch _:_ -> error - end, - URI = try - hd(xmpp_util:get_xdata_values( - <<"hat_uri">>, XData)) - catch _:_ -> error - end, - Title = case xmpp_util:get_xdata_values( - <<"hat_title">>, XData) of - [] -> <<"">>; - [T] -> T - end, - if - (JID /= error) and (URI /= error) -> - case add_hat(JID, URI, Title, StateData) of - {ok, NewStateData} -> - store_room(NewStateData), - send_update_presence( - JID, NewStateData, StateData), - {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{status = completed}), - NewStateData}; - {error, size_limit} -> - Txt = ?T("Hats limit exceeded"), - {error, xmpp:err_not_allowed(Txt, Lang)} - end; - true -> - {error, xmpp:err_bad_request()} - end; - {?MUC_HAT_ADD_CMD, complete} -> - {error, xmpp:err_bad_request()}; - {?MUC_HAT_ADD_CMD, _} -> - Txt = ?T("Incorrect value of 'action' attribute"), - {error, xmpp:err_bad_request(Txt, Lang)}; - {?MUC_HAT_REMOVE_CMD, execute} -> - Form = - #xdata{ - title = translate:translate( - Lang, ?T("Remove a hat from a user")), - type = form, - fields = - [#xdata_field{ - type = 'jid-single', - label = translate:translate(Lang, ?T("Jabber ID")), - required = true, - var = <<"jid">>}, - #xdata_field{ - type = 'text-single', - label = translate:translate(Lang, ?T("Hat URI")), - required = true, - var = <<"hat_uri">>} - ]}, - {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{ - status = executing, - xdata = Form})}; - {?MUC_HAT_REMOVE_CMD, complete} when XData /= undefined -> - JID = try - jid:decode(hd(xmpp_util:get_xdata_values( - <<"jid">>, XData))) - catch _:_ -> error - end, - URI = try - hd(xmpp_util:get_xdata_values( - <<"hat_uri">>, XData)) - catch _:_ -> error - end, - if - (JID /= error) and (URI /= error) -> - NewStateData = del_hat(JID, URI, StateData), - store_room(NewStateData), - send_update_presence( - JID, NewStateData, StateData), + xmpp_util:make_adhoc_response(Request, + #adhoc_command{status = Status, xdata = Form})}; + {Node, complete} + when XData /= undefined andalso Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD -> + case process_iq_adhoc_hats_complete(Node, XData, StateData, Lang) of + {ok, NewStateData} -> {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{status = completed}), + xmpp_util:make_adhoc_response(Request, + #adhoc_command{status = completed}), NewStateData}; - true -> + {error, XmlElement} -> + {error, XmlElement}; + error -> {error, xmpp:err_bad_request()} end; - {?MUC_HAT_REMOVE_CMD, complete} -> + {Node, complete} + when Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD -> {error, xmpp:err_bad_request()}; - {?MUC_HAT_REMOVE_CMD, _} -> - Txt = ?T("Incorrect value of 'action' attribute"), - {error, xmpp:err_bad_request(Txt, Lang)}; - {?MUC_HAT_LIST_CMD, execute} -> - Hats = get_all_hats(StateData), - Items = - lists:map( - fun({JID, URI, Title}) -> - [#xdata_field{ - var = <<"jid">>, - values = [jid:encode(JID)]}, - #xdata_field{ - var = <<"hat_title">>, - values = [URI]}, - #xdata_field{ - var = <<"hat_uri">>, - values = [Title]}] - end, Hats), - Form = - #xdata{ - title = translate:translate( - Lang, ?T("List of users with hats")), - type = result, - reported = - [#xdata_field{ - label = translate:translate(Lang, ?T("Jabber ID")), - var = <<"jid">>}, - #xdata_field{ - label = translate:translate(Lang, ?T("Hat title")), - var = <<"hat_title">>}, - #xdata_field{ - label = translate:translate(Lang, ?T("Hat URI")), - var = <<"hat_uri">>}], - items = Items}, - {result, - xmpp_util:make_adhoc_response( - Request, - #adhoc_command{ - status = completed, - xdata = Form})}; - {?MUC_HAT_LIST_CMD, _} -> + {Node, _} + when Node == ?MUC_HAT_CREATE_CMD; + Node == ?MUC_HAT_DESTROY_CMD; + Node == ?MUC_HAT_LISTHATS_CMD; + Node == ?MUC_HAT_ASSIGN_CMD; + Node == ?MUC_HAT_UNASSIGN_CMD; + Node == ?MUC_HAT_LISTUSERS_CMD -> Txt = ?T("Incorrect value of 'action' attribute"), {error, xmpp:err_bad_request(Txt, Lang)}; _ -> {error, xmpp:err_item_not_found()} end; - _ -> - {error, xmpp:err_forbidden()} + _ -> + {error, xmpp:err_forbidden()} end. --spec add_hat(jid(), binary(), binary(), state()) -> - {ok, state()} | {error, size_limit}. -add_hat(JID, URI, Title, StateData) -> - Hats = StateData#state.hats_users, - LJID = jid:remove_resource(jid:tolower(JID)), - UserHats = maps:get(LJID, Hats, #{}), - UserHats2 = maps:put(URI, Title, UserHats), - USize = maps:size(UserHats2), - if - USize =< ?MAX_HATS_PER_USER -> - Hats2 = maps:put(LJID, UserHats2, Hats), - Size = maps:size(Hats2), - if - Size =< ?MAX_HATS_USERS -> - {ok, StateData#state{hats_users = Hats2}}; - true -> - {error, size_limit} - end; - true -> - {error, size_limit} - end. +process_iq_adhoc_hats(?MUC_HAT_LISTHATS_CMD, StateData, Lang) -> + Hats = get_defined_hats(StateData), + Items = + lists:map(fun({URI, Title, Hue}) -> + [#xdata_field{var = <<"hats#uri">>, values = [URI]}, + #xdata_field{var = <<"hats#title">>, values = [Title]}, + #xdata_field{var = <<"hats#hue">>, values = [Hue]}] + end, + Hats), + Form = + #xdata{title = translate:translate(Lang, ?T("Hats List")), + type = result, + reported = + [#xdata_field{label = translate:translate(Lang, ?T("Hat URI")), + var = <<"hats#uri">>}, + #xdata_field{label = translate:translate(Lang, ?T("Hat Title")), + var = <<"hats#title">>}, + #xdata_field{label = translate:translate(Lang, ?T("Hat Hue")), + var = <<"hats#hue">>}], + items = Items}, + {completed, Form}; +process_iq_adhoc_hats(?MUC_HAT_CREATE_CMD, _StateData, Lang) -> + Form = + #xdata{title = translate:translate(Lang, ?T("Create a hat")), + type = form, + fields = + [#xdata_field{type = 'text-single', + label = translate:translate(Lang, ?T("Hat URI")), + required = true, + var = <<"hats#uri">>}, + #xdata_field{type = 'text-single', + label = translate:translate(Lang, ?T("Hat Title")), + required = true, + var = <<"hats#title">>}, + #xdata_field{type = 'text-single', + label = translate:translate(Lang, ?T("Hat Hue")), + var = <<"hats#hue">>}]}, + {executing, Form}; +process_iq_adhoc_hats(?MUC_HAT_DESTROY_CMD, _StateData, Lang) -> + Form = + #xdata{title = translate:translate(Lang, ?T("Destroy a hat")), + type = form, + fields = + [#xdata_field{type = 'text-single', + label = translate:translate(Lang, ?T("Hat URI")), + required = true, + var = <<"hat">>}]}, + {executing, Form}; +process_iq_adhoc_hats(?MUC_HAT_ASSIGN_CMD, StateData, Lang) -> + Hats = get_defined_hats(StateData), + Options = + [#xdata_option{label = Title, value = Uri} + || {Uri, Title, _Hue} <- lists:keysort(2, Hats)], + Form = + #xdata{title = translate:translate(Lang, ?T("Assign a hat to a user")), + type = form, + fields = + [#xdata_field{type = 'jid-single', + label = translate:translate(Lang, ?T("Jabber ID")), + required = true, + var = <<"hats#jid">>}, + #xdata_field{type = 'list-single', + label = translate:translate(Lang, ?T("The role")), + var = <<"hat">>, + options = Options}]}, + {executing, Form}; +process_iq_adhoc_hats(?MUC_HAT_UNASSIGN_CMD, StateData, Lang) -> + Hats = get_defined_hats(StateData), + Options = + [#xdata_option{label = Title, value = Uri} + || {Uri, Title, _Hue} <- lists:keysort(2, Hats)], + Form = + #xdata{title = translate:translate(Lang, ?T("Remove a hat from a user")), + type = form, + fields = + [#xdata_field{type = 'jid-single', + label = translate:translate(Lang, ?T("Jabber ID")), + required = true, + var = <<"hats#jid">>}, + #xdata_field{type = 'list-single', + label = translate:translate(Lang, ?T("The role")), + var = <<"hat">>, + options = Options}]}, + {executing, Form}; +process_iq_adhoc_hats(?MUC_HAT_LISTUSERS_CMD, StateData, Lang) -> + Hats = get_assigned_hats(StateData), + Items = + lists:map(fun({JID, URI}) -> + {URI, Title, Hue} = get_hat_details(URI, StateData), + [#xdata_field{var = <<"hats#jid">>, values = [jid:encode(JID)]}, + #xdata_field{var = <<"hats#uri">>, values = [URI]}, + #xdata_field{var = <<"hats#title">>, values = [Title]}, + #xdata_field{var = <<"hats#hue">>, values = [Hue]}] + end, + Hats), + Form = + #xdata{title = translate:translate(Lang, ?T("List of users with hats")), + type = result, + reported = + [#xdata_field{label = translate:translate(Lang, ?T("Jabber ID")), + var = <<"hats#jid">>}, + #xdata_field{label = translate:translate(Lang, ?T("Hat URI")), + var = <<"hats#uri">>}, + #xdata_field{label = translate:translate(Lang, ?T("Hat Title")), + var = <<"hats#title">>}, + #xdata_field{label = translate:translate(Lang, ?T("Hat Hue")), + var = <<"hats#hue">>}], + items = Items}, + {completed, Form}; +process_iq_adhoc_hats(_, _, _) -> + {executing, aaa}. --spec del_hat(jid(), binary(), state()) -> state(). -del_hat(JID, URI, StateData) -> - Hats = StateData#state.hats_users, - LJID = jid:remove_resource(jid:tolower(JID)), - UserHats = maps:get(LJID, Hats, #{}), - UserHats2 = maps:remove(URI, UserHats), - Hats2 = - case maps:size(UserHats2) of - 0 -> - maps:remove(LJID, Hats); - _ -> - maps:put(LJID, UserHats2, Hats) +process_iq_adhoc_hats_complete(?MUC_HAT_CREATE_CMD, XData, StateData, _Lang) -> + URI = try + hd(xmpp_util:get_xdata_values(<<"hats#uri">>, XData)) + catch + _:_ -> + error + end, + Title = + case xmpp_util:get_xdata_values(<<"hats#title">>, XData) of + [] -> + <<"">>; + [T] -> + T end, - StateData#state{hats_users = Hats2}. + Hue = try + hd(xmpp_util:get_xdata_values(<<"hats#hue">>, XData)) + catch + _:_ -> + error + end, + if (Title /= error) and (URI /= error) -> + {ok, AffectedJids, NewStateData} = create_hat(URI, Title, Hue, StateData), + store_room(NewStateData), + broadcast_hats_change(NewStateData), + [send_update_presence(AJid, NewStateData, StateData) || AJid <- AffectedJids], + {ok, NewStateData}; + true -> + error + end; +process_iq_adhoc_hats_complete(?MUC_HAT_DESTROY_CMD, XData, StateData, _Lang) -> + URI = try + hd(xmpp_util:get_xdata_values(<<"hat">>, XData)) + catch + _:_ -> + error + end, + if URI /= error -> + {ok, AffectedJids, NewStateData} = destroy_hat(URI, StateData), + store_room(NewStateData), + broadcast_hats_change(NewStateData), + [send_update_presence(AJid, NewStateData, StateData) || AJid <- AffectedJids], + {ok, NewStateData}; + true -> + error + end; +process_iq_adhoc_hats_complete(?MUC_HAT_ASSIGN_CMD, XData, StateData, Lang) -> + JID = try + jid:decode(hd(xmpp_util:get_xdata_values(<<"hats#jid">>, XData))) + catch + _:_ -> + error + end, + URI = try + hd(xmpp_util:get_xdata_values(<<"hat">>, XData)) + catch + _:_ -> + error + end, + if (JID /= error) and (URI /= error) -> + case assign_hat(JID, URI, StateData) of + {ok, NewStateData} -> + store_room(NewStateData), + send_update_presence(JID, NewStateData, StateData), + {ok, NewStateData}; + {error, size_limit} -> + Txt = ?T("Hats limit exceeded"), + {error, xmpp:err_not_allowed(Txt, Lang)} + end; + true -> + error + end; +process_iq_adhoc_hats_complete(?MUC_HAT_UNASSIGN_CMD, XData, StateData, _Lang) -> + JID = try + jid:decode(hd(xmpp_util:get_xdata_values(<<"hats#jid">>, XData))) + catch + _:_ -> + error + end, + URI = try + hd(xmpp_util:get_xdata_values(<<"hat">>, XData)) + catch + _:_ -> + error + end, + if (JID /= error) and (URI /= error) -> + {ok, NewStateData} = unassign_hat(JID, URI, StateData), + store_room(NewStateData), + send_update_presence(JID, NewStateData, StateData), + {ok, NewStateData}; + true -> + error + end. --spec get_all_hats(state()) -> list({jid(), binary(), binary()}). -get_all_hats(StateData) -> - lists:flatmap( - fun({LJID, H}) -> - JID = jid:make(LJID), - lists:map(fun({URI, Title}) -> {JID, URI, Title} end, - maps:to_list(H)) - end, - maps:to_list(StateData#state.hats_users)). +%% TODO +++ clean +create_hat(URI, Title, Hue, #state{hats_defs = Hats, hats_users = Users} = StateData) -> + Hats2 = maps:put(URI, {Title, Hue}, Hats), + + IsUpdate = + case maps:find(URI, Hats) of + {ok, {OldTitle, OldHue}} -> + (OldTitle /= Title) or (OldHue /= Hue); + error -> + false + end, + + AffectedJids = + case IsUpdate of + true -> + maps:fold(fun(Jid, AssignedHatsUris, ChangedAcc) -> + case lists:member(URI, AssignedHatsUris) of + false -> + ChangedAcc; + true -> + [Jid | ChangedAcc] + end + end, + [], + Users); + false -> + [] + end, + {ok, AffectedJids, StateData#state{hats_defs = Hats2}}. + +destroy_hat(URI, #state{hats_defs = Hats, hats_users = Users} = StateData) -> + Hats2 = maps:remove(URI, Hats), + {AffectedJids, Users2} = + maps:fold(fun(Jid, AssignedHatsUris, {ChangedAcc, UsersAcc}) -> + case AssignedHatsUris -- [URI] of + [] -> + {ChangedAcc, UsersAcc}; + AssignedHatsUris2 -> + {[Jid | ChangedAcc], maps:put(Jid, AssignedHatsUris2, UsersAcc)} + end + end, + {[], maps:new()}, + Users), + {ok, AffectedJids, StateData#state{hats_defs = Hats2, hats_users = Users2}}. + +broadcast_hats_change(StateData) -> + Codes = [104], + Message = + #message{type = groupchat, + id = p1_rand:get_string(), + sub_els = [#muc_user{status_codes = Codes}]}, + send_wrapped_multiple(StateData#state.jid, + get_users_and_subscribers_with_node(?NS_MUCSUB_NODES_CONFIG, StateData), + Message, + ?NS_MUCSUB_NODES_CONFIG, + StateData). + +-spec assign_hat(jid(), binary(), state()) -> {ok, state()} | {error, size_limit}. +assign_hat(JID, URI, StateData) -> + Hats = StateData#state.hats_users, + LJID = + jid:remove_resource( + jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, []), + UserHats2 = lists:umerge([URI], UserHats), + USize = length(UserHats2), + if USize =< ?MAX_HATS_PER_USER -> + Hats2 = maps:put(LJID, UserHats2, Hats), + Size = maps:size(Hats2), + if Size =< ?MAX_HATS_USERS -> + {ok, StateData#state{hats_users = Hats2}}; + true -> + {error, size_limit} + end; + true -> + {error, size_limit} + end. + +-spec unassign_hat(jid(), binary(), state()) -> {ok, state()} | {error, size_limit}. +unassign_hat(JID, URI, StateData) -> + Hats = StateData#state.hats_users, + LJID = + jid:remove_resource( + jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, []), + UserHats2 = lists:delete(URI, UserHats), + Hats2 = maps:put(LJID, UserHats2, Hats), + {ok, StateData#state{hats_users = Hats2}}. + +-spec get_defined_hats(state()) -> [{binary(), binary(), binary()}]. +get_defined_hats(StateData) -> + lists:map(fun({Uri, {Title, Hue}}) -> {Uri, Title, Hue} end, + maps:to_list(StateData#state.hats_defs)). + +-spec get_assigned_hats(state()) -> [{jid(), binary()}]. +get_assigned_hats(StateData) -> + lists:flatmap(fun({LJID, H}) -> + JID = jid:make(LJID), + lists:map(fun(URI) -> {JID, URI} end, H) + end, + maps:to_list(StateData#state.hats_users)). + +get_hats_hash(StateData) -> + str:sha( + misc:term_to_base64(get_assigned_hats(StateData))). + +get_hat_details(Uri, StateData) -> + lists:keyfind(Uri, 1, get_defined_hats(StateData)). -spec add_presence_hats(jid(), #presence{}, state()) -> #presence{}. add_presence_hats(JID, Pres, StateData) -> - case (StateData#state.config)#config.enable_hats of + case StateData#state.config#config.enable_hats of true -> Hats = StateData#state.hats_users, - LJID = jid:remove_resource(jid:tolower(JID)), - UserHats = maps:get(LJID, Hats, #{}), - case maps:size(UserHats) of - 0 -> Pres; + LJID = + jid:remove_resource( + jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, []), + case length(UserHats) of + 0 -> + Pres; _ -> Items = - lists:map(fun({URI, Title}) -> - #muc_hat{uri = URI, title = Title} + lists:map(fun(URI) -> + {URI, Title, Hue} = get_hat_details(URI, StateData), + #muc_hat{uri = URI, + title = Title, + hue = Hue} end, - maps:to_list(UserHats)), - xmpp:set_subtag(Pres, - #muc_hats{hats = Items}) + UserHats), + xmpp:set_subtag(Pres, #muc_hats{hats = Items}) end; false -> Pres end. +%% @format-end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec process_iq_moderate(jid(), iq(), binary(), binary() | undefined, state()) -> {result, undefined, state()} | From c0c69394b9deaf2eefac7af8fd59bf331d5847b9 Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Mon, 15 Sep 2025 17:44:25 +0200 Subject: [PATCH 1301/1302] Make mod_muc_sql properly handle new hats data (#4380) --- src/mod_muc.erl | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 592d18fae..bdb96be3e 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1210,28 +1210,28 @@ remove_user(User, Server) -> ok. opts_to_binary(Opts) -> - lists:map( + lists:flatmap( fun({title, Title}) -> - {title, iolist_to_binary(Title)}; + [{title, iolist_to_binary(Title)}]; ({description, Desc}) -> - {description, iolist_to_binary(Desc)}; + [{description, iolist_to_binary(Desc)}]; ({password, Pass}) -> - {password, iolist_to_binary(Pass)}; + [{password, iolist_to_binary(Pass)}]; ({subject, [C|_] = Subj}) when is_integer(C), C >= 0, C =< 255 -> - {subject, iolist_to_binary(Subj)}; + [{subject, iolist_to_binary(Subj)}]; ({subject_author, {AuthorNick, AuthorJID}}) -> - {subject_author, {iolist_to_binary(AuthorNick), AuthorJID}}; + [{subject_author, {iolist_to_binary(AuthorNick), AuthorJID}}]; ({subject_author, AuthorNick}) -> % ejabberd 23.04 or older - {subject_author, {iolist_to_binary(AuthorNick), #jid{}}}; + [{subject_author, {iolist_to_binary(AuthorNick), #jid{}}}]; ({allow_private_messages, Value}) -> % ejabberd 23.04 or older Value2 = case Value of true -> anyone; false -> none; _ -> Value end, - {allowpm, Value2}; + [{allowpm, Value2}]; ({AffOrRole, Affs}) when (AffOrRole == affiliation) or (AffOrRole == role) -> - {affiliations, lists:map( + [{affiliations, lists:map( fun({{U, S, R}, Aff}) -> NewAff = case Aff of @@ -1244,16 +1244,38 @@ opts_to_binary(Opts) -> iolist_to_binary(S), iolist_to_binary(R)}, NewAff} - end, Affs)}; + end, Affs)}]; ({captcha_whitelist, CWList}) -> - {captcha_whitelist, lists:map( + [{captcha_whitelist, lists:map( fun({U, S, R}) -> {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)} - end, CWList)}; + end, CWList)}]; + ({hats_users, HatsUsers}) -> % Update hats definitions + case lists:keymember(hats_defs, 1, Opts) of + true -> + [{hats_users, HatsUsers}]; + _ -> + {HatsDefs, HatsUsers2} = + lists:foldl(fun({Jid, UriTitleList}, {Defs, Assigns}) -> + Defs2 = + lists:foldl(fun({Uri, Title}, AccDef) -> + AccDef#{Uri => {Title, <<"">>}} + end, + Defs, + UriTitleList), + Assigns2 = + Assigns#{Jid => [ Uri || {Uri, _Title} <- UriTitleList ]}, + {Defs2, Assigns2} + end, + {maps:new(), maps:new()}, + HatsUsers), + [{hats_users, maps:to_list(HatsUsers2)}, + {hats_defs, maps:to_list(HatsDefs)}] + end; (Opt) -> - Opt + [Opt] end, Opts). export(LServer) -> From 060992bafa56437b53ff8f1fefdc306a2d0585d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Sautret?= Date: Tue, 30 Sep 2025 16:38:23 +0200 Subject: [PATCH 1302/1302] Improve roster API commands documentation --- src/mod_admin_extra.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index c3f605e59..4a7877d19 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -612,6 +612,7 @@ get_commands_spec() -> result = {res, rescode}}, #ejabberd_commands{name = add_rosteritem, tags = [roster], desc = "Add an item to a user's roster (supports ODBC)", + longdesc = "The client will receive a `jabber:iq:roster` IQ notifying them of the added entry.", module = ?MODULE, function = add_rosteritem, version = 1, note = "updated in 24.02", @@ -630,6 +631,7 @@ get_commands_spec() -> %%{"", "will add mike@server.com to peter@localhost roster"}, #ejabberd_commands{name = delete_rosteritem, tags = [roster], desc = "Delete an item from a user's roster (supports ODBC)", + longdesc = "The client will receive a `jabber:iq:roster` IQ notifying them of the removed entry.", module = ?MODULE, function = delete_rosteritem, args = [{localuser, binary}, {localhost, binary}, {user, binary}, {host, binary}],

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

Bp@CyE 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�bakSHB20!i(I6?rGh29VDFYC%+$4%eeM{{F6Y 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