diff options
61 files changed, 1553 insertions, 635 deletions
diff --git a/configure.ac b/configure.ac index 52239313d..d87fcd46a 100644 --- a/configure.ac +++ b/configure.ac @@ -103,8 +103,8 @@ esac],[full_xml=false]) AC_ARG_ENABLE(mssql, [AC_HELP_STRING([--enable-mssql], [use Microsoft SQL Server database (default: no, requires --enable-odbc)])], [case "${enableval}" in - yes) db_type=mssql ;; - no) db_type=generic ;; + yes) db_type=mssql; mssql=true ;; + no) db_type=generic; mssql=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-mssql) ;; esac],[db_type=generic]) @@ -267,7 +267,7 @@ if test "$sqlite" = "true"; then fi enabled_backends="" -for backend in odbc mysql pgsql sqlite redis; do +for backend in odbc mysql pgsql sqlite redis mssql; do if eval test x\${$backend} = xtrue; then if test "x$enabled_backends" = "x"; then enabled_backends=$backend diff --git a/ejabberd.yml.example b/ejabberd.yml.example index eeba1f9c5..8f2a870cb 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -210,6 +210,7 @@ modules: mod_shared_roster: {} mod_stream_mgmt: resend_on_timeout: if_offline + mod_stun_disco: {} mod_vcard: {} mod_vcard_xupdate: {} mod_version: @@ -85,17 +85,17 @@ defmodule Ejabberd.Mixfile do [{:lager, "~> 3.6.0"}, {:p1_utils, "~> 1.0"}, {:fast_xml, "~> 1.1"}, - {:xmpp, "~> 1.4"}, + {:xmpp, ">= 1.4.6"}, {:cache_tab, "~> 1.0"}, {:stringprep, "~> 1.0"}, {:fast_yaml, "~> 1.0"}, {:fast_tls, "~> 1.1"}, {:stun, "~> 1.0"}, - {:esip, "~> 1.0"}, + {:esip, "~> 1.0.32"}, {:p1_mysql, "~> 1.0"}, {:mqtree, "~> 1.0"}, {:p1_pgsql, "~> 1.1"}, - {:jiffy, "~> 1.0"}, + {:jiffy, "~> 1.0.4"}, {:p1_oauth2, "~> 0.6.1"}, {:distillery, "~> 2.0"}, {:pkix, "~> 1.0"}, @@ -5,34 +5,34 @@ "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm", "bbc7008b0161a6f130d8d903b5b3232351fccc9c31a991f8fcbf2a12ace22995"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"}, - "epam": {:hex, :epam, "1.0.6", "6e57e1f5a330fa02a08ee0d4b16d9161f95177351e48c6dfede2f89b7e2f589f", [:rebar3], [], "hexpm"}, + "epam": {:hex, :epam, "1.0.7", "55889bbfdc5ab9f2e785a710229f34e550784c5ead1960d7839ea77514aef44d", [:rebar3], [], "hexpm", "6b029ebd2b244bc339cbf5cb5908d0f2d50e43f33a6e7f70818912ea5d3fd596"}, "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm"}, - "esip": {:hex, :esip, "1.0.32", "b6d5d9eb8342b86509de02ac79e6a9a772dab011e936092441d4e92a7986ca29", [:rebar3], [{:fast_tls, "1.1.4", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.31", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "ab083aaa144c718d5f1c06b3034947cf6a9f73aedca04015813c983478ad94ed"}, + "esip": {:hex, :esip, "1.0.33", "d3c78bfb291f52e11d6955ecb29cb194a55eb0c4ce7ecf407619698005b815e3", [:rebar3], [{:fast_tls, "1.1.5", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.32", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "d09addd003dbe078832a2af6d72367b374a6c495f35fbe54b09bff338f4803be"}, "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, "ezlib": {:hex, :ezlib, "1.0.7", "c8adffd32e66831df77955d163d705cdcf0a3d66762e6f68f8123012e714bf05", [:rebar3], [], "hexpm", "5634b9f7112837f9338a61da1993601f4ab81615de84ff0baddcdc5a3fe940dc"}, - "fast_tls": {:hex, :fast_tls, "1.1.4", "a0320baf14be72fc9f99211543e411bb98077bf72c42e2d86fc4e2c10d60c258", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "027fc2a726d6f3ad36c7fa230780000e8d3fc72dc8b397c9bff2018d3f5ff06a"}, - "fast_xml": {:hex, :fast_xml, "1.1.39", "687080c0190a8c45d564a3576201f1a89f31ae413dd700a2def0821736f98d4d", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "2521816ed30b9c76befd85e477b5173c0329bd08fc8429f970b668c14bddb3f9"}, + "fast_tls": {:hex, :fast_tls, "1.1.5", "e1f60d8b415aa36cae1fc405e14c3f5ff069bb30f04f298287e8a8aa25efe01c", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "b4edad6a10b30827f819238cc10c1f82839797256f5791ab4b41bfe3e362f144"}, + "fast_xml": {:hex, :fast_xml, "1.1.40", "1e44357f9862d86cee4e0a9b9892463096092c0b8b5ee295822309fabbceb063", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d74864613e479fd5b0e9ebf8cf17f745e3133aa5314dd722d5e617850f473ac6"}, "fast_yaml": {:hex, :fast_yaml, "1.0.24", "d304799e6b961a21a509449830193154870b2b526cfc2e7046e9953ad413765f", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "71f4d5f868a2cfd4794e6bbd89495c4e4c54c45c6cb65b7f12d96271d6c02c84"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, "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.0.1", "4f25639772ca41202f41ba9c8f6ca0933554283dd4742c90651e03471c55e341", [:rebar3], [], "hexpm", "2568927f6f5e383cb775f6fd87bc6e80ea5f7d813418de450aa644b02c7f7112"}, + "jiffy": {:hex, :jiffy, "1.0.4", "72adeff75c52a2ff07de738f0813768abe7ce158026cc1115a170340259c0caa", [:rebar3], [], "hexpm", "113e5299ee4e6b9f40204256d7bbbd1caf646edeaef31ef0f7f5f842c0dad39e"}, "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm", "6429c4fee52b2dda7861ee19a4f09c8c1ffa213bee3a1ec187828fde95d447ed"}, "lager": {:hex, :lager, "3.6.10", "6172b43ab720ac33914ccd0aeb21fdbdf88213847707d4b91e6af57b2ae5c4d2", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm", "5d10499461826b79c5abee18bb594b3949cbdf76d9d9fd7e66d0a558137c21c9"}, - "luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"}, - "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"}, + "luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm", "1bc011c7297e43aec762e53b17ecb15b0ff29f9546cd153110b343cf5b043f5f"}, + "makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, "mqtree": {:hex, :mqtree, "1.0.7", "0d8f6101eb2bb6a6e27f0e5a60cfad04b27dd552e75f30294e565605ce7cd0d2", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "317db0349a8d9695bc89ef7062e9654e93347cd9576f827739650e26482825bb"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, "p1_acme": {:hex, :p1_acme, "1.0.5", "de54353100ed82d0c820fbc011b7a7ad54f65af052eb8112922ad8be8eadf8f1", [:rebar3], [{:idna, "~>6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "~>1.0.1", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "~>1.9.0", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "~>1.0.4", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "cbecfc70ce11d37d679875117c685aa2a7bc2502bfa51722c8e619602c92a0c0"}, - "p1_mysql": {:hex, :p1_mysql, "1.0.14", "ea2d58d0551d62fce9882f65fc7e0273ae6d683ab277aad657a661f514411198", [:rebar3], [], "hexpm", "70df0680b71118ace166e59fbf8be19058be3c3918bc3b7ede6e1f297a4a9dbe"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.15", "d24ac3cc154012733801ff4f7781e7ab7843dc85cbad61e757fad601a5d0b511", [:rebar3], [], "hexpm", "4a97e0c93a8bd61acad9a6f7894a6cc31881309cb87540a4734e4c78be41df9c"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.6", "b17053bd7a34621f9a1a7327285a3e37abd38eb1d176afccc8cfc39882ff0a44", [:rebar3], [], "hexpm", "8a5fd16fc581a50e62176ab8b78b83b6e7cc6f76f7f59f75f58d713b7c1ca7b2"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.9", "07ff9b037954dec06b4e30e33a82ac69a5a513e2860d2e59b7f6f4af23493c45", [:rebar3], [], "hexpm", "81aab8cff0203250dd3d9cc77a0232dc9f8e56c99fd742abbaedc51a0fd633a7"}, "p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"}, "pkix": {:hex, :pkix, "1.0.5", "407c02c70191d0791cd9b422ac2380df5f7f8304ec26a6d3b06e0e02be688fca", [:rebar3], [], "hexpm", "b86aed212afaf019ac97bf56857366e5f01c3003f38ee050af8ba16455e13719"}, - "sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"}, + "sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm", "cf9fa59c5b27de0d5d94a2ef464521379e23d8c6e9fa939abf8415c767f514bb"}, "stringprep": {:hex, :stringprep, "1.0.19", "79761de42960a625fb0cd6d31686f6118aef30540a7abb884b92f72861b6adde", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "3f77edbcb530899faffe95d57b6e2bac704a5a6ea1ead5957387f9e4ed8c5f07"}, - "stun": {:hex, :stun, "1.0.31", "577d845d4b77b155bad234598c2056f6e182f178468727de083bedf275dc83a1", [:rebar3], [{:fast_tls, "1.1.4", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "c7edd93d823c0e1438bfe70adf99c39cf4cbca73cbd3c086c6e461ad00c6f8d4"}, + "stun": {:hex, :stun, "1.0.32", "c1bf6c3ef4b6304c423541b2734adcfa46e265d96119b14f2a390da7119d0a42", [:rebar3], [{:fast_tls, "1.1.5", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "4178cf7514dd1df05502199b6d68ed8dc568d8cfa9dbad36a890843431190aac"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.4.5", "b226baa9ad960e8de041289b94bbcb6148a7980acc0c1ec58dfc8f24acded3ad", [:rebar3], [{:ezlib, "1.0.7", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.4", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.39", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.19", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "7bd21dd10ff3369f65128fcf730595073f1b6288a8b563d165874aecb2220317"}, + "xmpp": {:hex, :xmpp, "1.4.6", "99b24010ed9ba6423887c65a8686566f4e5408f0c7a75ef624aea1ac612034af", [:rebar3], [{:ezlib, "1.0.7", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.5", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.40", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.19", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "355d2d9eac87fc75c5290a94386a8c7b25b2874b3889e80e0b6af40bd0bb8adf"}, "yconf": {:hex, :yconf, "1.0.4", "f08dcc2ad041f68580e98753f70453976d256f2c1a40a29a985465ab16d489a6", [:rebar3], [{:fast_yaml, "1.0.24", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "44570111ad224ee4eec6e2bffa1e7223ef4b76f91f9dd2f768eee214d2dcabe2"}, } diff --git a/priv/msgs/cs.po b/priv/msgs/cs.po index c28b0b65f..135e7e917 100644 --- a/priv/msgs/cs.po +++ b/priv/msgs/cs.po @@ -291,8 +291,7 @@ msgid "Configuration" msgstr "Konfigurace" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfigurace místnosti ~s" #: ejabberd_web_admin.erl:937 @@ -495,8 +494,7 @@ msgid "Failed to parse HTTP response" msgstr "Chyba parsování HTTP odpovědi" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Chyba při zpracování možnosti '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -565,8 +563,8 @@ msgid "Given Name" msgstr "Křestní jméno" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Skupina " +msgid "Group" +msgstr "Skupina" #: mod_roster.erl:956 msgid "Groups" @@ -694,10 +692,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Pozvánky nejsou povoleny v této místnosti" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Není povoleno posílat chybové zprávy do místnosti. Účastník (~s) odeslal " "chybovou zprávu (~s) a byl vyhozen z místnosti" @@ -933,8 +930,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Přezdívka ~s v místnosti neexistuje" #: mod_muc_room.erl:3396 @@ -1767,8 +1763,7 @@ msgid "To" msgstr "Pro" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Pokud se chcete zaregistrovat, navštivte ~s" #: mod_configure.erl:666 @@ -1781,10 +1776,9 @@ msgid "Token TTL" msgstr "Token TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Příliš mnoho (~p) chybných pokusů o přihlášení z této IP adresy (~s). Adresa " "bude zablokována do ~s UTC" @@ -1996,19 +1990,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Hodnota 'set' atrubutu 'type' není povolena" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "Hodnota '~s' by měla být boolean" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "Hodnota '~s' by měla být datetime řetězec" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "Hodnota '~s' by měla být celé číslo" #: ejabberd_web_admin.erl:433 @@ -2109,10 +2100,9 @@ msgid "" msgstr "Fronta offline zpráv pro váš kontakt je plná. Zpráva byla zahozena." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Nesmíte posílat zprávy na ~s. Pro povolení navštivte ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/de.po b/priv/msgs/de.po index 2ad5360ce..3a066d636 100644 --- a/priv/msgs/de.po +++ b/priv/msgs/de.po @@ -300,8 +300,7 @@ msgid "Configuration" msgstr "Konfiguration" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfiguration für Raum ~s" #: ejabberd_web_admin.erl:937 @@ -507,8 +506,7 @@ msgid "Failed to parse HTTP response" msgstr "Konnte HTTP-Antwort nicht parsen" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Konnte Option '~s' nicht verarbeiten" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -578,8 +576,8 @@ msgid "Given Name" msgstr "Vorname" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Gruppe " +msgid "Group" +msgstr "Gruppe" #: mod_roster.erl:956 msgid "Groups" @@ -708,10 +706,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Einladungen sind in dieser Konferenz nicht erlaubt" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Es ist nicht erlaubt Fehlermeldungen an den Raum zu senden. Der Teilnehmer " "(~s) hat eine Fehlermeldung (~s) gesendet und wurde aus dem Raum gekickt" @@ -949,8 +946,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Der Benutzername ~s existiert im Raum nicht" #: mod_muc_room.erl:3396 @@ -1790,8 +1786,7 @@ msgid "To" msgstr "An" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Um sich anzumelden, besuchen Sie ~s" #: mod_configure.erl:666 @@ -1804,10 +1799,9 @@ msgid "Token TTL" msgstr "Token TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Zu viele (~p) fehlgeschlagene Anmeldeversuche von dieser IP Adresse (~s). " "Die Adresse wird bis ~s UTC blockiert." @@ -2019,19 +2013,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Wert 'set' des 'type'-Attributs ist nicht erlaubt" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "Wert von '~s' sollte boolesch sein" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "Wert von '~s' sollte datetime-String sein" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "Wert von '~s' sollte eine Ganzzahl sein" #: ejabberd_web_admin.erl:433 @@ -2140,10 +2131,9 @@ msgstr "" "verworfen." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Ihre Nachrichten an ~s werden blockiert. Um dies zu ändern, besuchen Sie ~s" diff --git a/priv/msgs/el.po b/priv/msgs/el.po index 38ee66032..aeca8ffd7 100644 --- a/priv/msgs/el.po +++ b/priv/msgs/el.po @@ -294,8 +294,7 @@ msgid "Configuration" msgstr "Διαμόρφωση" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Διαμόρφωση Αίθουσας σύνεδριασης ~s" #: ejabberd_web_admin.erl:937 @@ -505,9 +504,8 @@ msgid "Failed to parse HTTP response" msgstr "Αποτυχία ανάλυσης της απόκρισης HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" -msgstr "Αποτυχία επεξεργασίας της επιλογής '~ s'" +msgid "Failed to process option '~s'" +msgstr "Αποτυχία επεξεργασίας της επιλογής '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 #: mod_vcard_sql.erl:174 mod_vcard_ldap.erl:330 mod_vcard_ldap.erl:343 @@ -576,8 +574,8 @@ msgid "Given Name" msgstr "Ονομα" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Ομάδα " +msgid "Group" +msgstr "Ομάδα" #: mod_roster.erl:956 msgid "Groups" @@ -705,13 +703,12 @@ msgid "Invitations are not allowed in this conference" msgstr "Οι προσκλήσεις δεν επιτρέπονται σε αυτή τη διάσκεψη" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" -"Δεν επιτρέπεται η αποστολή μηνυμάτων σφάλματος στο δωμάτιο. Ο συμμετέχων (~ " -"s) έχει στείλει ένα μήνυμα σφάλματος (~ s) και έχει πέταχτεί έξω από την " +"Δεν επιτρέπεται η αποστολή μηνυμάτων σφάλματος στο δωμάτιο. Ο συμμετέχων (~s" +") έχει στείλει ένα μήνυμα σφάλματος (~s) και έχει πέταχτεί έξω από την " "αίθουσα" #: mod_muc_room.erl:564 mod_muc_room.erl:575 @@ -946,8 +943,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Ψευδώνυμο ~s δεν υπάρχει σε αυτή την αίθουσα" #: mod_muc_room.erl:3396 @@ -1797,9 +1793,8 @@ msgid "To" msgstr "Πρώς" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" -msgstr "Για να εγγραφείτε, επισκεφθείτε το ~ s" +msgid "To register, visit ~s" +msgstr "Για να εγγραφείτε, επισκεφθείτε το ~s" #: mod_configure.erl:666 #, fuzzy @@ -2025,20 +2020,17 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Δεν επιτρέπεται η παράμετρος 'set' του 'type'" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" -msgstr "Η τιμή του '~ s' πρέπει να είναι boolean" +msgid "Value of '~s' should be boolean" +msgstr "Η τιμή του '~s' πρέπει να είναι boolean" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" -msgstr "Η τιμή του '~ s' θα πρέπει να είναι χρονοσειρά" +msgid "Value of '~s' should be datetime string" +msgstr "Η τιμή του '~s' θα πρέπει να είναι χρονοσειρά" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" -msgstr "Η τιμή του '~ s' θα πρέπει να είναι ακέραιος" +msgid "Value of '~s' should be integer" +msgstr "Η τιμή του '~s' θα πρέπει να είναι ακέραιος" #: ejabberd_web_admin.erl:433 #, fuzzy @@ -2146,10 +2138,9 @@ msgstr "" "Η μνήμη χωρίς σύνδεση μήνυματών είναι πλήρης. Το μήνυμα έχει απορριφθεί." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Τα μηνύματά σας πρως ~s είναι αποκλεισμένα. Για αποδεσμεύση, επισκεφθείτε ~s" diff --git a/priv/msgs/eo.po b/priv/msgs/eo.po index 31146dadb..6c1ad6cf3 100644 --- a/priv/msgs/eo.po +++ b/priv/msgs/eo.po @@ -290,8 +290,7 @@ msgid "Configuration" msgstr "Agordo" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Agordo de babilejo ~s" #: ejabberd_web_admin.erl:937 @@ -566,8 +565,8 @@ msgid "Given Name" msgstr "Meza Nomo" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Grupo " +msgid "Group" +msgstr "Grupo" #: mod_roster.erl:956 msgid "Groups" @@ -936,8 +935,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Kaŝnomo ~s ne ekzistas en la babilejo" #: mod_muc_room.erl:3396 @@ -1782,10 +1780,9 @@ msgid "Token TTL" msgstr "" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Tro da malsukcesaj aŭtentprovoj (~p) de ĉi tiu IP-adreso (~s). La adreso " "estos malbarata je ~s UTC." @@ -2113,10 +2110,9 @@ msgstr "" "forĵetita" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Viaj mesaĝoj al ~s estas blokata. Por malbloki ilin, iru al ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/fr.po b/priv/msgs/fr.po index 8f616457d..fe36401f6 100644 --- a/priv/msgs/fr.po +++ b/priv/msgs/fr.po @@ -293,8 +293,7 @@ msgid "Configuration" msgstr "Configuration" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Configuration pour le salon ~s" #: ejabberd_web_admin.erl:937 @@ -501,8 +500,7 @@ msgid "Failed to parse HTTP response" msgstr "Echec de lecture de la réponse HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Echec de traitement de l'option '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -571,8 +569,8 @@ msgid "Given Name" msgstr "Nom" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Groupe " +msgid "Group" +msgstr "Groupe" #: mod_roster.erl:956 msgid "Groups" @@ -701,10 +699,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Les invitations ne sont pas autorisées dans ce salon" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "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" @@ -942,8 +939,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Le pseudo ~s n'existe pas dans ce salon" #: mod_muc_room.erl:3396 @@ -1784,8 +1780,7 @@ msgid "To" msgstr "A" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Pour vous enregistrer, visitez ~s" #: mod_configure.erl:666 @@ -1798,10 +1793,9 @@ msgid "Token TTL" msgstr "Jeton TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Trop (~p) d'authentification ont échoué pour cette adresse IP (~s). " "L'adresse sera débloquée à ~s UTC" @@ -2016,19 +2010,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "La valeur de l'attribut 'type' ne peut être 'set'" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "La valeur de '~s' ne peut être booléen" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "La valeur de '~s' doit être une chaine datetime" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "La valeur de '~s' doit être un entier" #: ejabberd_web_admin.erl:433 @@ -2139,10 +2130,9 @@ msgstr "" "été détruit." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Vos messages pour ~s sont bloqués. Pour les débloquer, veuillez visiter ~s" diff --git a/priv/msgs/gl.po b/priv/msgs/gl.po index cbb31aa58..7396e63a8 100644 --- a/priv/msgs/gl.po +++ b/priv/msgs/gl.po @@ -291,8 +291,7 @@ msgid "Configuration" msgstr "Configuración" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Configuración para a sala ~s" #: ejabberd_web_admin.erl:937 @@ -498,8 +497,7 @@ msgid "Failed to parse HTTP response" msgstr "Non se puido analizar a resposta HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Fallo ao procesar a opción '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -568,8 +566,8 @@ msgid "Given Name" msgstr "Nome" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Grupo " +msgid "Group" +msgstr "Grupo" #: mod_roster.erl:956 msgid "Groups" @@ -697,10 +695,9 @@ msgid "Invitations are not allowed in this conference" msgstr "As invitacións non están permitidas nesta sala" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Non está permitido enviar mensaxes de erro á sala. Este participante (~s) " "enviou unha mensaxe de erro (~s) e foi expulsado da sala" @@ -937,8 +934,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "O alcume ~s non existe na sala" #: mod_muc_room.erl:3396 @@ -1776,8 +1772,7 @@ msgid "To" msgstr "Para" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Para rexistrarse, visita ~s" #: mod_configure.erl:666 @@ -1790,10 +1785,9 @@ msgid "Token TTL" msgstr "Token TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Demasiados (~p) fallou autenticaciones desde esta dirección IP (~s). A " "dirección será desbloqueada as ~s UTC" @@ -2005,19 +1999,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "O valor \"set\" do atributo 'type' non está permitido" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "O valor de '~s' debería ser booleano" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "O valor de '~s' debería ser unha data" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "O valor de '~s' debería ser un enteiro" #: ejabberd_web_admin.erl:433 @@ -2122,10 +2113,9 @@ msgstr "" "descartouse." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "As súas mensaxes a ~s encóntranse bloqueadas. Para desbloquear, visite ~s" diff --git a/priv/msgs/he.po b/priv/msgs/he.po index 8292880ea..0bca2662c 100644 --- a/priv/msgs/he.po +++ b/priv/msgs/he.po @@ -295,8 +295,7 @@ msgstr "תצורה" # תצורה של חדר #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "תצורת חדר ~s" #: ejabberd_web_admin.erl:937 @@ -500,8 +499,7 @@ msgid "Failed to parse HTTP response" msgstr "נכשל לפענח תגובת HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "נכשל לעבד אפשרות '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -572,8 +570,8 @@ msgid "Given Name" msgstr "שם פרטי" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "קבוצה " +msgid "Group" +msgstr "קבוצה" #: mod_roster.erl:956 msgid "Groups" @@ -702,10 +700,9 @@ msgid "Invitations are not allowed in this conference" msgstr "הזמנות אינן מותרות בועידה זו" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "אין זה מותר לשלוח הודעות שגיאה לחדר. משתתף זה (~s) שלח הודעת שגיאה (~s) " "ונבעט מתוך החדר" @@ -1784,8 +1781,7 @@ msgid "To" msgstr "לכבוד" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "כדי להירשם, בקרו ~s" #: mod_configure.erl:666 @@ -1798,10 +1794,9 @@ msgid "Token TTL" msgstr "סימן TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "יותר מדי (~p) אימותים כושלים מתוך כתובת IP זו (~s). הכתובת תורשה לקבל גישה " "בשעה ~s UTC" @@ -2015,19 +2010,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "ערך של '~s' צריך להיות boolean" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "ערך של '~s' צריך להיות מחרוזת datetime" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "ערך של '~s' צריך להיות integer" # וירטואליים @@ -2132,10 +2124,9 @@ msgid "" msgstr "תור הודעות קשר לא מקוונות הינו מלא. ההודעה סולקה." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "ההודעות שלך לערוץ ~s הינן חסומות. כדי לבטל את חסימתן, בקר בכתובת ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/hu.po b/priv/msgs/hu.po index 384f31560..2edf94056 100644 --- a/priv/msgs/hu.po +++ b/priv/msgs/hu.po @@ -295,8 +295,8 @@ msgid "Configuration" msgstr "Beállítás" #: mod_muc_room.erl:3489 -msgid "Configuration of room ~ts" -msgstr "A(z) ~ts szoba beállítása" +msgid "Configuration of room ~s" +msgstr "A(z) ~s szoba beállítása" #: ejabberd_web_admin.erl:937 msgid "Connected Resources:" @@ -567,8 +567,8 @@ msgid "Given Name" msgstr "Keresztnév" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Csoport " +msgid "Group" +msgstr "Csoport" #: mod_roster.erl:956 msgid "Groups" @@ -694,11 +694,11 @@ msgstr "Meghívások nem engedélyezettek ebben a konferenciában" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" -"Nem engedélyezett hibaüzeneteket küldeni a szobába. A résztvevő (~ts) " -"hibaüzenetet (~ts) küldött, és ki lett rúgva a szobából" +"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" #: mod_muc_room.erl:564 mod_muc_room.erl:575 msgid "It is not allowed to send private messages" @@ -1734,8 +1734,8 @@ msgid "This room is not anonymous" msgstr "Ez a szoba nem névtelen" #: mod_multicast.erl:498 -msgid "This service can not process the address: ~ts" -msgstr "Ez a szolgáltatás nem tudja feldolgozni a címet: ~ts" +msgid "This service can not process the address: ~s" +msgstr "Ez a szolgáltatás nem tudja feldolgozni a címet: ~s" #: mod_muc_log.erl:470 msgid "Thursday" @@ -1758,8 +1758,8 @@ msgid "To" msgstr "Címzett" #: mod_register.erl:226 -msgid "To register, visit ~ts" -msgstr "Regisztráláshoz látogassa meg ezt az oldalt: ~ts" +msgid "To register, visit ~s" +msgstr "Regisztráláshoz látogassa meg ezt az oldalt: ~s" #: mod_configure.erl:666 msgid "To ~ts" @@ -1769,10 +1769,9 @@ msgstr "Címzett: ~ts" msgid "Token TTL" msgstr "Token élettartama" -#: mod_fail2ban.erl:219 msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "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" @@ -1981,17 +1980,17 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "A „type” attribútum „set” értéke nem engedélyezett" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -msgid "Value of '~ts' should be boolean" -msgstr "A(z) „~ts” értéke csak logikai lehet" +msgid "Value of '~s' should be boolean" +msgstr "A(z) „~s” értéke csak logikai lehet" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -msgid "Value of '~ts' should be datetime string" -msgstr "A(z) „~ts” értéke csak dátum és idő karakterlánc lehet" +msgid "Value of '~s' should be datetime string" +msgstr "A(z) „~s” értéke csak dátum és idő karakterlánc lehet" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -msgid "Value of '~ts' should be integer" -msgstr "A(z) „~ts” értéke csak egész szám lehet" +msgid "Value of '~s' should be integer" +msgstr "A(z) „~s” értéke csak egész szám lehet" #: ejabberd_web_admin.erl:433 msgid "Virtual Hosting" @@ -2097,12 +2096,12 @@ msgstr "" #: ejabberd_captcha.erl:97 msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" -"A feliratkozási kérelme és/vagy ~ts számára küldött üzenetei blokkolva " +"A feliratkozási kérelme és/vagy ~s számára küldött üzenetei blokkolva " "lettek. A feliratkozási kérelmének feloldásához látogassa meg ezt az oldalt: " -"~ts" +"~s" #: mod_disco.erl:438 msgid "ejabberd" diff --git a/priv/msgs/id.po b/priv/msgs/id.po index 8c9fd3538..45c9adcb7 100644 --- a/priv/msgs/id.po +++ b/priv/msgs/id.po @@ -291,8 +291,7 @@ msgid "Configuration" msgstr "Pengaturan" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Pengaturan ruangan ~s" #: ejabberd_web_admin.erl:937 @@ -568,10 +567,11 @@ msgid "Given Name" msgstr "Nama Tengah" #: mod_shared_roster.erl:879 -msgid "Group " +msgid "Group" msgstr "Grup" #: mod_roster.erl:956 +#, fuzzy msgid "Groups" msgstr "Grup" @@ -940,8 +940,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Nama Julukan ~s tidak berada di dalam ruangan" #: mod_muc_room.erl:3396 @@ -2126,10 +2125,9 @@ msgstr "" "Kontak offline Anda pada antrian pesan sudah penuh. Pesan telah dibuang." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Pesan Anda untuk ~s sedang diblokir. Untuk membuka blokir tersebut, kunjungi " "~s" diff --git a/priv/msgs/it.po b/priv/msgs/it.po index 99bdacca6..950a581aa 100644 --- a/priv/msgs/it.po +++ b/priv/msgs/it.po @@ -298,8 +298,7 @@ msgid "Configuration" msgstr "Configurazione" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Configurazione per la stanza ~s" #: ejabberd_web_admin.erl:937 @@ -577,8 +576,8 @@ msgid "Given Name" msgstr "Altro nome" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Gruppo " +msgid "Group" +msgstr "Gruppo" #: mod_roster.erl:956 msgid "Groups" @@ -947,8 +946,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Il nickname ~s non esiste nella stanza" #: mod_muc_room.erl:3396 @@ -2135,10 +2133,9 @@ msgstr "" "scartato" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "I messaggi verso ~s sono bloccati. Per sbloccarli, visitare ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/ja.po b/priv/msgs/ja.po index 8527f5d50..589ae91a1 100644 --- a/priv/msgs/ja.po +++ b/priv/msgs/ja.po @@ -288,8 +288,7 @@ msgid "Configuration" msgstr "設定" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "チャットルーム ~s の設定" #: ejabberd_web_admin.erl:937 @@ -565,10 +564,12 @@ msgid "Given Name" msgstr "ミドルネーム" #: mod_shared_roster.erl:879 -msgid "Group " +#, fuzzy +msgid "Group" msgstr "グループ" #: mod_roster.erl:956 +#, fuzzy msgid "Groups" msgstr "グループ" @@ -697,10 +698,9 @@ msgid "Invitations are not allowed in this conference" msgstr "この会議では、発言権の要求はできません" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "このルームにエラーメッセージを送ることは許可されていません。参加者(~s)はエ" "ラーメッセージを(~s)を送信してルームからキックされました。" @@ -939,8 +939,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "ニックネーム ~s はこのチャットルームにいません" #: mod_muc_room.erl:3396 @@ -1783,10 +1782,9 @@ msgid "Token TTL" msgstr "" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "~p回の認証に失敗しました。このIPアドレス(~s)は~s UTCまでブロックされます。" @@ -2109,10 +2107,9 @@ msgstr "" "相手先のオフラインメッセージキューが一杯です。このメッセージは破棄されます。" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "~s 宛のメッセージはブロックされています。解除するにはこちらを見てください ~s" diff --git a/priv/msgs/nl.po b/priv/msgs/nl.po index 4fa0ad0a4..cfeadd5ac 100644 --- a/priv/msgs/nl.po +++ b/priv/msgs/nl.po @@ -292,8 +292,7 @@ msgid "Configuration" msgstr "Instellingen" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Instellingen van chatruimte ~s" #: ejabberd_web_admin.erl:937 @@ -571,8 +570,8 @@ msgid "Given Name" msgstr "Tussennaam" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Groep " +msgid "Group" +msgstr "Groep" #: mod_roster.erl:956 msgid "Groups" @@ -946,8 +945,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "De bijnaam ~s bestaat niet in deze chatruimte" #: mod_muc_room.erl:3396 @@ -1807,10 +1805,9 @@ msgid "Token TTL" msgstr "" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Te veel (~p) mislukte authenticatie-pogingen van dit IP-adres (~s). Dit " "adres zal worden gedeblokkeerd om ~s UTC" @@ -2138,10 +2135,9 @@ msgstr "" "opgeslagen." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Uw berichten aan ~s worden geblokkeerd. Om ze te deblokkeren, ga naar ~s" diff --git a/priv/msgs/no.po b/priv/msgs/no.po index e556e7f3b..5800da136 100644 --- a/priv/msgs/no.po +++ b/priv/msgs/no.po @@ -290,8 +290,7 @@ msgid "Configuration" msgstr "Konfigurasjon" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfigurasjon for rom ~s" #: ejabberd_web_admin.erl:937 @@ -566,8 +565,8 @@ msgid "Given Name" msgstr "Mellomnavn" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Gruppe " +msgid "Group" +msgstr "Gruppe" #: mod_roster.erl:956 msgid "Groups" @@ -936,8 +935,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Kallenavn ~s eksisterer ikke i dette rommet" #: mod_muc_room.erl:3396 @@ -2108,10 +2106,9 @@ msgid "" msgstr "Kontaktens frakoblede meldingskø er full. Meldingen har blitt kassert." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Dine meldinger til ~s blir blokkert. For å åpne igjen, besøk ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/pl.po b/priv/msgs/pl.po index 777d21337..c03d28e88 100644 --- a/priv/msgs/pl.po +++ b/priv/msgs/pl.po @@ -294,8 +294,7 @@ msgid "Configuration" msgstr "Konfiguracja" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfiguracja pokoju ~s" #: ejabberd_web_admin.erl:937 @@ -500,8 +499,7 @@ msgid "Failed to parse HTTP response" msgstr "Nie udało się zanalizować odpowiedzi HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Nie udało się przetworzyć opcji '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -570,8 +568,8 @@ msgid "Given Name" msgstr "Imię" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Grupa " +msgid "Group" +msgstr "Grupa" #: mod_roster.erl:956 msgid "Groups" @@ -699,10 +697,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Zaproszenia są wyłączone w tym pokoju" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "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" @@ -1776,8 +1773,7 @@ msgid "To" msgstr "Do" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Żeby się zarejestrować odwiedź ~s" #: mod_configure.erl:666 @@ -1790,10 +1786,9 @@ msgid "Token TTL" msgstr "Limit czasu tokenu" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Zbyt wiele (~p) nieudanych prób logowanie z tego adresu IP (~s). Ten adres " "zostanie odblokowany o ~s UTC" @@ -2005,19 +2000,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Wartość 'set' dla atrybutu 'type' jest niedozwolona" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "Wartość '~s' powinna być typu logicznego" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "Wartość '~s' powinna być typu daty" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "Wartość '~s' powinna być liczbą" #: ejabberd_web_admin.erl:433 @@ -2118,10 +2110,9 @@ msgstr "" "Kolejka wiadomości offline adresata jest pełna. Wiadomość została odrzucona." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Twoje wiadomości do ~s są blokowane. Aby je odblokować, odwiedź ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/pt-br.po b/priv/msgs/pt-br.po index ad56176fb..f14697889 100644 --- a/priv/msgs/pt-br.po +++ b/priv/msgs/pt-br.po @@ -291,8 +291,7 @@ msgid "Configuration" msgstr "Configuração" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Configuração para ~s" #: ejabberd_web_admin.erl:937 @@ -499,8 +498,7 @@ msgid "Failed to parse HTTP response" msgstr "Falha ao analisar resposta HTTP" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Falha ao processar opção '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -570,8 +568,8 @@ msgid "Given Name" msgstr "Nome do meio" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Grupo " +msgid "Group" +msgstr "Grupo" #: mod_roster.erl:956 msgid "Groups" @@ -701,10 +699,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Convites estão desabilitados nesta sala de conferência" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Não é permitido o envio de mensagens de erro a esta sala. O membro (~s) " "enviou uma mensagem de erro (~s) e foi desconectado (\"kicked\")." @@ -942,8 +939,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "O apelido ~s não existe na sala" #: mod_muc_room.erl:3396 @@ -1784,8 +1780,7 @@ msgid "To" msgstr "Para" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Para registrar, visite ~s" #: mod_configure.erl:666 @@ -1798,10 +1793,9 @@ msgid "Token TTL" msgstr "Token TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Número excessivo (~p) de tentativas falhas de autenticação (~s). O endereço " "será desbloqueado às ~s UTC" @@ -2016,19 +2010,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Valor 'set' não permitido para atributo 'type'" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "Value de '~s' deveria ser um booleano" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "Valor de '~s' deveria ser data e hora" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "Valor de '~s' deveria ser um inteiro" #: ejabberd_web_admin.erl:433 @@ -2132,10 +2123,9 @@ msgid "" msgstr "Sua fila de mensagens offline esta cheia. Sua mensagem foi descartada" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Suas mensagens para ~s estão bloqueadas. Para desbloqueá-las, visite: ~s" diff --git a/priv/msgs/pt.po b/priv/msgs/pt.po index c7ae0686a..0b2be5579 100644 --- a/priv/msgs/pt.po +++ b/priv/msgs/pt.po @@ -300,7 +300,7 @@ msgstr "Configuração" #: mod_muc_room.erl:3489 #, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Configuração para " #: ejabberd_web_admin.erl:937 @@ -585,9 +585,8 @@ msgid "Given Name" msgstr "Segundo nome" #: mod_shared_roster.erl:879 -#, fuzzy msgid "Group " -msgstr "Grupos" +msgstr "" #: mod_roster.erl:956 msgid "Groups" @@ -969,8 +968,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "A alcunha ~s não existe na sala" #: mod_muc_room.erl:3396 diff --git a/priv/msgs/ru.po b/priv/msgs/ru.po index 85ae802ba..fd0363f21 100644 --- a/priv/msgs/ru.po +++ b/priv/msgs/ru.po @@ -289,8 +289,7 @@ msgid "Configuration" msgstr "Конфигурация" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Конфигурация комнаты ~s" #: ejabberd_web_admin.erl:937 @@ -491,8 +490,7 @@ msgid "Failed to parse HTTP response" msgstr "Ошибка разбора HTTP ответа" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "Ошибка обработки опции '~s'" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -559,8 +557,8 @@ msgid "Given Name" msgstr "Имя" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Группа " +msgid "Group" +msgstr "Группа" #: mod_roster.erl:956 msgid "Groups" @@ -685,10 +683,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Рассылка приглашений отключена в этой конференции" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Запрещено посылать сообщения об ошибках в эту комнату. Участник (~s) послал " "сообщение об ошибке (~s) и был выкинут из комнаты" @@ -924,8 +921,7 @@ msgid "Nickname can't be empty" msgstr "Псевдоним не может быть пустым значением" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Псевдоним ~s в комнате отсутствует" #: mod_muc_room.erl:3396 @@ -1726,8 +1722,7 @@ msgid "This room is not anonymous" msgstr "Эта комната не анонимная" #: mod_multicast.erl:498 -#, fuzzy -msgid "This service can not process the address: ~ts" +msgid "This service can not process the address: ~s" msgstr "Сервер не может обработать адрес: ~s" #: mod_muc_log.erl:470 @@ -1751,8 +1746,7 @@ msgid "To" msgstr "Кому" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "Для регистрации посетите ~s" #: mod_configure.erl:666 @@ -1765,10 +1759,9 @@ msgid "Token TTL" msgstr "Токен TTL" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Слишком много (~p) неудачных попыток аутентификации с этого IP-адреса (~s). " "Адрес будет разблокирован в ~s UTC" @@ -1977,19 +1970,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "Значение 'set' атрибута 'type' недопустимо" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "Значение '~s' должно быть булевым" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "Значение '~s' должно быть датой" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "Значение '~s' должно быть целочисленным" #: ejabberd_web_admin.erl:433 @@ -2089,10 +2079,9 @@ msgstr "" "было сохранено." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Ваши запросы на добавление в контакт-лист, а также сообщения к ~s " "блокируются. Для снятия блокировки перейдите по ссылке ~s" diff --git a/priv/msgs/sk.po b/priv/msgs/sk.po index 47c98a164..e832824a9 100644 --- a/priv/msgs/sk.po +++ b/priv/msgs/sk.po @@ -291,8 +291,7 @@ msgid "Configuration" msgstr "Konfigurácia" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfigurácia miestnosti ~s" #: ejabberd_web_admin.erl:937 @@ -567,8 +566,8 @@ msgid "Given Name" msgstr "Prostredné meno: " #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Skupina " +msgid "Group" +msgstr "Skupina" #: mod_roster.erl:956 msgid "Groups" @@ -936,8 +935,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Prezývka ~s v miestnosti neexistuje" #: mod_muc_room.erl:3396 @@ -2113,10 +2111,9 @@ msgid "" msgstr "Fronta offline správ tohoto kontaktu je plná. Správa bola zahodená." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Správa určená pre ~s bola zablokovaná. Oblokovať ju môžete na ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/sv.po b/priv/msgs/sv.po index adb38b569..d2be39e66 100644 --- a/priv/msgs/sv.po +++ b/priv/msgs/sv.po @@ -295,8 +295,7 @@ msgid "Configuration" msgstr "Konfiguration" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Konfiguration för ~s" #: ejabberd_web_admin.erl:937 @@ -570,8 +569,8 @@ msgid "Given Name" msgstr "Mellannamn" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Grupp " +msgid "Group" +msgstr "Grupp" #: mod_roster.erl:956 msgid "Groups" @@ -942,8 +941,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Smeknamnet ~s existerar inte i det här rummet" #: mod_muc_room.erl:3396 @@ -2121,10 +2119,9 @@ msgid "" msgstr "Din kontaktkö for offlinekontakter ar full" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "Dina meddelanden till ~s är blockerade. För att avblockera dem, gå till ~s" diff --git a/priv/msgs/th.po b/priv/msgs/th.po index 78ea0984f..3e7cccd54 100644 --- a/priv/msgs/th.po +++ b/priv/msgs/th.po @@ -569,8 +569,8 @@ msgid "Given Name" msgstr "ชื่อกลาง" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "กลุ่ม" +msgid "Group" +msgstr "กลุ่" #: mod_roster.erl:956 msgid "Groups" diff --git a/priv/msgs/tr.po b/priv/msgs/tr.po index af916bbd0..679fb8524 100644 --- a/priv/msgs/tr.po +++ b/priv/msgs/tr.po @@ -294,8 +294,7 @@ msgid "Configuration" msgstr "Ayarlar" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "~s odasının ayarları" #: ejabberd_web_admin.erl:937 @@ -573,7 +572,7 @@ msgstr "Ortanca İsim" #: mod_shared_roster.erl:879 msgid "Group " -msgstr "Group " +msgstr "" #: mod_roster.erl:956 msgid "Groups" @@ -943,8 +942,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "~s takma ismi odada yok" #: mod_muc_room.erl:3396 @@ -2128,10 +2126,9 @@ msgid "" msgstr "Çevirim-dışı mesaj kuyruğunuz dolu. Mesajını dikkate alınmadı." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" "~s kullanıcısına mesajlarınız engelleniyor. Durumu düzeltmek için ~s " "adresini ziyaret ediniz." diff --git a/priv/msgs/uk.po b/priv/msgs/uk.po index 16f57a358..07702761a 100644 --- a/priv/msgs/uk.po +++ b/priv/msgs/uk.po @@ -295,8 +295,7 @@ msgid "Configuration" msgstr "Конфігурація" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Конфігурація кімнати ~s" #: ejabberd_web_admin.erl:937 @@ -568,8 +567,8 @@ msgid "Given Name" msgstr "По-батькові" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Група " +msgid "Group" +msgstr "Група" #: mod_roster.erl:956 msgid "Groups" @@ -699,10 +698,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Голосові запити відключені в цій конференції" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "Не дозволяється відправляти помилкові повідомлення в кімнату. Учасник (~s) " "відправив помилкове повідомлення (~s), та був виганий з кімнати" @@ -940,8 +938,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Псевдонім ~s в кімнаті відсутній" #: mod_muc_room.erl:3396 @@ -1794,10 +1791,9 @@ msgid "Token TTL" msgstr "" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "Забагато (~p) помилок авторизації з цієї IP адреси (~s). Адресу буде " "розблоковано о ~s UTC" @@ -2126,10 +2122,9 @@ msgstr "" "збережено." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Ваші повідомлення до ~s блокуються. Для розблокування відвідайте ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/vi.po b/priv/msgs/vi.po index 3382fde35..4c3443140 100644 --- a/priv/msgs/vi.po +++ b/priv/msgs/vi.po @@ -571,8 +571,8 @@ msgid "Given Name" msgstr "Họ Đệm" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Nhóm " +msgid "Group" +msgstr "Nhóm" #: mod_roster.erl:956 msgid "Groups" @@ -946,8 +946,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Bí danh ~s không tồn tại trong phòng này" #: mod_muc_room.erl:3396 @@ -2140,10 +2139,9 @@ msgid "" msgstr "" "Danh sách chờ thư liên lạc ngoại tuyến của bạn đã đầy. Thư này đã bị loại bỏ." -#: ejabberd_captcha.erl:97 msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "" #: mod_disco.erl:438 diff --git a/priv/msgs/wa.po b/priv/msgs/wa.po index c931716b0..e29afed32 100644 --- a/priv/msgs/wa.po +++ b/priv/msgs/wa.po @@ -293,8 +293,7 @@ msgid "Configuration" msgstr "Apontiaedjes" #: mod_muc_room.erl:3489 -#, fuzzy -msgid "Configuration of room ~ts" +msgid "Configuration of room ~s" msgstr "Apontiaedje del såle ~s" #: ejabberd_web_admin.erl:937 @@ -571,8 +570,8 @@ msgid "Given Name" msgstr "No do mitan" #: mod_shared_roster.erl:879 -msgid "Group " -msgstr "Groupe " +msgid "Group" +msgstr "Groupe" #: mod_roster.erl:956 msgid "Groups" @@ -702,10 +701,9 @@ msgid "Invitations are not allowed in this conference" msgstr "Les dmandes di vwès sont dismetowes e cisse conferince ci" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "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û." @@ -943,8 +941,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "Li metou no ~s n' egzistêye nén dins l' såle" #: mod_muc_room.erl:3396 @@ -1798,10 +1795,9 @@ msgid "Token TTL" msgstr "" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "" "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" @@ -2129,10 +2125,9 @@ msgstr "" "messaedje a stî tapé å diale." #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "Vos messaedjes po ~s sont blokés. Po les disbloker, alez vey ~s" #: mod_disco.erl:438 diff --git a/priv/msgs/zh.po b/priv/msgs/zh.po index cabb215b5..34cd68e03 100644 --- a/priv/msgs/zh.po +++ b/priv/msgs/zh.po @@ -491,8 +491,7 @@ msgid "Failed to parse HTTP response" msgstr "HTTP响应解析失败" #: mod_muc_room.erl:3632 -#, fuzzy -msgid "Failed to process option '~ts'" +msgid "Failed to process option '~s'" msgstr "选项'~s'处理失败" #: mod_vcard_mnesia.erl:103 mod_vcard_mnesia.erl:117 mod_vcard_sql.erl:160 @@ -561,7 +560,7 @@ msgid "Given Name" msgstr "中间名" #: mod_shared_roster.erl:879 -msgid "Group " +msgid "Group" msgstr "组" #: mod_roster.erl:956 @@ -690,10 +689,9 @@ msgid "Invitations are not allowed in this conference" msgstr "此会议不允许邀请" #: mod_muc_room.erl:373 mod_muc_room.erl:519 mod_muc_room.erl:1293 -#, fuzzy msgid "" -"It is not allowed to send error messages to the room. The participant (~ts) " -"has sent an error message (~ts) and got kicked from the room" +"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" msgstr "" "不允许将错误消息发送到该房间. 参与者(~s)已发送过一条消息(~s)并已被踢出房间" @@ -928,8 +926,7 @@ msgid "Nickname can't be empty" msgstr "" #: mod_muc_room.erl:2994 -#, fuzzy -msgid "Nickname ~ts does not exist in the room" +msgid "Nickname ~s does not exist in the room" msgstr "昵称~s不在该房间" #: mod_muc_room.erl:3396 @@ -1754,8 +1751,7 @@ msgid "To" msgstr "到" #: mod_register.erl:226 -#, fuzzy -msgid "To register, visit ~ts" +msgid "To register, visit ~s" msgstr "要注册,请访问 ~s" #: mod_configure.erl:666 @@ -1768,10 +1764,9 @@ msgid "Token TTL" msgstr "TTL令牌" #: mod_fail2ban.erl:219 -#, fuzzy msgid "" -"Too many (~p) failed authentications from this IP address (~ts). The address " -"will be unblocked at ~ts UTC" +"Too many (~p) failed authentications from this IP address (~s). The address " +"will be unblocked at ~s UTC" msgstr "来自IP地址(~p)的(~s)失败认证太多. 该地址将在UTC时间~s被禁用." #: mod_muc_room.erl:2768 mod_muc_room.erl:3402 @@ -1981,19 +1976,16 @@ msgid "Value 'set' of 'type' attribute is not allowed" msgstr "不允许 'type' 属性的 'set' 值" #: pubsub_subscription.erl:237 pubsub_subscription_sql.erl:202 -#, fuzzy -msgid "Value of '~ts' should be boolean" +msgid "Value of '~s' should be boolean" msgstr "'~s' 的值应为布尔型" #: pubsub_subscription.erl:215 pubsub_subscription_sql.erl:180 -#, fuzzy -msgid "Value of '~ts' should be datetime string" +msgid "Value of '~s' should be datetime string" msgstr "'~s' 的值应为日期时间字符串" #: pubsub_subscription.erl:209 pubsub_subscription.erl:227 #: pubsub_subscription_sql.erl:174 pubsub_subscription_sql.erl:192 -#, fuzzy -msgid "Value of '~ts' should be integer" +msgid "Value of '~s' should be integer" msgstr "'~s' 的值应为整数" #: ejabberd_web_admin.erl:433 @@ -2093,10 +2085,9 @@ msgid "" msgstr "您的联系人离线消息队列已满. 消息已被丢弃" #: ejabberd_captcha.erl:97 -#, fuzzy msgid "" -"Your subscription request and/or messages to ~ts have been blocked. To " -"unblock your subscription request, visit ~ts" +"Your subscription request and/or messages to ~s have been blocked. To " +"unblock your subscription request, visit ~s" msgstr "您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s" #: mod_disco.erl:438 diff --git a/rebar.config b/rebar.config index 778716299..c1d3d9e6c 100644 --- a/rebar.config +++ b/rebar.config @@ -21,14 +21,14 @@ {deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager", "3.6.10"}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.18"}}}, {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.22"}}}, - {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.4"}}}, + {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.5"}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.19"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.39"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.40"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "c23e66ebac8fdec4aa08c8926091b0dcf6dacf22"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.4.6"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.24"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.4"}}}, - {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.0.1"}}}, + {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.0.4"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.6"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.5"}}}, {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.9.0"}}}, @@ -36,10 +36,10 @@ {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.7"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme.git", {tag, "1.0.5"}}}, {base64url, ".*", {git, "https://github.com/dvv/base64url.git", {tag, "v1.0"}}}, - {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.31"}}}}, - {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.32"}}}}, + {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.32"}}}}, + {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.33"}}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", - "604f7b339e845c2fb4219960a19fc5c048f16c0b"}}}, + {tag, "1.0.15"}}}}, {if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.9"}}}}, {if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 9e0274cf8..36a6af195 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -386,7 +386,7 @@ sasl_mechanisms(Mechs, #{lserver := LServer} = State) -> (<<"DIGEST-MD5">>) -> Type == plain; (<<"SCRAM-SHA-1">>) -> Type /= external; (<<"PLAIN">>) -> true; - (<<"X-OAUTH2">>) -> true; + (<<"X-OAUTH2">>) -> [ejabberd_auth_anonymous] /= ejabberd_auth:auth_modules(LServer); (<<"EXTERNAL">>) -> maps:get(tls_verify, State, false); (_) -> false end, Mechs -- Mechs1). diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 0a394b272..532c6eb7a 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -23,33 +23,14 @@ %%% %%%---------------------------------------------------------------------- -%%% @headerfile "ejabberd_ctl.hrl" - -%%% @doc Management of ejabberdctl commands and frontend to ejabberd commands. -%%% -%%% An ejabberdctl command is an abstract function identified by a -%%% name, with a defined number of calling arguments, that can be -%%% defined in any Erlang module and executed using ejabberdctl -%%% administration script. -%%% -%%% Note: strings cannot have blankspaces -%%% %%% Does not support commands that have arguments with ctypes: list, tuple -%%% -%%% TODO: Update the guide -%%% TODO: Mention this in the release notes -%%% Note: the commands with several words use now the underline: _ -%%% It is still possible to call the commands with dash: - -%%% but this is deprecated, and may be removed in a future version. - -module(ejabberd_ctl). -behaviour(gen_server). -author('alexey@process-one.net'). --export([start/0, start_link/0, process/1, process2/2, - register_commands/3, unregister_commands/3]). +-export([start/0, start_link/0, process/1, process2/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -111,8 +92,6 @@ start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> - ets:new(ejabberd_ctl_cmds, [named_table, set, public]), - ets:new(ejabberd_ctl_host_cmds, [named_table, set, public]), {ok, #state{}}. handle_call(Request, From, State) -> @@ -134,29 +113,9 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. %%----------------------------- -%% ejabberdctl Command management -%%----------------------------- - -register_commands(CmdDescs, Module, Function) -> - ets:insert(ejabberd_ctl_cmds, CmdDescs), - ejabberd_hooks:add(ejabberd_ctl_process, - Module, Function, 50), - ok. - -unregister_commands(CmdDescs, Module, Function) -> - lists:foreach(fun(CmdDesc) -> - ets:delete_object(ejabberd_ctl_cmds, CmdDesc) - end, CmdDescs), - ejabberd_hooks:delete(ejabberd_ctl_process, - Module, Function, 50), - ok. - - -%%----------------------------- %% Process %%----------------------------- - -spec process([string()]) -> non_neg_integer(). process(Args) -> process(Args, ?DEFAULT_VERSION). @@ -509,13 +468,6 @@ is_supported_args(Args) -> end, Args). -get_list_ctls() -> - case catch ets:tab2list(ejabberd_ctl_cmds) of - {'EXIT', _} -> []; - Cs -> [{NameArgs, [], Desc} || {NameArgs, Desc} <- Cs] - end. - - %%----------------------------- %% Print help %%----------------------------- @@ -541,8 +493,7 @@ print_usage(HelpMode, MaxC, ShCode, Version) -> {"restart", [], "Restart ejabberd"}, {"help", ["[--tags [tag] | com?*]"], "Show help (try: ejabberdctl help help)"}, {"mnesia", ["[info]"], "show information of Mnesia system"}] ++ - get_list_commands(Version) ++ - get_list_ctls(), + get_list_commands(Version), print( ["Usage: ", ?B("ejabberdctl"), " [--no-timeout] [--node ", ?U("nodename"), "] [--version ", ?U("api_version"), "] ", @@ -874,12 +825,3 @@ disable_logging() -> disable_logging() -> logger:set_primary_config(level, none). -endif. - -%%----------------------------- -%% Command management -%%----------------------------- - -%%+++ -%% Struct(Integer res) create_account(Struct(String user, String server, String password)) -%%format_usage_xmlrpc(ArgsDef, ResultDef) -> -%% ["aaaa bbb ccc"]. diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 50bccf7a0..20928eae7 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -485,7 +485,7 @@ doc() -> desc => ?T("A list of Erlang nodes to connect on ejabberd startup. " "This option is mostly intended for ejabberd customization " - "and sofisticated setups. The default value is an empty list.")}}, + "and sophisticated setups. The default value is an empty list.")}}, {define_macro, #{value => "{MacroName: MacroValue}", desc => @@ -827,7 +827,7 @@ doc() -> desc => {?T("Whether to use 'new' SQL schema. All schemas are located " "at <https://github.com/processone/ejabberd/tree/~s/sql>. " - "There are two schemas available. The default lecacy schema " + "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 " "single ejabberd database. Using this 'new' schema is best when " diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index ddcca0459..8b952f4e0 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -51,10 +51,8 @@ sqlite_file/1, encode_term/1, decode_term/1, - odbc_config/0, - freetds_config/0, odbcinst_config/0, - init_mssql/1, + init_mssql/0, keep_alive/2, to_list/2, to_array/2]). @@ -1109,8 +1107,9 @@ db_opts(Host) -> SSLOpts = get_ssl_opts(Transport, Host), case Type of mssql -> - [mssql, <<"DSN=", Host/binary, ";UID=", User/binary, - ";PWD=", Pass/binary>>, Timeout]; + [mssql, <<"DRIVER=FreeTDS;SERVER=", Server/binary, ";UID=", User/binary, + ";DATABASE=", DB/binary ,";PWD=", Pass/binary, + ";PORT=", (integer_to_binary(Port))/binary ,";CLIENT_CHARSET=UTF-8;">>, Timeout]; _ -> [Type, Server, Port, DB, User, Pass, Timeout, Transport, SSLOpts] end @@ -1152,44 +1151,15 @@ get_ssl_opts(ssl, Host) -> get_ssl_opts(tcp, _) -> []. -init_mssql(Host) -> - Server = ejabberd_option:sql_server(Host), - Port = ejabberd_option:sql_port(Host), - DB = case ejabberd_option:sql_database(Host) of - undefined -> <<"ejabberd">>; - D -> D - end, - FreeTDS = io_lib:fwrite("[~ts]~n" - "\thost = ~ts~n" - "\tport = ~p~n" - "\tclient charset = UTF-8~n" - "\ttds version = 7.1~n", - [Host, Server, Port]), - ODBCINST = io_lib:fwrite("[freetds]~n" - "Description = MSSQL connection~n" - "Driver = libtdsodbc.so~n" - "Setup = libtdsS.so~n" - "UsageCount = 1~n" - "FileUsage = 1~n", []), - ODBCINI = io_lib:fwrite("[~ts]~n" - "Description = MS SQL~n" - "Driver = freetds~n" - "Servername = ~ts~n" - "Database = ~ts~n" - "Port = ~p~n", - [Host, Host, DB, Port]), - ?DEBUG("~ts:~n~ts", [freetds_config(), FreeTDS]), +init_mssql() -> + ODBCINST = io_lib:fwrite("[FreeTDS]~n" + "Driver = libtdsodbc.so~n", []), ?DEBUG("~ts:~n~ts", [odbcinst_config(), ODBCINST]), - ?DEBUG("~ts:~n~ts", [odbc_config(), ODBCINI]), - case filelib:ensure_dir(freetds_config()) of + case filelib:ensure_dir(odbcinst_config()) of ok -> try - ok = write_file_if_new(freetds_config(), FreeTDS), ok = write_file_if_new(odbcinst_config(), ODBCINST), - ok = write_file_if_new(odbc_config(), ODBCINI), os:putenv("ODBCSYSINI", tmp_dir()), - os:putenv("FREETDS", freetds_config()), - os:putenv("FREETDSCONF", freetds_config()), ok catch error:{badmatch, {error, Reason} = Err} -> ?ERROR_MSG("Failed to create temporary files in ~ts: ~ts", @@ -1214,12 +1184,6 @@ tmp_dir() -> _ -> filename:join(["/tmp", "ejabberd"]) end. -odbc_config() -> - filename:join(tmp_dir(), "odbc.ini"). - -freetds_config() -> - filename:join(tmp_dir(), "freetds.conf"). - odbcinst_config() -> filename:join(tmp_dir(), "odbcinst.ini"). diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index 026c3fab6..ee37c7e61 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -84,8 +84,6 @@ stop() -> ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 20). init([]) -> - file:delete(ejabberd_sql:freetds_config()), - file:delete(ejabberd_sql:odbc_config()), file:delete(ejabberd_sql:odbcinst_config()), ejabberd_hooks:add(host_up, ?MODULE, start, 20), ejabberd_hooks:add(host_down, ?MODULE, stop, 90), @@ -98,7 +96,7 @@ init([Host]) -> sqlite -> check_sqlite_db(Host); mssql -> - ejabberd_sql:init_mssql(Host); + ejabberd_sql:init_mssql(); _ -> ok end, diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index 347598e6d..92552cc8a 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -46,7 +46,8 @@ start_link(_, _, _) -> fail(). -else. -export([tcp_init/2, udp_init/2, udp_recv/5, start/3, - start_link/3, accept/1, listen_opt_type/1, listen_options/0]). + start_link/3, accept/1, listen_opt_type/1, listen_options/0, + get_password/2]). -include("logger.hrl"). @@ -74,6 +75,21 @@ start_link(_SockMod, Socket, Opts) -> accept(_Pid) -> ok. +get_password(User, Realm) -> + case ejabberd_hooks:run_fold(stun_get_password, <<>>, [User, Realm]) of + Password when byte_size(Password) > 0 -> + Password; + <<>> -> + case ejabberd_auth:get_password_s(User, Realm) of + Password when is_binary(Password) -> + Password; + _ -> + ?INFO_MSG("Cannot use hashed password of ~s@~s for " + "STUN/TURN authentication", [User, Realm]), + <<>> + end + end. + %%%=================================================================== %%% Internal functions %%%=================================================================== @@ -85,27 +101,36 @@ prepare_turn_opts(Opts, _UseTurn = false) -> set_certfile(Opts); prepare_turn_opts(Opts, _UseTurn = true) -> NumberOfMyHosts = length(ejabberd_option:hosts()), - case proplists:get_value(turn_ip, Opts) of - undefined -> - ?WARNING_MSG("Option 'turn_ip' is undefined, " - "most likely the TURN relay won't be working " - "properly", []); - _ -> - ok - end, - AuthFun = fun ejabberd_auth:get_password_s/2, + TurnIP = case proplists:get_value(turn_ip, Opts) of + undefined -> + MyIP = misc:get_my_ip(), + case MyIP of + {127, _, _, _} -> + ?WARNING_MSG("Option 'turn_ip' is undefined and " + "the server's hostname doesn't " + "resolve to a public IPv4 address, " + "most likely the TURN relay won't be " + "working properly", []); + _ -> + ok + end, + [{turn_ip, MyIP}]; + _ -> + [] + end, + AuthFun = fun ejabberd_stun:get_password/2, Shaper = proplists:get_value(shaper, Opts, none), AuthType = proplists:get_value(auth_type, Opts, user), Realm = case proplists:get_value(auth_realm, Opts) of undefined when AuthType == user -> if NumberOfMyHosts > 1 -> - ?WARNING_MSG("You have several virtual " - "hosts configured, but option " - "'auth_realm' is undefined and " - "'auth_type' is set to 'user', " - "most likely the TURN relay won't " - "be working properly. Using ~ts as " - "a fallback", [ejabberd_config:get_myname()]); + ?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, @@ -114,8 +139,8 @@ prepare_turn_opts(Opts, _UseTurn = true) -> [] end, MaxRate = ejabberd_shaper:get_max_rate(Shaper), - Opts1 = Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} | - lists:keydelete(shaper, 1, Opts)], + Opts1 = TurnIP ++ Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} | + lists:keydelete(shaper, 1, Opts)], set_certfile(Opts1). set_certfile(Opts) -> diff --git a/src/gen_mod.erl b/src/gen_mod.erl index d424f4b5c..fbcc6b8bc 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -86,7 +86,7 @@ start_link() -> end. init([]) -> - ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50), + ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 60), ejabberd_hooks:add(host_up, ?MODULE, start_modules, 40), ejabberd_hooks:add(host_down, ?MODULE, stop_modules, 70), ets:new(ejabberd_modules, @@ -97,7 +97,7 @@ init([]) -> -spec stop() -> ok. stop() -> - ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50), + ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 60), ejabberd_hooks:delete(host_up, ?MODULE, start_modules, 40), ejabberd_hooks:delete(host_down, ?MODULE, stop_modules, 70), stop_modules(), diff --git a/src/misc.erl b/src/misc.erl index 8476e7cbf..e589c2e22 100644 --- a/src/misc.erl +++ b/src/misc.erl @@ -40,8 +40,8 @@ read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1, 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, - parse_ip_mask/1, match_ip_mask/3, format_hosts_list/1, format_cycle/1, - delete_dir/1]). + get_my_ip/0, parse_ip_mask/1, match_ip_mask/3, format_hosts_list/1, + format_cycle/1, delete_dir/1]). %% Deprecated functions -export([decode_base64/1, encode_base64/1]). @@ -509,6 +509,14 @@ format_exception(Level, Class, Reason, Stacktrace) -> end). -endif. +-spec get_my_ip() -> inet:ip_address(). +get_my_ip() -> + {ok, MyHostName} = inet:gethostname(), + case inet:getaddr(MyHostName, inet) of + {ok, Addr} -> Addr; + {error, _} -> {127, 0, 0, 1} + end. + -spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} | {ok, {inet:ip6_address(), 0..128}} | error. diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 68af99630..040890fcf 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -288,5 +288,5 @@ mod_doc() -> [{report_commands_node, #{value => "true | false", desc => - ?T("Provide the Commands item in the Service Disvocery. " + ?T("Provide the Commands item in the Service Discovery. " "Default value: 'false'.")}}]}. diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 1dfe42c11..69d240e4a 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -1604,13 +1604,7 @@ mod_doc() -> "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. For example, if " - "this command is called: " - "'ejabberdctl vhost example.org ban-account boby \"Spammed rooms\"', " - "then the sessions of the local account which JID is " - "boby@example.org will be kicked, and its password will be set " - "to something like this: " - "'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"), + "the reason and the date and time of the ban. See an example below."), ?T("- 'pushroster' (and 'pushroster-all'):"), ?T("The roster file must be placed, if using Windows, on the " "directory where you installed ejabberd: " @@ -1620,8 +1614,7 @@ mod_doc() -> ?T("- 'srg-create':"), ?T("If you want to put a group Name with blankspaces, use the " "characters \"\' and \'\" to define when the Name starts and " - "ends. For example: " - "'ejabberdctl srg-create g1 example.org \"\'Group number 1\'\" this_is_g1 g1'")], + "ends. See an example below.")], opts => [{module_resource, #{value => ?T("Resource"), @@ -1647,4 +1640,11 @@ mod_doc() -> {?T("Content of roster file for 'pushroster' command:"), ["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},", "{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},", - "{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]}]}. + "{<<\"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 " + "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"]}]}. diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl index 10c7e619c..a6c56f968 100644 --- a/src/mod_avatar.erl +++ b/src/mod_avatar.erl @@ -481,12 +481,9 @@ mod_doc() -> "the list of supported formats is detected at compile time " "depending on the image libraries installed in the system."), example => - [{?T("In this example avatars in WebP format are " - "converted to JPEG, all other formats are " - "converted to PNG:"), ["convert:", " webp: jpg", - " default: png"]}]}}, + " default: png"]}}, {rate_limit, #{value => ?T("Number"), desc => diff --git a/src/mod_caps.erl b/src/mod_caps.erl index 5e8cc2eda..0b8f034cc 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -143,7 +143,19 @@ user_send_packet(Acc) -> -spec user_receive_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}. user_receive_packet({#presence{from = From, type = available} = Pkt, #{lserver := LServer, jid := To} = State}) -> - IsRemote = not ejabberd_router:is_my_host(From#jid.lserver), + IsRemote = case From#jid.lresource of + % Don't store caps for presences sent by our muc rooms + <<>> -> + try ejabberd_router:host_of_route(From#jid.lserver) of + MaybeMuc -> + not lists:member(From#jid.lserver, + gen_mod:get_module_opt_hosts(MaybeMuc, mod_muc)) + catch error:{unregistered_route, _} -> + true + end; + _ -> + not ejabberd_router:is_my_host(From#jid.lserver) + end, if IsRemote -> case read_caps(Pkt) of nothing -> ok; diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl index 287fce624..f1639569c 100644 --- a/src/mod_http_fileserver.erl +++ b/src/mod_http_fileserver.erl @@ -522,12 +522,11 @@ mod_doc() -> ?T("Specify mappings of extension to content type. " "There are several content types already defined. " "With this option you can add new definitions " - "or modify existing ones."), + "or modify existing ones. The default values are:"), example => - [{?T("The default value is shown in the example below:"), - ["content_types:"| + ["content_types:"| [" " ++ binary_to_list(E) ++ ": " ++ binary_to_list(T) - || {E, T} <- ?DEFAULT_CONTENT_TYPES]]}]}}, + || {E, T} <- ?DEFAULT_CONTENT_TYPES]]}}, {default_content_type, #{value => ?T("Type"), desc => diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 8e3d483b2..f9708ef59 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -511,14 +511,14 @@ make_rooms_page(Host, Lang, {Sort_direction, Sort_column}) -> fun(Room) -> ?XE(<<"tr">>, [?XC(<<"td">>, E) || E <- Room]) end, Rooms_prepared), - Titles = [<<"Jabber ID">>, - <<"# participants">>, - <<"Last message">>, - <<"Public">>, - <<"Persistent">>, - <<"Logging">>, - <<"Just created">>, - <<"Room title">>], + Titles = [?T("Jabber ID"), + ?T("# participants"), + ?T("Last message"), + ?T("Public"), + ?T("Persistent"), + ?T("Logging"), + ?T("Just created"), + ?T("Room title")], {Titles_TR, _} = lists:mapfoldl( fun(Title, Num_column) -> diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 7c4f8f7e4..1b859b993 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -1249,7 +1249,7 @@ mod_doc() -> "is considered offline if no session presence priority > 0 " "are currently open."), "", ?T("NOTE: 'ejabberdctl' has a command to " - "delete expired messages (see chapter" + "delete expired messages (see chapter " "https://docs.ejabberd.im/admin/guide/managing" "[Managing an ejabberd server] in online documentation.")], opts => @@ -1281,7 +1281,7 @@ mod_doc() -> {use_mam_for_storage, #{value => "true | false", desc => - ?T("This is an experimetal option. Enabling this option " + ?T("This is an experimental option. Enabling this option " "will make 'mod_offline' not use the former spool " "table for storing MucSub offline messages, but will " "use the archive table instead. This use of the archive " diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl index fe07ca72a..5dcb79944 100644 --- a/src/mod_proxy65_service.erl +++ b/src/mod_proxy65_service.erl @@ -266,19 +266,11 @@ get_streamhost(Host, ServerHost) -> get_endpoint(Host) -> Port = mod_proxy65_opt:port(Host), IP = case mod_proxy65_opt:ip(Host) of - undefined -> get_my_ip(); + undefined -> misc:get_my_ip(); Addr -> Addr end, {Port, IP, tcp}. --spec get_my_ip() -> inet:ip_address(). -get_my_ip() -> - {ok, MyHostName} = inet:gethostname(), - case inet:getaddr(MyHostName, inet) of - {ok, Addr} -> Addr; - {error, _} -> {127, 0, 0, 1} - end. - max_connections(ServerHost) -> mod_proxy65_opt:max_connections(ServerHost). diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 664dda699..356857d74 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -997,7 +997,7 @@ shared_roster_group(Host, Group, Query, Lang) -> [?XCT(<<"td">>, ?T("Label:")), ?XE(<<"td">>, [?INPUT(<<"text">>, <<"label">>, Label)]), - ?XE(<<"td">>, [?C(<<"Name in the rosters where this group will be displayed">>)])]), + ?XE(<<"td">>, [?CT(?T("Name in the rosters where this group will be displayed"))])]), ?XE(<<"tr">>, [?XCT(<<"td">>, ?T("Description:")), ?XE(<<"td">>, @@ -1005,7 +1005,7 @@ shared_roster_group(Host, Group, Query, Lang) -> integer_to_binary(lists:max([3, DescNL])), <<"20">>, Description)]), - ?XE(<<"td">>, [?C(<<"Only admins can see this">>)]) + ?XE(<<"td">>, [?CT(?T("Only admins can see this"))]) ]), ?XE(<<"tr">>, [?XCT(<<"td">>, ?T("Members:")), @@ -1023,7 +1023,7 @@ shared_roster_group(Host, Group, Query, Lang) -> integer_to_binary(lists:max([3, length(FDisplayedGroups)])), <<"20">>, list_to_binary(FDisplayedGroups))]), - ?XE(<<"td">>, [?C(<<"Groups that will be displayed to the members">>)]) + ?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">>)) diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl new file mode 100644 index 000000000..52ced9b28 --- /dev/null +++ b/src/mod_stun_disco.erl @@ -0,0 +1,670 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_stun_disco.erl +%%% Author : Holger Weiss <holger@zedat.fu-berlin.de> +%%% Purpose : External Service Discovery (XEP-0215) +%%% Created : 18 Apr 2020 by Holger Weiss <holger@zedat.fu-berlin.de> +%%% +%%% +%%% ejabberd, Copyright (C) 2020 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_stun_disco). +-author('holger@zedat.fu-berlin.de'). +-protocol({xep, 215, '0.7'}). + +-behaviour(gen_server). +-behaviour(gen_mod). + +%% gen_mod callbacks. +-export([start/2, + stop/1, + reload/3, + mod_opt_type/1, + mod_options/1, + depends/2]). +-export([mod_doc/0]). + +%% gen_server callbacks. +-export([init/1, + handle_call/3, + handle_cast/2, + handle_info/2, + terminate/2, + code_change/3]). + +%% ejabberd_hooks callbacks. +-export([disco_local_features/5, stun_get_password/3]). + +%% gen_iq_handler callback. +-export([process_iq/1]). + +-include("logger.hrl"). +-include("translate.hrl"). +-include("xmpp.hrl"). + +-define(STUN_MODULE, ejabberd_stun). + +-type host_or_hash() :: binary() | {hash, binary()}. +-type service_type() :: stun | stuns | turn | turns | undefined. + +-record(request, + {host :: binary() | inet:ip_address() | undefined, + port :: 0..65535 | undefined, + transport :: udp | tcp | undefined, + type :: service_type(), + restricted :: true | undefined}). + +-record(state, + {host :: binary(), + services :: [service()], + secret :: binary(), + ttl :: non_neg_integer()}). + +-type request() :: #request{}. +-type state() :: #state{}. + +%%-------------------------------------------------------------------- +%% gen_mod callbacks. +%%-------------------------------------------------------------------- +-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, any()}. +start(Host, Opts) -> + Proc = get_proc_name(Host), + gen_mod:start_child(?MODULE, Host, Opts, Proc). + +-spec stop(binary()) -> ok | {error, any()}. +stop(Host) -> + Proc = get_proc_name(Host), + gen_mod:stop_child(Proc). + +-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok. +reload(Host, NewOpts, OldOpts) -> + cast(Host, {reload, NewOpts, OldOpts}). + +-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. +depends(_Host, _Opts) -> + []. + +-spec mod_opt_type(atom()) -> econf:validator(). +mod_opt_type(access) -> + econf:acl(); +mod_opt_type(credentials_lifetime) -> + econf:timeout(second); +mod_opt_type(offer_local_services) -> + econf:bool(); +mod_opt_type(secret) -> + econf:binary(); +mod_opt_type(services) -> + econf:list( + econf:and_then( + econf:options( + #{host => econf:either(econf:ip(), econf:binary()), + port => econf:port(), + type => econf:enum([stun, turn, stuns, turns]), + transport => econf:enum([tcp, udp]), + restricted => econf:bool()}, + [{required, [host]}]), + fun(Opts) -> + DefPort = fun(stun) -> 3478; + (turn) -> 3478; + (stuns) -> 5349; + (turns) -> 5349 + end, + DefTrns = fun(stun) -> udp; + (turn) -> udp; + (stuns) -> tcp; + (turns) -> tcp + end, + DefRstr = fun(stun) -> false; + (turn) -> true; + (stuns) -> false; + (turns) -> true + end, + Host = proplists:get_value(host, Opts), + Type = proplists:get_value(type, Opts, stun), + Port = proplists:get_value(port, Opts, DefPort(Type)), + Trns = proplists:get_value(transport, Opts, DefTrns(Type)), + Rstr = proplists:get_value(restricted, Opts, DefRstr(Type)), + #service{host = Host, + port = Port, + type = Type, + transport = Trns, + restricted = Rstr} + end)). + +-spec mod_options(binary()) -> [{services, [tuple()]} | {atom(), any()}]. +mod_options(_Host) -> + [{access, local}, + {credentials_lifetime, timer:minutes(10)}, + {offer_local_services, true}, + {secret, undefined}, + {services, []}]. + +mod_doc() -> + #{desc => + ?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]."), + opts => + [{access, + #{value => ?T("AccessName"), + desc => + ?T("This option defines which access rule will be used to " + "control who is allowed to discover STUN/TURN services " + "and to request temporary credentials. The default value " + "is 'local'.")}}, + {credentials_lifetime, + #{value => "timeout()", + desc => + ?T("The lifetime of temporary credentails offered to " + "clients. If a lifetime longer than the default value of " + "'10' minutes is specified, it's strongly recommended to " + "also specify a 'secret' (see below).")}}, + {offer_local_services, + #{value => "true | false", + desc => + ?T("This option specifies whether local STUN/TURN services " + "configured as ejabberd listeners should be announced " + "automatically. Note that this will not include " + "TLS-enabled services, which must be configured manually " + "using the 'services' option (see below). For " + "non-anonymous TURN services, temporary credentials will " + "be offered to the client. The default value is " + "'true'.")}}, + {secret, + #{value => ?T("Text"), + desc => + ?T("The secret used for generating temporary credentials. If " + "this option isn't specified, a secret will be " + "auto-generated. However, a secret must be specified if " + "non-anonymous TURN services running on other ejabberd " + "nodes and/or external TURN 'services' are configured. " + "Also note that auto-generated secrets are lost when the " + "node is restarted, which invalidates any credentials " + "offered before the restart. Therefore, the " + "'credentials_lifetime' should not exceed a few minutes " + "if no 'secret' is specified.")}}, + {services, + #{value => "[Service, ...]", + example => + ["services:", + " -", + " host: 203.0.113.3", + " port: 3478", + " type: stun", + " transport: udp", + " restricted: false", + " -", + " host: 203.0.113.3", + " port: 3478", + " type: turn", + " transport: udp", + " restricted: true", + " -", + " host: 203.0.113.3", + " port: 3478", + " type: stun", + " transport: tcp", + " restricted: false", + " -", + " host: 203.0.113.3", + " port: 3478", + " type: turn", + " transport: tcp", + " restricted: true", + " -", + " host: server.example.com", + " port: 5349", + " type: stuns", + " transport: tcp", + " restricted: false", + " -", + " host: server.example.com", + " port: 5349", + " type: turns", + " transport: tcp", + " restricted: true"], + desc => + ?T("The list of services offered to clients. This list can " + "include STUN/TURN services running on any ejabberd node " + "and/or external services. However, if any listed TURN " + "service not running on the local ejabberd node requires " + "authentication, a 'secret' must be specified explicitly, " + "and must be shared with that service. This will only " + "work with ejabberd's built-in STUN/TURN server and with " + "external servers that support the same " + "https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00" + "[REST API For Access To TURN Services]. Unless the " + "'offer_local_services' is set to 'false', the explicitly " + "listed services will be offered in addition to those " + "announced automatically.")}, + [{host, + #{value => ?T("Host"), + desc => + ?T("The host name or IPv4 address the STUN/TURN service is " + "listening on. For non-TLS services, it's recommended " + "to specify an IPv4 address (to avoid additional DNS " + "lookup latency on the client side). For TLS services, " + "the host name (or possible IPv4 address) should match " + "the certificate. Specifying the 'host' option is " + "mandatory.")}}, + {port, + #{value => "1..65535", + desc => + ?T("The port number the STUN/TURN service is listening " + "on. The default port number is 3478 for non-TLS " + "services and 5349 for TLS services.")}}, + {type, + #{value => "stun | turn | stuns | turns", + desc => + ?T("The type of service. Must be 'stun' or 'turn' for " + "non-TLS services, 'stuns' or 'turns' for TLS services. " + "The default type is 'stun'.")}}, + {transport, + #{value => "tcp | udp", + desc => + ?T("The transport protocol supported by the service. The " + "default is 'udp' for non-TLS services and 'tcp' for " + "TLS services.")}}, + {restricted, + #{value => "true | false", + desc => + ?T("This option determines whether temporary credentials " + "for accessing the service are offered. The default is " + "'false' for STUN/STUNS services and 'true' for " + "TURN/TURNS services.")}}]}]}. + +%%-------------------------------------------------------------------- +%% gen_server callbacks. +%%-------------------------------------------------------------------- +-spec init(list()) -> {ok, state()}. +init([Host, Opts]) -> + process_flag(trap_exit, true), + Services = get_configured_services(Opts), + Secret = get_configured_secret(Opts), + TTL = get_configured_ttl(Opts), + register_iq_handlers(Host), + register_hooks(Host), + {ok, #state{host = Host, services = Services, secret = Secret, ttl = TTL}}. + +-spec handle_call(term(), {pid(), term()}, state()) + -> {reply, {turn_disco, [service()] | binary()}, state()} | + {noreply, state()}. +handle_call({get_services, JID, #request{host = ReqHost, + port = ReqPort, + type = ReqType, + transport = ReqTrns, + restricted = ReqRstr}}, _From, + #state{host = Host, + services = List0, + secret = Secret, + ttl = TTL} = State) -> + ?DEBUG("Getting STUN/TURN service list for ~ts", [jid:encode(JID)]), + Hash = <<(hash(jid:encode(JID)))/binary, (hash(Host))/binary>>, + List = lists:filtermap( + fun(#service{host = H, port = P, type = T, restricted = R}) + when (ReqHost /= undefined) and (H /= ReqHost); + (ReqPort /= undefined) and (P /= ReqPort); + (ReqType /= undefined) and (T /= ReqType); + (ReqTrns /= undefined) and (T /= ReqTrns); + (ReqRstr /= undefined) and (R /= ReqRstr) -> + false; + (#service{restricted = false}) -> + true; + (#service{restricted = true} = Service) -> + {true, add_credentials(Service, Hash, Secret, TTL)} + end, List0), + ?INFO_MSG("Offering STUN/TURN services to ~ts (~s)", + [jid:encode(JID), Hash]), + {reply, {turn_disco, List}, State}; +handle_call({get_password, Username}, _From, #state{secret = Secret} = State) -> + ?DEBUG("Getting STUN/TURN password for ~ts", [Username]), + Password = make_password(Username, Secret), + {reply, {turn_disco, Password}, 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({reload, NewOpts, _OldOpts}, #state{host = Host} = State) -> + ?DEBUG("Reloading STUN/TURN discovery configuration for ~ts", [Host]), + Services = get_configured_services(NewOpts), + Secret = get_configured_secret(NewOpts), + TTL = get_configured_ttl(NewOpts), + {noreply, State#state{services = Services, secret = Secret, ttl = TTL}}; +handle_cast(Request, State) -> + ?ERROR_MSG("Got unexpected request from: ~p", [Request]), + {noreply, State}. + +-spec handle_info(term(), state()) -> {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}) -> + ?DEBUG("Stopping STUN/TURN discovery process for ~ts: ~p", + [Host, Reason]), + unregister_hooks(Host), + unregister_iq_handlers(Host). + +-spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}. +code_change(_OldVsn, #state{host = Host} = State, _Extra) -> + ?DEBUG("Updating STUN/TURN discovery process for ~ts", [Host]), + {ok, State}. + +%%-------------------------------------------------------------------- +%% Register/unregister hooks. +%%-------------------------------------------------------------------- +-spec register_hooks(binary()) -> ok. +register_hooks(Host) -> + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, + disco_local_features, 50), + ejabberd_hooks:add(stun_get_password, ?MODULE, + stun_get_password, 50). + +-spec unregister_hooks(binary()) -> ok. +unregister_hooks(Host) -> + ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, + disco_local_features, 50), + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_hooks:delete(stun_get_password, ?MODULE, + stun_get_password, 50); + true -> + ok + end. + +%%-------------------------------------------------------------------- +%% Hook callbacks. +%%-------------------------------------------------------------------- +-spec disco_local_features(mod_disco:features_acc(), jid(), jid(), binary(), + binary()) -> mod_disco:features_acc(). +disco_local_features(empty, From, To, Node, Lang) -> + disco_local_features({result, []}, From, To, Node, Lang); +disco_local_features({result, OtherFeatures} = Acc, From, + #jid{lserver = LServer}, <<"">>, _Lang) -> + Access = mod_stun_disco_opt:access(LServer), + case acl:match_rule(LServer, Access, From) of + allow -> + ?DEBUG("Announcing feature to ~ts", [jid:encode(From)]), + {result, [?NS_EXTDISCO_2 | OtherFeatures]}; + deny -> + ?DEBUG("Not announcing feature to ~ts", [jid:encode(From)]), + Acc + end; +disco_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + +-spec stun_get_password(any(), binary(), binary()) + -> binary() | {stop, binary()}. +stun_get_password(<<>>, Username, _Realm) -> + case binary:split(Username, <<$:>>) of + [Expiration, <<_UserHash:8/binary, HostHash:8/binary>>] -> + try binary_to_integer(Expiration) of + ExpireTime -> + case erlang:system_time(second) of + Now when Now < ExpireTime -> + ?DEBUG("Looking up password for: ~ts", [Username]), + {stop, get_password(Username, HostHash)}; + Now when Now >= ExpireTime -> + ?INFO_MSG("Credentials expired: ~ts", [Username]), + {stop, <<>>} + end + catch _:badarg -> + ?DEBUG("Non-numeric expiration field: ~ts", [Username]), + <<>> + end; + _ -> + ?DEBUG("Not an ephemeral username: ~ts", [Username]), + <<>> + end; +stun_get_password(Acc, _Username, _Realm) -> + Acc. + +%%-------------------------------------------------------------------- +%% IQ handlers. +%%-------------------------------------------------------------------- +-spec register_iq_handlers(binary()) -> ok. +register_iq_handlers(Host) -> + gen_iq_handler:add_iq_handler(ejabberd_local, Host, + ?NS_EXTDISCO_2, ?MODULE, process_iq). + +-spec unregister_iq_handlers(binary()) -> ok. +unregister_iq_handlers(Host) -> + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_EXTDISCO_2). + +-spec process_iq(iq()) -> iq(). +process_iq(#iq{type = get, + sub_els = [#services{type = ReqType}]} = IQ) -> + Request = #request{type = ReqType}, + process_iq_get(IQ, Request); +process_iq(#iq{type = get, + sub_els = [#credentials{ + services = [#service{ + host = ReqHost, + port = ReqPort, + type = ReqType, + transport = ReqTrns, + name = <<>>, + username = <<>>, + password = <<>>, + expires = undefined, + restricted = undefined, + action = undefined, + xdata = undefined}]}]} = IQ) -> + % Accepting the 'transport' request attribute is an ejabberd extension. + Request = #request{host = ReqHost, + port = ReqPort, + type = ReqType, + transport = ReqTrns, + restricted = true}, + process_iq_get(IQ, Request); +process_iq(#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_iq(#iq{lang = Lang} = IQ) -> + Txt = ?T("No module is handling this query"), + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_iq_get(iq(), request()) -> iq(). +process_iq_get(#iq{from = From, to = #jid{lserver = Host}, lang = Lang} = IQ, + 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} -> + xmpp:make_iq_result(IQ, #services{list = Services}); + {error, timeout} -> % Has been logged already. + Txt = ?T("Service list retrieval timed out"), + Err = xmpp:err_internal_server_error(Txt, Lang), + xmpp:make_error(IQ, Err) + end; + deny -> + ?DEBUG("Won't perform external service discovery for ~ts", + [jid:encode(From)]), + Txt = ?T("Access denied by service policy"), + xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)) + end. + +%%-------------------------------------------------------------------- +%% Internal functions. +%%-------------------------------------------------------------------- +-spec get_configured_services(gen_mod:opts()) -> [service()]. +get_configured_services(Opts) -> + LocalServices = case mod_stun_disco_opt:offer_local_services(Opts) of + true -> + ?DEBUG("Discovering local services", []), + find_local_services(); + false -> + ?DEBUG("Won't discover local services", []), + [] + end, + dedup(LocalServices ++ mod_stun_disco_opt:services(Opts)). + +-spec get_configured_secret(gen_mod:opts()) -> binary(). +get_configured_secret(Opts) -> + case mod_stun_disco_opt:secret(Opts) of + undefined -> + ?DEBUG("Auto-generating secret", []), + new_secret(); + Secret -> + ?DEBUG("Using configured secret", []), + Secret + end. + +-spec get_configured_ttl(gen_mod:opts()) -> non_neg_integer(). +get_configured_ttl(Opts) -> + mod_stun_disco_opt:credentials_lifetime(Opts) div 1000. + +-spec new_secret() -> binary(). +new_secret() -> + p1_rand:bytes(20). + +-spec add_credentials(service(), binary(), binary(), non_neg_integer()) + -> service(). +add_credentials(Service, Hash, Secret, TTL) -> + ExpireAt = erlang:system_time(second) + TTL, + Username = make_username(ExpireAt, Hash), + Password = make_password(Username, Secret), + ?DEBUG("Created ephemeral credentials: ~s | ~s", [Username, Password]), + Service#service{username = Username, + password = Password, + expires = seconds_to_timestamp(ExpireAt)}. + +-spec make_username(non_neg_integer(), binary()) -> binary(). +make_username(ExpireAt, Hash) -> + <<(integer_to_binary(ExpireAt))/binary, $:, Hash/binary>>. + +-spec make_password(binary(), binary()) -> binary(). +make_password(Username, Secret) -> + base64:encode(crypto:hmac(sha, Secret, Username)). + +-spec get_password(binary(), binary()) -> binary(). +get_password(Username, HostHash) -> + try call({hash, HostHash}, {get_password, Username}) of + {turn_disco, Password} -> + Password + catch + exit:{timeout, _} -> + ?ERROR_MSG("Asking ~ts for password timed out", [HostHash]), + <<>>; + exit:{noproc, _} -> % Can be triggered by bogus Username. + ?DEBUG("Cannot retrieve password for ~ts", [Username]), + <<>> + end. + +-spec get_services(binary(), jid(), request()) + -> {ok, [service()]} | {error, timeout}. +get_services(Host, JID, Request) -> + try call(Host, {get_services, JID, Request}) of + {turn_disco, Services} -> + {ok, Services} + catch + exit:{timeout, _} -> + ?ERROR_MSG("Asking ~ts for services timed out", [Host]), + {error, timeout} + end. + +-spec find_local_services() -> [service()]. +find_local_services() -> + ParseListener = fun(Listener) -> parse_listener(Listener) end, + lists:flatmap(ParseListener, ejabberd_option:listen()). + +-spec parse_listener(ejabberd_listener:listener()) -> [service()]. +parse_listener({_EndPoint, ?STUN_MODULE, #{tls := true}}) -> + ?DEBUG("Ignoring TLS-enabled STUN/TURN listener", []), + []; % Avoid certificate hostname issues. +parse_listener({{Port, _Addr, Transport}, ?STUN_MODULE, Opts}) -> + case get_listener_ip(Opts) of + {127, _, _, _} = Addr -> + ?INFO_MSG("Won't auto-announce STUN/TURN service with loopback " + "address: ~s:~B (~s), please specify a public 'turn_ip'", + [misc:ip_to_list(Addr), Port, Transport]), + []; + Addr -> + StunService = #service{host = Addr, + port = Port, + transport = Transport, + restricted = false, + type = stun}, + case Opts of + #{use_turn := true} -> + ?DEBUG("Found STUN/TURN listener: ~s:~B (~s)", + [misc:ip_to_list(Addr), Port, Transport]), + [StunService, #service{host = Addr, + port = Port, + transport = Transport, + restricted = is_restricted(Opts), + type = turn}]; + #{use_turn := false} -> + ?DEBUG("Found STUN listener: ~s:~B (~s)", + [misc:ip_to_list(Addr), Port, Transport]), + [StunService] + end + end; +parse_listener({_EndPoint, Module, _Opts}) -> + ?DEBUG("Ignoring ~s listener", [Module]), + []. + +-spec get_listener_ip(map()) -> inet:ip_address(). +get_listener_ip(#{ip := { 0, 0, 0, 0}} = Opts) -> get_turn_ip(Opts); +get_listener_ip(#{ip := {127, _, _, _}} = Opts) -> get_turn_ip(Opts); +get_listener_ip(#{ip := { 10, _, _, _}} = Opts) -> get_turn_ip(Opts); +get_listener_ip(#{ip := {172, 16, _, _}} = Opts) -> get_turn_ip(Opts); +get_listener_ip(#{ip := {192, 168, _, _}} = Opts) -> get_turn_ip(Opts); +get_listener_ip(#{ip := IP}) -> IP. + +-spec get_turn_ip(map()) -> inet:ip_address(). +get_turn_ip(#{turn_ip := {_, _, _, _} = TurnIP}) -> TurnIP; +get_turn_ip(#{turn_ip := undefined}) -> misc:get_my_ip(). + +-spec is_restricted(map()) -> boolean(). +is_restricted(#{auth_type := user}) -> true; +is_restricted(#{auth_type := anonymous}) -> false. + +-spec call(host_or_hash(), term()) -> term(). +call(Host, Request) -> + Proc = get_proc_name(Host), + gen_server:call(Proc, Request, timer:seconds(15)). + +-spec cast(host_or_hash(), term()) -> ok. +cast(Host, Request) -> + Proc = get_proc_name(Host), + gen_server:cast(Proc, Request). + +-spec get_proc_name(host_or_hash()) -> atom(). +get_proc_name(Host) when is_binary(Host) -> + get_proc_name({hash, hash(Host)}); +get_proc_name({hash, HostHash}) -> + gen_mod:get_module_proc(HostHash, ?MODULE). + +-spec hash(binary()) -> binary(). +hash(Host) -> + str:to_hexlist(binary_part(crypto:hash(sha, Host), 0, 4)). + +-spec dedup(list()) -> list(). +dedup([]) -> []; +dedup([H | T]) -> [H | [E || E <- dedup(T), E /= H]]. + +-spec seconds_to_timestamp(non_neg_integer()) -> erlang:timestamp(). +seconds_to_timestamp(Seconds) -> + {Seconds div 1000000, Seconds rem 1000000, 0}. diff --git a/src/mod_stun_disco_opt.erl b/src/mod_stun_disco_opt.erl new file mode 100644 index 000000000..43b8102e6 --- /dev/null +++ b/src/mod_stun_disco_opt.erl @@ -0,0 +1,41 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_stun_disco_opt). + +-export([access/1]). +-export([credentials_lifetime/1]). +-export([offer_local_services/1]). +-export([secret/1]). +-export([services/1]). + +-spec access(gen_mod:opts() | global | binary()) -> 'local' | acl:acl(). +access(Opts) when is_map(Opts) -> + gen_mod:get_opt(access, Opts); +access(Host) -> + gen_mod:get_module_opt(Host, mod_stun_disco, access). + +-spec credentials_lifetime(gen_mod:opts() | global | binary()) -> pos_integer(). +credentials_lifetime(Opts) when is_map(Opts) -> + gen_mod:get_opt(credentials_lifetime, Opts); +credentials_lifetime(Host) -> + gen_mod:get_module_opt(Host, mod_stun_disco, credentials_lifetime). + +-spec offer_local_services(gen_mod:opts() | global | binary()) -> boolean(). +offer_local_services(Opts) when is_map(Opts) -> + gen_mod:get_opt(offer_local_services, Opts); +offer_local_services(Host) -> + gen_mod:get_module_opt(Host, mod_stun_disco, offer_local_services). + +-spec secret(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). +secret(Opts) when is_map(Opts) -> + gen_mod:get_opt(secret, Opts); +secret(Host) -> + gen_mod:get_module_opt(Host, mod_stun_disco, secret). + +-spec services(gen_mod:opts() | global | binary()) -> [tuple()]. +services(Opts) when is_map(Opts) -> + gen_mod:get_opt(services, Opts); +services(Host) -> + gen_mod:get_module_opt(Host, mod_stun_disco, services). + diff --git a/test/README b/test/README index de1a96aa2..68ff183dc 100644 --- a/test/README +++ b/test/README @@ -1,9 +1,12 @@ -You need MySQL, PostgreSQL and Redis up and running. +You need MySQL, MSSQL, PostgreSQL and Redis up and running. MySQL should be accepting TCP connections on localhost:3306. +MSSQL should be accepting TCP connections on localhost:1433. PostgreSQL should be accepting TCP connections on localhost:5432. Redis should be accepting TCP connections on localhost:6379. MySQL and PostgreSQL should grant full access to user 'ejabberd_test' with password 'ejabberd_test' on database 'ejabberd_test'. +MSSQL should grant full access to user 'ejabberd_test' with +password 'ejabberd_Test1' on database 'ejabberd_test'. Here is a quick setup example: @@ -24,3 +27,23 @@ 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 + +------------------- + MS SQL Server +------------------- +$ sqlcmd -U SA -P ejabberd_Test1 -S localhost +1> CREATE DATABASE ejabberd_test; +2> GO +1> USE ejabberd_test; +2> GO +Changed database context to 'ejabberd_test'. +1> CREATE LOGIN ejabberd_test WITH PASSWORD = 'ejabberd_Test1'; +2> GO +1> CREATE USER ejabberd_test FOR LOGIN ejabberd_test; +2> GO +1> GRANT ALL TO ejabberd_test; +2> GO +The ALL permission is deprecated and maintained only for compatibility. It DOES NOT imply ALL permissions defined on the entity. +1> GRANT CONTROL ON SCHEMA ::dbo TO ejabberd_test; +2> GO +$ sqlcmd -U ejabberd_test -P ejabberd_Test1 -S localhost -d ejabberd_test -i sql/mssql.sql diff --git a/test/docker/README.md b/test/docker/README.md index 22324a6e0..24c675cab 100644 --- a/test/docker/README.md +++ b/test/docker/README.md @@ -4,7 +4,7 @@ You can start the Docker environment with Docker Compose, from ejabberd repository root. -The following command will launch MySQL, PostgreSQL, Redis and keep the console +The following command will launch MySQL, MSSQL, PostgreSQL, Redis and keep the console attached to it. ``` @@ -15,6 +15,17 @@ mkdir test/docker/db/postgres/data You can stop all the databases with CTRL-C. +## Creating database for MSSQL + +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 cp test/docker/db/mssql/initdb/initdb_mssql.sql ejabberd-mssql:/ +docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql +docker cp sql/mssql.sql ejabberd-mssql:/ +docker exec ejabberd-mssql /opt/mssql-tools/bin/sqlcmd -U SA -P ejabberd_Test1 -S localhost -i /mssql.sql +``` + ## Running tests Before running the test, you can ensure there is no running instance of Erlang common test tool. You can run the following @@ -43,4 +54,5 @@ If you want to clean the data, you can remove the data directories after the `do ``` rm -rf test/docker/db/mysql/data rm -rf test/docker/db/postgres/data +docker volume rm docker_mssqldata ``` diff --git a/test/docker/db/mssql/initdb/initdb_mssql.sql b/test/docker/db/mssql/initdb/initdb_mssql.sql new file mode 100644 index 000000000..a9ec5a5a8 --- /dev/null +++ b/test/docker/db/mssql/initdb/initdb_mssql.sql @@ -0,0 +1,23 @@ +USE [master] +GO + +IF DB_ID('ejabberd_test') IS NOT NULL + set noexec on -- prevent creation when already exists + +CREATE DATABASE ejabberd_test; +GO + +USE ejabberd_test; +GO + +CREATE LOGIN ejabberd_test WITH PASSWORD = 'ejabberd_Test1'; +GO + +CREATE USER ejabberd_test FOR LOGIN ejabberd_test; +GO + +GRANT ALL TO ejabberd_test; +GO + +GRANT CONTROL ON SCHEMA ::dbo TO ejabberd_test; +GO diff --git a/test/docker/docker-compose.yml b/test/docker/docker-compose.yml index f98612036..c6b800dad 100644 --- a/test/docker/docker-compose.yml +++ b/test/docker/docker-compose.yml @@ -17,6 +17,18 @@ services: MYSQL_USER: ejabberd_test MYSQL_PASSWORD: ejabberd_test + mssql: + image: mcr.microsoft.com/mssql/server + container_name: ejabberd-mssql + volumes: + - mssqldata:/var/opt/mssql + restart: always + ports: + - 1433:1433 + environment: + ACCEPT_EULA: Y + SA_PASSWORD: ejabberd_Test1 + postgres: image: postgres:latest container_name: ejabberd-postgres @@ -35,3 +47,6 @@ services: container_name: ejabberd-redis ports: - 6379:6379 + +volumes: + mssqldata: diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 6658f6734..63cf864f6 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -77,12 +77,12 @@ init_per_group(Group, Config) -> do_init_per_group(Group, Config); Backends -> %% Skipped backends that were not explicitely enabled - case lists:member(Group, Backends) of - true -> - do_init_per_group(Group, Config); - false -> - {skip, {disabled_backend, Group}} - end + case lists:member(Group, Backends) of + true -> + do_init_per_group(Group, Config); + false -> + {skip, {disabled_backend, Group}} + end end end. @@ -104,6 +104,15 @@ do_init_per_group(mysql, Config) -> Err -> {skip, {mysql_not_available, Err}} end; +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)), + set_opt(server, ?MSSQL_VHOST, Config); + Err -> + {skip, {mssql_not_available, Err}} + end; do_init_per_group(pgsql, Config) -> case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of {selected, _, _} -> @@ -158,6 +167,8 @@ end_per_group(redis, _Config) -> ok; end_per_group(mysql, _Config) -> ok; +end_per_group(mssql, _Config) -> + ok; end_per_group(pgsql, _Config) -> ok; end_per_group(sqlite, _Config) -> @@ -361,6 +372,7 @@ no_db_tests() -> muc_tests:master_slave_cases(), proxy65_tests:single_cases(), proxy65_tests:master_slave_cases(), + stundisco_tests:single_cases(), replaced_tests:master_slave_cases(), upload_tests:single_cases(), carbons_tests:single_cases(), @@ -485,6 +497,7 @@ groups() -> {mnesia, [sequence], db_tests(mnesia)}, {redis, [sequence], db_tests(redis)}, {mysql, [sequence], db_tests(mysql)}, + {mssql, [sequence], db_tests(mssql)}, {pgsql, [sequence], db_tests(pgsql)}, {sqlite, [sequence], db_tests(sqlite)}]. @@ -494,6 +507,7 @@ all() -> {group, mnesia}, {group, redis}, {group, mysql}, + {group, mssql}, {group, pgsql}, {group, sqlite}, {group, extauth}, @@ -1012,6 +1026,14 @@ clear_sql_tables(Type, BaseDir) -> "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 -> diff --git a/test/ejabberd_SUITE_data/ejabberd.cfg b/test/ejabberd_SUITE_data/ejabberd.cfg index a92608b60..f071ffe8b 100644 --- a/test/ejabberd_SUITE_data/ejabberd.cfg +++ b/test/ejabberd_SUITE_data/ejabberd.cfg @@ -2,6 +2,7 @@ {hosts, ["localhost", "mnesia.localhost", "mysql.localhost", + "mssql.localhost", "pgsql.localhost", "sqlite.localhost", "extauth.localhost", @@ -102,6 +103,27 @@ {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}, diff --git a/test/ejabberd_SUITE_data/ejabberd.mssql.yml b/test/ejabberd_SUITE_data/ejabberd.mssql.yml new file mode 100644 index 000000000..1ecf77ba8 --- /dev/null +++ b/test/ejabberd_SUITE_data/ejabberd.mssql.yml @@ -0,0 +1,71 @@ +define_macro: + MSSQL_CONFIG: + sql_username: MSSQL_USER + sql_type: mssql + sql_server: MSSQL_SERVER + sql_port: MSSQL_PORT + sql_pool_size: 1 + sql_password: MSSQL_PASS + sql_database: MSSQL_DB + auth_method: sql + sm_db_type: sql + modules: + mod_announce: + db_type: sql + access: local + mod_blocking: [] + mod_caps: + db_type: sql + mod_last: + db_type: sql + mod_muc: + db_type: sql + ram_db_type: sql + vcard: VCARD + mod_offline: + use_cache: true + db_type: sql + mod_privacy: + db_type: sql + mod_private: + db_type: sql + mod_pubsub: + db_type: sql + access_createnode: pubsub_createnode + ignore_pep_from_offline: true + last_item_cache: false + plugins: + - "flat" + - "pep" + vcard: VCARD + mod_roster: + versioning: true + store_current_id: true + db_type: sql + mod_mam: + db_type: sql + mod_vcard: + db_type: sql + vcard: VCARD + mod_vcard_xupdate: [] + mod_adhoc: [] + mod_configure: [] + mod_disco: [] + mod_ping: [] + mod_proxy65: [] + mod_push: + db_type: sql + include_body: false + mod_push_keepalive: [] + mod_s2s_dialback: [] + mod_stream_mgmt: + resume_timeout: 3 + mod_legacy_auth: [] + mod_register: + welcome_message: + subject: "Welcome!" + body: "Hi. +Welcome to this XMPP server." + mod_stats: [] + mod_time: [] + mod_version: [] diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 922325609..8ae915e0b 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -4,6 +4,7 @@ include_config_file: - ejabberd.ldap.yml - ejabberd.mnesia.yml - ejabberd.mysql.yml + - ejabberd.mssql.yml - ejabberd.pgsql.yml - ejabberd.redis.yml - ejabberd.sqlite.yml @@ -12,6 +13,7 @@ host_config: pgsql.localhost: PGSQL_CONFIG sqlite.localhost: SQLITE_CONFIG mysql.localhost: MYSQL_CONFIG + mssql.localhost: MSSQL_CONFIG mnesia.localhost: MNESIA_CONFIG redis.localhost: REDIS_CONFIG ldap.localhost: LDAP_CONFIG @@ -26,6 +28,7 @@ hosts: - mnesia.localhost - redis.localhost - mysql.localhost + - mssql.localhost - pgsql.localhost - extauth.localhost - ldap.localhost @@ -91,6 +94,12 @@ listen: "/upload": mod_http_upload "/captcha": ejabberd_captcha - + port: STUN_PORT + module: ejabberd_stun + transport: udp + use_turn: true + turn_ip: "203.0.113.3" + - port: COMPONENT_PORT module: ejabberd_service password: PASSWORD @@ -124,6 +133,12 @@ Welcome to this XMPP server." mod_stream_mgmt: max_ack_queue: 10 resume_timeout: 3 + mod_stun_disco: + secret: "cryptic" + services: + - + host: "example.com" + type: turns mod_time: [] mod_version: [] mod_http_upload: diff --git a/test/ejabberd_SUITE_data/macros.yml b/test/ejabberd_SUITE_data/macros.yml index fdd467584..e4270d4c1 100644 --- a/test/ejabberd_SUITE_data/macros.yml +++ b/test/ejabberd_SUITE_data/macros.yml @@ -4,6 +4,7 @@ define_macro: C2S_PORT: @@c2s_port@@ S2S_PORT: @@s2s_port@@ WEB_PORT: @@web_port@@ + STUN_PORT: @@stun_port@@ COMPONENT_PORT: @@component_port@@ PROXY_PORT: @@proxy_port@@ PASSWORD: >- @@ -18,6 +19,11 @@ define_macro: MYSQL_PORT: @@mysql_port@@ MYSQL_PASS: "@@mysql_pass@@" MYSQL_DB: "@@mysql_db@@" + MSSQL_USER: "@@mssql_user@@" + MSSQL_SERVER: "@@mssql_server@@" + MSSQL_PORT: @@mssql_port@@ + MSSQL_PASS: "@@mssql_pass@@" + MSSQL_DB: "@@mssql_db@@" PGSQL_USER: "@@pgsql_user@@" PGSQL_SERVER: "@@pgsql_server@@" PGSQL_PORT: @@pgsql_port@@ diff --git a/test/stundisco_tests.erl b/test/stundisco_tests.erl new file mode 100644 index 000000000..8cb026dc0 --- /dev/null +++ b/test/stundisco_tests.erl @@ -0,0 +1,192 @@ +%%%------------------------------------------------------------------- +%%% Author : Holger Weiss <holger@zedat.fu-berlin.de> +%%% Created : 22 Apr 2020 by Holger Weiss <holger@zedat.fu-berlin.de> +%%% +%%% +%%% ejabberd, Copyright (C) 2020 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(stundisco_tests). + +%% API +-compile(export_all). +-import(suite, [send_recv/2, disconnect/1, is_feature_advertised/2, + server_jid/1]). + +-include("suite.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +%%%=================================================================== +%%% Single user tests +%%%=================================================================== +single_cases() -> + {stundisco_single, [sequence], + [single_test(feature_enabled), + single_test(stun_service), + single_test(turn_service), + single_test(turns_service), + single_test(turn_credentials), + single_test(turns_credentials)]}. + +feature_enabled(Config) -> + true = is_feature_advertised(Config, ?NS_EXTDISCO_2), + disconnect(Config). + +stun_service(Config) -> + ServerJID = server_jid(Config), + Host = {203, 0, 113, 3}, + Port = ct:get_config(stun_port, 3478), + Type = stun, + Transport = udp, + Request = #services{type = Type}, + #iq{type = result, + sub_els = [#services{ + type = undefined, + list = [#service{host = Host, + port = Port, + type = Type, + transport = Transport, + restricted = false, + username = <<>>, + password = <<>>, + expires = undefined, + action = undefined, + xdata = undefined}]}]} = + send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), + disconnect(Config). + +turn_service(Config) -> + ServerJID = server_jid(Config), + Host = {203, 0, 113, 3}, + Port = ct:get_config(stun_port, 3478), + Type = turn, + Transport = udp, + Request = #services{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}]}]} = + send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), + true = check_password(Username, Password), + true = check_expires(Expires), + disconnect(Config). + +turns_service(Config) -> + ServerJID = server_jid(Config), + Host = <<"example.com">>, + Port = 5349, + Type = turns, + Transport = tcp, + Request = #services{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}]}]} = + send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), + true = check_password(Username, Password), + true = check_expires(Expires), + disconnect(Config). + +turn_credentials(Config) -> + ServerJID = server_jid(Config), + Host = {203, 0, 113, 3}, + Port = ct:get_config(stun_port, 3478), + Type = turn, + Transport = udp, + Request = #credentials{services = [#service{host = Host, + 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}]}]} = + send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), + true = check_password(Username, Password), + true = check_expires(Expires), + disconnect(Config). + +turns_credentials(Config) -> + ServerJID = server_jid(Config), + Host = <<"example.com">>, + Port = 5349, + Type = turns, + Transport = tcp, + Request = #credentials{services = [#service{host = Host, + 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}]}]} = + send_recv(Config, #iq{type = get, to = ServerJID, sub_els = [Request]}), + true = check_password(Username, Password), + true = check_expires(Expires), + disconnect(Config). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +single_test(T) -> + list_to_atom("stundisco_" ++ atom_to_list(T)). + +check_password(Username, Password) -> + Secret = <<"cryptic">>, + Password == base64:encode(crypto:hmac(sha, Secret, Username)). + +check_expires({_, _, _} = Expires) -> + Now = {MegaSecs, Secs, MicroSecs} = erlang:timestamp(), + Later = {MegaSecs + 1, Secs, MicroSecs}, + (Expires > Now) and (Expires < Later). diff --git a/test/suite.erl b/test/suite.erl index 78732d00b..883d3a3eb 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -60,6 +60,7 @@ init_config(Config) -> {loglevel, 4}, {new_schema, false}, {s2s_port, 5269}, + {stun_port, 3478}, {component_port, 5270}, {web_port, 5280}, {proxy_port, 7777}, @@ -69,6 +70,11 @@ init_config(Config) -> {mysql_db, <<"ejabberd_test">>}, {mysql_user, <<"ejabberd_test">>}, {mysql_pass, <<"ejabberd_test">>}, + {mssql_server, <<"localhost">>}, + {mssql_port, 1433}, + {mssql_db, <<"ejabberd_test">>}, + {mssql_user, <<"ejabberd_test">>}, + {mssql_pass, <<"ejabberd_Test1">>}, {pgsql_server, <<"localhost">>}, {pgsql_port, 5432}, {pgsql_db, <<"ejabberd_test">>}, @@ -132,25 +138,26 @@ init_config(Config) -> copy_backend_configs(DataDir, CWD, Backends) -> Files = filelib:wildcard(filename:join([DataDir, "ejabberd.*.yml"])), lists:foreach( - fun(Src) -> - File = filename:basename(Src), - case string:tokens(File, ".") of - ["ejabberd", SBackend, "yml"] -> - Backend = list_to_atom(SBackend), - Macro = list_to_atom(string:to_upper(SBackend) ++ "_CONFIG"), - Dst = filename:join([CWD, File]), - case lists:member(Backend, Backends) of - true -> - {ok, _} = file:copy(Src, Dst); - false -> - ok = file:write_file( - Dst, fast_yaml:encode( - [{define_macro, [{Macro, []}]}])) - end; - _ -> - ok - end - end, Files). + fun(Src) -> + io:format("copying ~p", [Src]), + File = filename:basename(Src), + case string:tokens(File, ".") of + ["ejabberd", SBackend, "yml"] -> + Backend = list_to_atom(SBackend), + Macro = list_to_atom(string:to_upper(SBackend) ++ "_CONFIG"), + Dst = filename:join([CWD, File]), + case lists:member(Backend, Backends) of + true -> + {ok, _} = file:copy(Src, Dst); + false -> + ok = file:write_file( + Dst, fast_yaml:encode( + [{define_macro, [{Macro, []}]}])) + end; + _ -> + ok + end + end, Files). find_top_dir(Dir) -> case file:read_file_info(filename:join([Dir, ebin])) of diff --git a/test/suite.hrl b/test/suite.hrl index 194c48eb5..b3bab6c12 100644 --- a/test/suite.hrl +++ b/test/suite.hrl @@ -92,6 +92,7 @@ -define(MNESIA_VHOST, <<"mnesia.localhost">>). -define(REDIS_VHOST, <<"redis.localhost">>). -define(MYSQL_VHOST, <<"mysql.localhost">>). +-define(MSSQL_VHOST, <<"mssql.localhost">>). -define(PGSQL_VHOST, <<"pgsql.localhost">>). -define(SQLITE_VHOST, <<"sqlite.localhost">>). -define(LDAP_VHOST, <<"ldap.localhost">>). @@ -99,7 +100,7 @@ -define(S2S_VHOST, <<"s2s.localhost">>). -define(UPLOAD_VHOST, <<"upload.localhost">>). --define(BACKENDS, [mnesia, redis, mysql, pgsql, sqlite, ldap, extauth]). +-define(BACKENDS, [mnesia, redis, mysql, mssql, odbc, pgsql, sqlite, ldap, extauth]). insert(Val, N, Tuple) -> L = tuple_to_list(Tuple), |