diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..b90b54f50 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Version NEXT + +* + +# Version 18.12 + +* MAM data store compression +* Proxy protocol support (http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) +* MUC Self-Ping optimization (XEP-0410) +* Bookmarks conversion (XEP-0411) diff --git a/config/ejabberd.yml b/config/ejabberd.yml deleted file mode 100644 index d49377128..000000000 --- a/config/ejabberd.yml +++ /dev/null @@ -1,665 +0,0 @@ -### -### ejabberd configuration file -### -### - -### The parameters used in this configuration file are explained in more detail -### in the ejabberd Installation and Operation Guide. -### Please consult the Guide in case of doubts, it is included with -### your copy of ejabberd, and is also available online at -### http://www.process-one.net/en/ejabberd/docs/ - -### The configuration file is written in YAML. -### Refer to http://en.wikipedia.org/wiki/YAML for the brief description. -### However, ejabberd treats different literals as different types: -### -### - unquoted or single-quoted strings. They are called "atoms". -### Example: dog, 'Jupiter', '3.14159', YELLOW -### -### - numeric literals. Example: 3, -45.0, .0 -### -### - quoted or folded strings. -### Examples of quoted string: "Lizzard", "orange". -### Example of folded string: -### > Art thou not Romeo, -### and a Montague? - -### ======= -### LOGGING - -## -## loglevel: Verbosity of log files generated by ejabberd. -## 0: No ejabberd log at all (not recommended) -## 1: Critical -## 2: Error -## 3: Warning -## 4: Info -## 5: Debug -## -loglevel: 4 - -## -## rotation: Describe how to rotate logs. Either size and/or date can trigger -## log rotation. Setting count to N keeps N rotated logs. Setting count to 0 -## does not disable rotation, it instead rotates the file and keeps no previous -## versions around. Setting size to X rotate log when it reaches X bytes. -## To disable rotation set the size to 0 and the date to "" -## Date syntax is taken from the syntax newsyslog uses in newsyslog.conf. -## Some examples: -## $D0 rotate every night at midnight -## $D23 rotate every day at 23:00 hr -## $W0D23 rotate every week on Sunday at 23:00 hr -## $W5D16 rotate every week on Friday at 16:00 hr -## $M1D0 rotate on the first day of every month at midnight -## $M5D6 rotate on every 5th day of the month at 6:00 hr -## -log_rotate_size: 10485760 -log_rotate_date: "" -log_rotate_count: 1 - -## -## overload protection: If you want to limit the number of messages per second -## allowed from error_logger, which is a good idea if you want to avoid a flood -## of messages when system is overloaded, you can set a limit. -## 100 is ejabberd's default. -log_rate_limit: 100 - -## -## watchdog_admins: Only useful for developers: if an ejabberd process -## consumes a lot of memory, send live notifications to these XMPP -## accounts. -## -## watchdog_admins: -## - "bob@example.com" - - -### ================ -### SERVED HOSTNAMES - -## -## hosts: Domains served by ejabberd. -## You can define one or several, for example: -## hosts: -## - "example.net" -## - "example.com" -## - "example.org" -## -hosts: - - "localhost" - -## -## route_subdomains: Delegate subdomains to other XMPP servers. -## For example, if this ejabberd serves example.org and you want -## to allow communication with an XMPP server called im.example.org. -## -## route_subdomains: s2s - -### =============== -### LISTENING PORTS - -## -## listen: The ports ejabberd will listen on, which service each is handled -## by and what options to start it with. -## -listen: - - - port: 5222 - module: ejabberd_c2s - ## - ## If TLS is compiled in and you installed a SSL - ## certificate, specify the full path to the - ## file and uncomment these lines: - ## - ## certfile: "/path/to/ssl.pem" - ## starttls: true - ## - ## To enforce TLS encryption for client connections, - ## use this instead of the "starttls" option: - ## - ## starttls_required: true - ## - ## Custom OpenSSL options - ## - ## protocol_options: - ## - "no_sslv3" - ## - "no_tlsv1" - max_stanza_size: 65536 - shaper: c2s_shaper - access: c2s - - - port: 5269 - module: ejabberd_s2s_in - ## - ## ejabberd_service: Interact with external components (transports, ...) - ## - ## - - ## port: 8888 - ## module: ejabberd_service - ## access: all - ## shaper_rule: fast - ## ip: "127.0.0.1" - ## hosts: - ## "icq.example.org": - ## password: "secret" - ## "sms.example.org": - ## password: "secret" - - ## - ## ejabberd_stun: Handles STUN Binding requests - ## - ## - - ## port: 3478 - ## transport: udp - ## module: ejabberd_stun - - ## - ## To handle XML-RPC requests that provide admin credentials: - ## - ## - - ## port: 4560 - ## module: ejabberd_xmlrpc - - - port: 5280 - module: ejabberd_http - ## request_handlers: - ## "/pub/archive": mod_http_fileserver - web_admin: true - http_bind: true - ## register: true - captcha: true - -## -## s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. -## Allowed values are: false optional required required_trusted -## You must specify a certificate file. -## -## s2s_use_starttls: optional - -## -## s2s_certfile: Specify a certificate file. -## -## s2s_certfile: "/path/to/ssl.pem" - -## Custom OpenSSL options -## -## s2s_protocol_options: -## - "no_sslv3" -## - "no_tlsv1" - -## -## domain_certfile: Specify a different certificate for each served hostname. -## -## host_config: -## "example.org": -## domain_certfile: "/path/to/example_org.pem" -## "example.com": -## domain_certfile: "/path/to/example_com.pem" - -## -## S2S whitelist or blacklist -## -## Default s2s policy for undefined hosts. -## -## s2s_access: s2s - -## -## Outgoing S2S options -## -## Preferred address families (which to try first) and connect timeout -## in milliseconds. -## -## outgoing_s2s_families: -## - ipv4 -## - ipv6 -## outgoing_s2s_timeout: 10000 - -### ============== -### AUTHENTICATION - -## -## auth_method: Method used to authenticate the users. -## The default method is the internal. -## If you want to use a different method, -## comment this line and enable the correct ones. -## -auth_method: internal - -## -## Store the plain passwords or hashed for SCRAM: -## auth_password_format: plain -## auth_password_format: scram -## -## Define the FQDN if ejabberd doesn't detect it: -## fqdn: "server3.example.com" - -## -## Authentication using external script -## Make sure the script is executable by ejabberd. -## -## auth_method: external -## extauth_program: "/path/to/authentication/script" - -## -## Authentication using ODBC -## Remember to setup a database in the next section. -## -## auth_method: odbc - -## -## Authentication using PAM -## -## auth_method: pam -## pam_service: "pamservicename" - -## -## Authentication using LDAP -## -## auth_method: ldap -## -## List of LDAP servers: -## ldap_servers: -## - "localhost" -## -## Encryption of connection to LDAP servers: -## ldap_encrypt: none -## ldap_encrypt: tls -## -## Port to connect to on LDAP servers: -## ldap_port: 389 -## ldap_port: 636 -## -## LDAP manager: -## ldap_rootdn: "dc=example,dc=com" -## -## Password of LDAP manager: -## ldap_password: "******" -## -## Search base of LDAP directory: -## ldap_base: "dc=example,dc=com" -## -## LDAP attribute that holds user ID: -## ldap_uids: -## - "mail": "%u@mail.example.org" -## -## LDAP filter: -## ldap_filter: "(objectClass=shadowAccount)" - -## -## Anonymous login support: -## auth_method: anonymous -## anonymous_protocol: sasl_anon | login_anon | both -## allow_multiple_connections: true | false -## -## host_config: -## "public.example.org": -## auth_method: anonymous -## allow_multiple_connections: false -## anonymous_protocol: sasl_anon -## -## To use both anonymous and internal authentication: -## -## host_config: -## "public.example.org": -## auth_method: -## - internal -## - anonymous - -### ============== -### DATABASE SETUP - -## ejabberd by default uses the internal Mnesia database, -## so you do not necessarily need this section. -## This section provides configuration examples in case -## you want to use other database backends. -## Please consult the ejabberd Guide for details on database creation. - -## -## MySQL server: -## -## odbc_type: mysql -## odbc_server: "server" -## odbc_database: "database" -## odbc_username: "username" -## odbc_password: "password" -## -## If you want to specify the port: -## odbc_port: 1234 - -## -## PostgreSQL server: -## -## odbc_type: pgsql -## odbc_server: "server" -## odbc_database: "database" -## odbc_username: "username" -## odbc_password: "password" -## -## If you want to specify the port: -## odbc_port: 1234 -## -## If you use PostgreSQL, have a large database, and need a -## faster but inexact replacement for "select count(*) from users" -## -## pgsql_users_number_estimate: true - -## -## ODBC compatible or MSSQL server: -## -## odbc_type: odbc -## odbc_server: "DSN=ejabberd;UID=ejabberd;PWD=ejabberd" - -## -## Number of connections to open to the database for each virtual host -## -## odbc_pool_size: 10 - -## -## Interval to make a dummy SQL request to keep the connections to the -## database alive. Specify in seconds: for example 28800 means 8 hours -## -## odbc_keepalive_interval: undefined - -### =============== -### TRAFFIC SHAPERS - -shaper: - ## - ## The "normal" shaper limits traffic speed to 1000 B/s - ## - normal: 1000 - - ## - ## The "fast" shaper limits traffic speed to 50000 B/s - ## - fast: 50000 - -## -## This option specifies the maximum number of elements in the queue -## of the FSM. Refer to the documentation for details. -## -max_fsm_queue: 1000 - -###. ==================== -###' ACCESS CONTROL LISTS -acl: - ## - ## The 'admin' ACL grants administrative privileges to XMPP accounts. - ## You can put here as many accounts as you want. - ## - ## admin: - ## user: - ## - "aleksey": "localhost" - ## - "ermine": "example.org" - ## - ## Blocked users - ## - ## blocked: - ## user: - ## - "baduser": "example.org" - ## - "test" - - ## Local users: don't modify this. - ## - local: - user_regexp: "" - - ## - ## More examples of ACLs - ## - ## jabberorg: - ## server: - ## - "jabber.org" - ## aleksey: - ## user: - ## - "aleksey": "jabber.ru" - ## test: - ## user_regexp: "^test" - ## user_glob: "test*" - - ## - ## Loopback network - ## - loopback: - ip: - - "127.0.0.0/8" - - ## - ## Bad XMPP servers - ## - ## bad_servers: - ## server: - ## - "xmpp.zombie.org" - ## - "xmpp.spam.com" - -## -## Define specific ACLs in a virtual host. -## -## host_config: -## "localhost": -## acl: -## admin: -## user: -## - "bob-local": "localhost" - -### ============ -### ACCESS RULES -access: - ## Maximum number of simultaneous sessions allowed for a single user: - max_user_sessions: - all: 10 - ## Maximum number of offline messages that users can have: - max_user_offline_messages: - admin: 5000 - all: 100 - ## This rule allows access only for local users: - local: - local: allow - ## Only non-blocked users can use c2s connections: - c2s: - blocked: deny - all: allow - ## For C2S connections, all users except admins use the "normal" shaper - c2s_shaper: - admin: none - all: normal - ## All S2S connections use the "fast" shaper - s2s_shaper: - all: fast - ## Only admins can send announcement messages: - announce: - admin: allow - ## Only admins can use the configuration interface: - configure: - admin: allow - ## Admins of this server are also admins of the MUC service: - muc_admin: - admin: allow - ## Only accounts of the local ejabberd server can create rooms: - muc_create: - local: allow - ## All users are allowed to use the MUC service: - muc: - all: allow - ## Only accounts on the local ejabberd server can create Pubsub nodes: - pubsub_createnode: - local: allow - ## In-band registration allows registration of any possible username. - ## To disable in-band registration, replace 'allow' with 'deny'. - register: - all: allow - ## Only allow to register from localhost - trusted_network: - loopback: allow - ## Do not establish S2S connections with bad servers - ## s2s: - ## bad_servers: deny - ## all: allow - -## By default the frequency of account registrations from the same IP -## is limited to 1 account every 10 minutes. To disable, specify: infinity -## registration_timeout: 600 - -## -## Define specific Access Rules in a virtual host. -## -## host_config: -## "localhost": -## access: -## c2s: -## admin: allow -## all: deny -## register: -## all: deny - -### ================ -### DEFAULT LANGUAGE - -## -## language: Default language used for server messages. -## -language: "en" - -## -## Set a different default language in a virtual host. -## -## host_config: -## "localhost": -## language: "ru" - -### ======= -### CAPTCHA - -## -## Full path to a script that generates the image. -## -## captcha_cmd: "/lib/ejabberd/priv/bin/captcha.sh" - -## -## Host for the URL and port where ejabberd listens for CAPTCHA requests. -## -## captcha_host: "example.org:5280" - -## -## Limit CAPTCHA calls per minute for JID/IP to avoid DoS. -## -## captcha_limit: 5 - -### ======= -### MODULES - -## -## Modules enabled in all ejabberd virtual hosts. -## -modules: - mod_adhoc: {} - ## mod_admin_extra: {} - mod_announce: # recommends mod_adhoc - access: announce - mod_blocking: {} # requires mod_privacy - mod_caps: {} - mod_carboncopy: {} - mod_client_state: - queue_chat_states: true - queue_presence: false - mod_configure: {} # requires mod_adhoc - mod_disco: {} - ## mod_echo: {} - mod_http_bind: {} - ## mod_http_fileserver: - ## docroot: "/var/www" - ## accesslog: "/var/log/ejabberd/access.log" - mod_last: {} - mod_muc: - ## host: "conference.@HOST@" - access: muc - access_create: muc_create - access_persistent: muc_create - access_admin: muc_admin - ## mod_muc_log: {} - mod_offline: - access_max_user_messages: max_user_offline_messages - mod_ping: {} - ## mod_pres_counter: - ## count: 5 - ## interval: 60 - mod_privacy: {} - mod_private: {} - ## mod_proxy65: {} - mod_pubsub: - access_createnode: pubsub_createnode - ## reduces resource consumption, but XEP incompliant - ignore_pep_from_offline: true - ## XEP compliant, but increases resource consumption - ## ignore_pep_from_offline: false - last_item_cache: false - plugins: - - "flat" - - "hometree" - - "pep" # pep requires mod_caps - mod_register: - ## - ## Protect In-Band account registrations with CAPTCHA. - ## - ## captcha_protected: true - - ## - ## Set the minimum informational entropy for passwords. - ## - ## password_strength: 32 - - ## - ## After successful registration, the user receives - ## a message with this subject and body. - ## - welcome_message: - subject: "Welcome!" - body: |- - Hi. - Welcome to this XMPP server. - - ## - ## When a user registers, send a notification to - ## these XMPP accounts. - ## - ## registration_watchers: - ## - "admin1@example.org" - - ## - ## Only clients in the server machine can register accounts - ## - ip_access: trusted_network - - ## - ## Local c2s or remote s2s users cannot register accounts - ## - ## access_from: deny - - access: register - mod_roster: {} - mod_shared_roster: {} - mod_stats: {} - mod_time: {} - mod_vcard: {} - mod_version: {} - -## -## Enable modules with custom options in a specific virtual host -## -## host_config: -## "localhost": -## modules: -## mod_echo: -## host: "mirror.localhost" - -## -## Enable modules management via ejabberdctl for installation and -## uninstallation of public/private contributed modules -## (enabled by default) -## - -allow_contrib_modules: true - -### Local Variables: -### mode: yaml -### End: -### vim: set filetype=yaml tabstop=8 diff --git a/configure.ac b/configure.ac index f83c08024..d23bf9b6b 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.53) AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd]) -REQUIRE_ERLANG_MIN="6.4 (Erlang/OTP 17.5)" +REQUIRE_ERLANG_MIN="8.0 (Erlang/OTP 19.0)" REQUIRE_ERLANG_MAX="100.0.0 (No Max)" AC_CONFIG_MACRO_DIR([m4]) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index a032081a3..66d65450c 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -39,6 +39,24 @@ certfiles: - "/etc/letsencrypt/live/localhost/fullchain.pem" - "/etc/letsencrypt/live/localhost/privkey.pem" +define_macro: + # TLS options for client not being able to use modern ciphers (Windows XP+, Android 3.0+) + CIPHERS_INTERMEDIATE: "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS" + PROTOCOL_OPTIONS_INTERMEDIATE: + - "no_sslv2" + - "no_sslv3" + + # TLS options for client able to use moder ciphers (Windows 7+, Android 5.0+) + CIPHERS_MODERN: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" + PROTOCOL_OPTIONS_MODERN: + - "no_sslv2" + - "no_sslv3" + - "no_tlsv1" + - "no_tlsv1_1" + +c2s_ciphers: CIPHERS_INTERMEDIATE +c2s_protocol_options: PROTOCOL_OPTIONS_INTERMEDIATE + listen: - port: 5222 @@ -64,6 +82,8 @@ listen: "/ws": ejabberd_http_ws web_admin: true captcha: true + ciphers: CIPHERS_INTERMEDIATE + protocol_options: PROTOCOL_OPTIONS_INTERMEDIATE tls: true s2s_use_starttls: optional @@ -75,7 +95,6 @@ acl: ip: - "127.0.0.0/8" - "::1/128" - - "::FFFF:127.0.0.1/128" access_rules: local: diff --git a/mix.exs b/mix.exs index 2e136884d..176e229f6 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do def project do [app: :ejabberd, - version: "18.9.0", + version: "18.12.0", description: description(), elixir: "~> 1.4", elixirc_paths: ["lib"], @@ -73,7 +73,7 @@ defmodule Ejabberd.Mixfile do {:jiffy, "~> 0.14.7"}, {:p1_oauth2, "~> 0.6.1"}, {:distillery, "~> 1.0"}, - {:pkix, github: "processone/pkix"}, + {:pkix, "~> 1.0"}, {:ex_doc, ">= 0.0.0", only: :dev}, {:eimp, "~> 1.0"}, {:base64url, "~> 0.0.1"}, diff --git a/mix.lock b/mix.lock index e971cd3f8..c97ae37d7 100644 --- a/mix.lock +++ b/mix.lock @@ -1,15 +1,15 @@ %{ "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, - "cache_tab": {:hex, :cache_tab, "1.0.16", "0223820e5071d3252b9921db9dcc74a09ec8a660120271fdb47c3c40b6b52bee", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "distillery": {:hex, :distillery, "1.5.3", "b2f4fc34ec71ab4f1202a796f9290e068883b042319aa8c9aa45377ecac8597a", [:mix], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"}, + "cache_tab": {:hex, :cache_tab, "1.0.17", "0020e1036d7074d83a71be28b329ceb3e7f9d41cc5a8529b06c32ce4d8ee4995", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, + "distillery": {:hex, :distillery, "1.5.5", "c6132d5c0152bdce6850fb6c942d58f1971b169b6965d908dc4e8767cfa51a95", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm"}, "eimp": {:hex, :eimp, "1.0.9", "daf0d2904be3ef5e4128d946e158113cdb4d52555023746d29b83ce86b531f3c", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "epam": {:hex, :epam, "1.0.4", "2a5e40cbf9b2cf41df515782894c3b33c81b8ad32fff2fc847c3f725071dfaed", [:rebar3], [], "hexpm"}, "eredis": {:hex, :eredis, "1.1.0", "8d8d74496f35216679b97726b75fb1c8715e99dd7f3ef9f9824a2264c3e0aac0", [:rebar3], [], "hexpm"}, - "esip": {:hex, :esip, "1.0.26", "b50c92f8ac3e8e8ba901f0a6cc7e0e47fdc832b0f3044eddb6032ca26845cf97", [:rebar3], [{:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.25", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.18.3", "f4b0e4a2ec6f333dccf761838a4b253d75e11f714b85ae271c9ae361367897b7", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "esip": {:hex, :esip, "1.0.27", "13a94542b659a9b3e4e013aedaf2f6a92de53d35945902d693657a67c6955b83", [:rebar3], [{:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.26", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ezlib": {:hex, :ezlib, "1.0.4", "2434e4bb549cb060d5ac02261ba48fbe1a69b2ae4e1bf7485a3b27b3f3ec618d", [:rebar3], [], "hexpm"}, - "fast_tls": {:hex, :fast_tls, "1.0.25", "cbf875fe709d6fd03d3266c920bfe15f4d22736535d73421300cebf9d86bd851", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, + "fast_tls": {:hex, :fast_tls, "1.0.26", "38d78859ca56e8600aca3ef73137582a279a280d71f7581c64a1eddbde38accb", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "fast_xml": {:hex, :fast_xml, "1.1.34", "d76fc639d3607a44c4f0fb2dfdee1067b6c37b02b39706d8f067cb77eb2f6016", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "fast_yaml": {:hex, :fast_yaml, "1.0.17", "e945ef64e0cb7c311c7b42804dbe32a24e13a2afc0ffe249b7e0f9f9ac08e176", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm"}, @@ -19,18 +19,21 @@ "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, "lager": {:hex, :lager, "3.6.7", "2fbf823944caa0fc10df5ec13f3f047524a249bb32f0d801b7900c9610264286", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm"}, "luerl": {:hex, :luerl, "0.3.1", "5412807630aac1aaf59ffe5a1bc09259c447b4faeb1d3fe2d4ef41b87676cb04", [:rebar3], [], "hexpm"}, - "meck": {:hex, :meck, "0.8.10", "455aaef8403be46752272206613e7a15467c014d40994b22fb54cde4d1ff7075", [:rebar3], [], "hexpm"}, + "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "meck": {:hex, :meck, "0.8.12", "1f7b1a9f5d12c511848fec26bbefd09a21e1432eadb8982d9a8aceb9891a3cf2", [:rebar3], [], "hexpm"}, "moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]}, - "p1_mysql": {:hex, :p1_mysql, "1.0.7", "9fbadf8fa283ae8657faa4f6bbe13f2e3b9da0cdcfbc699cfc120d0905282056", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"}, + "p1_mysql": {:hex, :p1_mysql, "1.0.8", "34ed5fe2f0e16a6ee5805c0c6c1d30ffbc4c5c9753197cdf384ee6e82c57b506", [:rebar3], [], "hexpm"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.3", "fbd91ba86bd7f03d2a4f6e62affa86bab9930abfd6b473d61eefb148f246cd46", [:rebar3], [], "hexpm"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.6", "631004602b06ca6f55d759001f180185685c7097e583f3b0f7dd9b8e05ba5db1", [:rebar3], [], "hexpm"}, "p1_utils": {:hex, :p1_utils, "1.0.13", "176577cafb54a8c2fdc0a7fc62b9a21ab0f66588f4062792cd9e65f3e500bfdb", [:rebar3], [], "hexpm"}, - "pkix": {:git, "https://github.com/processone/pkix.git", "58856450e84f69c66b28c8807e898a30e1959680", []}, + "pkix": {:hex, :pkix, "1.0.0", "d88658eccc30227e929efa91c6ca6a4d2b4d40b4db3635ebd6ed9e246ecfcf82", [:rebar3], [], "hexpm"}, "riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"}, "riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"}, "samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]}, "sqlite3": {:hex, :sqlite3, "1.1.6", "4ea71af0b45908b5f02c9b09e4c87177039ef404f20accb35049cd8924cc417c", [:rebar3], [], "hexpm"}, "stringprep": {:hex, :stringprep, "1.0.14", "230a2d1c576bba168749d653fd6681166d02431ef07161a918444f3edb478ad0", [:rebar3], [{:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "stun": {:hex, :stun, "1.0.25", "e324c94c28d636578db79eb26979cd07140f0dabcdc0d5b197650ba0bc44a31a", [:rebar3], [{:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, - "xmpp": {:hex, :xmpp, "1.2.5", "98ae86706013e51349e962b67c30293d14672603b5c7d782b2c79b52ceaa659f", [:rebar3], [{:ezlib, "1.0.4", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.0.25", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.34", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.14", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"}, + "stun": {:hex, :stun, "1.0.26", "87b05229d0519f0db5c6b67b5c25ed3b79766beb96eba83d29bde4cae9e702e4", [:rebar3], [{:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm"}, + "xmpp": {:hex, :xmpp, "1.2.8", "c506ea4c7e4b8d042654d54b080f4b6b4135c93658d7e156968a073c5b5f99a1", [:rebar3], [{:ezlib, "1.0.4", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.0.26", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.34", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.13", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.14", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm"}, } diff --git a/rebar.config b/rebar.config index a464c9345..72908852e 100644 --- a/rebar.config +++ b/rebar.config @@ -20,7 +20,7 @@ {deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager", "3.6.7"}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.13"}}}, - {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "6493974"}}}, + {cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.17"}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.26"}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.14"}}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.34"}}}, @@ -28,9 +28,9 @@ {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.17"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.3"}}}, - {pkix, ".*", {git, "https://github.com/processone/pkix"}}, + {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.0"}}}, {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}}, - {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.8"}}}, + {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.9"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.26"}}}}, {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.27"}}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", @@ -76,6 +76,7 @@ epam, ezlib, eimp, + pkix, iconv]}}. {erl_first_files, ["src/ejabberd_sql_pt.erl", "src/ejabberd_config.erl", diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 9bffbae35..90c36a593 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -152,10 +152,10 @@ start_apps() -> crypto:start(), ejabberd:start_app(sasl), ejabberd:start_app(ssl), - ejabberd:start_app(pkix), ejabberd:start_app(p1_utils), ejabberd:start_app(fast_yaml), ejabberd:start_app(fast_tls), + ejabberd:start_app(pkix), ejabberd:start_app(xmpp), ejabberd:start_app(cache_tab), ejabberd:start_app(eimp). diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 5659ee389..bc0211548 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -41,7 +41,8 @@ get_password_s/2, get_password_with_authmodule/2, user_exists/2, user_exists_in_other_modules/3, remove_user/2, remove_user/3, plain_password_required/1, - store_type/1, entropy/1, backend_type/1, password_format/1]). + store_type/1, entropy/1, backend_type/1, password_format/1, + which_users_exists/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -411,6 +412,47 @@ user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> maybe end. +-spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}). +which_users_exists(USPairs) -> + ByServer = lists:foldl( + fun({User, Server}, Dict) -> + LServer = jid:nameprep(Server), + LUser = jid:nodeprep(User), + case gb_trees:lookup(LServer, Dict) of + none -> + gb_trees:insert(LServer, gb_sets:singleton(LUser), Dict); + {value, Set} -> + gb_trees:update(LServer, gb_sets:add(LUser, Set), Dict) + end + end, gb_trees:empty(), USPairs), + Set = lists:foldl( + fun({LServer, UsersSet}, Results) -> + UsersList = gb_sets:to_list(UsersSet), + lists:foldl( + fun(M, Results2) -> + try M:which_users_exists(LServer, UsersList) of + {error, _} -> + Results2; + Res -> + gb_sets:union( + gb_sets:from_list([{U, LServer} || U <- Res]), + Results2) + catch + _:undef -> + lists:foldl( + fun(U, R2) -> + case user_exists(U, LServer) of + true -> + gb_sets:add({U, LServer}, R2); + _ -> + R2 + end + end, Results2, UsersList) + end + end, Results, auth_modules(LServer)) + end, gb_sets:empty(), gb_trees:to_list(ByServer)), + gb_sets:to_list(Set). + -spec remove_user(binary(), binary()) -> ok. remove_user(User, Server) -> case validate_credentials(User, Server) of diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 4b774642a..cd9c02b91 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -35,7 +35,7 @@ -export([start/1, stop/1, set_password/3, try_register/3, get_users/2, count_users/2, get_password/2, remove_user/2, store_type/1, plain_password_required/1, - convert_to_scram/1, opt_type/1, export/1]). + convert_to_scram/1, opt_type/1, export/1, which_users_exists/2]). -include("scram.hrl"). -include("logger.hrl"). @@ -247,6 +247,32 @@ users_number(LServer, [{prefix, Prefix}]) users_number(LServer, []) -> users_number(LServer). +which_users_exists(LServer, LUsers) when length(LUsers) =< 100 -> + try ejabberd_sql:sql_query( + LServer, + ?SQL("select @(username)s from users where username in %(LUsers)ls")) of + {selected, Matching} -> + [U || {U} <- Matching]; + {error, _} = E -> + E + catch _:B -> + {error, B} + end; +which_users_exists(LServer, LUsers) -> + {First, Rest} = lists:split(100, LUsers), + case which_users_exists(LServer, First) of + {error, _} = E -> + E; + V -> + case which_users_exists(LServer, Rest) of + {error, _} = E2 -> + E2; + V2 -> + V ++ V2 + end + end. + + convert_to_scram(Server) -> LServer = jid:nameprep(Server), if diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index c65e71bdf..ba5b04af8 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -708,13 +708,11 @@ process_presence_out(#{lserver := LServer, jid := JID, end. -spec process_self_presence(state(), presence()) -> state(). -process_self_presence(#{ip := IP, conn := Conn, lserver := LServer, - auth_module := AuthMod, sid := SID, +process_self_presence(#{lserver := LServer, sid := SID, user := U, server := S, resource := R} = State, #presence{type = unavailable} = Pres) -> Status = xmpp:get_text(Pres#presence.status), - Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthMod}], - ejabberd_sm:unset_presence(SID, U, S, R, Status, Info), + ejabberd_sm:unset_presence(SID, U, S, R, Status), {Pres1, State1} = ejabberd_hooks:run_fold( c2s_self_presence, LServer, {Pres, State}, []), State2 = broadcast_presence_unavailable(State1, Pres1), @@ -732,13 +730,11 @@ process_self_presence(#{lserver := LServer} = State, process_self_presence(State, _Pres) -> State. --spec update_priority(state(), presence()) -> ok. -update_priority(#{ip := IP, conn := Conn, auth_module := AuthMod, - sid := SID, user := U, server := S, resource := R}, +-spec update_priority(state(), presence()) -> ok | {error, notfound}. +update_priority(#{sid := SID, user := U, server := S, resource := R}, Pres) -> Priority = get_priority_from_presence(Pres), - Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthMod}], - ejabberd_sm:set_presence(SID, U, S, R, Priority, Pres, Info). + ejabberd_sm:set_presence(SID, U, S, R, Priority, Pres). -spec broadcast_presence_unavailable(state(), presence()) -> state(). broadcast_presence_unavailable(#{jid := JID, pres_a := PresA} = State, Pres) -> diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 48fdcefbc..90bbed179 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -500,7 +500,8 @@ get_config_option_key(Name, Val) -> maps_to_lists(IMap) -> maps:fold(fun(Name, Map, Res) when Name == host_config orelse Name == append_host_config -> - [{Name, [{Host, maps_to_lists(SMap)} || {Host,SMap} <- maps:values(Map)]} | Res]; + [{Name, [{jid:nameprep(Host), maps_to_lists(SMap)} || + {Host,SMap} <- maps:values(Map)]} | Res]; (Name, Map, Res) when is_map(Map) -> [{Name, maps:values(Map)} | Res]; (Name, Val, Res) -> @@ -513,8 +514,9 @@ merge_configs(Terms, ResMap) -> New = lists:foldl(fun(SVal, OMap) -> NVal = if Name == host_config orelse Name == append_host_config -> {Host, Opts} = SVal, - {_, SubMap} = maps:get(Host, OMap, {Host, #{}}), - {Host, merge_configs(Opts, SubMap)}; + HostNP = jid:nameprep(Host), + {_, SubMap} = maps:get(HostNP, OMap, {HostNP, #{}}), + {HostNP, merge_configs(Opts, SubMap)}; true -> SVal end, diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl index 727b57f8f..769577371 100644 --- a/src/ejabberd_http.erl +++ b/src/ejabberd_http.erl @@ -69,7 +69,8 @@ default_host, custom_headers, trail = <<>>, - addr_re + addr_re, + sock_peer_name = none }). -define(XHTML_DOCTYPE, @@ -143,6 +144,7 @@ init({SockMod, Socket}, Opts) -> true -> [{[], ejabberd_xmlrpc}]; false -> [] end, + SockPeer = proplists:get_value(sock_peer_name, Opts, none), DefinedHandlers = proplists:get_value(request_handlers, Opts, []), RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++ Admin ++ Bind ++ XMLRPC, @@ -159,6 +161,7 @@ init({SockMod, Socket}, Opts) -> custom_headers = CustomHeaders, options = Opts, request_handlers = RequestHandlers, + sock_peer_name = SockPeer, addr_re = RE}, try receive_headers(State) of V -> V @@ -463,6 +466,7 @@ process_request(#state{request_method = Method, request_version = Version, sockmod = SockMod, socket = Socket, + sock_peer_name = SockPeer, options = Options, request_host = Host, request_port = Port, @@ -481,13 +485,17 @@ process_request(#state{request_method = Method, {State2, false} -> {State2, make_bad_request(State)}; {State2, {LPath, LQuery, Data}} -> - PeerName = - case SockMod of - gen_tcp -> - inet:peername(Socket); - _ -> - SockMod:peername(Socket) - end, + PeerName = case SockPeer of + none -> + case SockMod of + gen_tcp -> + inet:peername(Socket); + _ -> + SockMod:peername(Socket) + end; + {_, Peer} -> + {ok, Peer} + end, IPHere = case PeerName of {ok, V} -> V; {error, _} = E -> throw(E) diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 3a1448c0b..e8742413b 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -204,26 +204,49 @@ accept(ListenSocket, Module, Opts, Sup, Interval) -> NewInterval = check_rate_limit(Interval), case gen_tcp:accept(ListenSocket) of {ok, Socket} -> - case {inet:sockname(Socket), inet:peername(Socket)} of - {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} -> - Receiver = case start_connection(Module, Socket, Opts, Sup) of - {ok, RecvPid} -> - RecvPid; - _ -> - gen_tcp:close(Socket), - none - end, - ?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p", - [Receiver, - ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), - PPort, inet_parse:ntoa(Addr), Port]); + case proplists:get_value(use_proxy_protocol, Opts, false) of + true -> + case proxy_protocol:decode(gen_tcp, Socket, 10000) of + {error, Err} -> + ?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s", + [ListenSocket, inet:format_error(Err)]), + gen_tcp:close(Socket); + {{Addr, Port}, {PAddr, PPort}} = SP -> + Opts2 = [{sock_peer_name, SP} | Opts], + Receiver = case start_connection(Module, Socket, Opts2, Sup) of + {ok, RecvPid} -> + RecvPid; + _ -> + gen_tcp:close(Socket), + none + end, + ?INFO_MSG("(~p) Accepted proxied connection ~s:~p -> ~s:~p", + [Receiver, + ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), + PPort, inet_parse:ntoa(Addr), Port]) + end; _ -> - gen_tcp:close(Socket) + case {inet:sockname(Socket), inet:peername(Socket)} of + {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} -> + Receiver = case start_connection(Module, Socket, Opts, Sup) of + {ok, RecvPid} -> + RecvPid; + _ -> + gen_tcp:close(Socket), + none + end, + ?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p", + [Receiver, + ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)), + PPort, inet_parse:ntoa(Addr), Port]); + _ -> + gen_tcp:close(Socket) + end end, accept(ListenSocket, Module, Opts, Sup, NewInterval); {error, Reason} -> ?ERROR_MSG("(~w) Failed TCP accept: ~s", - [ListenSocket, inet:format_error(Reason)]), + [ListenSocket, inet:format_error(Reason)]), accept(ListenSocket, Module, Opts, Sup, NewInterval) end. @@ -665,7 +688,9 @@ listen_opt_type(max_fsm_queue) -> listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1; listen_opt_type(access) -> - fun acl:access_rules_validator/1. + fun acl:access_rules_validator/1; +listen_opt_type(use_proxy_protocol) -> + fun(B) when is_boolean(B) -> B end. listen_options() -> [module, port, @@ -675,6 +700,7 @@ listen_options() -> {inet6, false}, {accept_interval, 0}, {backlog, 5}, + {use_proxy_protocol, false}, {supervisor, true}]. opt_type(listen) -> fun validate_cfg/1; diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 119a70939..4643f46b7 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -48,8 +48,8 @@ disconnect_removed_user/2, get_user_resources/2, get_user_present_resources/2, - set_presence/7, - unset_presence/6, + set_presence/6, + unset_presence/5, close_session_unset_presence/5, dirty_get_sessions_list/0, dirty_get_my_sessions_list/0, @@ -316,26 +316,48 @@ del_user_info(User, Server, Resource, Key) -> end. -spec set_presence(sid(), binary(), binary(), binary(), - prio(), presence(), info()) -> ok. + prio(), presence()) -> ok | {error, notfound}. -set_presence(SID, User, Server, Resource, Priority, - Presence, Info) -> - set_session(SID, User, Server, Resource, Priority, - Info), - ejabberd_hooks:run(set_presence_hook, - jid:nameprep(Server), - [User, Server, Resource, Presence]). +set_presence(SID, User, Server, Resource, Priority, Presence) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + case lists:keyfind(SID, 1, Ss) of + #session{info = Info} -> + set_session(SID, User, Server, Resource, Priority, Info), + ejabberd_hooks:run(set_presence_hook, + LServer, + [User, Server, Resource, Presence]); + false -> + {error, notfound} + end + end. -spec unset_presence(sid(), binary(), binary(), - binary(), binary(), info()) -> ok. + binary(), binary()) -> ok | {error, notfound}. -unset_presence(SID, User, Server, Resource, Status, - Info) -> - set_session(SID, User, Server, Resource, undefined, - Info), - ejabberd_hooks:run(unset_presence_hook, - jid:nameprep(Server), - [User, Server, Resource, Status]). +unset_presence(SID, User, Server, Resource, Status) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + case lists:keyfind(SID, 1, Ss) of + #session{info = Info} -> + set_session(SID, User, Server, Resource, undefined, Info), + ejabberd_hooks:run(unset_presence_hook, + LServer, + [User, Server, Resource, Status]); + false -> + {error, notfound} + end + end. -spec close_session_unset_presence(sid(), binary(), binary(), binary(), binary()) -> ok. diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 3d3741548..9e088f211 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -37,12 +37,12 @@ sql_query_t/1, sql_transaction/2, sql_bloc/2, - abort/1, - restart/1, - use_new_schema/0, - sql_query_to_iolist/1, + abort/1, + restart/1, + use_new_schema/0, + sql_query_to_iolist/1, escape/1, - standard_escape/1, + standard_escape/1, escape_like/1, escape_like_arg/1, escape_like_arg_circumflex/1, @@ -55,7 +55,8 @@ freetds_config/0, odbcinst_config/0, init_mssql/1, - keep_alive/2]). + keep_alive/2, + to_list/2]). %% gen_fsm callbacks -export([init/1, handle_event/3, handle_sync_event/4, @@ -258,6 +259,10 @@ to_bool(true) -> true; to_bool(1) -> true; to_bool(_) -> false. +to_list(EscapeFun, Val) -> + Escaped = lists:join(<<",">>, lists:map(EscapeFun, Val)), + [<<"(">>, Escaped, <<")">>]. + encode_term(Term) -> escape(list_to_binary( erl_prettypr:format(erl_syntax:abstract(Term), diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index eb7905bf0..1f6134d07 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -306,6 +306,20 @@ parse1([$%, $( | S], Acc, State) -> false -> append_string("0=0", State3) end; + {list, InternalType} -> + Convert = erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(to_list), + [erl_syntax:record_access( + erl_syntax:variable(?ESCAPE_VAR), + erl_syntax:atom(?ESCAPE_RECORD), + erl_syntax:atom(InternalType)), + erl_syntax:variable(Name)]), + State2#state{'query' = [{var, Var} | State2#state.'query'], + args = [Convert | State2#state.args], + params = [Var | State2#state.params], + param_pos = State2#state.param_pos + 1, + used_vars = [Name | State2#state.used_vars]}; _ -> Convert = erl_syntax:application( @@ -335,6 +349,19 @@ parse_name(S, IsArg, State) -> parse_name([], _Acc, _Depth, _IsArg, State) -> throw({error, State#state.loc, "expected ')', found end of string"}); +parse_name([$), $l, T | S], Acc, 0, true, State) -> + Type = case T of + $d -> {list, integer}; + $s -> {list, string}; + $b -> {list, boolean}; + _ -> + throw({error, State#state.loc, + ["unknown type specifier 'l", T, "'"]}) + end, + {lists:reverse(Acc), Type, S, State}; +parse_name([$), $l, T | _], _Acc, 0, false, State) -> + throw({error, State#state.loc, + ["list type 'l", T, "' is not allowed for outputs"]}); parse_name([$), T | S], Acc, 0, IsArg, State) -> Type = case T of diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 4c05f84c3..e1f82e872 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -38,6 +38,7 @@ iq_handler/1, disco_features/5, is_carbon_copy/1, mod_opt_type/1, depends/2, mod_options/1]). +-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]). %% For debugging purposes -export([list/2]). @@ -45,6 +46,7 @@ -include("xmpp.hrl"). -type direction() :: sent | received. +-type c2s_state() :: ejabberd_c2s:state(). -spec is_carbon_copy(stanza()) -> boolean(). is_carbon_copy(#message{meta = #{carbon_copy := true}}) -> @@ -57,6 +59,9 @@ start(Host, _Opts) -> %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) ejabberd_hooks:add(user_send_packet,Host, ?MODULE, user_send_packet, 89), ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), + ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), + ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), + ejabberd_hooks:add(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler). stop(Host) -> @@ -64,7 +69,10 @@ stop(Host) -> ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), %% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90) ejabberd_hooks:delete(user_send_packet,Host, ?MODULE, user_send_packet, 89), - ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89). + ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89), + ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50), + ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50), + ejabberd_hooks:delete(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50). reload(_Host, _NewOpts, _OldOpts) -> ok. @@ -123,6 +131,29 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) -> Pkt -> {Pkt, C2SState} end. +-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state(). +c2s_copy_session(State, #{user := U, server := S, resource := R}) -> + case ejabberd_sm:get_user_info(U, S, R) of + offline -> State; + Info -> + case lists:keyfind(carboncopy, 1, Info) of + {_, CC} -> State#{carboncopy => CC}; + false -> State + end + end. + +-spec c2s_session_resumed(c2s_state()) -> c2s_state(). +c2s_session_resumed(#{user := U, server := S, resource := R, + carboncopy := CC} = State) -> + ejabberd_sm:set_user_info(U, S, R, carboncopy, CC), + maps:remove(carboncopy, State); +c2s_session_resumed(State) -> + State. + +-spec c2s_session_opened(c2s_state()) -> c2s_state(). +c2s_session_opened(State) -> + maps:remove(carboncopy, State). + % Modified from original version: % - registered to the user_send_packet hook, to be called only once even for multicast % - do not support "private" message mode, and do not modify the original packet in any way diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 4d4a40f79..3fb0d5981 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -325,15 +325,20 @@ handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) -> format_command_result(Call, Auth, Res, Version) end. -get_elem_delete(A, L) -> +get_elem_delete(A, L, F) -> case proplists:get_all_values(A, L) of [Value] -> {Value, proplists:delete(A, L)}; [_, _ | _] -> %% Crash reporting the error exit({duplicated_attribute, A, L}); [] -> - %% Report the error and then force a crash - exit({attribute_not_found, A, L}) + case F of + {list, _} -> + {[], L}; + _ -> + %% Report the error and then force a crash + exit({attribute_not_found, A, L}) + end end. format_args(Args, ArgsFormat) -> @@ -342,7 +347,7 @@ format_args(Args, ArgsFormat) -> {Args1, Res}) -> {ArgValue, Args2} = get_elem_delete(ArgName, - Args1), + Args1, ArgFormat), Formatted = format_arg(ArgValue, ArgFormat), {Args2, Res ++ [Formatted]} @@ -471,6 +476,9 @@ format_result(Code, {Name, restuple}) -> format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) -> {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; +format_result(Els, {Name, {list, {_, {tuple, [{name, string}, {value, _}]}} = Fmt}}) -> + {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}}; + format_result(Els, {Name, {list, Def}}) -> {misc:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]}; @@ -479,6 +487,11 @@ format_result(Tuple, {_Name, {tuple, [{_, atom}, ValFmt]}}) -> {_, Val2} = format_result(Val, ValFmt), {misc:atom_to_binary(Name2), Val2}; +format_result(Tuple, {_Name, {tuple, [{name, string}, {value, _} = ValFmt]}}) -> + {Name2, Val} = Tuple, + {_, Val2} = format_result(Val, ValFmt), + {iolist_to_binary(Name2), Val2}; + format_result(Tuple, {Name, {tuple, Def}}) -> Els = lists:zip(tuple_to_list(Tuple), Def), {misc:atom_to_binary(Name), {[format_result(El, ElDef) || {El, ElDef} <- Els]}}; diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl index c114b1dce..f2cc682d6 100644 --- a/src/mod_offline_sql.erl +++ b/src/mod_offline_sql.erl @@ -90,8 +90,17 @@ remove_expired_messages(_LServer) -> remove_old_messages(Days, LServer) -> case ejabberd_sql:sql_query( LServer, - ?SQL("DELETE FROM spool" - " WHERE created_at < NOW() - INTERVAL %(Days)d DAY")) of + fun(pgsql, _) -> + ejabberd_sql:sql_query_t( + ?SQL("DELETE FROM spool" + " WHERE created_at <" + " NOW() - INTERVAL '%(Days)d DAY'")); + (_, _) -> + ejabberd_sql:sql_query_t( + ?SQL("DELETE FROM spool" + " WHERE created_at < NOW() - INTERVAL %(Days)d DAY")) + end) + of {updated, N} -> ?INFO_MSG("~p message(s) deleted from offline spool", [N]); _Error -> diff --git a/src/mod_private.erl b/src/mod_private.erl index db82e1295..b32fff98e 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -37,9 +37,12 @@ import/5, import_start/2, mod_opt_type/1, set_data/2, mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6]). +-export([get_commands_spec/0, bookmarks_to_pep/2]). + -include("logger.hrl"). -include("xmpp.hrl"). -include("mod_private.hrl"). +-include("ejabberd_commands.hrl"). -define(PRIVATE_CACHE, private_cache). @@ -61,13 +64,20 @@ start(Host, Opts) -> ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq). + gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq), + ejabberd_commands:register_commands(get_commands_spec()). stop(Host) -> ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50), ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE). + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE), + case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of + false -> + ejabberd_commands:unregister_commands(get_commands_spec()); + true -> + ok + end. reload(Host, NewOpts, OldOpts) -> NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE), @@ -264,6 +274,50 @@ pubsub_publish_item(LServer, ?NS_STORAGE_BOOKMARKS, pubsub_publish_item(_, _, _, _, _, _) -> ok. +%%%=================================================================== +%%% Commands +%%%=================================================================== +-spec get_commands_spec() -> [ejabberd_commands()]. +get_commands_spec() -> + [#ejabberd_commands{name = bookmarks_to_pep, tags = [private], + desc = "Export private XML storage bookmarks to PEP", + module = ?MODULE, function = bookmarks_to_pep, + args = [{user, binary}, {server, binary}], + args_desc = ["Username", "Server"], + args_example = [<<"bob">>, <<"example.com">>], + result = {res, restuple}, + result_desc = "Result tuple", + result_example = {ok, <<"Bookmarks exported">>}}]. + +-spec bookmarks_to_pep(binary(), binary()) + -> {ok, binary()} | {error, binary()}. +bookmarks_to_pep(User, Server) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Res = case use_cache(Mod, LServer) of + true -> + ets_cache:lookup( + ?PRIVATE_CACHE, {LUser, LServer, ?NS_STORAGE_BOOKMARKS}, + fun() -> + Mod:get_data(LUser, LServer, ?NS_STORAGE_BOOKMARKS) + end); + false -> + Mod:get_data(LUser, LServer, ?NS_STORAGE_BOOKMARKS) + end, + case Res of + {ok, El} -> + Data = [{?NS_STORAGE_BOOKMARKS, El}], + case publish_data(jid:make(User, Server), Data) of + ok -> + {ok, <<"Bookmarks exported to PEP node">>}; + {error, Err} -> + {error, xmpp:format_stanza_error(Err)} + end; + _ -> + {error, <<"Cannot retrieve bookmarks from private XML storage">>} + end. + %%%=================================================================== %%% Cache %%%=================================================================== diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 3367c192e..72edea9ce 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -2990,6 +2990,7 @@ send_last_pep(From, To) -> Host = host(ServerHost), Publisher = jid:tolower(From), Owner = jid:remove_resource(Publisher), + RecipientIsOwner = jid:remove_resource(jid:tolower(To)) == Owner, lists:foreach( fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) -> case match_option(Options, send_last_published_item, on_sub_and_presence) of @@ -2998,8 +2999,11 @@ send_last_pep(From, To) -> Subscribed = case get_option(Options, access_model) of open -> true; presence -> true; - whitelist -> false; % subscribers are added manually - authorize -> false; % likewise + %% TODO: Fix the 'whitelist'/'authorize' + %% cases. Currently, only node owners + %% receive last PEP notifications. + whitelist -> RecipientIsOwner; + authorize -> RecipientIsOwner; roster -> Grps = get_option(Options, roster_groups_allowed, []), {OU, OS, _} = Owner, diff --git a/src/proxy_protocol.erl b/src/proxy_protocol.erl new file mode 100644 index 000000000..2103a4004 --- /dev/null +++ b/src/proxy_protocol.erl @@ -0,0 +1,184 @@ +%%%---------------------------------------------------------------------- +%%% File : ejabberd_http.erl +%%% Author : Paweł Chmielowski +%%% Purpose : +%%% Created : 27 Nov 2018 by Paweł Chmielowski +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2018 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(proxy_protocol). +-author("pawel@process-one.net"). + +%% API +-export([decode/3]). + +decode(SockMod, Socket, Timeout) -> + V = SockMod:recv(Socket, 6, Timeout), + case V of + {ok, <<"PROXY ">>} -> + decode_v1(SockMod, Socket, Timeout); + {ok, <<16#0d, 16#0a, 16#0d, 16#0a, 16#00, 16#0d>>} -> + decode_v2(SockMod, Socket, Timeout); + _ -> + {error, eproto} + end. + +decode_v1(SockMod, Socket, Timeout) -> + case read_until_rn(SockMod, Socket, <<>>, false, Timeout) of + {error, _} = Err -> + Err; + Val -> + case binary:split(Val, <<" ">>, [global]) of + [<<"TCP4">>, SAddr, DAddr, SPort, DPort] -> + try {inet_parse:ipv4strict_address(binary_to_list(SAddr)), + inet_parse:ipv4strict_address(binary_to_list(DAddr)), + binary_to_integer(SPort), + binary_to_integer(DPort)} + of + {{ok, DA}, {ok, SA}, DP, SP} -> + {{SA, SP}, {DA, DP}}; + _ -> + {error, eproto} + catch + error:badarg -> + {error, eproto} + end; + [<<"TCP6">>, SAddr, DAddr, SPort, DPort] -> + try {inet_parse:ipv6strict_address(binary_to_list(SAddr)), + inet_parse:ipv6strict_address(binary_to_list(DAddr)), + binary_to_integer(SPort), + binary_to_integer(DPort)} + of + {{ok, DA}, {ok, SA}, DP, SP} -> + {{SA, SP}, {DA, DP}}; + _ -> + {error, eproto} + catch + error:badarg -> + {error, eproto} + end; + [<<"UNKNOWN">> | _] -> + {undefined, undefined} + end + end. + +decode_v2(SockMod, Socket, Timeout) -> + case SockMod:recv(Socket, 10, Timeout) of + {error, _} = Err -> + Err; + {ok, <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a, + 2:4, Command:4, Transport:8, AddrLen:16/big-unsigned-integer>>} -> + case SockMod:recv(Socket, AddrLen, Timeout) of + {error, _} = Err -> + Err; + {ok, Data} -> + case Command of + 0 -> + case {inet:sockname(Socket), inet:peername(Socket)} of + {{ok, SA}, {ok, DA}} -> + {SA, DA}; + {{error, _} = E, _} -> + E; + {_, {error, _} = E} -> + E + end; + 1 -> + case Transport of + % UNSPEC or UNIX + V when V == 0; V == 16#31; V == 16#32 -> + {{unknown, unknown}, {unknown, unknown}}; + % IPV4 over TCP or UDP + V when V == 16#11; V == 16#12 -> + case Data of + <> -> + {{{S1, S2, S3, S4}, SP}, + {{D1, D2, D3, D4}, DP}}; + _ -> + {error, eproto} + end; + % IPV6 over TCP or UDP + V when V == 16#21; V == 16#22 -> + case Data of + <> -> + {{{S1, S2, S3, S4, S5, S6, S7, S8}, SP}, + {{D1, D2, D3, D4, D5, D6, D7, D8}, DP}}; + _ -> + {error, eproto} + end + end; + _ -> + {error, eproto} + end + end; + <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a, _/binary>> -> + {error, eproto}; + _ -> + {error, eproto} + end. + +read_until_rn(_SockMod, _Socket, Data, _, _) when size(Data) > 107 -> + {error, eproto}; +read_until_rn(SockMod, Socket, Data, true, Timeout) -> + case SockMod:recv(Socket, 1, Timeout) of + {ok, <<"\n">>} -> + Data; + {ok, <<"\r">>} -> + read_until_rn(SockMod, Socket, <>, + true, Timeout); + {ok, Other} -> + read_until_rn(SockMod, Socket, <>, + false, Timeout); + {error, _} = Err -> + Err + end; +read_until_rn(SockMod, Socket, Data, false, Timeout) -> + case SockMod:recv(Socket, 2, Timeout) of + {ok, <<"\r\n">>} -> + Data; + {ok, <>} -> + read_until_rn(SockMod, Socket, <>, + true, Timeout); + {ok, Other} -> + read_until_rn(SockMod, Socket, <>, + false, Timeout); + {error, _} = Err -> + Err + end.